<?php
declare(strict_types=1);

namespace App\Framework\Discovery;

use App\Framework\Attributes\Route;
use App\Framework\Auth\AuthMapper;
use App\Framework\Cache\Cache;
use App\Framework\CommandBus\CommandHandlerMapper;
use App\Framework\Config\Configuration;
use App\Framework\Core\AttributeMapper;
use App\Framework\Core\Events\EventHandlerMapper;
use App\Framework\Core\PathProvider;
use App\Framework\Core\RouteMapper;
use App\Framework\Database\Migration\Migration;
use App\Framework\DI\Container;
use App\Framework\DI\Initializer;
use App\Framework\DI\InitializerMapper;
use App\Framework\Discovery\Results\DiscoveryResults;
use App\Framework\Http\HttpMiddleware;
use App\Framework\QueryBus\QueryHandlerMapper;
use App\Framework\View\DomProcessor;
use App\Framework\View\StringProcessor;

/**
 * Bootstrapper für den Discovery-Service
 * Ersetzt die alte AttributeDiscoveryService-Integration
 */
final readonly class DiscoveryServiceBootstrapper
{
    public function __construct(private Container $container) {}

    /**
     * Bootstrapt den Discovery-Service und führt die Discovery durch
     */
    public function bootstrap(): DiscoveryResults
    {
        $pathProvider = $this->container->get(PathProvider::class);
        $cache = $this->container->get(Cache::class);
        $config = $this->container->get(Configuration::class);

        // Discovery-Service erstellen
        $discoveryService = $this->createDiscoveryService($pathProvider, $cache, $config);

        // Discovery durchführen
        $results = $discoveryService->discover();

        // Ergebnisse im Container registrieren
        $this->container->singleton(DiscoveryResults::class, $results);
        $this->container->instance(UnifiedDiscoveryService::class, $discoveryService);

        // Führe Initializers aus (Kompatibilität mit bestehendem Code)
        $this->executeInitializers($results);

        return $results;
    }

    /**
     * Erstellt den Discovery-Service mit der richtigen Konfiguration
     */
    private function createDiscoveryService(
        PathProvider $pathProvider,
        Cache $cache,
        Configuration $config
    ): UnifiedDiscoveryService {
        $useCache = $config->get('discovery.use_cache', true);
        $showProgress = $config->get('discovery.show_progress', false);

        // Attribute-Mapper aus Konfiguration oder Standard-Werte
        $attributeMappers = $config->get('discovery.attribute_mappers', [
            RouteMapper::class,
            EventHandlerMapper::class,
            \App\Framework\EventBus\EventHandlerMapper::class,
            QueryHandlerMapper::class,
            CommandHandlerMapper::class,
            InitializerMapper::class,
            AuthMapper::class,
        ]);

        // Ziel-Interfaces aus Konfiguration oder Standard-Werte
        $targetInterfaces = $config->get('discovery.target_interfaces', [
            AttributeMapper::class,
            HttpMiddleware::class,
            DomProcessor::class,
            StringProcessor::class,
            Migration::class,
            #Initializer::class,
        ]);

        return new UnifiedDiscoveryService(
            $pathProvider,
            $cache,
            $attributeMappers,
            $targetInterfaces,
            $useCache,
            $showProgress
        );
    }

    /**
     * Führt die gefundenen Initializers aus (Kompatibilität)
     */
    private function executeInitializers(DiscoveryResults $results): void
    {
        $initializerResults = $results->get(Initializer::class);

        #debug($initializerResults);

        foreach ($initializerResults as $initializerData) {
            if (!isset($initializerData['class'])) {
                continue;
            }

            try {
                $className = $initializerData['class'];
                $methodName = $initializerData['method'] ?? '__invoke';
                $returnType = $initializerData['return'] ?? null;

                #debug($initializerData);


                $instance = $this->container->invoker->invoke($className, $methodName);


                // Registriere das Ergebnis im Container falls Return-Type angegeben
                if ($returnType && $instance !== null) {
                    $this->container->instance($returnType, $instance);
                }
            } catch (\Throwable $e) {
                debug('Fehler beim Ausführen des Initializers: ' . $e->getMessage());
                error_log("Fehler beim Ausführen des Initializers {$className}: " . $e->getMessage());
            }
        }
    }

    /**
     * Führt einen inkrementellen Discovery-Scan durch
     */
    public function incrementalBootstrap(): DiscoveryResults
    {
        if (!$this->container->has(UnifiedDiscoveryService::class)) {
            // Fallback auf vollständigen Bootstrap
            return $this->bootstrap();
        }

        $discoveryService = $this->container->get(UnifiedDiscoveryService::class);
        $results = $discoveryService->incrementalDiscover();

        // Aktualisiere Container
        $this->container->instance(DiscoveryResults::class, $results);

        return $results;
    }

    /**
     * Hilfsmethode um zu prüfen, ob Discovery erforderlich ist
     */
    public function isDiscoveryRequired(): bool
    {
        if (!$this->container->has(UnifiedDiscoveryService::class)) {
            return true;
        }

        $discoveryService = $this->container->get(UnifiedDiscoveryService::class);
        return $discoveryService->shouldRescan();
    }
}
