<?php

declare(strict_types=1);

namespace App\Framework\Http;

use App\Framework\DI\Container;
use App\Framework\Logging\Logger;

final readonly class HttpMiddlewareChain implements HttpMiddlewareChainInterface
{
    private MiddlewareInvoker $invoker;

    private Logger $logger;

    private RequestStateManager $stateManager;

    public function __construct(
        private array     $middlewares,
        private Container $container
    ) {
        $this->invoker = new MiddlewareInvoker($this->container);
        $this->logger = $this->container->get(Logger::class);
    }

    public function handle(Request $request): Response
    {
        // Kontext mit dem Request erstellen
        $context = new MiddlewareContext($request);

        $this->stateManager = new MiddlewareStateManager()->forRequest($request);

        // Start der Middleware-Chain loggen
        error_log("🚀 MIDDLEWARE CHAIN START - URI: {$request->path}, Method: {$request->method->value}, Middleware-Count: " . count($this->middlewares));

        // Middleware-Stack durchlaufen
        $resultContext = $this->doProcessMiddlewareStack($context, 0);

        // Ende der Middleware-Chain loggen
        if ($resultContext->hasResponse()) {
            error_log("✅ MIDDLEWARE CHAIN COMPLETE - Final Response Status: {$resultContext->response?->status->value}");

            return $resultContext->response;
        }

        // Wenn keine Response vorhanden ist, ist das ein Fehler in der Middleware-Konfiguration
        error_log("❌ MIDDLEWARE CHAIN FAILED - No response created after processing all middlewares");

        throw new \RuntimeException(sprintf(
            'Keine Response nach Middleware-Chain erstellt. Stellen Sie sicher, dass eine DefaultResponseMiddleware konfiguriert ist. URI: %s, Method: %s, Middleware-Count: %d',
            $resultContext->request->path,
            $resultContext->request->method->value,
            count($this->middlewares)
        ));
    }

    private function doProcessMiddlewareStack(MiddlewareContext $context, int $index): MiddlewareContext
    {
        // Wenn das Ende der Middleware-Kette erreicht ist
        if ($index >= count($this->middlewares)) {
            $this->logDebug("Ende der Middleware-Kette erreicht", $context, $index);

            return $context;
        }

        $middleware = $this->middlewares[$index];
        $middlewareName = $this->getMiddlewareName($middleware);

        // Start der Middleware loggen
        error_log("🔄 MIDDLEWARE #{$index} START: {$middlewareName}");

        // Status VOR der Middleware loggen
        $this->logDebug("VOR Middleware #{$index} ({$middlewareName})", $context, $index);

        // Next-Handler erstellen, der zur nächsten Middleware weiterleitet
        $next = new readonly class ($this, $index, $middlewareName) implements Next {
            public function __construct(
                private HttpMiddlewareChain $chain,
                private int                 $index,
                private string              $middlewareName
            ) {
            }

            public function __invoke(MiddlewareContext $context): MiddlewareContext
            {
                // Status beim Aufruf von $next() loggen
                $this->chain->logDebug("NEXT aufgerufen in #{$this->index} ({$this->middlewareName})", $context, $this->index);

                // Zur nächsten Middleware weitergehen
                $resultContext = $this->chain->processMiddlewareStack($context, $this->index + 1);

                // Status beim Rückgabewert von $next() loggen
                $this->chain->logDebug("NEXT Rückgabe an #{$this->index} ({$this->middlewareName})", $resultContext, $this->index);

                // Detaillierte Prüfung auf verlorene Response
                if ($context->hasResponse() && ! $resultContext->hasResponse()) {
                    $this->chain->logError("RESPONSE VERLOREN zwischen Middleware #{$this->index} ({$this->middlewareName}) und nachfolgenden Middlewares!");
                }

                return $resultContext;
            }
        };

        // Response-Status VOR der Middleware merken
        $hadResponseBefore = $context->hasResponse();
        $responseBeforeStatus = $hadResponseBefore ? $context->response?->status->value : null;

        // Middleware mit dem Invoker ausführen
        $resultContext = $this->invoker->invoke($middleware, $context, $next, $this->stateManager);

        // Response-Status NACH der Middleware prüfen
        $hasResponseAfter = $resultContext->hasResponse();
        $responseAfterStatus = $hasResponseAfter ? $resultContext->response?->status->value : null;

        // Detaillierte Response-Analyse
        $this->analyzeResponseChanges(
            $middlewareName,
            $index,
            $hadResponseBefore,
            $hasResponseAfter,
            $responseBeforeStatus,
            $responseAfterStatus
        );

        // Status NACH der Middleware loggen
        $this->logDebug("NACH Middleware #{$index} ({$middlewareName})", $resultContext, $index);

        return $resultContext;
    }

    /**
     * Ermittelt einen lesbaren Namen für die Middleware
     */
    private function getMiddlewareName(mixed $middleware): string
    {
        if (is_string($middleware)) {
            return $middleware;
        }

        if (is_object($middleware)) {
            $className = get_class($middleware);

            return substr($className, strrpos($className, '\\') + 1);
        }

        return gettype($middleware);
    }

    /**
     * Analysiert Response-Änderungen durch eine Middleware
     */
    private function analyzeResponseChanges(
        string $middlewareName,
        int $index,
        bool $hadResponseBefore,
        bool $hasResponseAfter,
        ?int $responseBeforeStatus,
        ?int $responseAfterStatus
    ): void {
        // Response wurde erstellt
        if (! $hadResponseBefore && $hasResponseAfter) {
            error_log("✅ RESPONSE CREATED by Middleware #{$index} ({$middlewareName}) - Status: {$responseAfterStatus}");

            return;
        }

        // Response wurde verloren
        if ($hadResponseBefore && ! $hasResponseAfter) {
            error_log("❌ RESPONSE LOST by Middleware #{$index} ({$middlewareName}) - Previous Status: {$responseBeforeStatus}");

            return;
        }

        // Response-Status wurde geändert
        if ($hadResponseBefore && $hasResponseAfter && $responseBeforeStatus !== $responseAfterStatus) {
            error_log("🔄 RESPONSE MODIFIED by Middleware #{$index} ({$middlewareName}) - Status: {$responseBeforeStatus} → {$responseAfterStatus}");

            return;
        }

        // Response blieb unverändert (normal)
        if ($hadResponseBefore && $hasResponseAfter && $responseBeforeStatus === $responseAfterStatus) {
            error_log("➡️ RESPONSE PASSED THROUGH Middleware #{$index} ({$middlewareName}) - Status: {$responseAfterStatus}");

            return;
        }

        // Keine Response vor und nach der Middleware (normal für frühe Middlewares)
        // @phpstan-ignore booleanNot.alwaysTrue
        if (! $hadResponseBefore && ! $hasResponseAfter) {
            error_log("⚪ NO RESPONSE before/after Middleware #{$index} ({$middlewareName})");
        }
    }

    /**
     * Debug-Logging mit Context-Informationen
     */
    public function logDebug(string $message, MiddlewareContext $context, int $index): void
    {
        $responseStatus = $context->hasResponse() ?
            "Response✅ (Status: " . ($context->response->status->value ?? 'unknown') . ")" :
            "Response❌";
    }

    /**
     * Info-Logging für wichtige Events
     */
    private function logInfo(string $message, array $context = []): void
    {
        $this->logger->info('Middleware Chain: {message}', array_merge([
            'message' => $message,
            'component' => 'MiddlewareChain',
        ], $context));
    }

    /**
     * Makes processMiddlewareStack accessible to anonymous NextHandler class
     */
    public function processMiddlewareStack(MiddlewareContext $context, int $index): MiddlewareContext
    {
        return $this->doProcessMiddlewareStack($context, $index);
    }

    /**
     * Error-Logging für Probleme
     */
    public function logError(string $message, array $context = []): void
    {
        $this->logger->error('Middleware Chain ERROR: {message}', array_merge([
            'message' => $message,
            'component' => 'MiddlewareChain',
            'stack_trace' => debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5),
        ], $context));
    }
}
