<?php

declare(strict_types=1);

namespace Flowaxy\Bootstrap;

use Flowaxy\Core\System\PathResolver;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use RuntimeException;
use Throwable;

use function class_exists;
use function file_exists;
use function is_readable;
use function is_dir;
use function is_array;
use function in_array;
use function strpos;
use function basename;
use function rtrim;
use function str_replace;
use function is_callable;
use function defined;
use function define;

use const DIRECTORY_SEPARATOR;

// Ініціалізатор автозавантажувача класів
final class AutoloaderInitializer
{
    // Ініціалізація автозавантажувача та реєстрація ClassAutoloader
    public static function initialize(callable $buildClassMap): ?object
    {
        if (!class_exists(PathResolver::class)) {
            return null;
        }

        $flowaxyDir = PathResolver::flowaxy();
        $ds = defined('DS') ? DS : DIRECTORY_SEPARATOR;

        // Спочатку завантажуємо інтерфейси
        $autoloaderInterfaceFile = $flowaxyDir . $ds . 'Core' . $ds . 'Contracts' . $ds . 'AutoloaderInterface.php';
        if (file_exists($autoloaderInterfaceFile) && is_readable($autoloaderInterfaceFile)) {
            require_once $autoloaderInterfaceFile;
        }

        $loggerInterfaceFile = $flowaxyDir . $ds . 'Core' . $ds . 'Contracts' . $ds . 'LoggerInterface.php';
        if (file_exists($loggerInterfaceFile) && is_readable($loggerInterfaceFile)) {
            require_once $loggerInterfaceFile;
        }

        // Тепер завантажуємо ClassAutoloader
        $autoloaderFile = $flowaxyDir . $ds . 'Core' . $ds . 'System' . $ds . 'Autoloader' . $ds . 'ClassAutoloader.php';

        // Fallback для зворотної сумісності
        $engineDir = PathResolver::engine();
        if (!file_exists($autoloaderFile) || !is_readable($autoloaderFile)) {
            $autoloaderFile = $engineDir . $ds . 'core' . $ds . 'system' . $ds . 'ClassAutoloader.php';
        }

        if (!file_exists($autoloaderFile) || !is_readable($autoloaderFile)) {
            throw new RuntimeException('ClassAutoloader file not found or not readable: ' . $autoloaderFile);
        }

        require_once $autoloaderFile;

        // Перевіряємо правильний namespace
        if (!class_exists('Flowaxy\Core\System\Autoloader\ClassAutoloader') && !class_exists('Flowaxy\Core\System\ClassAutoloader') && !class_exists('ClassAutoloader')) {
            throw new RuntimeException('ClassAutoloader class not found after require');
        }

        // Константи для базових шляхів
        if (!defined('ENGINE_PATH_CORE')) {
            define('ENGINE_PATH_CORE', 'core/');
        }
        if (!defined('ENGINE_PATH_INFRA')) {
            define('ENGINE_PATH_INFRA', 'infrastructure/');
        }
        if (!defined('ENGINE_PATH_DOMAIN')) {
            define('ENGINE_PATH_DOMAIN', 'domain/');
        }
        // ENGINE_PATH_APP removed - Application layer moved to Domain
        if (!defined('ENGINE_PATH_INTERFACE')) {
            define('ENGINE_PATH_INTERFACE', 'interface/');
        }

        try {
            // Використовуємо новий namespace з fallback
            $autoloaderClass = class_exists('Flowaxy\Core\System\Autoloader\ClassAutoloader')
                ? 'Flowaxy\Core\System\Autoloader\ClassAutoloader'
                : (class_exists('Flowaxy\Core\System\ClassAutoloader')
                    ? 'Flowaxy\Core\System\ClassAutoloader'
                    : 'ClassAutoloader');
            // ВАЖЛИВО: rootDir для автолоадера має бути коренем проекту (workbench/),
            // щоб fallback-пошук `rootDir/flowaxy/...` працював коректно.
            $autoloader = new $autoloaderClass(PathResolver::root());
            $autoloader->enableMissingClassLogging(true);

            // Додавання class map
            if (is_callable($buildClassMap)) {
                $classMap = $buildClassMap();
                if (is_array($classMap) && !empty($classMap)) {
                    $autoloader->addClassMap($classMap);
                }
            }

            // Автоматичне рекурсивне сканування всіх директорій flowaxy/ для ленивого завантаження класів
            $classDirectories = self::scanFlowaxyDirectories($flowaxyDir, $ds);

            if (!empty($classDirectories)) {
                $autoloader->addDirectories($classDirectories);
            }

            $autoloader->register();
            $GLOBALS['engineAutoloader'] = $autoloader;

            return $autoloader;
        } catch (Throwable $e) {
            throw new RuntimeException('Failed to initialize ClassAutoloader: ' . $e->getMessage(), 0, $e);
        }
    }

    // Автоматичне рекурсивне сканування всіх директорій flowaxy/ для ленивого завантаження класів
    private static function scanFlowaxyDirectories(string $flowaxyDir, string $ds): array
    {
        if (!is_dir($flowaxyDir) || !is_readable($flowaxyDir)) {
            return [];
        }

        $directories = [];
        $excludedDirs = ['.', '..', '.git', '.svn', 'node_modules', 'vendor', 'tests', 'test', 'testing'];

        try {
            $iterator = new RecursiveIteratorIterator(
                new RecursiveDirectoryIterator(
                    $flowaxyDir,
                    RecursiveDirectoryIterator::SKIP_DOTS | RecursiveDirectoryIterator::FOLLOW_SYMLINKS
                ),
                RecursiveIteratorIterator::SELF_FIRST
            );

            foreach ($iterator as $file) {
                if (!$file->isDir()) {
                    continue;
                }

                $dirPath = $file->getRealPath();
                if ($dirPath === false) {
                    continue;
                }

                $dirName = basename($dirPath);

                // Виключаємо службові директорії та content/ (там немає класів)
                if (in_array($dirName, $excludedDirs, true)) {
                    continue;
                }

                // Виключаємо директорії, які містять content/ в шляху
                if (strpos($dirPath, $ds . 'content' . $ds) !== false) {
                    continue;
                }

                // Додаємо директорію, якщо вона ще не додана
                $normalizedPath = rtrim($dirPath, $ds) . $ds;
                if (!in_array($normalizedPath, $directories, true)) {
                    $directories[] = $normalizedPath;
                }
            }
        } catch (Throwable $e) {
            // У випадку помилки повертаємо базові директорії
            return self::getBaseDirectories($flowaxyDir, $ds);
        }

        // Якщо сканування не дало результатів, повертаємо базові директорії
        if (empty($directories)) {
            return self::getBaseDirectories($flowaxyDir, $ds);
        }

        return $directories;
    }

    // Отримання базових директорій flowaxy/ (fallback якщо рекурсивне сканування не працює)
    private static function getBaseDirectories(string $flowaxyDir, string $ds): array
    {
        $baseDirs = [
            'Bootstrap',
            'Core',
            'Core/System',
            'Core/System/Autoloader',
            'Core/System/Container',
            'Core/System/Kernel',
            'Core/System/Environment',
            'Core/System/Base',
            'Core/System/Auth',
            'Core/System/Auth/Guards',
            'Core/System/Auth/Services',
            'Core/System/Errors',
            'Core/System/Errors/Services',
            'Core/System/Errors/Templates',
            'Core/System/Registry',
            'Core/System/Tasks',
            'Core/System/Queue',
            'Core/Hooks',
            'Core/Routing',
            'Core/Events',
            'Core/Contracts',
            // Interface layer (CLI/HTTP/API/UI)
            'Interface',
            'Interface/CLI',
            'Interface/CLI/Commands',
            'Interface/Http',
            'Interface/Http/Controllers',
            'Interface/Http/Middleware',
            'Interface/API',
            'Interface/UI',
            'Contracts',
            'Infrastructure',
            'Infrastructure/Cache',
            'Infrastructure/Cache/Drivers',
            'Infrastructure/Config',
            'Infrastructure/Filesystem',
            'Infrastructure/Filesystem/Archive',
            'Infrastructure/Logging',
            'Infrastructure/Mail',
            'Infrastructure/Persistence',
            'Infrastructure/Persistence/Repositories',
            'Infrastructure/Security',
            'Infrastructure/Compilers',
            'Infrastructure/Assets',
            'Domain',
            'Domain/User',
            'Domain/User/Entities',
            'Domain/User/Services',
            'Domain/Plugin',
            'Domain/Plugin/Entities',
            'Domain/Plugin/Services',
            'Domain/Theme',
            'Domain/Theme/Entities',
            'Domain/Theme/Services',
            'Domain/Content',
            'Domain/Settings',
            'Interface',
            'Interface/Http',
            'Interface/Http/controllers',
            'Interface/Http/router',
            'Interface/Http/Middleware',
            'Interface/CLI',
            'Interface/UI',
            'Interface/API',
            'Support',
            'Support/Base',
            'Support/Helpers',
            'Support/Managers',
            'Support/Facades',
            'Support/Containers',
            'Support/Isolation',
            'Support/Security',
            'Support/Theme',
            'Support/Validation',
            'Support/Validators',
            'Support/Traits',
            'Support/Actions',
            'Support/Services',
        ];

        $directories = [];
        foreach ($baseDirs as $dir) {
            $fullPath = $flowaxyDir . $ds . str_replace('/', $ds, $dir);
            if (is_dir($fullPath) && is_readable($fullPath)) {
                $directories[] = rtrim($fullPath, $ds) . $ds;
            }
        }

        return $directories;
    }
}
