<?php
declare(strict_types=1);

namespace ROITheme\Shared\Infrastructure\Validators;

/**
 * Validador de Fase 04: FormBuilders (UI Admin)
 *
 * Valida que:
 * - FormBuilder existe en ubicación correcta (admin/)
 * - Namespace y clase correctos
 * - Inyecta AdminDashboardRenderer
 * - Tiene método buildForm()
 * - Es modular (métodos privados build*)
 * - Usa AdminDashboardRenderer correctamente
 * - Usa Bootstrap 5
 * - Escaping correcto
 * - NO accede directamente a BD
 */
final class Phase04Validator implements PhaseValidatorInterface
{
    public function validate(string $componentName, string $themePath): ValidationResult
    {
        $result = new ValidationResult();

        $result->addInfo("Validando FormBuilder para: {$componentName}");

        // Buscar FormBuilder en Admin/
        $formBuilderPath = $this->findFormBuilderPath($componentName, $themePath);

        if ($formBuilderPath === null) {
            $result->addError("FormBuilder no encontrado en Admin/");
            $pascalCaseName = str_replace('-', '', ucwords($componentName, '-'));
            $result->addInfo("Ubicación esperada: Admin/{$pascalCaseName}/Infrastructure/Ui/*FormBuilder.php");
            return $result;
        }

        $result->addInfo("Archivo encontrado: {$formBuilderPath}");

        // Leer contenido
        $content = file_get_contents($formBuilderPath);

        // Validaciones del FormBuilder
        $this->validateNamespaceAndClass($content, $componentName, $result);
        $this->validateRendererInjection($content, $result);
        $this->validateBuildFormMethod($content, $result);
        $this->validateModularity($content, $result);
        $this->validateRendererUsage($content, $result);
        $this->validateBootstrapCompliance($content, $result);
        $this->validateEscaping($content, $result);
        $this->validateNoDirectDatabaseAccess($content, $result);

        // Validaciones de integración con Admin Panel (CRÍTICAS)
        $this->validateComponentRegistration($componentName, $themePath, $result);
        $this->validateAjaxFieldMapping($componentName, $themePath, $content, $result);
        $this->validateResetButton($componentName, $content, $result);

        // Estadísticas
        $fileSize = filesize($formBuilderPath);
        $lineCount = substr_count($content, "\n") + 1;
        $buildMethodsCount = $this->countBuildMethods($content);

        $result->setStat('Archivo', basename($formBuilderPath));
        $result->setStat('Líneas', $lineCount);
        $result->setStat('Métodos build*', $buildMethodsCount);
        $result->setStat('Tamaño', $fileSize . ' bytes');

        if ($lineCount > 500) {
            $result->addWarning("Archivo excede 500 líneas ({$lineCount}) - considerar dividir en múltiples FormBuilders.");
        } elseif ($lineCount > 300) {
            $result->addInfo("ℹ️  Archivo tiene {$lineCount} líneas (recomendado: <300, aceptable: <500)");
        }

        if ($buildMethodsCount < 2) {
            $result->addWarning("Solo {$buildMethodsCount} método(s) build*. FormBuilders modulares deben tener al menos 2-3 métodos privados.");
        }

        return $result;
    }

    private function findFormBuilderPath(string $componentName, string $themePath): ?string
    {
        // Convertir kebab-case a PascalCase (para carpetas y archivos)
        $pascalCaseName = str_replace('-', '', ucwords($componentName, '-'));

        // Buscar en Admin/ con PascalCase (PSR-4 standard)
        $adminPath = $themePath . '/Admin/' . $pascalCaseName . '/Infrastructure/Ui/' . $pascalCaseName . 'FormBuilder.php';
        if (file_exists($adminPath)) {
            return $adminPath;
        }

        return null;
    }

    private function validateNamespaceAndClass(string $content, string $componentName, ValidationResult $result): void
    {
        $pascalCaseName = str_replace('-', '', ucwords($componentName, '-'));

        // Validar namespace (Ui en PascalCase, primera letra mayúscula)
        if (!preg_match('/namespace\s+ROITheme\\\\Admin\\\\' . preg_quote($pascalCaseName, '/') . '\\\\Infrastructure\\\\Ui;/', $content)) {
            $result->addError("Namespace incorrecto. Debe ser: ROITheme\\Admin\\{$pascalCaseName}\\Infrastructure\\Ui");
        }

        // Validar clase final
        if (!preg_match('/final\s+class\s+' . preg_quote($pascalCaseName, '/') . 'FormBuilder/', $content)) {
            $result->addError("Clase debe ser: final class {$pascalCaseName}FormBuilder");
        }
    }

    private function validateRendererInjection(string $content, ValidationResult $result): void
    {
        // Verificar que constructor recibe AdminDashboardRenderer
        if (!preg_match('/public\s+function\s+__construct\([^)]*AdminDashboardRenderer\s+\$renderer/s', $content)) {
            $result->addError("Constructor NO inyecta AdminDashboardRenderer");
        }

        // Verificar propiedad privada
        if (!preg_match('/private\s+AdminDashboardRenderer\s+\$renderer/', $content)) {
            $result->addError("Falta propiedad: private AdminDashboardRenderer \$renderer");
        }
    }

    private function validateBuildFormMethod(string $content, ValidationResult $result): void
    {
        // Verificar método buildForm existe
        if (!preg_match('/public\s+function\s+buildForm\s*\(\s*string\s+\$componentId\s*\)\s*:\s*string/', $content)) {
            $result->addError("Falta método: public function buildForm(string \$componentId): string");
        }
    }

    private function validateModularity(string $content, ValidationResult $result): void
    {
        // Contar métodos privados build*
        $buildMethodsCount = $this->countBuildMethods($content);

        if ($buildMethodsCount === 0) {
            $result->addError("FormBuilder NO tiene métodos privados build* - Debe ser modular");
        } elseif ($buildMethodsCount < 2) {
            $result->addWarning("Solo {$buildMethodsCount} método build* - Considerar más modularidad");
        } else {
            $result->addInfo("✓ Modularidad: {$buildMethodsCount} métodos build* detectados");
        }
    }

    private function countBuildMethods(string $content): int
    {
        preg_match_all('/private\s+function\s+build[A-Z][a-zA-Z]*\s*\(/', $content, $matches);
        return count($matches[0]);
    }

    private function validateRendererUsage(string $content, ValidationResult $result): void
    {
        // Verificar que usa $this->renderer->getFieldValue()
        if (!preg_match('/\$this->renderer->getFieldValue\s*\(/', $content)) {
            $result->addWarning("No se detectó uso de \$this->renderer->getFieldValue() - Verificar que obtiene datos del renderer");
        }
    }

    private function validateBootstrapCompliance(string $content, ValidationResult $result): void
    {
        $bootstrapClasses = [
            'form-control' => false,
            'form-select' => false,
            'form-check' => false,
            'card' => false,
            'row' => false,
        ];

        foreach ($bootstrapClasses as $class => $found) {
            // Buscar clase dentro de atributos class="..." o class='...'
            // Acepta: class="row", class="row g-3", class="form-check form-switch", etc.
            if (preg_match('/class=["\'][^"\']*\b' . preg_quote($class, '/') . '\b[^"\']*["\']/', $content)) {
                $bootstrapClasses[$class] = true;
            }
        }

        $usedClasses = array_keys(array_filter($bootstrapClasses));

        if (count($usedClasses) === 0) {
            $result->addWarning("No se detectaron clases Bootstrap 5 - Verificar que usa Bootstrap");
        } else {
            $result->addInfo("✓ Bootstrap 5: Usa " . implode(', ', $usedClasses));
        }
    }

    private function validateEscaping(string $content, ValidationResult $result): void
    {
        $escapingUsed = false;

        if (preg_match('/esc_attr\s*\(/', $content)) {
            $escapingUsed = true;
        }

        if (preg_match('/esc_html\s*\(/', $content)) {
            $escapingUsed = true;
        }

        if (!$escapingUsed) {
            $result->addWarning("No se detectó uso de esc_attr() o esc_html() - Verificar escaping de valores");
        }
    }

    private function validateNoDirectDatabaseAccess(string $content, ValidationResult $result): void
    {
        // Verificar que NO usa global $wpdb
        if (preg_match('/global\s+\$wpdb/', $content)) {
            $result->addError("FormBuilder usa 'global \$wpdb' - Acceso a BD debe estar en Repository o AdminDashboardRenderer");
        }

        // Verificar que NO usa funciones directas de BD
        $dbFunctions = ['get_option', 'update_option', 'get_post_meta', 'update_post_meta'];
        foreach ($dbFunctions as $func) {
            if (preg_match('/\b' . $func . '\s*\(/', $content)) {
                $result->addError("FormBuilder usa '{$func}()' - Debe usar AdminDashboardRenderer, NO acceder BD directamente");
            }
        }
    }

    /**
     * Valida que el componente está registrado en AdminDashboardRenderer::getComponents()
     */
    private function validateComponentRegistration(string $componentName, string $themePath, ValidationResult $result): void
    {
        $dashboardRendererPath = $themePath . '/Admin/Infrastructure/Ui/AdminDashboardRenderer.php';

        if (!file_exists($dashboardRendererPath)) {
            $result->addWarning("No se encontró AdminDashboardRenderer.php - No se puede validar registro del componente");
            return;
        }

        $dashboardContent = file_get_contents($dashboardRendererPath);

        // Buscar si el componente está registrado en getComponents()
        // Busca patrones como: 'cta-lets-talk' => [ o "cta-lets-talk" => [
        $pattern = '/[\'"]' . preg_quote($componentName, '/') . '[\'"]\s*=>\s*\[/';

        if (!preg_match($pattern, $dashboardContent)) {
            $result->addError("Componente NO registrado en AdminDashboardRenderer::getComponents()");
            $result->addInfo("Agregar en Admin/Infrastructure/Ui/AdminDashboardRenderer.php método getComponents():");
            $result->addInfo("'{$componentName}' => ['id' => '{$componentName}', 'label' => '...', 'icon' => 'bi-...'],");
        } else {
            $result->addInfo("✓ Componente registrado en AdminDashboardRenderer::getComponents()");
        }
    }

    /**
     * Valida que existe mapeo AJAX para los campos del FormBuilder
     */
    private function validateAjaxFieldMapping(string $componentName, string $themePath, string $formBuilderContent, ValidationResult $result): void
    {
        $ajaxHandlerPath = $themePath . '/Admin/Infrastructure/Api/Wordpress/AdminAjaxHandler.php';

        if (!file_exists($ajaxHandlerPath)) {
            $result->addWarning("No se encontró AdminAjaxHandler.php - No se puede validar mapeo AJAX");
            return;
        }

        $ajaxContent = file_get_contents($ajaxHandlerPath);

        // Extraer todos los IDs de campos del FormBuilder (id="...")
        preg_match_all('/id=["\']([a-zA-Z0-9_]+)["\']/', $formBuilderContent, $fieldMatches);
        $formFieldIds = array_unique($fieldMatches[1]);

        // Filtrar solo los IDs que corresponden a inputs (no contenedores)
        // Los IDs de inputs generalmente tienen un patrón como componentNameFieldName
        $fieldPrefix = $this->getFieldPrefix($componentName);

        $inputFieldIds = array_filter($formFieldIds, function($id) use ($fieldPrefix) {
            // Solo IDs que empiezan con el prefijo del componente
            return stripos($id, $fieldPrefix) === 0;
        });

        if (empty($inputFieldIds)) {
            $result->addWarning("No se detectaron campos con prefijo '{$fieldPrefix}' en FormBuilder");
            return;
        }

        // Verificar que cada campo tiene mapeo en getFieldMapping()
        $unmappedFields = [];
        foreach ($inputFieldIds as $fieldId) {
            // Buscar el ID en el contenido del AjaxHandler
            if (!preg_match('/[\'"]' . preg_quote($fieldId, '/') . '[\'"]\s*=>/', $ajaxContent)) {
                $unmappedFields[] = $fieldId;
            }
        }

        $totalFields = count($inputFieldIds);
        $mappedFields = $totalFields - count($unmappedFields);

        if (count($unmappedFields) > 0) {
            $result->addError("Mapeo AJAX incompleto: {$mappedFields}/{$totalFields} campos mapeados");
            $result->addInfo("Campos sin mapeo en AdminAjaxHandler::getFieldMapping():");
            foreach (array_slice($unmappedFields, 0, 5) as $field) {
                $result->addInfo("  - {$field}");
            }
            if (count($unmappedFields) > 5) {
                $result->addInfo("  ... y " . (count($unmappedFields) - 5) . " más");
            }
        } else {
            $result->addInfo("✓ Mapeo AJAX completo: {$totalFields}/{$totalFields} campos mapeados");
        }
    }

    /**
     * Valida que el botón "Restaurar valores por defecto" sigue el patrón correcto
     *
     * El patrón correcto es:
     * - class="btn-reset-defaults"
     * - data-component="nombre-componente"
     *
     * NO usar id="reset*Defaults" (patrón antiguo hardcodeado)
     */
    private function validateResetButton(string $componentName, string $content, ValidationResult $result): void
    {
        // Verificar patrón correcto: class="btn-reset-defaults" data-component="..."
        $correctPattern = '/class=["\'][^"\']*btn-reset-defaults[^"\']*["\'][^>]*data-component=["\']' . preg_quote($componentName, '/') . '["\']/';
        $correctPatternAlt = '/data-component=["\']' . preg_quote($componentName, '/') . '["\'][^>]*class=["\'][^"\']*btn-reset-defaults[^"\']*["\']/';

        $hasCorrectPattern = preg_match($correctPattern, $content) || preg_match($correctPatternAlt, $content);

        // Verificar patrón incorrecto: id="reset*Defaults" (hardcodeado)
        $incorrectPattern = '/id=["\']reset[A-Za-z]+Defaults["\']/';
        $hasIncorrectPattern = preg_match($incorrectPattern, $content);

        if ($hasIncorrectPattern && !$hasCorrectPattern) {
            $result->addError("Botón reset usa patrón hardcodeado (id=\"reset*Defaults\")");
            $result->addInfo("Cambiar a: class=\"btn-reset-defaults\" data-component=\"{$componentName}\"");
        } elseif (!$hasCorrectPattern) {
            $result->addWarning("No se detectó botón 'Restaurar valores por defecto' con patrón dinámico");
            $result->addInfo("Agregar: <button class=\"btn-reset-defaults\" data-component=\"{$componentName}\">...</button>");
        } else {
            $result->addInfo("✓ Botón reset con patrón dinámico correcto (data-component=\"{$componentName}\")");
        }
    }

    /**
     * Obtiene el prefijo de campos para un componente
     * Permite prefijos personalizados más cortos para componentes con nombres largos
     */
    private function getFieldPrefix(string $componentName): string
    {
        // Mapeo de prefijos personalizados (más cortos/legibles)
        $customPrefixes = [
            'top-notification-bar' => 'topBar',
        ];

        if (isset($customPrefixes[$componentName])) {
            return $customPrefixes[$componentName];
        }

        // Por defecto: convertir kebab-case a camelCase
        $pascalCaseName = str_replace('-', '', ucwords($componentName, '-'));
        return lcfirst($pascalCaseName);
    }

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

    public function getPhaseDescription(): string
    {
        return 'FormBuilders (UI Admin)';
    }
}
