<?php
namespace App\Core;

class Router
{
   
    public array $routes = [];

    
    public function add(string $method, string $pattern,array|string|callable $handler, array $middlewares): static
    {
        $this->routes[] = new Route($method, $pattern, $handler, $middlewares);
        return $this;
    }

    public function dispatch(string $method, string $uri): void
    {
        $uri = parse_url($uri, PHP_URL_PATH);
        foreach ($this->routes as $route) {
            if (($params = $route->match($method, $uri)) !== null) {
                $this->invoke($route->handler, $route->middlewares, $params);
                return;
            }
        }
        http_response_code(404);
        echo '404 Not Found';
    }

    /** Accepts closure, [Class, method], or "Class@method" */
    private function invoke(array|string|callable $handler, array $middlewares, array $params): void
    {   
        if(!empty($middlewares)) {
            foreach($middlewares as $m) {
              $this->callMiddleware($m, $params);          
            }
        }
        // "Class@method" convenience
        if (is_string($handler) && str_contains($handler, '@')) {
            [$class, $method] = explode('@', $handler, 2);
            $handler = [$class, $method];
        }
        
        if (is_array($handler) && is_string($handler[0])) {
            $handler[0] = new $handler[0];
        }
        echo $handler(...$params);
    }

    private function callMiddleware(array|string|callable $handler, array $params) {
        if (is_string($handler) && str_contains($handler, '@')) {
            [$class, $method] = explode('@', $handler, 2);
            $handler = [$class, $method];
        }
        
        if (is_array($handler) && is_string($handler[0])) {
            $handler[0] = new $handler[0];
        }
        $handler(...$params);
    }
}
