<?php
declare(strict_types=1);

namespace ROITheme\Shared\Infrastructure\Validators;

/**
 * Validador de Fase 02: Sincronización JSON→BD
 *
 * Valida que:
 * - Schema JSON existe y es válido
 * - Tabla de BD existe
 * - Todos los campos del JSON están sincronizados en BD
 * - No hay campos huérfanos en BD
 * - is_editable coincide entre JSON y BD
 * - No hay duplicados
 */
final class Phase02Validator implements PhaseValidatorInterface
{
    private const TABLE_NAME = 'wp_roi_theme_component_settings';

    public function validate(string $componentName, string $themePath): ValidationResult
    {
        global $wpdb;

        $result = new ValidationResult();

        $result->addInfo("Validando sincronización JSON→BD para: {$componentName}");

        // 1. Verificar que schema JSON existe
        $schemaPath = $themePath . '/Schemas/' . $componentName . '.json';

        if (!file_exists($schemaPath)) {
            $result->addError("Schema JSON no encontrado: {$schemaPath}");
            $result->addInfo("Ejecutar primero: wp roi-theme sync-component {$componentName}");
            return $result;
        }

        // 2. Parsear JSON
        $jsonContent = file_get_contents($schemaPath);
        $schema = json_decode($jsonContent, true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            $result->addError("JSON inválido: " . json_last_error_msg());
            return $result;
        }

        // 3. Verificar que tabla existe
        $tableName = $wpdb->prefix . 'roi_theme_component_settings';
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $tableExists = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = %s",
            $tableName
        ));

        if ($tableExists == 0) {
            $result->addError("Tabla '{$tableName}' no existe en la base de datos");
            $result->addInfo("La tabla debería crearse automáticamente en functions.php");
            return $result;
        }

        // 4. Obtener todos los campos del JSON
        $jsonFields = $this->extractFieldsFromSchema($schema);
        $totalJsonFields = count($jsonFields);

        // 5. Obtener todos los registros de BD para este componente
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $dbRecords = $wpdb->get_results($wpdb->prepare(
            "SELECT component_name, group_name, attribute_name, is_editable FROM {$tableName} WHERE component_name = %s",
            $componentName
        ), ARRAY_A);

        $totalDbRecords = count($dbRecords);

        // 6. Validar sincronización
        $this->validateSync($componentName, $jsonFields, $dbRecords, $result);

        // 7. Validar no hay duplicados
        $this->validateNoDuplicates($componentName, $tableName, $wpdb, $result);

        // Estadísticas
        $result->setStat('Schema JSON', "schemas/{$componentName}.json");
        $result->setStat('Campos en JSON', $totalJsonFields);
        $result->setStat('Registros en BD', $totalDbRecords);
        $result->setStat('Tabla BD', $tableName);

        return $result;
    }

    /**
     * Extrae todos los campos del schema JSON
     *
     * @param array $schema
     * @return array Array de arrays con ['group' => '', 'attribute' => '', 'editable' => bool]
     */
    private function extractFieldsFromSchema(array $schema): array
    {
        $fields = [];

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

        foreach ($schema['groups'] as $groupName => $group) {
            if (!isset($group['fields'])) {
                continue;
            }

            foreach ($group['fields'] as $attributeName => $field) {
                $fields[] = [
                    'group' => $groupName,
                    'attribute' => $attributeName,
                    'editable' => $field['editable'] ?? false,
                ];
            }
        }

        return $fields;
    }

    private function validateSync(string $componentName, array $jsonFields, array $dbRecords, ValidationResult $result): void
    {
        // Crear índice de registros de BD para búsqueda rápida
        $dbIndex = [];
        foreach ($dbRecords as $record) {
            $key = $record['group_name'] . '.' . $record['attribute_name'];
            $dbIndex[$key] = $record;
        }

        // Validar que cada campo del JSON está en BD
        $missingInDb = [];
        $editableMismatch = [];

        foreach ($jsonFields as $field) {
            $key = $field['group'] . '.' . $field['attribute'];

            if (!isset($dbIndex[$key])) {
                $missingInDb[] = $key;
            } else {
                // Validar is_editable coincide
                $dbEditable = (bool) $dbIndex[$key]['is_editable'];
                $jsonEditable = $field['editable'];

                if ($dbEditable !== $jsonEditable) {
                    $editableMismatch[] = "{$key} (JSON: " . ($jsonEditable ? 'true' : 'false') .
                                           ", BD: " . ($dbEditable ? 'true' : 'false') . ")";
                }

                // Remover de índice para detectar huérfanos
                unset($dbIndex[$key]);
            }
        }

        // Campos faltantes en BD
        if (!empty($missingInDb)) {
            foreach ($missingInDb as $field) {
                $result->addError("Campo '{$field}' existe en JSON pero NO en BD");
            }
            $result->addInfo("Ejecutar: wp roi-theme sync-component {$componentName}");
        }

        // Campos huérfanos en BD (no están en JSON)
        if (!empty($dbIndex)) {
            foreach ($dbIndex as $key => $record) {
                $result->addWarning("Campo '{$key}' existe en BD pero NO en JSON (campo huérfano)");
            }
        }

        // is_editable no coincide
        if (!empty($editableMismatch)) {
            foreach ($editableMismatch as $mismatch) {
                $result->addError("Campo {$mismatch} tiene is_editable diferente entre JSON y BD");
            }
            $result->addInfo("Ejecutar: wp roi-theme sync-component {$componentName}");
        }

        // Si todo está sincronizado
        if (empty($missingInDb) && empty($editableMismatch) && empty($dbIndex)) {
            $result->addInfo("✓ Todos los campos están sincronizados correctamente");
        }
    }

    private function validateNoDuplicates(string $componentName, string $tableName, $wpdb, ValidationResult $result): void
    {
        // Buscar duplicados
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $duplicates = $wpdb->get_results($wpdb->prepare(
            "SELECT component_name, group_name, attribute_name, COUNT(*) as count
             FROM {$tableName}
             WHERE component_name = %s
             GROUP BY component_name, group_name, attribute_name
             HAVING count > 1",
            $componentName
        ), ARRAY_A);

        if (!empty($duplicates)) {
            foreach ($duplicates as $dup) {
                $result->addError(
                    "Duplicado en BD: {$dup['group_name']}.{$dup['attribute_name']} " .
                    "({$dup['count']} registros)"
                );
            }
            $result->addInfo("El constraint UNIQUE debería prevenir duplicados. Revisar integridad de BD.");
        }
    }

    public function getPhaseNumber(): int|string
    {
        return 2;
    }

    public function getPhaseDescription(): string
    {
        return 'JSON→DB Sync';
    }
}
