<?php

declare(strict_types=1);

namespace App\Core\Routing;

use App\Core\Middleware\MiddlewareInterface;

final class Router
{
    /** @var Route[] */
    private array $routes = [];

    public function get(string $path, callable $handler, array $middleware = []): void
    {
        $this->add('GET', $path, $handler, $middleware);
    }

    public function post(string $path, callable $handler, array $middleware = []): void
    {
        $this->add('POST', $path, $handler, $middleware);
    }

    public function add(string $method, string $path, callable $handler, array $middleware = []): void
    {
        $this->routes[] = new Route(strtoupper($method), $this->normalizePath($path), $handler, $middleware);
    }

    public function dispatch(string $method, string $uri, string $basePath = ''): mixed
    {
        $normalizedMethod = strtoupper($method);
        $requestPath = parse_url($uri, PHP_URL_PATH) ?: '/';

        if ($basePath !== '' && str_starts_with($requestPath, $basePath)) {
            $requestPath = substr($requestPath, strlen($basePath)) ?: '/';
        }

        $normalizedPath = $this->normalizePath($requestPath);

        foreach ($this->routes as $route) {
            if ($route->method !== $normalizedMethod || $route->path !== $normalizedPath) {
                continue;
            }

            $runner = array_reduce(
                array_reverse($route->middleware),
                function (callable $next, MiddlewareInterface $middleware): callable {
                    return static fn () => $middleware->handle($next);
                },
                static fn () => ($route->handler)()
            );

            return $runner();
        }

        http_response_code(404);
        echo '404 Not Found';
        return null;
    }

    private function normalizePath(string $path): string
    {
        $path = '/' . trim($path, '/');
        return $path === '//' ? '/' : $path;
    }
}
