<?php

namespace App\Framework\Http;

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

final readonly class HttpMiddlewareChain implements HttpMiddlewareNext
{
    private MiddlewareInvoker $invoker;
    private DefaultLogger $logger;
    private RequestStateManager $stateManager;

    public function __construct(
        private array     $middlewares,
        private Closure   $fallbackHandler,
        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);

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

        // Am Ende die Response aus dem Kontext zurückgeben
        if ($resultContext->hasResponse()) {
            return $resultContext->response;
        }

        // Warnung über fehlende Response
        $this->logger->warning('Keine Response nach Middleware-Chain - verwende Fallback-Handler', [
            'uri' => $resultContext->request->path,
            'method' => $resultContext->request->method->value,
            'middleware_count' => count($this->middlewares),
        ]);

        // Fallback-Handler aufrufen
        $response = ($this->fallbackHandler)($resultContext->request);

        if (!$response instanceof Response) {
            throw new \RuntimeException(sprintf(
                'Fallback-Handler hat keine Response zurückgegeben, sondern %s',
                is_object($response) ? get_class($response) : gettype($response)
            ));
        }

        return $response;
    }

    private function processMiddlewareStack(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);

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

        // Next-Funktion erstellen, die zur nächsten Middleware weiterleitet
        $next = function (MiddlewareContext $nextContext) use ($index, $middlewareName) {
            // Status beim Aufruf von $next() loggen
            $this->logDebug("NEXT aufgerufen in #{$index} ({$middlewareName})", $nextContext, $index);

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

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

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

            return $resultContext;
        };

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

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

        /*
        // Prüfung ob diese Middleware eine Response hinzugefügt oder entfernt hat
        if (!$context->hasResponse() && $resultContext->hasResponse()) {
            $this->logInfo("✅ Response ERSTELLT von Middleware #{$index} ({$middlewareName})", [
                'middleware_name' => $middlewareName,
                'middleware_index' => $index,
                'response_status' => $resultContext->response?->status,
            ]);
        } elseif ($context->hasResponse() && !$resultContext->hasResponse()) {
            $this->logError("❌ Response ENTFERNT von Middleware #{$index} ({$middlewareName})!", [
                'middleware_name' => $middlewareName,
                'middleware_index' => $index,
                'original_response_status' => $context->response?->status,
            ]);
        }
        */


        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);
    }

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

        /*
        $this->logger->debug('Middleware Chain: {message}', [
            'message' => $message,
            'response_status' => $responseStatus,
            'uri' => $context->request->uri ?? 'unknown',
            'method' => $context->request->method->value ?? 'unknown',
            'middleware_index' => $index,
            'has_response' => $context->hasResponse(),
            'response_code' => $context->response?->status->value,
        ]);
        */
    }

    /**
     * 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));
    }

    /**
     * Error-Logging für Probleme
     */
    private 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));
    }
}
