<?php
/// file: app/Core/Router.php

declare(strict_types=1);

namespace App\Core;

final class Router
{
    /** @var list<array{method:string,pattern:string,regex:string,paramNames:list<string>,handler:string,middleware:list<string>}> */
    private array $routes = [];

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

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

    /**
     * @param list<string> $middleware
     */
    private function add(string $method, string $pattern, string $handler, array $middleware): void
    {
        $paramNames = [];
        $regex = preg_replace_callback('/\{([a-zA-Z_][a-zA-Z0-9_]*)\}/', function ($m) use (&$paramNames) {
            $paramNames[] = $m[1];
            return '([^/]+)';
        }, $pattern);
        if ($regex === null) {
            return;
        }
        $regex = '#^' . $regex . '$#';
        $this->routes[] = [
            'method' => $method,
            'pattern' => $pattern,
            'regex' => $regex,
            'paramNames' => $paramNames,
            'handler' => $handler,
            'middleware' => $middleware,
        ];
    }

    public function dispatch(Request $request, App $app): Response
    {
        $method = $request->method();
        $uri = $request->uri();
        if ($uri !== '/' && str_ends_with($uri, '/')) {
            $uri = rtrim($uri, '/') ?: '/';
        }
        foreach ($this->routes as $route) {
            if ($route['method'] !== $method) {
                continue;
            }
            if (! preg_match($route['regex'], $uri, $matches)) {
                continue;
            }
            array_shift($matches);
            $params = [];
            foreach ($route['paramNames'] as $i => $name) {
                $params[$name] = $matches[$i] ?? '';
            }
            $req = $request->withParams($params);
            foreach ($route['middleware'] as $mw) {
                $instance = $app->make($mw);
                if (method_exists($instance, 'handle')) {
                    $res = $instance->handle($req, $app);
                    if ($res instanceof Response) {
                        return $res;
                    }
                }
            }
            [$class, $action] = explode('@', $route['handler']);
            $controller = $app->make($class);
            if (! method_exists($controller, $action)) {
                throw new \RuntimeException("Handler {$route['handler']} not found.");
            }
            return $controller->{$action}($req);
        }
        $notFound = $app->make(\App\Controllers\ErrorController::class);
        return $notFound->notFound($request);
    }
}
