<?php
namespace Misuzu;

use RuntimeException;
use Misuzu\Auth\{AuthTokenBuilder,AuthTokenCookie,AuthTokenInfo};

require_once __DIR__ . '/../misuzu.php';

if(!isset($msz) || !($msz instanceof \Misuzu\MisuzuContext))
    die('Misuzu is not initialised.');

if(class_exists(\Whoops\Run::class))
    (function($whoops) {
        $whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
        $whoops->register();
    })(new \Whoops\Run);
else
    set_exception_handler(function(\Throwable $ex) {
        \Sentry\captureException($ex);

        http_response_code(500);
        ob_clean();

        header('Content-Type: text/html; charset=utf-8');
        header('X-Accel-Redirect: /error-500.html');
        exit;
    });

// The whole wall of shit before the router setup and dispatch should be worked away
// Lockdown things should be middleware when there's no more legacy files

ob_start();

if(is_file($msz->dbCtx->getMigrateLockPath())) {
    http_response_code(503);
    header('Content-Type: text/html; charset=utf-8');
    header('X-Accel-Redirect: /error-503.html');
    exit;
}

$request = \Index\Http\HttpRequest::fromRequest();

$tokenPacker = $msz->authCtx->createAuthTokenPacker();

if(filter_has_var(INPUT_COOKIE, 'msz_auth'))
    $tokenInfo = $tokenPacker->unpack(filter_input(INPUT_COOKIE, 'msz_auth'));
elseif(filter_has_var(INPUT_COOKIE, 'msz_uid') && filter_has_var(INPUT_COOKIE, 'msz_sid')) {
    $tokenBuilder = new AuthTokenBuilder;
    $tokenBuilder->setUserId((string)filter_input(INPUT_COOKIE, 'msz_uid', FILTER_SANITIZE_NUMBER_INT));
    $tokenBuilder->setSessionToken((string)filter_input(INPUT_COOKIE, 'msz_sid'));
    $tokenInfo = $tokenBuilder->toInfo();
    $tokenBuilder = null;
} else
    $tokenInfo = AuthTokenInfo::empty();

$userInfo = null;
$sessionInfo = null;
$userInfoReal = null;
$remoteAddr = (string)filter_input(INPUT_SERVER, 'REMOTE_ADDR');

if($tokenInfo->hasUserId && $tokenInfo->hasSessionToken) {
    $tokenBuilder = new AuthTokenBuilder($tokenInfo);

    try {
        $sessionInfo = $msz->authCtx->sessions->getSession(sessionToken: $tokenInfo->sessionToken);

        if($sessionInfo->expired) {
            $tokenBuilder->removeUserId();
            $tokenBuilder->removeSessionToken();
        } elseif($sessionInfo->userId === $tokenInfo->userId) {
            $userInfo = $msz->usersCtx->users->getUser($tokenInfo->userId, 'id');

            if($userInfo->deleted) {
                $tokenBuilder->removeUserId();
                $tokenBuilder->removeSessionToken();
            } else {
                $msz->usersCtx->users->recordUserActivity($userInfo, remoteAddr: $remoteAddr);
                $msz->authCtx->sessions->recordSessionActivity(sessionInfo: $sessionInfo, remoteAddr: $remoteAddr);
                if($sessionInfo->shouldBumpExpires)
                    $tokenBuilder->setEdited();

                if($tokenInfo->hasImpersonatedUserId) {
                    $allowToImpersonate = $userInfo->super;
                    $impersonatedUserId = $tokenInfo->impersonatedUserId;

                    if(!$allowToImpersonate) {
                        $allowImpersonateUsers = $msz->config->getArray(sprintf('impersonate.allow.u%s', $userInfo->id));
                        $allowToImpersonate = in_array((string)$impersonatedUserId, $allowImpersonateUsers, true);
                    }

                    if($allowToImpersonate) {
                        $userInfoReal = $userInfo;

                        try {
                            $userInfo = $msz->usersCtx->users->getUser($impersonatedUserId, 'id');
                        } catch(RuntimeException $ex) {
                            $userInfo = $userInfoReal;
                            $userInfoReal = null;
                            $tokenBuilder->removeImpersonatedUserId();
                        }
                    } else $tokenBuilder->removeImpersonatedUserId();
                }
            }
        }
    } catch(RuntimeException $ex) {
        $tokenBuilder->removeUserId();
        $tokenBuilder->removeSessionToken();
        $tokenBuilder->removeImpersonatedUserId();
        $userInfo = null;
        $sessionInfo = null;
        $userInfoReal = null;
    }

    if($tokenBuilder->isEdited()) {
        $tokenInfo = $tokenBuilder->toInfo();
        AuthTokenCookie::apply($tokenPacker->pack($tokenInfo));
    }
}

$msz->authInfo->setInfo($tokenInfo, $userInfo, $sessionInfo, $userInfoReal);

CSRF::init(
    $msz->config->getString('csrf.secret', 'soup'),
    ($msz->authInfo->loggedIn ? $sessionInfo->token : $remoteAddr)
);

// order for these two currently matters i think: it shouldn't.
$router = $msz->createRouting($request);
$msz->startTemplating();

if(in_array('main', $msz->env->getArray(sprintf('domain:%s', $request->getHeaderLine('Host'))))) {
    $mszRequestPath = substr($request->path, 1);
    $mszLegacyPathPrefix = Misuzu::PATH_PUBLIC_LEGACY . '/';
    $mszLegacyPath = $mszLegacyPathPrefix . $mszRequestPath;

    if(str_starts_with($mszLegacyPath, $mszLegacyPathPrefix)) {
        $mszLegacyPathReal = realpath($mszLegacyPath);
        if($mszLegacyPath === $mszLegacyPathReal || $mszLegacyPath === $mszLegacyPathReal . '/') {
            if(str_starts_with($mszRequestPath, 'manage') && !$msz->hasManageAccess())
                Template::throwError(403);

            if(is_dir($mszLegacyPath))
                $mszLegacyPath .= '/index.php';

            if(is_file($mszLegacyPath)) {
                require_once $mszLegacyPath;
                return;
            }
        }
    }
}

$router->dispatch($request);