<?php
declare(strict_types=1);

namespace App\Core;

final class Router
{
    private array $routes = [];
    private array $namedRoutes = [];

    public function addRoute(string $method, string $path, string $handler, array $middleware = [], ?string $permission = null, ?string $name = null): void
    {
        $route = [
            'method'     => strtoupper($method),
            'path'       => $path,
            'handler'    => $handler,
            'middleware'  => $middleware,
            'permission' => $permission,
            'name'       => $name,
        ];

        $this->routes[] = $route;

        if ($name !== null) {
            $this->namedRoutes[$name] = $route;
        }
    }

    public function dispatch(Request $request): Response
    {
        EventBus::dispatch('router.before_dispatch', ['request' => $request]);

        $method = $request->method();
        $path = $request->path();

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

            $params = $this->matchPath($route['path'], $path);
            if ($params === false) {
                continue;
            }

            $request->setRouteParams($params);
            $request->setAttribute('_route', $route);
            $request->setAttribute('_permission', $route['permission']);

            $response = $this->runMiddleware($route['middleware'], $request, function (Request $req) use ($route, $params) {
                return $this->callHandler($route['handler'], $req, $params);
            });

            EventBus::dispatch('router.after_dispatch', ['request' => $request, 'response' => $response]);
            return $response;
        }

        $methodMatched = false;
        foreach ($this->routes as $route) {
            if ($this->matchPath($route['path'], $path) !== false) {
                $methodMatched = true;
                break;
            }
        }

        if ($methodMatched) {
            return $this->handleError(405, 'الطريقة غير مسموح بها');
        }

        return $this->handleError(404, 'الصفحة غير موجودة');
    }

    private function matchPath(string $routePath, string $requestPath): array|false
    {
        $routePath = '/' . trim($routePath, '/');
        $requestPath = '/' . trim($requestPath, '/');

        if ($routePath === $requestPath) {
            return [];
        }

        $pattern = preg_replace_callback('/\{(\w+)(?::([^}]+))?\}/', function ($matches) {
            $name = $matches[1];
            $constraint = $matches[2] ?? '[^/]+';
            return '(?P<' . $name . '>' . $constraint . ')';
        }, $routePath);

        $pattern = '#^' . $pattern . '$#';

        if (preg_match($pattern, $requestPath, $matches)) {
            $params = [];
            foreach ($matches as $key => $value) {
                if (is_string($key)) {
                    $params[$key] = $value;
                }
            }
            return $params;
        }

        return false;
    }

    private function runMiddleware(array $middlewareList, Request $request, callable $final): Response
    {
        $middlewareMap = [
            'csrf'       => \App\Middleware\CSRFMiddleware::class,
            'auth'       => \App\Middleware\AuthMiddleware::class,
            'permission' => \App\Middleware\PermissionMiddleware::class,
            'audit'      => \App\Middleware\AuditMiddleware::class,
            'rate_limit' => \App\Middleware\RateLimitMiddleware::class,
            'guest'      => null,
        ];

        $stack = $final;

        foreach (array_reverse($middlewareList) as $name) {
            $class = $middlewareMap[$name] ?? $name;
            if ($class === null || !class_exists($class)) {
                continue;
            }
            $currentStack = $stack;
            $stack = function (Request $req) use ($class, $currentStack) {
                $mw = new $class();
                return $mw->handle($req, $currentStack);
            };
        }

        return $stack($request);
    }

    private function callHandler(string $handler, Request $request, array $params): Response
    {
        [$controllerName, $method] = explode('@', $handler);
        $controllerClass = 'App\\Modules\\' . $controllerName;

        if (!class_exists($controllerClass)) {
            return $this->handleError(500, "Controller not found: {$controllerClass}");
        }

        $controller = new $controllerClass($request);

        if (!method_exists($controller, $method)) {
            return $this->handleError(500, "Method not found: {$controllerClass}@{$method}");
        }

        $result = $controller->$method($request, ...array_values($params));

        if ($result instanceof Response) {
            return $result;
        }

        $response = new Response();
        if (is_string($result)) {
            return $response->html($result);
        }
        if (is_array($result)) {
            return $response->json($result);
        }

        return $response->html((string) $result);
    }

    private function handleError(int $code, string $message): Response
    {
        $response = new Response();
        $app = App::getInstance();

        if ($app->isDebug()) {
            return $response->html(
                '<html dir="rtl" lang="ar"><head><meta charset="utf-8"><title>خطأ ' . $code . '</title>'
                . '<style>body{font-family:Cairo,sans-serif;text-align:center;padding:60px;background:#f9fafb;}'
                . 'h1{color:#DC2626;font-size:72px;margin-bottom:10px;}p{color:#6B7280;font-size:18px;}</style></head>'
                . '<body><h1>' . $code . '</h1><p>' . htmlspecialchars($message) . '</p></body></html>',
                $code
            );
        }

        $msg = $code === 404 ? 'الصفحة المطلوبة غير موجودة' : 'حدث خطأ في الخادم';
        return $response->html(
            '<html dir="rtl" lang="ar"><head><meta charset="utf-8"><title>خطأ</title>'
            . '<style>body{font-family:Cairo,sans-serif;text-align:center;padding:60px;background:#f9fafb;}'
            . 'h1{color:#DC2626;font-size:72px;margin-bottom:10px;}p{color:#6B7280;font-size:18px;}</style></head>'
            . '<body><h1>' . $code . '</h1><p>' . $msg . '</p></body></html>',
            $code
        );
    }

    public static function url(string $name, array $params = []): string
    {
        $app = App::getInstance();
        $router = $app->router();

        if (!isset($router->namedRoutes[$name])) {
            return '#';
        }

        $path = $router->namedRoutes[$name]['path'];
        foreach ($params as $key => $value) {
            $path = preg_replace('/\{' . $key . '(?::[^}]+)?\}/', (string) $value, $path);
        }

        return $app->config('app.url', '') . '/' . ltrim($path, '/');
    }
}