<?php

declare(strict_types=1);

namespace ROITheme\Shared\Infrastructure\Persistence\WordPress;

use ROITheme\Shared\Domain\Contracts\ComponentSettingsRepositoryInterface;

/**
 * Implementaci�n de repositorio de configuraciones usando WordPress/MySQL
 *
 * Infrastructure Layer - WordPress specific
 * Trabaja con la tabla wp_roi_theme_component_settings
 *
 * @package ROITheme\Shared\Infrastructure\Persistence\WordPress
 */
final class WordPressComponentSettingsRepository implements ComponentSettingsRepositoryInterface
{
    private string $tableName;

    public function __construct(
        private \wpdb $wpdb
    ) {
        $this->tableName = $this->wpdb->prefix . 'roi_theme_component_settings';
    }

    /**
     * {@inheritDoc}
     */
    public function getComponentSettings(string $componentName): array
    {
        $sql = $this->wpdb->prepare(
            "SELECT group_name, attribute_name, attribute_value
             FROM {$this->tableName}
             WHERE component_name = %s
             ORDER BY group_name, attribute_name",
            $componentName
        );

        $rows = $this->wpdb->get_results($sql, ARRAY_A);

        if (empty($rows)) {
            return [];
        }

        // Agrupar por grupo
        $settings = [];
        foreach ($rows as $row) {
            $groupName = $row['group_name'];
            $attributeName = $row['attribute_name'];
            $value = $row['attribute_value'];

            // Convertir valores seg�n tipo
            $value = $this->unserializeValue($value);

            if (!isset($settings[$groupName])) {
                $settings[$groupName] = [];
            }

            $settings[$groupName][$attributeName] = $value;
        }

        return $settings;
    }

    /**
     * {@inheritDoc}
     */
    public function saveComponentSettings(string $componentName, array $settings): int
    {
        $updated = 0;

        foreach ($settings as $groupName => $attributes) {
            foreach ($attributes as $attributeName => $value) {
                if ($this->saveFieldValue($componentName, $groupName, $attributeName, $value)) {
                    $updated++;
                }
            }
        }

        return $updated;
    }

    /**
     * {@inheritDoc}
     */
    public function getFieldValue(string $componentName, string $groupName, string $attributeName): mixed
    {
        $sql = $this->wpdb->prepare(
            "SELECT attribute_value
             FROM {$this->tableName}
             WHERE component_name = %s
               AND group_name = %s
               AND attribute_name = %s
             LIMIT 1",
            $componentName,
            $groupName,
            $attributeName
        );

        $value = $this->wpdb->get_var($sql);

        if ($value === null) {
            return null;
        }

        return $this->unserializeValue($value);
    }

    /**
     * {@inheritDoc}
     *
     * Implementa UPSERT: si el registro no existe, lo crea; si existe, lo actualiza.
     * Esto es necesario para grupos especiales como _page_visibility y _exclusions
     * que no vienen del schema JSON.
     */
    public function saveFieldValue(string $componentName, string $groupName, string $attributeName, mixed $value): bool
    {
        // Serializar valor
        $serializedValue = $this->serializeValue($value);

        // Verificar si el registro existe
        $exists = $this->fieldExists($componentName, $groupName, $attributeName);

        if ($exists) {
            // UPDATE
            $result = $this->wpdb->update(
                $this->tableName,
                ['attribute_value' => $serializedValue],
                [
                    'component_name' => $componentName,
                    'group_name' => $groupName,
                    'attribute_name' => $attributeName
                ],
                ['%s'],
                ['%s', '%s', '%s']
            );
        } else {
            // INSERT - crear nuevo registro
            $result = $this->wpdb->insert(
                $this->tableName,
                [
                    'component_name' => $componentName,
                    'group_name' => $groupName,
                    'attribute_name' => $attributeName,
                    'attribute_value' => $serializedValue
                ],
                ['%s', '%s', '%s', '%s']
            );
        }

        return $result !== false;
    }

    /**
     * Verifica si un campo existe en la BD
     */
    private function fieldExists(string $componentName, string $groupName, string $attributeName): bool
    {
        $sql = $this->wpdb->prepare(
            "SELECT COUNT(*) FROM {$this->tableName}
             WHERE component_name = %s
               AND group_name = %s
               AND attribute_name = %s",
            $componentName,
            $groupName,
            $attributeName
        );

        return (int) $this->wpdb->get_var($sql) > 0;
    }

    /**
     * {@inheritDoc}
     */
    public function resetToDefaults(string $componentName, string $schemaPath): int
    {
        if (!file_exists($schemaPath)) {
            return 0;
        }

        $schema = json_decode(file_get_contents($schemaPath), true);

        if (!$schema || !isset($schema['groups'])) {
            return 0;
        }

        $updated = 0;

        // Iterar grupos y campos del schema
        foreach ($schema['groups'] as $groupName => $group) {
            foreach ($group['fields'] as $fieldName => $field) {
                $defaultValue = $field['default'] ?? '';

                if ($this->saveFieldValue($componentName, $groupName, $fieldName, $defaultValue)) {
                    $updated++;
                }
            }
        }

        return $updated;
    }

    /**
     * Serializa un valor para guardarlo en la BD
     *
     * @param mixed $value
     * @return string
     */
    private function serializeValue(mixed $value): string
    {
        if (is_bool($value)) {
            return $value ? '1' : '0';
        }

        if (is_array($value)) {
            return json_encode($value);
        }

        return (string) $value;
    }

    /**
     * Deserializa un valor desde la BD
     *
     * @param string $value
     * @return mixed
     */
    private function unserializeValue(string $value): mixed
    {
        // Intentar decodificar JSON
        if (str_starts_with($value, '{') || str_starts_with($value, '[')) {
            $decoded = json_decode($value, true);
            if (json_last_error() === JSON_ERROR_NONE) {
                return $decoded;
            }
        }

        // Convertir booleanos
        if ($value === '1' || $value === '0') {
            return $value === '1';
        }

        // Devolver como est�
        return $value;
    }
}
