<?php
declare(strict_types=1);

namespace ROITheme\Shared\Infrastructure\Validators;

/**
 * Validador de Fase 05: Validación General SOLID
 *
 * Escanea TODOS los archivos .php del componente y valida:
 * - Domain Purity (sin WordPress, sin echo, sin HTML)
 * - Application Purity (sin WordPress)
 * - CERO CSS hardcodeado
 * - Dependency Injection
 * - SRP (Single Responsibility - tamaño de archivos)
 * - ISP (Interface Segregation - tamaño de interfaces)
 * - Encapsulación (propiedades private/protected)
 * - NO instanciación directa en Domain/Application
 */
final class Phase05Validator implements PhaseValidatorInterface
{
    private const WORDPRESS_FUNCTIONS = [
        'global \$wpdb',
        'add_action',
        'add_filter',
        'get_option',
        'update_option',
        'wp_enqueue_',
        'register_post_type',
        'add_shortcode',
        '\$_POST',
        '\$_GET',
        '\$_SESSION',
        '\$_COOKIE',
    ];

    private const CSS_PATTERNS = [
        'style\s*=\s*["\']',           // style="..."
        '<<<STYLE',                     // HEREDOC con STYLE
        '<<<CSS',                       // HEREDOC con CSS
        'onmouseover\s*=',             // inline JS events
        'onmouseout\s*=',
        'onclick\s*=',
        'onload\s*=',
        'onchange\s*=',
    ];

    private int $filesScanned = 0;

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

        $result->addInfo("Validación SOLID general para: {$componentName}");

        // Buscar directorio del componente
        $componentPath = $this->findComponentPath($componentName, $themePath);

        if ($componentPath === null) {
            $result->addError("Componente no encontrado en Admin/ ni Public/");
            return $result;
        }

        $result->addInfo("Escaneando: {$componentPath}");

        // Escanear todos los archivos .php
        $this->filesScanned = 0;
        $this->scanDirectory($componentPath, $result);

        // Estadísticas
        $result->setStat('Archivos escaneados', $this->filesScanned);

        return $result;
    }

    private function findComponentPath(string $componentName, string $themePath): ?string
    {
        // Convertir kebab-case a PascalCase (PSR-4 standard)
        $pascalCaseName = str_replace('-', '', ucwords($componentName, '-'));

        // Intentar en Public/ con PascalCase
        $publicPath = $themePath . '/Public/' . $pascalCaseName;
        if (is_dir($publicPath)) {
            return $publicPath;
        }

        // Intentar en Admin/ con PascalCase
        $adminPath = $themePath . '/Admin/' . $pascalCaseName;
        if (is_dir($adminPath)) {
            return $adminPath;
        }

        return null;
    }

    private function scanDirectory(string $dir, ValidationResult $result): void
    {
        $iterator = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
            \RecursiveIteratorIterator::SELF_FIRST
        );

        foreach ($iterator as $file) {
            if ($file->isFile() && $file->getExtension() === 'php') {
                $this->validateFile($file->getPathname(), $result);
            }
        }
    }

    private function validateFile(string $filePath, ValidationResult $result): void
    {
        $this->filesScanned++;
        $content = file_get_contents($filePath);
        $fileName = basename($filePath);

        // Detectar capa (Domain, Application, Infrastructure)
        $layer = $this->detectLayer($filePath);

        // REGLA 1: Domain NO puede tener WordPress
        if ($layer === 'domain') {
            $this->checkDomainPurity($content, $fileName, $result);
        }

        // REGLA 2: Application NO puede tener WordPress
        if ($layer === 'application') {
            $this->checkApplicationPurity($content, $fileName, $result);
        }

        // REGLA 3: CERO CSS hardcodeado en PHP (general)
        $this->checkNoHardcodedCSS($content, $fileName, $result);

        // REGLA 4: DIP - Constructores deben recibir interfaces
        if ($layer === 'infrastructure') {
            $this->checkDependencyInjection($content, $fileName, $result);
        }

        // REGLA 5: SRP - Archivos no deben exceder 300 líneas
        $this->checkFileLength($content, $fileName, $result);

        // REGLA 6: ISP - Interfaces no deben ser gordas
        if (strpos($filePath, 'Interface.php') !== false) {
            $this->checkInterfaceSize($content, $fileName, $result);
        }

        // REGLA 7: Propiedades deben ser private/protected
        $this->checkEncapsulation($content, $fileName, $result);

        // REGLA 8: NO debe haber new ConcreteClass() en Domain/Application
        if ($layer === 'domain' || $layer === 'application') {
            $this->checkNoDirectInstantiation($content, $fileName, $result);
        }
    }

    private function detectLayer(string $filePath): ?string
    {
        if (stripos($filePath, DIRECTORY_SEPARATOR . 'domain' . DIRECTORY_SEPARATOR) !== false) {
            return 'domain';
        }
        if (stripos($filePath, DIRECTORY_SEPARATOR . 'application' . DIRECTORY_SEPARATOR) !== false) {
            return 'application';
        }
        if (stripos($filePath, DIRECTORY_SEPARATOR . 'infrastructure' . DIRECTORY_SEPARATOR) !== false) {
            return 'infrastructure';
        }
        return null;
    }

    private function checkDomainPurity(string $content, string $file, ValidationResult $result): void
    {
        foreach (self::WORDPRESS_FUNCTIONS as $wpFunction) {
            if (preg_match('/' . $wpFunction . '/i', $content)) {
                $result->addError("Domain tiene código WordPress '{$wpFunction}': {$file}");
            }
        }

        // Domain NO debe tener echo/print
        if (preg_match('/\b(echo|print|print_r|var_dump)\s+/', $content)) {
            $result->addError("Domain tiene output directo (echo/print/var_dump): {$file}");
        }

        // Domain NO debe tener HTML
        if (preg_match('/<(div|span|p|a|ul|li|table|form|input|button|h[1-6])\s/i', $content)) {
            $result->addError("Domain tiene HTML hardcodeado: {$file}");
        }
    }

    private function checkApplicationPurity(string $content, string $file, ValidationResult $result): void
    {
        foreach (self::WORDPRESS_FUNCTIONS as $wpFunction) {
            if (preg_match('/' . $wpFunction . '/i', $content)) {
                $result->addError("Application tiene código WordPress '{$wpFunction}': {$file}");
            }
        }
    }

    private function checkNoHardcodedCSS(string $content, string $file, ValidationResult $result): void
    {
        // Skip si es un archivo de assets o FormBuilder (ya validado en Phase04)
        if (strpos($file, 'assets') !== false || strpos($file, 'FormBuilder') !== false) {
            return;
        }

        $violations = 0;
        foreach (self::CSS_PATTERNS as $pattern) {
            if (preg_match('/' . $pattern . '/i', $content)) {
                $violations++;
            }
        }

        if ($violations > 0) {
            $result->addError("CSS hardcodeado encontrado ({$violations} patrones): {$file}");
        }
    }

    private function checkDependencyInjection(string $content, string $file, ValidationResult $result): void
    {
        // Buscar constructores
        if (preg_match('/__construct\s*\([^)]*\)/', $content, $matches)) {
            $constructor = $matches[0];

            // Verificar si recibe parámetros
            if (strpos($constructor, '$') !== false) {
                // Buscar parámetros que son clases concretas (Service, Repository, Manager)
                if (preg_match('/private\s+([A-Z][a-zA-Z]+)(Service|Repository|Manager)\s+\$/', $constructor)) {
                    // Verificar si NO termina en Interface
                    if (!preg_match('/interface\s+\$/', $constructor)) {
                        $result->addWarning("Constructor recibe clase concreta en lugar de interface: {$file}");
                    }
                }
            }
        }

        // Verificar si tiene new dentro del código (excepto DTOs, VOs, Entities)
        if (preg_match('/new\s+([A-Z][a-zA-Z]+)(Service|Repository|Manager|Controller|Builder)\s*\(/i', $content)) {
            $result->addError("Instanciación directa de clase de infraestructura (debe usar DI): {$file}");
        }
    }

    private function checkFileLength(string $content, string $file, ValidationResult $result): void
    {
        $lines = substr_count($content, "\n") + 1;

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

    private function checkInterfaceSize(string $content, string $file, ValidationResult $result): void
    {
        // Contar métodos públicos en la interface
        $methodCount = preg_match_all('/public\s+function\s+\w+\s*\(/i', $content);

        if ($methodCount > 10) {
            $result->addError("Interface tiene {$methodCount} métodos (máx 10) - viola ISP: {$file}");
        } elseif ($methodCount > 5) {
            $result->addWarning("Interface tiene {$methodCount} métodos (recomendado: 3-5) - considerar dividir: {$file}");
        }
    }

    private function checkEncapsulation(string $content, string $file, ValidationResult $result): void
    {
        // Buscar propiedades public (excluir const y function)
        if (preg_match('/\bpublic\s+(?!const|function|static\s+function)\$\w+/i', $content)) {
            $result->addWarning("Propiedades públicas encontradas - usar private/protected: {$file}");
        }
    }

    private function checkNoDirectInstantiation(string $content, string $file, ValidationResult $result): void
    {
        // Permitir new de: DTOs, Value Objects, Entities, Exceptions
        $allowedPatterns = [
            'DTO', 'Request', 'Response', 'Exception',
            'ComponentName', 'ComponentConfiguration', 'ComponentVisibility',
            'Color', 'Url', 'Email', 'ComponentId', 'MenuItem',
            'self', 'static', 'parent', 'stdClass', 'DateTime'
        ];

        if (preg_match_all('/new\s+([A-Z][a-zA-Z]+)\s*\(/i', $content, $matches)) {
            foreach ($matches[1] as $className) {
                $isAllowed = false;
                foreach ($allowedPatterns as $pattern) {
                    if (strpos($className, $pattern) !== false || $className === $pattern) {
                        $isAllowed = true;
                        break;
                    }
                }

                if (!$isAllowed) {
                    $result->addWarning("Instanciación directa de '{$className}' en Domain/Application - considerar inyección: {$file}");
                }
            }
        }
    }

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

    public function getPhaseDescription(): string
    {
        return 'General SOLID (todos los archivos)';
    }
}
