<?php

declare(strict_types=1);

namespace Flowaxy\Core\System\Autoloader;

use Flowaxy\Core\System\PathResolver;

/**
 * Генератор preload map для PHP opcache.preload
 * Разделяет на core (всегда) и optional (только безопасные классы без side-effects)
 *
 * @package Flowaxy\Core\System\Autoloader
 */
final class PreloadMapGenerator
{
    private array $coreClasses = [];
    private array $optionalClasses = [];

    /**
     * Проверка, имеет ли класс side-effects
     *
     * @param string $filePath Путь к файлу
     * @return bool True, если класс имеет side-effects
     */
    private function hasSideEffects(string $filePath): bool
    {
        if (!file_exists($filePath)) {
            return true;
        }

        $content = file_get_contents($filePath);
        if ($content === false) {
            return true;
        }

        // Проверка на side-effects: вызовы функций, обращения к БД, файловой системе
        $sideEffectPatterns = [
            '/\b(require|include|require_once|include_once)\s*\(/',
            '/\b(file_get_contents|file_put_contents|fopen|fwrite|fread)\s*\(/',
            '/\b(mysqli|PDO|pg_connect|sqlite_open)\s*\(/',
            '/\b(exec|system|shell_exec|passthru)\s*\(/',
            '/\b(header|setcookie|session_start)\s*\(/',
            '/\b(register_shutdown_function|set_error_handler)\s*\(/',
            '/\b(ini_set|ini_get)\s*\(/',
            '/\b(define|constant)\s*\(/',
            '/\b\$GLOBALS\s*\[/',
            '/\b$_SESSION\s*\[/',
            '/\b$_COOKIE\s*\[/',
            '/\b$_POST\s*\[/',
            '/\b$_GET\s*\[/',
            '/\b$_REQUEST\s*\[/',
        ];

        foreach ($sideEffectPatterns as $pattern) {
            if (preg_match($pattern, $content)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Проверка, является ли класс безопасным для preload_optional
     *
     * @param string $className Полное имя класса
     * @param string $filePath Путь к файлу
     * @return bool True, если класс безопасен для optional preload
     */
    private function isSafeForOptional(string $className, string $filePath): bool
    {
        // Contracts всегда безопасны (только интерфейсы)
        if (str_starts_with($className, 'Flowaxy\\Contracts\\')) {
            return true;
        }

        // Core классы без side-effects
        if (str_starts_with($className, 'Flowaxy\\Core\\')) {
            // Исключаем классы с IO операциями
            if (str_contains($className, '\\Database\\') ||
                str_contains($className, '\\Cache\\') ||
                str_contains($className, '\\Filesystem\\') ||
                str_contains($className, '\\Mail\\') ||
                str_contains($className, '\\Logging\\')) {
                return false;
            }
            return !$this->hasSideEffects($filePath);
        }

        // Infrastructure без IO операций
        if (str_starts_with($className, 'Flowaxy\\Infrastructure\\')) {
            // Исключаем классы с IO операциями
            if (str_contains($className, '\\Persistence\\Database\\') ||
                str_contains($className, '\\Cache\\') ||
                str_contains($className, '\\Filesystem\\') ||
                str_contains($className, '\\Mail\\') ||
                str_contains($className, '\\Logging\\')) {
                return false;
            }
            return !$this->hasSideEffects($filePath);
        }

        // Все остальное не безопасно для optional
        return false;
    }

    /**
     * Генерация preload map
     *
     * @param array<string, string> $classMap Class map (className => filePath)
     * @return array{core: array<string, string>, optional: array<string, string>}
     */
    public function generate(array $classMap): array
    {
        $this->coreClasses = [];
        $this->optionalClasses = [];

        foreach ($classMap as $className => $filePath) {
            // Все классы идут в core
            $this->coreClasses[$className] = $filePath;

            // Только безопасные классы идут в optional
            if ($this->isSafeForOptional($className, $filePath)) {
                $this->optionalClasses[$className] = $filePath;
            }
        }

        return [
            'core' => $this->coreClasses,
            'optional' => $this->optionalClasses,
        ];
    }

    /**
     * Сохранение preload_core.php
     *
     * @param string $outputFile Путь к файлу
     * @param array<string, string> $coreClasses Core классы (className => absoluteFilePath)
     * @return bool
     */
    public function saveCorePreload(string $outputFile, array $coreClasses): bool
    {
        $content = "<?php\n\n";
        $content .= "/**\n";
        $content .= " * Preload core classes for PHP opcache.preload\n";
        $content .= " * Generated automatically - DO NOT EDIT\n";
        $content .= " */\n\n";

        $flowaxyDir = PathResolver::flowaxy();
        $storageDir = dirname($outputFile);

        foreach ($coreClasses as $className => $filePath) {
            if (!file_exists($filePath)) {
                continue;
            }

            // Вычисляем относительный путь от storage директории
            $relativePath = str_replace('\\', '/', $filePath);
            $relativePath = str_replace($flowaxyDir, '../flowaxy', $relativePath);
            $relativePath = str_replace('\\', '/', $relativePath);

            $content .= "require_once __DIR__ . '/{$relativePath}';\n";
            $content .= "opcache_compile_file(__DIR__ . '/{$relativePath}');\n";
        }

        $dir = dirname($outputFile);
        if (!is_dir($dir)) {
            mkdir($dir, 0755, true);
        }

        return file_put_contents($outputFile, $content) !== false;
    }

    /**
     * Сохранение preload_optional.php
     *
     * @param string $outputFile Путь к файлу
     * @param array<string, string> $optionalClasses Optional классы (className => absoluteFilePath)
     * @return bool
     */
    public function saveOptionalPreload(string $outputFile, array $optionalClasses): bool
    {
        $content = "<?php\n\n";
        $content .= "/**\n";
        $content .= " * Preload optional classes for PHP opcache.preload\n";
        $content .= " * Only safe classes without side-effects (Contracts, Core without IO, Infrastructure without IO)\n";
        $content .= " * Generated automatically - DO NOT EDIT\n";
        $content .= " */\n\n";

        $flowaxyDir = PathResolver::flowaxy();
        $storageDir = dirname($outputFile);

        foreach ($optionalClasses as $className => $filePath) {
            if (!file_exists($filePath)) {
                continue;
            }

            // Вычисляем относительный путь от storage директории
            $relativePath = str_replace('\\', '/', $filePath);
            $relativePath = str_replace($flowaxyDir, '../flowaxy', $relativePath);
            $relativePath = str_replace('\\', '/', $relativePath);

            $content .= "require_once __DIR__ . '/{$relativePath}';\n";
            $content .= "opcache_compile_file(__DIR__ . '/{$relativePath}');\n";
        }

        $dir = dirname($outputFile);
        if (!is_dir($dir)) {
            mkdir($dir, 0755, true);
        }

        return file_put_contents($outputFile, $content) !== false;
    }
}
