<?php

namespace App\Core;

/**
 * Basic regex-based router supporting dynamic parameters, middlewares, and CORS OPTIONS.
 */
class Router
{
    private array $routes = [];
    private array $globalMiddleware = [];

    /**
     * Define a GET route
     */
    public function get(string $path, $handler, array $middleware = []): void
    {
        $this->addRoute('GET', $path, $handler, $middleware);
    }

    /**
     * Define a POST route
     */
    public function post(string $path, $handler, array $middleware = []): void
    {
        $this->addRoute('POST', $path, $handler, $middleware);
    }

    /**
     * Define a PUT route
     */
    public function put(string $path, $handler, array $middleware = []): void
    {
        $this->addRoute('PUT', $path, $handler, $middleware);
    }

    /**
     * Define a DELETE route
     */
    public function delete(string $path, $handler, array $middleware = []): void
    {
        $this->addRoute('DELETE', $path, $handler, $middleware);
    }

    /**
     * Add global middleware applied to all routes
     */
    public function use($middleware): void
    {
        $this->globalMiddleware[] = $middleware;
    }

    private function addRoute(string $method, string $path, $handler, array $middleware): void
    {
        // Convert path matching expressions: /api/tickets/{id} -> regex
        $pattern = preg_replace('/\{([a-zA-Z0-9_]+)\}/', '(?P<$1>[^/]+)', $path);
        $pattern = '#^' . $pattern . '$#';

        $this->routes[] = [
            'method' => $method,
            'path' => $path,
            'pattern' => $pattern,
            'handler' => $handler,
            'middleware' => $middleware
        ];
    }

    /**
     * Match current request and execute middleware and controller action
     */
    public function dispatch(Request $request, Response $response): void
    {
        $method = $request->getMethod();
        $path = $request->getPath();

        // Handle CORS Preflight Preemptively
        if ($method === 'OPTIONS') {
            $allowedOrigin = getenv('ALLOWED_ORIGIN') ?: '*';
            $response->setHeader('Access-Control-Allow-Origin', $allowedOrigin);
            $response->setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
            $response->setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With');
            $response->setHeader('Vary', 'Origin');
            $response->setStatusCode(200);
            exit;
        }

        foreach ($this->routes as $route) {
            if ($route['method'] === $method && preg_match($route['pattern'], $path, $matches)) {
                // Filter named captures from regex match
                $params = array_filter($matches, 'is_string', ARRAY_FILTER_USE_KEY);

                // Run global middleware first
                foreach ($this->globalMiddleware as $mw) {
                    $mwInstance = new $mw();
                    $mwInstance->handle($request, $response);
                }

                // Run route specific middleware
                foreach ($route['middleware'] as $mw) {
                    $mwInstance = new $mw();
                    $mwInstance->handle($request, $response);
                }

                // Execute Controller
                $handler = $route['handler'];
                if (is_array($handler) && count($handler) === 2) {
                    list($controllerClass, $action) = $handler;
                    if (class_exists($controllerClass)) {
                        $controller = new $controllerClass();
                        if (method_exists($controller, $action)) {
                            // Call action with Request, Response and URI dynamic parameters
                            call_user_func_array([$controller, $action], array_merge([$request, $response], $params));
                            return;
                        }
                    }
                } elseif (is_callable($handler)) {
                    call_user_func_array($handler, array_merge([$request, $response], $params));
                    return;
                }

                error_log("Handler error for route: [{$method}] {$path}");
                $response->error("Internal Server Error", 500);
                return;
            }
        }

        // Route not found
        error_log("Route not found: [{$method}] {$path}");
        $response->error("Not Found", 404);
    }
}
