<?php

declare(strict_types=1);

namespace App\Framework\Core;

use App\Framework\Cache\Cache;
use App\Framework\Cache\CacheInitializer;
use App\Framework\DateTime\Clock;
use App\Framework\DateTime\SystemClock;
use App\Framework\DI\Container;
use App\Framework\DI\ContainerCompiler;
use App\Framework\DI\DefaultContainer;
use App\Framework\DI\DependencyResolver;
use App\Framework\Discovery\DiscoveryServiceBootstrapper;
use App\Framework\Http\ResponseEmitter;
use App\Framework\Logging\DefaultLogger;
use App\Framework\Logging\Logger;
use App\Framework\Performance\Contracts\PerformanceCollectorInterface;
use App\Framework\Reflection\CachedReflectionProvider;

final readonly class ContainerBootstrapper
{
    public function __construct(
        private Container $container,
    ) {
    }

    /**
     * Bootstrap container with intelligent compilation strategy
     */
    public function bootstrap(
        string $basePath,
        PerformanceCollectorInterface $collector,
    ): Container {
        // Temporarily disable compiled container to fix bootstrap issues
        // TODO: Re-enable once container interface compatibility is fixed
        // $optimizedContainer = $this->tryLoadCompiledContainer($basePath, $collector);
        // if ($optimizedContainer !== null) {
        //     return $optimizedContainer;
        // }

        // Fallback to fresh container bootstrap
        return $this->bootstrapFreshContainer($basePath, $collector);
    }

    /**
     * Try to load compiled container if valid
     */
    private function tryLoadCompiledContainer(
        string $basePath,
        PerformanceCollectorInterface $collector
    ): ?Container {
        try {
            $cacheDir = sys_get_temp_dir() . '/framework-cache';
            $compiledPath = ContainerCompiler::getCompiledContainerPath($cacheDir);

            // Quick existence check first
            if (! file_exists($compiledPath)) {
                return null;
            }

            // Create temporary fresh container to validate against
            $tempContainer = $this->createFreshContainer($basePath, $collector);

            // Create compiler for validation
            $reflectionProvider = new CachedReflectionProvider();
            $dependencyResolver = new DependencyResolver($reflectionProvider, $tempContainer);
            $compiler = new ContainerCompiler($reflectionProvider, $dependencyResolver);

            // Validate compiled container
            if (! $compiler->isCompiledContainerValid($tempContainer, $compiledPath)) {
                return null; // Invalid, will trigger fresh bootstrap
            }

            // Load and return compiled container
            $compiledContainer = ContainerCompiler::load($compiledPath);

            // Add runtime instances that can't be compiled
            $this->addRuntimeInstances($compiledContainer, $basePath, $collector);

            return $compiledContainer;

        } catch (\ParseError $e) {
            // Parse error in compiled container - delete cache and fallback
            $this->clearCompiledCache($compiledPath);
            error_log("Compiled container has syntax error, deleted cache: " . $e->getMessage());

            return null;
        } catch (\Exception $e) {
            // Any other error means we fallback to fresh container
            return null;
        }
    }

    /**
     * Bootstrap fresh container and compile for next request
     */
    private function bootstrapFreshContainer(
        string $basePath,
        PerformanceCollectorInterface $collector
    ): Container {
        $container = $this->createFreshContainer($basePath, $collector);

        // Compile for next request (async in production)
        $this->compileContainerAsync($container);

        return $container;
    }

    /**
     * Create fresh container with all bindings
     */
    private function createFreshContainer(
        string $basePath,
        PerformanceCollectorInterface $collector
    ): DefaultContainer {
        // Use the existing container or create new DefaultContainer
        $container = $this->container instanceof DefaultContainer
            ? $this->container
            : new DefaultContainer();

        $this->addRuntimeInstances($container, $basePath, $collector);
        $this->autowire($container);

        return $container;
    }

    /**
     * Add instances that must be created at runtime
     */
    private function addRuntimeInstances(
        Container $container,
        string $basePath,
        PerformanceCollectorInterface $collector
    ): void {
        // Core services that need runtime data
        $container->instance(Logger::class, new DefaultLogger());
        $container->instance(PerformanceCollectorInterface::class, $collector);
        $container->instance(Cache::class, new CacheInitializer($collector, $container)());
        $container->instance(PathProvider::class, new PathProvider($basePath));
        $container->instance(ResponseEmitter::class, new ResponseEmitter());
        $container->instance(Clock::class, new SystemClock());

        // TEMPORARY FIX: Manual RequestFactory binding until Discovery issue is resolved
        $container->singleton(\App\Framework\Http\Request::class, function ($container) {
            error_log("ContainerBootstrapper: Creating Request singleton");

            // Get Cache from container (it was just registered above)
            $frameworkCache = $container->get(\App\Framework\Cache\Cache::class);
            $parserCache = new \App\Framework\Http\Parser\ParserCache($frameworkCache);
            $parser = new \App\Framework\Http\Parser\HttpRequestParser($parserCache);
            $factory = new \App\Framework\Http\RequestFactory($parser);

            error_log("ContainerBootstrapper: About to call factory->createFromGlobals()");
            $request = $factory->createFromGlobals();
            error_log("ContainerBootstrapper: Request created successfully");

            return $request;
        });

    }

    /**
     * Compile container asynchronously for next request
     */
    private function compileContainerAsync(DefaultContainer $container): void
    {
        try {
            $cacheDir = sys_get_temp_dir() . '/framework-cache';
            $compiledPath = ContainerCompiler::getCompiledContainerPath($cacheDir);

            $reflectionProvider = new CachedReflectionProvider();
            $dependencyResolver = new DependencyResolver($reflectionProvider, $container);
            $compiler = new ContainerCompiler($reflectionProvider, $dependencyResolver);

            // Compile (async in production, sync in development)
            #$isProduction = $this->config->get('app.environment') === 'production';
            $isProduction = false;
            if ($isProduction) {
                $compiler->compileAsync($container, $compiledPath);
            } else {
                $compiler->compile($container, $compiledPath);
            }

        } catch (\Exception $e) {
            // Compilation errors should not break the application
            // In production, log this error
        }
    }

    private function autowire(Container $container): void
    {
        // Discovery service bootstrapping
        $clock = $container->get(Clock::class);
        $bootstrapper = new DiscoveryServiceBootstrapper($container, $clock);
        $results = $bootstrapper->bootstrap();
    }

    /**
     * Clear compiled container cache files
     */
    private function clearCompiledCache(string $compiledPath): void
    {
        try {
            // Delete the compiled container file
            if (file_exists($compiledPath)) {
                @unlink($compiledPath);
            }

            // Also clear related cache directory if it exists
            $cacheDir = dirname($compiledPath);
            if (is_dir($cacheDir)) {
                // Clear all cache files in the directory
                $files = glob($cacheDir . '/*');
                foreach ($files as $file) {
                    if (is_file($file)) {
                        @unlink($file);
                    }
                }
            }
        } catch (\Throwable $e) {
            // Ignore cache cleanup errors, just log them
            error_log("Error clearing compiled cache: " . $e->getMessage());
        }
    }
}
