<?php

declare(strict_types=1);

namespace apexl\Io\middleware;

use apexl\Io\enums\HttpMethod;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Slim\App;
use Slim\Psr7\Factory\ResponseFactory;
use Slim\Routing\RoutingResults;

/**
 * Answers CORS preflight (OPTIONS) requests. Preflight always succeeds with a 204
 * (a non-2xx would make the browser block the real request and mask its true
 * status), and a known path additionally advertises the methods it accepts via the
 * Allow header. An unknown path still gets a 204 — the subsequent real request is
 * what surfaces the 404/405.
 *
 * Sits inside {@see AddCorsHeaders} (so the preflight response still receives CORS
 * headers) and before RoutingMiddleware, so a preflight never dispatches a
 * controller or its per-route permission middleware.
 */
final readonly class HandleCorsPreflight implements MiddlewareInterface
{
    public function __construct(
        private App $application,
        private ResponseFactory $responseFactory,
    ) {}

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        if (!HttpMethod::OPTIONS->equals($request->getMethod())) {
            return $handler->handle($request);
        }

        $results = $this->application->getRouteResolver()->computeRoutingResults(
            $request->getUri()->getPath(),
            $request->getMethod(),
        );

        $response = $this->responseFactory->createResponse(204);

        // Unknown path: succeed anyway (keep preflight CORS-friendly) but don't
        // advertise an Allow — the real request will surface the 404.
        if ($results->getRouteStatus() === RoutingResults::NOT_FOUND) {
            return $response;
        }

        $allowed = array_values(array_unique([...$results->getAllowedMethods(), $request->getMethod()]));

        return $response->withHeader('Allow', implode(', ', $allowed));
    }
}
