<?php
declare(strict_types=1);

namespace App\Framework\Http;

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

final readonly class MiddlewareInvoker
{
    private DefaultLogger $logger;

    public function __construct(
        private Container $container
    ) {
        $this->logger = $this->container->get(DefaultLogger::class);
    }

    /**
     * Führt eine einzelne Middleware aus und gibt den resultierenden Kontext zurück
     */
    public function invoke(
        HttpMiddleware|string $middleware,
        MiddlewareContext $context,
        callable $next,
        RequestStateManager $stateManager
    ): MiddlewareContext {
        // Middleware-Instanz holen, falls ein Klassenname übergeben wurde
        if (is_string($middleware)) {
            $middleware = $this->container->get($middleware);
        }

        // Debug-Ausgabe vor der Ausführung
        $this->logger->debug('VOR: Middleware ' . get_class($middleware) .
            ' - hasResponse: ' . ($context->hasResponse() ? 'ja' : 'nein'));

        try {
            // Middleware ausführen
            /* @var $middleware HttpMiddleware */
            $resultContext = $middleware($context, $next, $stateManager);

            // Typüberprüfung
            if (!$resultContext instanceof MiddlewareContext) {
                throw new \RuntimeException(sprintf(
                    'Middleware %s hat keinen MiddlewareContext zurückgegeben, sondern %s',
                    get_class($middleware),
                    is_object($resultContext) ? get_class($resultContext) : gettype($resultContext)
                ));
            }

            // Debug-Ausgabe nach der Ausführung
            $this->logger->debug('NACH: Middleware ' . get_class($middleware) .
                ' - hasResponse: ' . ($resultContext->hasResponse() ? 'ja' : 'nein'));

            return $resultContext;
        } catch (\Throwable $e) {
            $this->logger->error('Fehler in Middleware ' . get_class($middleware) . ': ' . $e->getMessage());

            throw $e;

            // Bei Fehler den ursprünglichen Kontext zurückgeben
            return $context;
        }
    }
}
