<?php
declare(strict_types=1);

namespace App\Framework\Http\Middlewares;

use App\Framework\Auth\Auth;
use App\Framework\Config\TypedConfiguration;
use App\Framework\Http\HttpMiddleware;
use App\Framework\Http\MiddlewareContext;
use App\Framework\Http\MiddlewarePriority;
use App\Framework\Http\MiddlewarePriorityAttribute;
use App\Framework\Http\RequestStateManager;
use App\Framework\Meta\MetaData;
use App\Framework\Router\ActionResult;
use App\Framework\Router\Exception\RouteNotFound;
use App\Framework\Router\HttpRouter;
use App\Framework\Router\Result\ContentNegotiationResult;
use App\Framework\Router\Result\JsonResult;
use App\Framework\Router\Result\Redirect;
use App\Framework\Router\Result\ViewResult;
use App\Framework\Router\RouteDispatcher;

#[MiddlewarePriorityAttribute(MiddlewarePriority::ROUTING)]
final readonly class RoutingMiddleware implements HttpMiddleware
{
    public function __construct(
        private HttpRouter $router,
        private RouteDispatcher $dispatcher,
        private TypedConfiguration $config,
    ) {}

    /**
     * Invokes the middleware logic to process a request and response through the provided context.
     * It enriches the request based on the matched route and dispatches the corresponding controller logic.
     * Then, it updates the context with the enriched request and passes it to the next middleware.
     *
     * @param MiddlewareContext $context The context containing the request and response.
     * @param callable $next The next middleware to invoke in the chain.
     * @param RequestStateManager $stateManager
     * @return MiddlewareContext The updated middleware context after processing.
     * @throws RouteNotFound If the route cannot be matched in the current context.
     */
    public function __invoke(MiddlewareContext $context, callable $next, RequestStateManager $stateManager): MiddlewareContext
    {
        $request = $context->request;
        $routeContext = $this->router->match($request);

        if (!$routeContext->isSuccess()) {
            throw new RouteNotFound($routeContext->path);
        }

        if (!$this->config->app->debug && in_array(Auth::class, $routeContext->match->route->attributes)) {

            #debug($request->server->getClientIp());
            $wireguardIp = '172.20.0.1';

            $ip = $request->server->getClientIp();
            if ($ip->value !== $wireguardIp) {
                throw new RouteNotFound($routeContext->path);
            }
        }


        // Controller-Logik ausführen
        $result = $this->dispatcher->dispatch($routeContext);

        if ($result instanceof ContentNegotiationResult) {
            $result = $this->contentNegotiation($result);
        }

        $stateManager->set('controllerResult', $result);

        // Erstellen eines angereicherten Requests mit dem Controller-Ergebnis
        #$enrichedRequest = new EnrichedRequest($request, $result);

        // Kontext mit dem angereicherten Request aktualisieren
        #$updatedContext = new MiddlewareContext($enrichedRequest, $context->response);

        $updatedContext = $context;

        // Nächste Middleware aufrufen
        return $next($updatedContext);
    }

    private function contentNegotiation(ContentNegotiationResult $response): ActionResult
    {
        $isJson = true;

        if ($isJson) {
            return new JsonResult($response->jsonPayload);
        }
        if ($response->redirectTo !== null) {
            return new Redirect($response->redirectTo);
        }
        if ($response->viewTemplate !== null) {
            return new ViewResult(template: $response->viewTemplate,metaData: new MetaData(''), data: $response->viewData);
        }
        // Optional: Fallback, z.B. Fehler- oder Defaultseite
        return new JsonResult(['message' => 'Not found']);
    }
}
