<?php
namespace App\Core;

class Router
{
    private $routes = [];

    public function add(string $method, string $path, $handler): void
    {
        $this->routes[] = [$method, $path, $handler];
    }

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

        if (strpos($path, '/index.php') === 0) {
            $path = (string)substr($path, strlen('/index.php'));
            if ($path === '') {
                $path = '/';
            }
        }

        $path = '/' . ltrim($path, '/');
        if ($path !== '/' && substr($path, -1) === '/') {
            $path = rtrim($path, '/');
        }

        foreach ($this->routes as [$m, $pattern, $handler]) {
            if ($m !== $method) continue;
            $regex = '#^' . preg_replace('#\{([a-zA-Z_][a-zA-Z0-9_]*)\}#', '(?P<$1>[^/]+)', $pattern) . '$#';
            if (preg_match($regex, $path, $matches)) {
                $params = array_filter($matches, 'is_string', ARRAY_FILTER_USE_KEY);
                $this->invoke($handler, $params);
                return;
            }
        }
        http_response_code(404);
        echo '404 Not Found';
    }

    private function invoke($handler, array $params): void
    {
        if (is_callable($handler)) {
            call_user_func($handler, $params);
            return;
        }
        if (is_string($handler)) {
            [$class, $method] = explode('@', $handler);
            $class = 'App\\Controllers\\' . $class;
            $controller = new $class();
            call_user_func_array([$controller, $method], $params);
            return;
        }
        throw new \RuntimeException('Invalid route handler');
    }
}
