<?php

declare(strict_types=1);

namespace Core;

final class Router
{
    private array $routes = [];

    public function get(string $path, array $action): void
    {
        $this->addRoute('GET', $path, $action);
    }

    public function post(string $path, array $action): void
    {
        $this->addRoute('POST', $path, $action);
    }

    private function addRoute(string $method, string $path, array $action): void
    {
        $pattern = preg_replace('#\{([a-zA-Z_][a-zA-Z0-9_]*)\}#', '(?P<$1>[^/]+)', $path);
        $pattern = '#^' . rtrim($pattern, '/') . '/?$#';

        $this->routes[$method][] = [
            'pattern' => $pattern,
            'action' => $action,
        ];
    }

    public function dispatch(string $method, string $uri): void
    {
        $normalizedMethod = strtoupper($method);
        if ($normalizedMethod === 'HEAD') {
            $normalizedMethod = 'GET';
        }

        $uri = '/' . trim($uri, '/');

        foreach ($this->routes[$normalizedMethod] ?? [] as $route) {
            if (preg_match($route['pattern'], $uri, $matches)) {
                [$controller, $handler] = $route['action'];
                $instance = new $controller();
                $params = array_filter($matches, fn ($k) => !is_int($k), ARRAY_FILTER_USE_KEY);
                call_user_func_array([$instance, $handler], $params);
                return;
            }
        }

        $allowedMethods = [];
        foreach ($this->routes as $registeredMethod => $routes) {
            foreach ($routes as $route) {
                if (preg_match($route['pattern'], $uri) === 1) {
                    $allowedMethods[] = $registeredMethod;
                    break;
                }
            }
        }

        if ($allowedMethods !== []) {
            $allowedMethods = array_values(array_unique($allowedMethods));
            if (in_array('GET', $allowedMethods, true) && !in_array('HEAD', $allowedMethods, true)) {
                $allowedMethods[] = 'HEAD';
            }

            header('Allow: ' . implode(', ', $allowedMethods), true, 405);
            echo 'Método não permitido.';
            return;
        }

        http_response_code(404);
        echo 'Página não encontrada.';
    }
}
