<?php

declare(strict_types=1);

namespace App\Framework\Reflection\Factory;

use App\Framework\Core\ValueObjects\ClassName;
use App\Framework\Reflection\Cache\AttributeCache;
use App\Framework\Reflection\Cache\ClassCache;
use App\Framework\Reflection\Cache\MetadataCacheManager;
use App\Framework\Reflection\Cache\MethodCache;
use App\Framework\Reflection\Cache\ParameterCache;
use App\Framework\Reflection\ReflectionCache;

/**
 * Factory for creating and coordinating reflection caches with optimization
 */
final readonly class ReflectionCacheFactory
{
    // Common framework classes that should be pre-warmed
    private const FRAMEWORK_CORE_CLASSES = [
        'App\Framework\Core\Application',
        'App\Framework\DI\Container',
        'App\Framework\Http\HttpRequest',
        'App\Framework\Router\HttpRouter',
        'App\Framework\Database\EntityManager',
        'App\Framework\Cache\ServiceCacheDecorator',
        'App\Framework\Logging\Logger',
        'App\Framework\Config\Configuration',
    ];

    public function __construct()
    {
    }

    public function create(): ReflectionCache
    {
        $metadataCache = $this->createMetadataCache();
        $attributeCache = new AttributeCache();
        $parameterCache = new ParameterCache();
        $methodCache = new MethodCache($parameterCache, $attributeCache);
        $classCache = new ClassCache($methodCache, $attributeCache, $metadataCache);

        return new ReflectionCache(
            $classCache,
            $methodCache,
            $parameterCache,
            $attributeCache,
            $metadataCache
        );
    }

    /**
     * Create cache with optimized settings for production
     */
    public function createOptimized(): ReflectionCache
    {
        $cache = $this->create();

        // Pre-warm with framework core classes
        $this->warmupCache($cache, self::FRAMEWORK_CORE_CLASSES);

        return $cache;
    }

    /**
     * Create cache with pre-warmed common classes
     * @param array<string> $classNames
     */
    public function createWithWarmup(array $classNames = []): ReflectionCache
    {
        $cache = $this->create();

        // Combine provided classes with framework core classes
        $allClasses = array_unique(array_merge(self::FRAMEWORK_CORE_CLASSES, $classNames));

        $this->warmupCache($cache, $allClasses);

        return $cache;
    }

    /**
     * Create cache for development with minimal warmup
     */
    public function createForDevelopment(): ReflectionCache
    {
        // In development, don't pre-warm to allow for class reloading
        return $this->create();
    }

    /**
     * Warm up cache with specified classes
     * @param array<string> $classNames
     */
    private function warmupCache(ReflectionCache $cache, array $classNames): void
    {
        foreach ($classNames as $className) {
            try {
                if (! empty($className) && ClassName::create($className)->exists()) {
                    $cache->classCache->getClass(ClassName::create($className));
                }
            } catch (\Throwable) {
                // Silently skip classes that can't be loaded
                continue;
            }
        }
    }

    /**
     * Create metadata cache manager
     */
    private function createMetadataCache(): MetadataCacheManager
    {
        // Use APCu cache for cross-request metadata persistence
        $cacheDriver = extension_loaded('apcu') && apcu_enabled()
            ? new \App\Framework\Cache\Driver\ApcuCache('reflection_metadata:')
            : new \App\Framework\Cache\Driver\InMemoryCache();

        // Create serializer for cache operations
        $serializer = new \App\Framework\Serializer\Php\PhpSerializer();
        $cache = new \App\Framework\Cache\GeneralCache($cacheDriver, $serializer);

        return new MetadataCacheManager($cache);
    }

    /**
     * Get recommended classes for warmup based on application type
     * @return array<string>
     */
    public function getRecommendedWarmupClasses(string $applicationType = 'web'): array
    {
        $webClasses = [
            'App\Framework\Http\Middlewares\RoutingMiddleware',
            'App\Framework\Http\Middlewares\SecurityHeaderMiddleware',
            'App\Framework\Http\Session\SessionMiddleware',
            'App\Framework\Router\RouteDispatcher',
            'App\Framework\View\Engine',
        ];

        $apiClasses = [
            'App\Framework\Http\Responses\JsonResponse',
            'App\Framework\Api\ApiRequestTrait',
            'App\Framework\Validation\ValidationResult',
            'App\Framework\CommandBus\CommandBus',
        ];

        $cliClasses = [
            'App\Framework\Console\ConsoleApplication',
            'App\Framework\Console\ConsoleCommand',
            'App\Framework\Logging\Handlers\ConsoleHandler',
        ];

        return match ($applicationType) {
            'web' => array_merge(self::FRAMEWORK_CORE_CLASSES, $webClasses),
            'api' => array_merge(self::FRAMEWORK_CORE_CLASSES, $apiClasses),
            'cli' => array_merge(self::FRAMEWORK_CORE_CLASSES, $cliClasses),
            'all' => array_merge(self::FRAMEWORK_CORE_CLASSES, $webClasses, $apiClasses, $cliClasses),
            default => self::FRAMEWORK_CORE_CLASSES,
        };
    }
}
