<?php
declare(strict_types=1);

namespace ROITheme\Shared\Infrastructure\Validators;

/**
 * Validador de Conflictos CSS
 *
 * Detecta archivos CSS en Assets/ que podrían sobrescribir
 * estilos generados dinámicamente por Renderers.
 *
 * Valida que:
 * - NO existan archivos CSS hardcodeados para componentes con Renderer dinámico
 * - NO haya reglas !important que sobrescriban estilos dinámicos
 * - NO se encolen CSS externos que conflictúen con Renderers
 *
 * @since 1.0.0
 */
final class CSSConflictValidator implements PhaseValidatorInterface
{
    /**
     * Mapeo de componentes con Renderer dinámico a sus posibles archivos CSS conflictivos
     */
    private const DYNAMIC_RENDERER_COMPONENTS = [
        'top-notification-bar' => [
            'css_files' => ['componente-top-bar.css', 'top-notification-bar.css'],
            'css_classes' => ['.top-notification-bar', '.top-bar'],
        ],
        'navbar' => [
            'css_files' => ['componente-navbar.css', 'navbar.css'],
            'css_classes' => ['.navbar', '.nav-link', '.navbar-brand', '.dropdown-menu'],
        ],
        'cta-lets-talk' => [
            'css_files' => ['componente-boton-lets-talk.css', 'cta-lets-talk.css'],
            'css_classes' => ['.btn-lets-talk', '.cta-lets-talk'],
        ],
    ];

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

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

        // Solo validar componentes con Renderer dinámico conocidos
        if (!isset(self::DYNAMIC_RENDERER_COMPONENTS[$componentName])) {
            $result->addInfo("Componente no tiene Renderer dinámico registrado - Omitiendo validación CSS");
            return $result;
        }

        $componentConfig = self::DYNAMIC_RENDERER_COMPONENTS[$componentName];

        // 1. Verificar que existe un Renderer dinámico
        $hasRenderer = $this->hasRendererFile($componentName, $themePath);
        if (!$hasRenderer) {
            $result->addInfo("No se encontró Renderer dinámico - Validación CSS no aplica");
            return $result;
        }

        $result->addInfo("✓ Renderer dinámico detectado");

        // 2. Buscar archivos CSS conflictivos en Assets/css/
        $cssData = $this->validateAssetsCSSFiles($componentName, $componentConfig, $themePath, $result);

        // 3. Validar enqueue-scripts.php y determinar errores vs warnings
        $this->validateEnqueueScripts($componentName, $componentConfig, $themePath, $result, $cssData);

        return $result;
    }

    /**
     * Verifica si existe un Renderer para el componente
     */
    private function hasRendererFile(string $componentName, string $themePath): bool
    {
        $pascalCaseName = str_replace('-', '', ucwords($componentName, '-'));

        $publicPath = $themePath . '/Public/' . $pascalCaseName . '/Infrastructure/Ui/' . $pascalCaseName . 'Renderer.php';
        if (file_exists($publicPath)) {
            return true;
        }

        $adminPath = $themePath . '/Admin/' . $pascalCaseName . '/Infrastructure/Ui/' . $pascalCaseName . 'Renderer.php';
        if (file_exists($adminPath)) {
            return true;
        }

        return false;
    }

    /**
     * Valida archivos CSS en Assets/css/
     *
     * @return array{files: string[], important: array<string, int>} Archivos encontrados y violaciones
     */
    private function validateAssetsCSSFiles(
        string $componentName,
        array $componentConfig,
        string $themePath,
        ValidationResult $result
    ): array {
        $assetsPath = $themePath . '/Assets/Css';

        $cssFilesFound = [];
        $importantViolations = [];

        if (!is_dir($assetsPath)) {
            $result->addInfo("No existe carpeta Assets/css/");
            return ['files' => [], 'important' => []];
        }

        foreach ($componentConfig['css_files'] as $cssFileName) {
            $cssFilePath = $assetsPath . '/' . $cssFileName;

            if (file_exists($cssFilePath)) {
                $cssFilesFound[] = $cssFileName;
                $content = file_get_contents($cssFilePath);

                // Buscar reglas !important
                $importantCount = $this->countImportantRules($content);

                if ($importantCount > 0) {
                    $importantViolations[$cssFileName] = $importantCount;
                }

                // Buscar clases CSS del componente
                $conflictingClasses = $this->findConflictingClasses($content, $componentConfig['css_classes']);

                if (!empty($conflictingClasses)) {
                    $result->addWarning(
                        "Archivo '{$cssFileName}' define clases del componente: " .
                        implode(', ', $conflictingClasses)
                    );
                }
            }
        }

        // Reportar archivos encontrados
        if (!empty($cssFilesFound)) {
            $result->addWarning(
                "Archivos CSS hardcodeados encontrados en Assets/css/: " .
                implode(', ', $cssFilesFound)
            );
            $result->addInfo(
                "⚠️  Estos archivos podrían sobrescribir estilos generados dinámicamente por el Renderer"
            );
        } else {
            $result->addInfo("✓ Sin archivos CSS hardcodeados conflictivos en Assets/css/");
        }

        // Stats
        $result->setStat('Archivos CSS conflictivos', count($cssFilesFound));
        $result->setStat('Reglas !important', array_sum($importantViolations));

        return ['files' => $cssFilesFound, 'important' => $importantViolations];
    }

    /**
     * Cuenta reglas !important en contenido CSS
     */
    private function countImportantRules(string $content): int
    {
        preg_match_all('/!important/i', $content, $matches);
        return count($matches[0]);
    }

    /**
     * Encuentra clases CSS conflictivas en el contenido
     */
    private function findConflictingClasses(string $content, array $cssClasses): array
    {
        $found = [];

        foreach ($cssClasses as $className) {
            // Escapar el punto para regex
            $pattern = '/' . preg_quote($className, '/') . '\s*[{,]/';
            if (preg_match($pattern, $content)) {
                $found[] = $className;
            }
        }

        return $found;
    }

    /**
     * Valida Inc/enqueue-scripts.php para detectar CSS encolados
     *
     * @param array{files: string[], important: array<string, int>} $cssData Datos de archivos CSS encontrados
     */
    private function validateEnqueueScripts(
        string $componentName,
        array $componentConfig,
        string $themePath,
        ValidationResult $result,
        array $cssData
    ): void {
        $enqueueScriptsPath = $themePath . '/Inc/enqueue-scripts.php';

        if (!file_exists($enqueueScriptsPath)) {
            $result->addWarning("No se encontró Inc/enqueue-scripts.php");
            return;
        }

        $content = file_get_contents($enqueueScriptsPath);

        $enqueuedFiles = [];
        $commentedFiles = [];

        foreach ($componentConfig['css_files'] as $cssFileName) {
            // Buscar si el archivo está siendo encolado (multiline - wp_enqueue_style puede tener saltos de línea)
            $pattern = '/wp_enqueue_style\s*\([^;]*' . preg_quote($cssFileName, '/') . '[^;]*\);/s';

            if (preg_match($pattern, $content, $match)) {
                // Verificar si está comentado
                $matchPos = strpos($content, $match[0]);
                $lineStart = strrpos(substr($content, 0, $matchPos), "\n") + 1;
                $lineContent = substr($content, $lineStart, $matchPos - $lineStart);

                // Verificar si la línea está en un bloque comentado /* */
                $beforeMatch = substr($content, 0, $matchPos);
                $lastCommentOpen = strrpos($beforeMatch, '/*');
                $lastCommentClose = strrpos($beforeMatch, '*/');

                $isCommented = false;
                if ($lastCommentOpen !== false) {
                    if ($lastCommentClose === false || $lastCommentOpen > $lastCommentClose) {
                        $isCommented = true;
                    }
                }

                // También verificar comentario de línea //
                if (strpos($lineContent, '//') !== false) {
                    $isCommented = true;
                }

                if ($isCommented) {
                    $commentedFiles[] = $cssFileName;
                } else {
                    $enqueuedFiles[] = $cssFileName;
                }
            }
        }

        // Reportar resultados de enqueue
        if (!empty($enqueuedFiles)) {
            $result->addError(
                "❌ CRÍTICO: CSS conflictivo ACTIVO en enqueue-scripts.php: " .
                implode(', ', $enqueuedFiles)
            );
            $result->addInfo(
                "SOLUCIÓN: Comentar wp_enqueue_style() para estos archivos ya que el Renderer genera CSS dinámico"
            );
        }

        if (!empty($commentedFiles)) {
            $result->addInfo(
                "✓ CSS correctamente deshabilitado en enqueue-scripts.php: " .
                implode(', ', $commentedFiles)
            );
        }

        if (empty($enqueuedFiles) && empty($commentedFiles)) {
            $result->addInfo("✓ Sin conflictos de enqueue detectados");
        }

        // Reportar !important violations basado en estado de enqueue
        $importantViolations = $cssData['important'] ?? [];
        if (!empty($importantViolations)) {
            foreach ($importantViolations as $file => $count) {
                if (in_array($file, $enqueuedFiles, true)) {
                    // CSS activo con !important → ERROR CRÍTICO
                    $result->addError(
                        "❌ CRÍTICO: '{$file}' ACTIVO con {$count} regla(s) !important que SOBRESCRIBEN estilos dinámicos"
                    );
                } elseif (in_array($file, $commentedFiles, true)) {
                    // CSS deshabilitado con !important → WARNING (considerar eliminar archivo)
                    $result->addWarning(
                        "'{$file}' deshabilitado pero tiene {$count} regla(s) !important - Considerar eliminar archivo"
                    );
                } else {
                    // Archivo existe pero no está en enqueue-scripts.php → WARNING
                    $result->addWarning(
                        "'{$file}' no está en enqueue-scripts.php pero tiene {$count} regla(s) !important"
                    );
                }
            }
        }
    }

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

    public function getPhaseDescription(): string
    {
        return 'CSS Conflicts (Assets vs Dynamic Renderers)';
    }
}
