<?php

namespace App\Core;

/**
 * ITSWEBER CMS – Router
 *
 * Simple pattern-based router supporting static and parameterized routes.
 */
class Router
{
    private App $app;
    private array $routes = [];
    private array $groupPrefix = [];
    private array $groupMiddleware = [];

    public function __construct(App $app)
    {
        $this->app = $app;
    }

    /**
     * Register a GET route.
     */
    public function get(string $pattern, $handler, array $middleware = []): self
    {
        return $this->addRoute('GET', $pattern, $handler, $middleware);
    }

    /**
     * Register a POST route.
     */
    public function post(string $pattern, $handler, array $middleware = []): self
    {
        return $this->addRoute('POST', $pattern, $handler, $middleware);
    }

    /**
     * Register a route for both GET and POST.
     */
    public function any(string $pattern, $handler, array $middleware = []): self
    {
        $this->addRoute('GET', $pattern, $handler, $middleware);
        $this->addRoute('POST', $pattern, $handler, $middleware);
        return $this;
    }

    /**
     * Group routes with a shared prefix and middleware.
     */
    public function group(string $prefix, array $middleware, callable $callback): void
    {
        $this->groupPrefix[] = $prefix;
        $this->groupMiddleware[] = $middleware;
        $callback($this);
        array_pop($this->groupPrefix);
        array_pop($this->groupMiddleware);
    }

    /**
     * Add a route to the internal registry.
     */
    private function addRoute(string $method, string $pattern, $handler, array $middleware): self
    {
        $prefix = implode('', $this->groupPrefix);
        $mw = array_merge(
            array_merge(...($this->groupMiddleware ?: [[]])),
            $middleware
        );

        $this->routes[] = [
            'method'     => $method,
            'pattern'    => $prefix . $pattern,
            'handler'    => $handler,
            'middleware'  => $mw,
        ];

        return $this;
    }

    /**
     * Dispatch the request to the matching route.
     */
    public function dispatch(Request $request): void
    {
        $method = $request->method();
        $uri = $request->uri();

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

            $params = $this->matchRoute($route['pattern'], $uri);
            if ($params !== false) {
                // Run middleware
                foreach ($route['middleware'] as $mwClass) {
                    $mw = new $mwClass();
                    $result = $mw->handle($request, $this->app);
                    if ($result === false) {
                        return;
                    }
                }

                $this->callHandler($route['handler'], $request, $params);
                return;
            }
        }

        // No route matched – 404
        $this->notFound($request);
    }

    /**
     * Match a route pattern against a URI.
     * Returns params array on match, false otherwise.
     */
    private function matchRoute(string $pattern, string $uri): array|false
    {
        // Convert pattern params like {id} to regex groups
        $regex = preg_replace('/\{([a-zA-Z_]+)\}/', '(?P<$1>[^/]+)', $pattern);
        $regex = '#^' . $regex . '$#';

        if (preg_match($regex, $uri, $matches)) {
            // Filter out numeric keys
            return array_filter($matches, fn($key) => !is_int($key), ARRAY_FILTER_USE_KEY);
        }

        return false;
    }

    /**
     * Call the route handler.
     */
    private function callHandler($handler, Request $request, array $params): void
    {
        if (is_callable($handler)) {
            call_user_func($handler, $request, $params, $this->app);
            return;
        }

        if (is_string($handler) && str_contains($handler, '@')) {
            [$class, $method] = explode('@', $handler);
            $controller = new $class($this->app);
            $controller->$method($request, $params);
            return;
        }

        throw new \RuntimeException("Invalid route handler.");
    }

    /**
     * Handle 404 Not Found.
     */
    private function notFound(Request $request): void
    {
        http_response_code(404);
        $view = $this->app->service('view');
        try {
            echo $view->render('404', ['request' => $request]);
        } catch (\Exception $e) {
            echo '<h1>404 – Page Not Found</h1>';
        }
    }

    /**
     * Get all registered routes (for debugging).
     */
    public function getRoutes(): array
    {
        return $this->routes;
    }
}
