<?php
class App
{
    private Router $router;
    private Request $request;

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

    public function run(): void
    {
        /**
         * BUG 6 FIX: Register error handler alongside exception handler
         * Convert PHP errors to ErrorException for consistent handling
         */
        set_error_handler(function (int $severity, string $message, string $file, int $line) {
            if (!(error_reporting() & $severity)) {
                return false;
            }
            throw new \ErrorException($message, 0, $severity, $file, $line);
        });

        set_exception_handler(function (\Throwable $e) {
            Logger::critical($e->getMessage(), [
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
            ]);

            $appConfig = require ROOT_PATH . '/config/app.php';
            if ($appConfig['debug']) {
                http_response_code(500);
                echo '<!DOCTYPE html><html><head><title>Error</title>';
                echo '<style>body{font-family:monospace;padding:40px;background:#1a1a2e;color:#e94560}pre{background:#16213e;padding:20px;border-radius:8px;color:#eee;overflow-x:auto}h1{color:#e94560}</style>';
                echo '</head><body>';
                echo '<h1>&#x26A0; ' . htmlspecialchars(get_class($e)) . '</h1>';
                echo '<p><strong>' . htmlspecialchars($e->getMessage()) . '</strong></p>';
                echo '<p>' . htmlspecialchars($e->getFile()) . ':' . $e->getLine() . '</p>';
                echo '<pre>' . htmlspecialchars($e->getTraceAsString()) . '</pre>';
                echo '</body></html>';
            } else {
                http_response_code(500);
                $viewFile = ROOT_PATH . '/views/errors/500.php';
                if (file_exists($viewFile)) {
                    include $viewFile;
                } else {
                    echo '<h1>500 Internal Server Error</h1><p>Something went wrong.</p>';
                }
            }
            exit;
        });

        // Language switching via query param
        if (isset($_GET['lang']) && in_array($_GET['lang'], ['en', 'ar'])) {
            Lang::set($_GET['lang']);
        }

        /**
         * BUG 4 FIX: Serve module static assets (CSS, JS, images)
         * Module files live outside public/. This intercepts requests matching
         * /modules/{name}/(css|js|images)/{file} and serves them with proper
         * content types and security checks.
         */
        $uri = $this->request->uri();
        if (preg_match('#^/modules/([a-zA-Z0-9_-]+)/(css|js|images|fonts)/(.+)$#', $uri, $matches)) {
            $this->serveModuleAsset($matches[1], $matches[2], $matches[3]);
            return;
        }

        $this->router->dispatch($this->request);
    }

    /**
     * Serve static files from module directories.
     * Only allows specific file extensions. Sanitizes path to prevent traversal.
     */
    private function serveModuleAsset(string $module, string $type, string $file): void
    {
        // Sanitize: no path traversal
        $module = preg_replace('/[^a-zA-Z0-9_-]/', '', $module);
        $file = str_replace(['..', "\0"], '', $file);

        // Allowed extensions per type
        $allowedExtensions = [
            'css'    => ['css', 'css.map'],
            'js'     => ['js', 'js.map'],
            'images' => ['png', 'jpg', 'jpeg', 'gif', 'svg', 'webp', 'ico'],
            'fonts'  => ['woff', 'woff2', 'ttf', 'eot', 'otf'],
        ];

        $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
        $allowed = $allowedExtensions[$type] ?? [];
        if (!in_array($ext, $allowed)) {
            http_response_code(403);
            exit;
        }

        $filePath = ROOT_PATH . '/modules/' . $module . '/' . $type . '/' . $file;

        // Verify the resolved path is actually inside the modules directory (prevents traversal)
        $realPath = realpath($filePath);
        $modulesDir = realpath(ROOT_PATH . '/modules');
        if ($realPath === false || $modulesDir === false || !str_starts_with($realPath, $modulesDir)) {
            http_response_code(404);
            exit;
        }

        // Content types
        $contentTypes = [
            'css'   => 'text/css',
            'js'    => 'application/javascript',
            'png'   => 'image/png',
            'jpg'   => 'image/jpeg',
            'jpeg'  => 'image/jpeg',
            'gif'   => 'image/gif',
            'svg'   => 'image/svg+xml',
            'webp'  => 'image/webp',
            'ico'   => 'image/x-icon',
            'woff'  => 'font/woff',
            'woff2' => 'font/woff2',
            'ttf'   => 'font/ttf',
            'eot'   => 'application/vnd.ms-fontobject',
            'otf'   => 'font/otf',
            'map'   => 'application/json',
        ];

        $contentType = $contentTypes[$ext] ?? 'application/octet-stream';

        header('Content-Type: ' . $contentType);
        header('Content-Length: ' . filesize($realPath));
        header('Cache-Control: public, max-age=86400');
        header('ETag: ' . md5_file($realPath));

        // Handle conditional requests
        $etag = md5_file($realPath);
        if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && trim($_SERVER['HTTP_IF_NONE_MATCH']) === $etag) {
            http_response_code(304);
            exit;
        }

        readfile($realPath);
        exit;
    }

    private function registerCoreRoutes(): void
    {
        $r = $this->router;

        // Guest routes
        $r->addRoute('GET', '/login', 'AuthController@showLogin', null, null, ['GuestMiddleware']);
        $r->addRoute('POST', '/login', 'AuthController@login', null, null, ['GuestMiddleware']);

        // Auth routes
        $r->addRoute('POST', '/logout', 'AuthController@logout', null, null, ['AuthMiddleware']);
        $r->addRoute('GET', '/dashboard', 'DashboardController@index', null, null, ['AuthMiddleware']);
        $r->addRoute('GET', '/', function () { Response::redirect('/dashboard'); });

        $r->addRoute('GET', '/profile', 'ProfileController@show', null, null, ['AuthMiddleware']);
        $r->addRoute('POST', '/profile', 'ProfileController@update', null, null, ['AuthMiddleware']);
        $r->addRoute('GET', '/change-password', 'ProfileController@showChangePassword', null, null, ['AuthMiddleware']);
        $r->addRoute('POST', '/change-password', 'ProfileController@changePassword', null, null, ['AuthMiddleware']);
        $r->addRoute('GET', '/lang/{code}', 'LangController@switchLang');
    }

    private function registerModuleRoutes(): void
    {
        $moduleRoutes = ModuleRegistry::getRoutes();
        foreach ($moduleRoutes as $route) {
            $this->router->addRoute(
                $route[0],            // method
                $route[1],            // pattern
                $route[2],            // handler
                $route[3] ?? null,    // permission
                $route['_module'],    // module name
                ['AuthMiddleware']    // all module routes require auth
            );
        }
    }
}