<?php
/**
 * Routeur centralisé — dispatche les requêtes HTTP vers des contrôleurs.
 *
 * Supporte GET/POST, groupes de routes (prefixes), et paramètres
 * dynamiques (:id). Point d'entrée unique : mon-appli/index.php.
 *
 * Usage :
 *   $router = new Router();
 *   $router->get('/login', [LoginController::class, 'showForm']);
 *   $router->post('/login', [LoginController::class, 'submit']);
 *   $router->group('/admin', function (Router $r) {
 *       $r->get('/users', [UsersController::class, 'list']);
 *   });
 *   $router->dispatch($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']);
 */
class Router
{
    private array $routes = [];
    private string $groupPrefix = '';

    public function get(string $path, array|callable $handler): self
    {
        return $this->addRoute('GET', $path, $handler);
    }

    public function post(string $path, array|callable $handler): self
    {
        return $this->addRoute('POST', $path, $handler);
    }

    public function any(string $path, array|callable $handler): self
    {
        $this->addRoute('GET', $path, $handler);
        $this->addRoute('POST', $path, $handler);
        return $this;
    }

    public function group(string $prefix, callable $callback): self
    {
        $prev = $this->groupPrefix;
        $this->groupPrefix = $prev . $prefix;
        $callback($this);
        $this->groupPrefix = $prev;
        return $this;
    }

    public function dispatch(string $method, string $uri): void
    {
        $method = strtoupper($method);
        $path   = parse_url($uri, PHP_URL_PATH);
        $path   = '/' . trim($path, '/');

        foreach ($this->routes as $route) {
            if ($route['method'] !== $method) continue;

            $params = $this->match($route['pattern'], $path);
            if ($params === false) continue;

            $this->callHandler($route['handler'], $params);
            return;
        }

        http_response_code(404);
        if (file_exists(WEBROOT_PATH . '/includes/404.php')) {
            include WEBROOT_PATH . '/includes/404.php';
        } else {
            echo '404 Not Found';
        }
    }

    private function addRoute(string $method, string $path, array|callable $handler): self
    {
        $fullPath = $this->groupPrefix . $path;
        $this->routes[] = [
            'method'  => $method,
            'pattern' => $fullPath,
            'handler' => $handler,
        ];
        return $this;
    }

    /**
     * @return array|false — paramètres extraits ou false si pas de match
     */
    private function match(string $pattern, string $path): array|false
    {
        $pattern = '/' . trim($pattern, '/');
        if ($pattern === '/') $pattern = '/';

        if (!str_contains($pattern, ':')) {
            return ($pattern === $path) ? [] : false;
        }

        $regex = preg_replace('#:([a-zA-Z_]+)#', '(?P<$1>[^/]+)', $pattern);
        $regex = '#^' . $regex . '$#';

        if (preg_match($regex, $path, $m)) {
            $params = [];
            foreach ($m as $k => $v) {
                if (is_string($k)) $params[$k] = $v;
            }
            return $params;
        }
        return false;
    }

    private function callHandler(array|callable $handler, array $params): void
    {
        if (is_callable($handler) && !is_array($handler)) {
            $handler(...array_values($params));
            return;
        }

        [$class, $method] = $handler;
        $controller = new $class();
        $controller->$method(...array_values($params));
    }
}
