<?php

declare(strict_types=1);

namespace App\Framework\Http;

use App\Framework\Cache\Cache;
use App\Framework\Core\ValueObjects\ClassName;
use App\Framework\DI\Container;
use App\Framework\Discovery\Results\DiscoveryRegistry;
use App\Framework\Http\Middlewares\DDoSProtectionMiddleware;
use App\Framework\Http\Middlewares\RateLimitMiddleware;
use App\Framework\Http\Middlewares\WafMiddleware;
use App\Framework\Logging\Logger;
use App\Framework\Logging\ValueObjects\LogContext;
use App\Framework\Reflection\ReflectionProvider;

final readonly class MiddlewareManager implements MiddlewareManagerInterface
{
    public HttpMiddlewareChain $chain;

    public function __construct(
        private Container        $container,
        private DiscoveryRegistry $discoveryRegistry,
        private ReflectionProvider $reflectionProvider,
        private Cache $cache,
    ) {
        $middlewares = $this->buildMiddlewareStack();

        // Debug logging mit strukturiertem Logger falls verfügbar
        if ($this->container->has(Logger::class)) {
            try {
                $logger = $this->container->get(Logger::class);
                $logger->debug('Middleware stack initialized', LogContext::withData([
                    'middleware_count' => count($middlewares),
                    'middleware_stack' => array_map(fn ($m) => basename($m), $middlewares),
                    'component' => 'MiddlewareManager',
                ]));
            } catch (\Throwable $e) {
                // Ignore logger errors during initialization
            }
        }

        $this->chain = new HttpMiddlewareChain(
            $middlewares,
            $this->container
        );
    }

    /**
     * @return array<int, string>
     */
    private function buildMiddlewareStack(): array
    {
        // Explizite Reihenfolge definieren - wichtigste zuerst
        /** @var array<int, string> */
        $explicitOrder = [
            // 0. Exception Handling - MUSS absolut zuerst kommen, um alle Exceptions zu fangen
            \App\Framework\Http\Middlewares\ExceptionHandlingMiddleware::class,

            // 1. System und Error Handling
            \App\Framework\Http\Middlewares\RequestIdMiddleware::class,

            // 2. Production Security - MUST come early to block routes
            \App\Framework\Http\Middlewares\ProductionSecurityMiddleware::class,

            // 3. Security und DDoS Protection
            DDoSProtectionMiddleware::class,
            WafMiddleware::class,

            // 4. Session - MUSS vor Auth und CSRF kommen!
            \App\Framework\Http\Session\SessionMiddleware::class,

            // 5. Security und Rate Limiting
            RateLimitMiddleware::class,
            #\App\Application\Security\Middleware\SecurityEventMiddleware::class,

            // 6. Headers und CORS
            \App\Framework\Http\Middlewares\SecurityHeaderMiddleware::class,
            \App\Framework\Http\Middlewares\RemovePoweredByMiddleware::class,
            \App\Framework\Http\Middlewares\CORSMiddleware::class,

            // 7. Authentication und CSRF (brauchen Session)
            \App\Framework\Http\Middlewares\AuthMiddleware::class,
            \App\Framework\Http\Middlewares\CsrfMiddleware::class,
            \App\Framework\Http\Middlewares\HoneypotMiddleware::class,

            // 8. Routing und Request Processing
            \App\Framework\Http\Middlewares\RoutingMiddleware::class,
            \App\Framework\Http\Middlewares\ControllerRequestMiddleware::class,

            // 9. Content und Static Files
            #\App\Framework\Http\Middlewares\ServeStaticFilesMiddleware::class,
            \App\Framework\Http\Middlewares\ResponseGeneratorMiddleware::class,
            \App\Framework\Http\Middlewares\FormDataResponseMiddleware::class, // Temporarily disabled

            // 10. Monitoring und Analytics
            \App\Framework\Analytics\Middleware\AnalyticsMiddleware::class,
            \App\Framework\Performance\Middleware\RequestPerformanceMiddleware::class,
            \App\Framework\Performance\Middleware\RoutingPerformanceMiddleware::class,
            \App\Framework\Performance\Middleware\ControllerPerformanceMiddleware::class,
            \App\Framework\Tracing\TracingMiddleware::class,

            // 11. Logging (am Ende)
            \App\Framework\Http\Middlewares\RequestLoggingMiddleware::class,
            \App\Framework\Http\Middlewares\LoggingMiddleware::class,
            \App\Framework\Performance\Middleware\PerformanceDebugMiddleware::class,
            \App\Framework\Security\RequestSigning\RequestSigningMiddleware::class,

            // 12. FALLBACK - Absolut letztes Middleware (nur wenn keine Response vorhanden)
            \App\Framework\Http\Middlewares\DefaultResponseMiddleware::class,
        ];

        // Use the refactored robust dependency resolution
        try {
            $resolved = $this->resolveDependencies($explicitOrder);
            #error_log("MiddlewareManager: Using dependency resolution - Count: " . count($resolved));

            // WICHTIG: Array umkehren, da die Liste in gewünschter Ausführungsreihenfolge definiert ist
            // aber die Middleware-Chain sie in der gegebenen Reihenfolge abarbeitet
            return array_reverse($resolved);
        } catch (\Throwable $e) {
            #error_log("MiddlewareManager: Dependency resolution failed: " . $e->getMessage());
            #error_log("MiddlewareManager: Falling back to explicit order - Count: " . count($explicitOrder));
            #foreach ($explicitOrder as $i => $middleware) {
            #    error_log("MiddlewareManager: Explicit #{$i}: " . basename($middleware));
            #}

            // WICHTIG: Array umkehren, da die Liste in gewünschter Ausführungsreihenfolge definiert ist
            return array_reverse($explicitOrder);
        }
    }

    /**
     * Resolve middleware dependencies using constructor analysis
     * @param array<int, string> $middlewares
     * @return array<int, string>
     */
    private function resolveDependencies(array $middlewares): array
    {
        $logger = $this->container->get(Logger::class);
        $resolver = new MiddlewareDependencyResolver(
            $this->reflectionProvider,
            $this->container,
            $logger
        );

        $resolved = $resolver->resolve($middlewares);

        // Log dependency resolution info
        #error_log("MiddlewareManager: Dependency resolution completed");
        #error_log("MiddlewareManager: Resolved " . $resolved->count() . " middlewares");
        #error_log("MiddlewareManager: Execution order: " . implode(' → ', array_map(fn ($m) => basename($m), $resolved->getMiddlewares())));

        // Log dependency chains for debugging
        $info = $resolved->getResolutionInfo();
        #if (! empty($info['dependency_chains'])) {
        #    error_log("MiddlewareManager: Dependency chains: " . json_encode($info['dependency_chains']));
        #}

        #if (! empty($info['circular_dependencies'])) {
        #    error_log("MiddlewareManager: WARNING - Circular dependencies detected: " . json_encode($info['circular_dependencies']));
        #}

        return $resolved->getMiddlewares();
    }

    /**
     * Get middleware dependency information for debugging
     */
    /**
     * @return array<string, mixed>
     */
    public function getDependencyInfo(): array
    {
        $explicitOrder = $this->getExplicitOrder();
        $classNameObjects = array_map(fn ($class) => ClassName::create($class), $explicitOrder);

        $logger = $this->container->get(Logger::class);
        $resolver = new MiddlewareDependencyResolver(
            $this->reflectionProvider,
            $this->container,
            $logger
        );

        return $resolver->getDependencyInfo($classNameObjects);
    }

    /**
     * Get the explicit order array (for testing and debugging)
     * @return array<int, string>
     */
    private function getExplicitOrder(): array
    {
        // Return the same explicit order array
        return [
            // 1. System und Error Handling
            \App\Framework\Http\Middlewares\RequestIdMiddleware::class,
            \App\Framework\Http\Middlewares\ExceptionHandlingMiddleware::class,

            // 4. Session - MUSS vor Auth und CSRF kommen!
            \App\Framework\Http\Session\SessionMiddleware::class,

            // 2. Security und Rate Limiting
            RateLimitMiddleware::class,
            #\App\Application\Security\Middleware\SecurityEventMiddleware::class,

            // 3. Headers und CORS
            \App\Framework\Http\Middlewares\SecurityHeaderMiddleware::class,
            \App\Framework\Http\Middlewares\RemovePoweredByMiddleware::class,
            \App\Framework\Http\Middlewares\CORSMiddleware::class,

            // 5. Authentication und CSRF (brauchen Session)
            \App\Framework\Http\Middlewares\AuthMiddleware::class,
            \App\Framework\Http\Middlewares\CsrfMiddleware::class,
            \App\Framework\Http\Middlewares\HoneypotMiddleware::class,

            // 6. Routing und Request Processing
            \App\Framework\Http\Middlewares\RoutingMiddleware::class,
            \App\Framework\Http\Middlewares\ControllerRequestMiddleware::class,

            // 7. Content und Static Files
            #\App\Framework\Http\Middlewares\ServeStaticFilesMiddleware::class,
            \App\Framework\Http\Middlewares\ResponseGeneratorMiddleware::class,
            \App\Framework\Http\Middlewares\FormDataResponseMiddleware::class, // Temporarily disabled

            // 8. Monitoring und Analytics
            \App\Framework\Analytics\Middleware\AnalyticsMiddleware::class,
            \App\Framework\Performance\Middleware\RequestPerformanceMiddleware::class,
            \App\Framework\Performance\Middleware\RoutingPerformanceMiddleware::class,
            \App\Framework\Performance\Middleware\ControllerPerformanceMiddleware::class,
            \App\Framework\Tracing\TracingMiddleware::class,

            // 9. Logging (am Ende)
            \App\Framework\Http\Middlewares\RequestLoggingMiddleware::class,
            \App\Framework\Http\Middlewares\LoggingMiddleware::class,
            \App\Framework\Performance\Middleware\PerformanceDebugMiddleware::class,
            \App\Framework\Security\RequestSigning\RequestSigningMiddleware::class,

            // 10. FALLBACK - Absolut letztes Middleware (nur wenn keine Response vorhanden)
            \App\Framework\Http\Middlewares\DefaultResponseMiddleware::class,
        ];

    }

    public function getPriorityForClass(object|string $middlewareClass): int
    {
        $className = is_string($middlewareClass) ? $middlewareClass : $middlewareClass::class;
        $className = ltrim($className, '\\');

        $cacheKey = "middleware_priority:{$className}";

        $cacheItem = $this->cache->remember($cacheKey, function () use ($className) {
            return $this->resolvePriorityFromReflection($className);
        }, 3600);

        return is_int($cacheItem->value) ? $cacheItem->value : MiddlewarePriority::BUSINESS_LOGIC->value;
    }

    private function resolvePriorityFromReflection(string $className): int
    {
        try {
            $classNameObj = ClassName::create($className);
            $attrs = $this->reflectionProvider->getAttributes($classNameObj, MiddlewarePriorityAttribute::class);

            if ($attrs->count() > 0) {
                /** @var MiddlewarePriorityAttribute|null $attr */
                $attr = $attrs->getFirstByType(MiddlewarePriorityAttribute::class)?->newInstance();
                if ($attr instanceof MiddlewarePriorityAttribute) {
                    $basePriority = $attr->priority->value;
                    $offset = $attr->offset ?? 0;

                    return $basePriority + $offset;
                }
            }
        } catch (\ReflectionException $e) {
            // Class doesn't exist or can't be reflected, use default
        }

        // Default priority
        return MiddlewarePriority::BUSINESS_LOGIC->value;
    }

    // Legacy static method for backward compatibility
    public static function getPriority(object|string $middlewareClass): int
    {
        // This is a fallback for any code that still uses the static method
        $className = is_string($middlewareClass) ? $middlewareClass : $middlewareClass::class;
        $className = ltrim($className, '\\');

        try {
            $reflection = new \ReflectionClass($className);
            $attrs = $reflection->getAttributes(MiddlewarePriorityAttribute::class);

            if ($attrs) {
                /** @var MiddlewarePriorityAttribute $attr */
                $attr = $attrs[0]->newInstance();
                $basePriority = $attr->priority->value;
                $offset = $attr->offset ?? 0;

                return $basePriority + $offset;
            }
        } catch (\ReflectionException $e) {
            // Class doesn't exist or can't be reflected, use default
        }

        return MiddlewarePriority::BUSINESS_LOGIC->value;
    }

    /**
     * Methode zum Sortieren der Middleware nach Priorität
     * @param array<int, object|string> $middlewares
     * @return array<int, object|string>
     */
    private function sortMiddlewaresByPriority(array $middlewares): array
    {
        usort($middlewares, function (object|string $a, object|string $b) {
            $priorityA = $this->getMiddlewarePriority($a);
            $priorityB = $this->getMiddlewarePriority($b);

            return $priorityB <=> $priorityA; // Höhere Priorität zuerst
        });

        return $middlewares;
    }

    // Hilfsmethode zum Abrufen der Middleware-Priorität
    private function getMiddlewarePriority(object|string $middlewareClass): int
    {
        $className = is_string($middlewareClass) ? $middlewareClass : $middlewareClass::class;
        $className = ltrim($className, '\\');

        $cacheKey = "middleware_priority_discovery:{$className}";

        $cacheItem = $this->cache->remember($cacheKey, function () use ($className) {
            // Use discovery registry to find priority
            $priorities = $this->discoveryRegistry->attributes()->get(MiddlewarePriorityAttribute::class);

            foreach ($priorities as $discoveredAttribute) {
                if ($discoveredAttribute->className->getFullyQualified() === $className) {
                    $additionalData = $discoveredAttribute->additionalData;
                    $priority = $additionalData['priority'] ?? null;
                    if (! is_int($priority)) {
                        $priority = $priority->value;
                    }

                    return $priority + ($additionalData['offset'] ?? 0);
                }
            }

            // Default priority
            return MiddlewarePriority::BUSINESS_LOGIC->value;
        }, 3600);

        return is_int($cacheItem->value) ? $cacheItem->value : MiddlewarePriority::BUSINESS_LOGIC->value;
    }

    /**
     * Clear the priority cache (useful for testing)
     */
    public function clearPriorityCache(): void
    {
        // Clear cache entries that start with our middleware priority prefix
        $this->cache->forget('middleware_priority:*');
        $this->cache->forget('middleware_priority_discovery:*');
    }

    /**
     * Get cache statistics for debugging
     *
     * @return array<string, string>
     */
    public function getCacheStats(): array
    {
        return [
            'cache_implementation' => get_class($this->cache),
            'reflection_provider' => get_class($this->reflectionProvider),
        ];
    }
}
