<?php
declare(strict_types=1);

namespace ROITheme\Shared\Infrastructure\Validators;

/**
 * Validador de Fase 03: Renderers (DB→HTML/CSS)
 *
 * Valida que:
 * - Renderer existe en ubicación correcta
 * - Namespace y clase correctos
 * - Inyecta CSSGeneratorInterface
 * - CERO CSS hardcodeado (CRÍTICO)
 * - Tiene métodos obligatorios (render, getVisibilityClasses)
 * - Usa escaping correcto
 * - NO usa WordPress de BD
 */
final class Phase03Validator implements PhaseValidatorInterface
{
    /**
     * Componentes especiales que NO requieren CSSGeneratorInterface ni getVisibilityClasses
     *
     * Estos son componentes de inyeccion (no visuales) que:
     * - NO renderizan HTML visual
     * - NO generan CSS dinamico (inyectan CSS del usuario tal cual)
     * - NO necesitan clases de visibilidad responsive
     * - Inyectan codigo en hooks (wp_head, wp_footer)
     */
    private const INJECTION_COMPONENTS = ['theme-settings'];

    public function validate(string $componentName, string $themePath): ValidationResult
    {
        $result = new ValidationResult();

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

        // Determinar contexto (admin o public) - intentar ambos
        $rendererPath = $this->findRendererPath($componentName, $themePath);

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

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

        // Leer contenido del archivo
        $content = file_get_contents($rendererPath);

        // Validaciones
        $this->validateNamespaceAndClass($content, $componentName, $result);

        // Validaciones especiales para componentes de inyeccion
        $isInjectionComponent = in_array($componentName, self::INJECTION_COMPONENTS, true);

        if ($isInjectionComponent) {
            $result->addInfo("✓ Componente de inyección - validaciones CSS/visibility omitidas");
        } else {
            $this->validateCSSGeneratorInjection($content, $result);
            $this->validateNoCSSHardcoded($content, $result); // CRÍTICO
            $this->validateGetVisibilityClassesMethod($content, $result);
        }

        $this->validateRenderMethod($content, $componentName, $result);
        $this->validateEscaping($content, $result);
        $this->validateNoDirectDatabaseAccess($content, $result);

        // Estadísticas
        $fileSize = filesize($rendererPath);
        $lineCount = substr_count($content, "\n") + 1;

        $result->setStat('Archivo', basename($rendererPath));
        $result->setStat('Líneas', $lineCount);
        $result->setStat('Tamaño', $fileSize . ' bytes');

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

        return $result;
    }

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

        // Intentar en Public/ con PascalCase (PSR-4 standard)
        $publicPath = $themePath . '/Public/' . $pascalCaseName . '/Infrastructure/Ui/' . $pascalCaseName . 'Renderer.php';
        if (file_exists($publicPath)) {
            return $publicPath;
        }

        // Intentar en Admin/ con PascalCase (PSR-4 standard)
        $adminPath = $themePath . '/Admin/' . $pascalCaseName . '/Infrastructure/Ui/' . $pascalCaseName . 'Renderer.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\\\\(Public|Admin)\\\\' . preg_quote($pascalCaseName, '/') . '\\\\Infrastructure\\\\Ui;/', $content)) {
            $result->addError("Namespace incorrecto. Debe ser: ROITheme\\Public\\{$pascalCaseName}\\Infrastructure\\Ui");
        }

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

    private function validateCSSGeneratorInjection(string $content, ValidationResult $result): void
    {
        // Verificar que constructor recibe CSSGeneratorInterface
        if (!preg_match('/public\s+function\s+__construct\([^)]*CSSGeneratorInterface\s+\$cssGenerator/s', $content)) {
            $result->addError("Constructor NO inyecta CSSGeneratorInterface (debe recibir interfaz, no clase concreta)");
        }

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

    private function validateRenderMethod(string $content, string $componentName, ValidationResult $result): void
    {
        // Verificar método render existe (aceptar Component object o array - Component es preferido)
        $hasComponentSignature = preg_match('/public\s+function\s+render\s*\(\s*Component\s+\$component\s*\)\s*:\s*string/', $content);
        $hasArraySignature = preg_match('/public\s+function\s+render\s*\(\s*array\s+\$data\s*\)\s*:\s*string/', $content);

        if (!$hasComponentSignature && !$hasArraySignature) {
            $result->addError("Falta método: public function render(Component \$component): string o render(array \$data): string");
        } elseif ($hasArraySignature) {
            $result->addWarning("Método usa render(array \$data) - Considerar migrar a render(Component \$component) para type safety");
        }

        // Componentes de inyeccion no requieren validacion de visibility
        $isInjectionComponent = in_array($componentName, self::INJECTION_COMPONENTS, true);

        if (!$isInjectionComponent) {
            // Verificar que valida is_enabled
            $hasArrayValidation = preg_match('/\$data\s*\[\s*[\'"]visibility[\'"]\s*\]\s*\[\s*[\'"]is_enabled[\'"]\s*\]/', $content);
            $hasComponentValidation = preg_match('/\$component->getVisibility\(\)->isEnabled\(\)/', $content);

            if (!$hasArrayValidation && !$hasComponentValidation) {
                $result->addWarning("Método render() debería validar visibilidad (is_enabled)");
            }
        }
    }

    private function validateNoCSSHardcoded(string $content, ValidationResult $result): void
    {
        $violations = [];

        // Detectar style="..." (pero permitir algunos casos específicos del design system)
        if (preg_match_all('/style\s*=\s*["\']/', $content, $matches, PREG_OFFSET_CAPTURE)) {
            // Contar ocurrencias
            $count = count($matches[0]);

            // Si hay más de 2-3 (casos permitidos del design system), es violación
            if ($count > 3) {
                $violations[] = "Encontrado style=\"...\" inline ({$count} ocurrencias) - PROHIBIDO";
            } elseif ($count > 0) {
                $result->addWarning("Encontrado {$count} uso(s) de style=\"...\" - Verificar que sea del design system aprobado");
            }
        }

        // Detectar heredoc <<<STYLE o <<<CSS
        if (preg_match('/<<<(STYLE|CSS)/', $content)) {
            $violations[] = "Encontrado heredoc <<<STYLE o <<<CSS - PROHIBIDO";
        }

        // Detectar eventos inline (onclick, onmouseover, etc.)
        $inlineEvents = ['onclick', 'onmouseover', 'onmouseout', 'onload', 'onchange', 'onsubmit'];
        foreach ($inlineEvents as $event) {
            if (preg_match('/' . $event . '\s*=/', $content)) {
                $violations[] = "Encontrado evento inline '{$event}=\"...\"' - PROHIBIDO";
            }
        }

        // Verificar que usa $this->cssGenerator->generate()
        if (!preg_match('/\$this->cssGenerator->generate\s*\(/', $content)) {
            $violations[] = "NO usa \$this->cssGenerator->generate() - CSS debe generarse vía servicio";
        }

        if (!empty($violations)) {
            foreach ($violations as $violation) {
                $result->addError("❌ CRÍTICO - CSS HARDCODEADO: {$violation}");
            }
        } else {
            $result->addInfo("✓ CERO CSS hardcodeado detectado");
        }
    }

    private function validateGetVisibilityClassesMethod(string $content, ValidationResult $result): void
    {
        // Verificar firma del método
        if (!preg_match('/private\s+function\s+getVisibilityClasses\s*\(\s*bool\s+\$desktop\s*,\s*bool\s+\$mobile\s*\)\s*:\s*\?string/', $content)) {
            $result->addError("Falta método: private function getVisibilityClasses(bool \$desktop, bool \$mobile): ?string");
            return;
        }

        // Verificar implementación de tabla Bootstrap
        $requiredPatterns = [
            '/d-none d-lg-block/' => "Patrón 'd-none d-lg-block' (desktop only)",
            '/d-lg-none/' => "Patrón 'd-lg-none' (mobile only)",
        ];

        foreach ($requiredPatterns as $pattern => $description) {
            if (!preg_match($pattern, $content)) {
                $result->addWarning("getVisibilityClasses() puede no implementar correctamente: {$description}");
            }
        }
    }

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

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

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

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

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

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

        // 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->addWarning("Renderer usa '{$func}()' - Considerar mover a Repository");
            }
        }
    }

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

    public function getPhaseDescription(): string
    {
        return 'Renderers (DB→HTML/CSS)';
    }
}
