<?php
namespace Core;

/**
 * REST-style Router
 */
class Router
{
    private array $routes = [];
    private array $namedRoutes = [];

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

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

    public function put(string $path, string $handler, string $name = ''): void
    {
        $this->addRoute('PUT', $path, $handler, $name);
    }

    public function delete(string $path, string $handler, string $name = ''): void
    {
        $this->addRoute('DELETE', $path, $handler, $name);
    }

    private function addRoute(string $method, string $path, string $handler, string $name): void
    {
        $pattern = preg_replace('/\{([a-zA-Z_]+)\}/', '([^/]+)', $path);
        $pattern = '#^' . $pattern . '$#';
        $this->routes[] = [
            'method'  => $method,
            'path'    => $path,
            'pattern' => $pattern,
            'handler' => $handler,
            'name'    => $name,
        ];
        if ($name) {
            $this->namedRoutes[$name] = $path;
        }
    }

    public function dispatch(): void
    {
        $method = $_SERVER['REQUEST_METHOD'];
        if ($method === 'POST' && isset($_POST['_method'])) {
            $method = strtoupper($_POST['_method']);
        }

        $uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
        $basePath = str_replace('/index.php', '', dirname($_SERVER['SCRIPT_NAME']));
        $uri = '/' . ltrim(substr($uri, strlen($basePath)), '/');

        foreach ($this->routes as $route) {
            if ($route['method'] !== $method) continue;
            if (preg_match($route['pattern'], $uri, $matches)) {
                array_shift($matches);
                [$controllerName, $action] = explode('@', $route['handler']);
                $controllerClass = "App\\Controllers\\{$controllerName}";
                if (!class_exists($controllerClass)) {
                    $this->abort(500, "Controller {$controllerClass} not found.");
                    return;
                }
                $controller = new $controllerClass();
                if (!method_exists($controller, $action)) {
                    $this->abort(500, "Method {$action} not found in {$controllerClass}.");
                    return;
                }
                call_user_func_array([$controller, $action], $matches);
                return;
            }
        }
        $this->abort(404);
    }

    public function url(string $name, array $params = []): string
    {
        if (!isset($this->namedRoutes[$name])) return '#';
        $url = $this->namedRoutes[$name];
        foreach ($params as $k => $v) {
            $url = str_replace('{' . $k . '}', $v, $url);
        }
        return APP_URL . $url;
    }

    private function abort(int $code, string $message = ''): void
    {
        http_response_code($code);
        if ($code === 404) {
            include VIEWS_PATH . '/errors/404.php';
        } else {
            echo "<h1>Error {$code}</h1><p>{$message}</p>";
        }
    }
}
