<?php

declare(strict_types=1);

namespace App\Helpers;

use App\Helpers\Request;
use App\Helpers\Response;

class Router
{
    private array $routes = [];
    private Request $request;

    public function __construct()
    {
        $this->request = new Request();
    }

    public function get(string $path, array $handler, array $middlewares = []): void
    {
        $this->add('GET', $path, $handler, $middlewares);
    }

    public function post(string $path, array $handler, array $middlewares = []): void
    {
        $this->add('POST', $path, $handler, $middlewares);
    }

    public function put(string $path, array $handler, array $middlewares = []): void
    {
        $this->add('PUT', $path, $handler, $middlewares);
    }

    public function patch(string $path, array $handler, array $middlewares = []): void
    {
        $this->add('PATCH', $path, $handler, $middlewares);
    }

    public function delete(string $path, array $handler, array $middlewares = []): void
    {
        $this->add('DELETE', $path, $handler, $middlewares);
    }

    private function add(string $method, string $path, array $handler, array $middlewares): void
    {
        $path = '/' . trim($path, '/');
        $this->routes[] = [
            'method' => $method,
            'path' => $path,
            'handler' => $handler,
            'middlewares' => $middlewares,
            'pattern' => $this->pathToPattern($path),
        ];
    }

    private function pathToPattern(string $path): string
    {
        $pattern = preg_replace('/\/:([a-zA-Z_]+)/', '/(?P<$1>[^/]+)', $path);
        return '#^' . $pattern . '$#';
    }

    public function dispatch(): void
    {
        $method = $this->request->method();
        $uri = $this->request->uri();

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

            if (preg_match($route['pattern'], $uri, $matches)) {
                $params = array_filter($matches, 'is_string', ARRAY_FILTER_USE_KEY);
                $this->request->setRouteParams($params);

                foreach ($route['middlewares'] as $middlewareDef) {
                    if (is_array($middlewareDef)) {
                        $class = array_shift($middlewareDef);
                        $middleware = new $class(...$middlewareDef);
                    } else {
                        $middleware = new $middlewareDef();
                    }
                    $result = $middleware->handle($this->request);
                    if ($result !== null) {
                        Response::error($result, 403);
                    }
                }

                [$controllerClass, $methodName] = $route['handler'];
                $controller = new $controllerClass($this->request);
                $controller->$methodName();
                return;
            }
        }

        Response::error('Ruta no encontrada', 404);
    }

    public function getRequest(): Request
    {
        return $this->request;
    }
}
