<?php

declare(strict_types=1);

namespace Fennec\Kernel;

use Fennec\Container\Container;
use Fennec\Exception\ExceptionHandler;
use Fennec\Http\JsonResponse;
use Fennec\Http\Request;
use Fennec\Middleware\MiddlewarePipeline;
use Fennec\Routing\Router;
use Fennec\Security\AuthenticationMiddleware;
use Fennec\Security\Firewall;
use Fennec\Security\RoleMiddleware;

/**
 * Orchestre le cycle de vie complet d'une requête : routing, firewall/middlewares,
 * résolution du controller via le Container, et traduction de toute exception
 * non gérée en JsonResponse.
 */
final class Kernel
{
    public function __construct(
        private readonly Router $router,
        private readonly Firewall $firewall,
        private readonly Container $container,
        private readonly AuthenticationMiddleware $authenticationMiddleware,
        private readonly ExceptionHandler $exceptionHandler,
    ) {
    }

    public function handle(Request $request): JsonResponse
    {
        try {
            return $this->handleUnsafe($request);
        } catch (\Throwable $exception) {
            return $this->exceptionHandler->handle($exception);
        }
    }

    private function handleUnsafe(Request $request): JsonResponse
    {
        $routeMatch = $this->router->match($request->getUri(), $request->getMethod());
        $request->setRouteParameters($routeMatch->parameters);

        $firewallRule = $this->firewall->resolveFor($request->getUri());

        $middlewares = [];

        if (!$firewallRule->isPublic) {
            $middlewares[] = $this->authenticationMiddleware;
            $middlewares[] = new RoleMiddleware($firewallRule->effectiveMinimumRole());
        }

        $pipeline = new MiddlewarePipeline($middlewares);

        return $pipeline->handle($request, function (Request $request) use ($routeMatch): JsonResponse {
            $controller = $this->container->get($routeMatch->controllerClass);
            $controllerMethod = $routeMatch->controllerMethod;

            return $controller->$controllerMethod($request);
        });
    }
}
