<?php

declare(strict_types=1);

namespace ServerManager\Core;

class Router
{
    private array $routes = [];
    private array $groupStack = [];
    private ?string $currentGroup = null;
    private array $groupMiddlewares = [];
    private array $middlewares = [];
    private array $patterns = [
        ':id' => '(\d+)',
        ':uuid' => '([a-f0-9\-]{36})',
        ':slug' => '([a-zA-Z0-9\-]+)',
        ':any' => '([^/]+)',
        ':all' => '(.*)',
    ];

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

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

    public function put(string $path, callable|array $handler, array $middleware = []): self
    {
        return $this->addRoute('PUT', $path, $handler, $middleware);
    }

    public function delete(string $path, callable|array $handler, array $middleware = []): self
    {
        return $this->addRoute('DELETE', $path, $handler, $middleware);
    }

    public function patch(string $path, callable|array $handler, array $middleware = []): self
    {
        return $this->addRoute('PATCH', $path, $handler, $middleware);
    }

    public function group(array $properties, callable $callback): void
    {
        $previousGroup = $this->currentGroup;
        $previousMiddlewares = $this->groupMiddlewares;

        $this->currentGroup = $properties['prefix'] ?? '';
        $this->groupMiddlewares = array_merge(
            $this->groupMiddlewares,
            $properties['middleware'] ?? []
        );

        $callback($this);

        $this->currentGroup = $previousGroup;
        $this->groupMiddlewares = $previousMiddlewares;
    }

    public function middleware(array $middleware): self
    {
        $this->middlewares = $middleware;
        return $this;
    }

    private function addRoute(string $method, string $path, callable|array $handler, array $middleware = []): self
    {
        $prefix = $this->currentGroup ? '/' . trim($this->currentGroup, '/') : '';
        $fullPath = $prefix . '/' . trim($path, '/');
        $fullPath = $fullPath === '/' ? '/' : '/' . trim($fullPath, '/');

        $allMiddleware = array_merge($this->groupMiddlewares, $this->middlewares, $middleware);

        $this->routes[] = [
            'method' => $method,
            'path' => $fullPath,
            'handler' => $handler,
            'middleware' => $allMiddleware,
            'pattern' => $this->compilePattern($fullPath),
        ];

        $this->middlewares = [];
        return $this;
    }

    private function compilePattern(string $path): string
    {
        $pattern = preg_quote($path, '#');
        foreach ($this->patterns as $key => $regex) {
            $pattern = str_replace(preg_quote($key, '#'), $regex, $pattern);
        }
        return '#^' . $pattern . '$#';
    }

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

        if ($method === 'POST' && isset($_POST['_method'])) {
            $method = strtoupper($_POST['_method']);
        }

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

            if (preg_match($route['pattern'], $uri, $matches)) {
                array_shift($matches);
                $params = array_map(function ($param) {
                    return ctype_digit($param) ? (int) $param : $param;
                }, array_values($matches));

                $middlewares = array_unique($route['middleware']);
                foreach ($middlewares as $mwClass) {
                    $middleware = new $mwClass();
                    $result = $middleware->handle();
                    if ($result !== null) {
                        return $result;
                    }
                }

                $handler = $route['handler'];
                if (is_array($handler)) {
                    [$class, $method] = $handler;
                    $controller = new $class();
                    return $controller->{$method}(...$params);
                }

                return $handler(...$params);
            }
        }

        $view = new View(new Security());
        $view->error(404);
    }
}
