<?php

declare(strict_types=1);

namespace App\Core;

/**
 * Application
 *
 * The single place that boot order is decided. public/index.php calls
 * Application::boot() and then Application::handle() — everything
 * about HOW the app starts up lives here, not scattered across the
 * front controller, so the boot sequence is easy to reason about and
 * easy to reuse from CLI entry points (bin/migrate.php, bin/seed.php)
 * that need config/DB but not the full HTTP request cycle.
 */
final class Application
{
    private static bool $booted = false;

    public static function boot(string $basePath): void
    {
        if (self::$booted) {
            return;
        }

        Env::load($basePath . '/.env');

        $appConfig = require $basePath . '/config/app.php';

        date_default_timezone_set($appConfig['timezone']);
        Logger::setLogDirectory($basePath . '/storage/logs');
        ErrorHandler::register($appConfig['debug']);

        mb_internal_encoding('UTF-8');

        self::$booted = true;
    }

    /** Starts the session with the app's security config. Skipped for stateless API-key requests if a route opts out. */
    public static function startSession(): void
    {
        Session::start(config('session'));
    }

    public static function router(): Router
    {
        static $router = null;

        if ($router !== null) {
            return $router;
        }

        $router = new Router();

        // $router->registerMiddleware('auth', fn () => new \App\Middleware\AuthMiddleware());
        // $router->registerMiddleware('guest', fn () => new \App\Middleware\GuestMiddleware());
        // $router->registerMiddleware('csrf', fn () => new \App\Middleware\CsrfMiddleware());
        // $router->registerMiddleware('permission', fn (?string $slug) => new \App\Middleware\PermissionMiddleware($slug));
        // $router->registerMiddleware('throttle', fn (?string $name) => new \App\Middleware\RateLimitMiddleware($name));
        // $router->registerMiddleware('api_key', fn () => new \App\Middleware\ApiKeyAuthMiddleware());
        
        
        $router->registerMiddleware('auth', fn () => new \App\Middleware\AuthMiddleware());
        $router->registerMiddleware('guest', fn () => new \App\Middleware\GuestMiddleware());
        $router->registerMiddleware('csrf', fn () => new \App\Middleware\CsrfMiddleware());
        $router->registerMiddleware('permission', fn (?string $slug) => new \App\Middleware\PermissionMiddleware($slug));
        $router->registerMiddleware('throttle', fn (?string $name) => new \App\Middleware\RateLimitMiddleware($name));
        $router->registerMiddleware('api_key', fn () => new \App\Middleware\ApiKeyAuthMiddleware());
        $router->registerMiddleware('cors', fn () => new \App\Middleware\CorsMiddleware());
        $router->registerMiddleware('tenant', fn () => new \App\Middleware\TenantMiddleware());
        $router->registerMiddleware('platform_admin', fn () => new \App\Middleware\PlatformAdminMiddleware());



        $router->notFound(fn () => Response::html('404 Not Found', 404));

        require dirname(__DIR__, 2) . '/routes/web.php';
        require dirname(__DIR__, 2) . '/routes/api.php';
        require dirname(__DIR__, 2) . '/routes/public.php';
        require dirname(__DIR__, 2) . '/routes/platform.php';

        return $router;
    }

    public static function handle(): void
    {
        self::startSession();

        $request = Request::capture();
        self::resolveTenantContext($request);

        try {
            $response = self::router()->dispatch($request);
        } catch (ValidationException $e) {
            if ($request->isAjax() || $request->isJson()) {
                $response = \App\Helpers\AjaxResponse::validationError($e->errors());
            } else {
                Session::flash('_errors', $e->errors());
                Session::flash('_old_input', $request->all());
                $response = Response::redirect($_SERVER['HTTP_REFERER'] ?? base_url());
            }
        } catch (\Throwable $e) {
            ErrorHandler::handle($e, (bool) config('app.debug'));

            return;
        }

        $response->send();
    }

    /**
     * Best-effort tenant resolution, run for every request before
     * routing. Only SETS the context (Tenant::setCurrent) — it never
     * redirects, so /signup, /webhooks/stripe, and /platform/* keep
     * working on hosts with no matching tenant. The actual "you must
     * have a valid, unexpired tenant to be here" enforcement happens in
     * AuthMiddleware (for the normal CRM app) and TenantMiddleware (for
     * the small set of routes in routes/public.php that need tenant
     * context but not a login, e.g. the billing wall).
     */
    private static function resolveTenantContext(Request $request): void
    {
        $appDomain = (string) config('app.domain', '');

        if ($appDomain === '') {
            // APP_DOMAIN not configured yet — e.g. local development, or
            // a deployment that hasn't set up wildcard DNS. Rather than
            // lock everyone out, fall back to "single tenant" mode: if
            // there's exactly one active company, use it. Once a second
            // company exists this fallback deliberately stops working
            // (ambiguous which one a bare hostname means) — set
            // APP_DOMAIN and use real subdomains at that point.
            $rows = \App\Core\Database::connection()
                ->query('SELECT * FROM business_lines WHERE is_active = 1 LIMIT 2')
                ->fetchAll(\PDO::FETCH_ASSOC);

            if (count($rows) === 1) {
                \App\Core\Tenant::setCurrent($rows[0]);
            }

            return;
        }

        $host = (string) ($request->header('Host') ?? '');
        $slug = \App\Core\Tenant::subdomainFromHost($host, $appDomain);

        if ($slug !== null) {
            \App\Core\Tenant::setCurrent(\App\Core\Tenant::findBySlug($slug));
        }
    }
}
