<?php

use MintyPHP\Analyzer;
use MintyPHP\Buffer;
use MintyPHP\DB;
use MintyPHP\Debugger;
use MintyPHP\Firewall;
use MintyPHP\Http\AccessControl;
use MintyPHP\Http\AssetDetector;
use MintyPHP\Http\IntendedUrlService;
use MintyPHP\Http\LocaleResolver;
use MintyPHP\Http\RequestContext;
use MintyPHP\Http\Request;
use MintyPHP\I18n;
use MintyPHP\Router;
use MintyPHP\Service\Access\UiAccessService;
use MintyPHP\Service\Auth\AuthServicesFactory;
use MintyPHP\Session;
use MintyPHP\Support\Flash;

// Change directory to project root
chdir(__DIR__ . '/..');
// Load the libraries
require 'vendor/autoload.php';
// Load the config parameters
require 'config/config.php';
// Load the routes
require 'config/router.php';
// Register shortcut functions
require 'lib/Support/helpers.php';
$container = require 'lib/App/registerContainer.php';
setAppContainer($container);
RequestContext::start();
header('X-Request-Id: ' . RequestContext::requestHeaderValue());

// Start the firewall
Firewall::start();

// Start the session
Session::start();
$authServicesFactory = app(AuthServicesFactory::class);
$authService = $authServicesFactory->createAuthService();
$rememberMeService = $authServicesFactory->createRememberMeService();

// Initialize default locale from tenant/app settings
$defaultLocale = appDefaultLocale();
if ($defaultLocale !== null) {
    I18n::$defaultLocale = $defaultLocale;
    if (I18n::$locale === '') {
        I18n::$locale = $defaultLocale;
    }
}

// Auto-login from remember-me cookie
if (empty($_SESSION['user']['id'])) {
    $autoLoginResult = $rememberMeService->autoLoginFromCookie();
    if ($autoLoginResult) {
        // The session timeout flash is no longer relevant — the user was
        // seamlessly re-authenticated and never saw the login page.
        Flash::dismissByKey('session_timeout', 'login');

        // After auto-login (e.g. session timeout with active remember-me cookie),
        // honour ?return_to= so the user lands on the page they were on.
        $returnToParam = trim((string) ($_GET['return_to'] ?? ''));
        if ($returnToParam !== '') {
            $intendedUrlService = app(IntendedUrlService::class);
            $sanitized = $intendedUrlService->sanitize($returnToParam);
            if ($sanitized !== '') {
                $routePath = $intendedUrlService->toRoutePath($sanitized);
                Router::redirect($routePath);
            }
        }
    }
}

// Session timeout enforcement (idle + absolute)
if (!empty($_SESSION['user']['id'])) {
    $now = time();
    $sessionStartedAt = (int) ($_SESSION['session_started_at'] ?? 0);
    $sessionLastActivity = (int) ($_SESSION['session_last_activity'] ?? 0);

    // Gracefully handle pre-existing sessions that lack timestamps.
    if ($sessionStartedAt === 0) {
        $_SESSION['session_started_at'] = $now;
        $sessionStartedAt = $now;
    }
    if ($sessionLastActivity === 0) {
        $_SESSION['session_last_activity'] = $now;
        $sessionLastActivity = $now;
    }

    $sessionGateway = app(\MintyPHP\Service\Settings\SettingsSessionGateway::class);
    $idleTimeoutSeconds = $sessionGateway->getSessionIdleTimeoutSeconds();
    $absoluteTimeoutSeconds = $sessionGateway->getSessionAbsoluteTimeoutSeconds();

    $isIdle = ($now - $sessionLastActivity) > $idleTimeoutSeconds;
    $isAbsolute = ($now - $sessionStartedAt) > $absoluteTimeoutSeconds;

    if ($isIdle || $isAbsolute) {
        // Capture the current URL before session destruction so the user
        // can be redirected back after re-authentication.
        $intendedUrl = $_SERVER['REQUEST_URI'] ?? '';
        $authService->logoutDueToSessionTimeout();
        Flash::error(t('Your session has expired. Please log in again.'), 'login', 'session_timeout');
        $intendedUrlService = app(IntendedUrlService::class);
        $locale = I18n::$locale ?? I18n::$defaultLocale;
        $loginTarget = $intendedUrlService->buildLoginRedirect(
            Request::withLocale('login', $locale),
            $intendedUrl
        );
        Router::redirect($loginTarget);
    }

    $_SESSION['session_last_activity'] = $now;
}

// Parse request URI and resolve locale
$originalUri = $_SERVER['REQUEST_URI'] ?? '';
$_SERVER['MINTY_ORIGINAL_URI'] = $originalUri;

$localeResolver = new LocaleResolver();
$parsedUri = $localeResolver->parseUri($originalUri);
$pathWithoutLocale = $parsedUri['pathWithoutLocale'];
$requestedLocale = $parsedUri['locale'];
$_SERVER['MINTY_LOCALE'] = $requestedLocale;
RequestContext::setPath($pathWithoutLocale);

// Rewrite REQUEST_URI without locale segment for router
if ($parsedUri['hadLocaleInUrl']) {
    $_SERVER['REQUEST_URI'] = $localeResolver->buildUriWithoutLocale(
        $pathWithoutLocale,
        $parsedUri['query']
    );
}

// Resolve effective locale from URL > Session > Cookie > Default
I18n::$locale = $localeResolver->resolveLocale(
    $requestedLocale,
    $_SESSION['user']['locale'] ?? null,
    $_COOKIE['locale'] ?? null
);

$isAssetRequest = AssetDetector::isAssetRequest($pathWithoutLocale);
$isApiRequest = str_starts_with($pathWithoutLocale, 'api/');
RequestContext::setChannel($isApiRequest ? 'api' : 'web');

if ($isApiRequest) {
    if (!defined('MINTY_API_REQUEST')) {
        define('MINTY_API_REQUEST', true);
    }

    // Minty router enforces CSRF for non-GET requests before action execution.
    // Our API is Bearer-token based and does not use form-CSRF, so mark only
    // API write requests as AJAX to bypass the router-level form CSRF gate.
    $requestMethod = strtoupper((string) ($_SERVER['REQUEST_METHOD'] ?? 'GET'));
    $isApiWriteMethod = !in_array($requestMethod, ['GET', 'HEAD', 'OPTIONS'], true);
    if ($isApiWriteMethod && empty($_SERVER['HTTP_X_REQUESTED_WITH'])) {
        $_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest';
    }
}

// Refresh tenant context if stale or missing (skip for assets and API requests)
if (!empty($_SESSION['user']['id']) && !$isAssetRequest && !$isApiRequest) {
    $userId = (int) ($_SESSION['user']['id'] ?? 0);
    if ($userId > 0) {
        $authState = $authService->refreshSessionAuthState($userId);
        if (!empty($authState['logout_required'])) {
            $authService->logout();
            $reason = (string) ($authState['reason'] ?? '');
            if ($reason === 'inactive') {
                Flash::error(t('Account is inactive'), 'login', 'login_inactive');
            } else {
                Flash::error(t('No active tenant assigned'), 'login', 'no_active_tenant');
            }
            Router::redirect('login');
        }

        $ttl = 120;
        $lastRefresh = (int) ($_SESSION['tenant_snapshot_at'] ?? 0);
        $needsRefresh = empty($_SESSION['current_tenant']) || empty($_SESSION['available_tenants']);
        if ((time() - $lastRefresh) >= $ttl) {
            $needsRefresh = true;
        }
        if ($needsRefresh) {
            $authService->loadTenantDataIntoSession($userId);
            $_SESSION['tenant_snapshot_at'] = time();
        }
    }
}

if ($requestedLocale === '' && !$isAssetRequest && !$isApiRequest) {
    $method = strtoupper((string) ($_SERVER['REQUEST_METHOD'] ?? 'GET'));
    if (in_array($method, ['GET', 'HEAD'], true)) {
        $target = Request::withLocale($pathWithoutLocale, I18n::$locale ?? I18n::$defaultLocale);
        $target .= $parsedUri['query'] !== '' ? '?' . $parsedUri['query'] : '';
        Router::redirect($target);
    }
}

// Guard: restrict non-public pages to logged-in users
$accessControl = new AccessControl();
$accessControl->requireAuthOrRedirect($pathWithoutLocale, !empty($_SESSION['user']['id']));

// Analyze the PHP code
if (Debugger::$enabled) {
    Analyzer::execute();
}

// Load the action into body
ob_start();
if (Router::getTemplateAction()) {
    require Router::getTemplateAction();
}
if (ob_get_contents()) {
    ob_end_flush();
    if (!defined('MINTY_ALLOW_OUTPUT') || !MINTY_ALLOW_OUTPUT) { // @phpstan-ignore booleanNot.alwaysFalse
        trigger_error('MintyPHP template action"' . Router::getTemplateAction() . '" should not send output. Error raised ', E_USER_WARNING);
    }
} else {
    ob_end_clean();
}
if (defined('MINTY_ALLOW_OUTPUT') && MINTY_ALLOW_OUTPUT) { // @phpstan-ignore booleanAnd.rightAlwaysTrue
    Session::end();
    DB::close();
    exit;
}

ob_start();
if (Router::getAction()) {
    extract(Router::getParameters());
    require Router::getAction();
}
if (ob_get_contents()) {
    ob_end_flush();
    if (!defined('MINTY_ALLOW_OUTPUT') || !MINTY_ALLOW_OUTPUT) { // @phpstan-ignore booleanNot.alwaysFalse
        trigger_error('MintyPHP action "' . Router::getAction() . '" should not send output. Error raised ', E_USER_WARNING);
    }
} else {
    ob_end_clean();
}
if (defined('MINTY_ALLOW_OUTPUT') && MINTY_ALLOW_OUTPUT) { // @phpstan-ignore booleanAnd.rightAlwaysTrue
    Session::end();
    DB::close();
    exit;
}

// Resolve layout capabilities while DB/session are still open.
// Templates render after DB::close(), so authz lookup must be prepared here.
if (Router::getTemplateView()) {
    $viewAuth = is_array($viewAuth ?? null) ? $viewAuth : [];
    $layoutAuth = $viewAuth['layout'] ?? null;
    if (!is_array($layoutAuth)) {
        $actorUserId = (int) (($_SESSION['user']['id'] ?? 0));
        $viewAuth['layout'] = app(UiAccessService::class)->layoutCapabilities($actorUserId);
        $layoutAuth = $viewAuth['layout'];
    }
    $layoutNav = appBuildLayoutNavContext(
        is_array($layoutAuth) ? $layoutAuth : [],
        is_array($_SESSION ?? null) ? $_SESSION : [],
        requestInput()->queryAll()
    );
}

// End the session
Session::end();

// Close the database connection
DB::close();

if (Router::getTemplateView()) {
    Buffer::start('html');
    if (Router::getView()) {
        require Router::getView();
    }

    // Show developer toolbar
    if (Debugger::$enabled) {
        Debugger::toolbar();
    }

    Buffer::end('html');
    // Load body into template
    require Router::getTemplateView();
} else { // Handle the 'none' template case
    if (Router::getView()) {
        require Router::getView();
    }
}
