<?php
namespace Misuzu;

use Exception;
use Misuzu\Auth\AuthTokenCookie;

$urls = $msz->getURLs();
$authInfo = $msz->getAuthInfo();
if($authInfo->isLoggedIn()) {
    Tools::redirect($urls->format('index'));
    return;
}

$authCtx = $msz->getAuthContext();
$users = $msz->getUsersContext()->getUsers();
$sessions = $authCtx->getSessions();
$loginAttempts = $authCtx->getLoginAttempts();

if(!empty($_GET['resolve'])) {
    header('Content-Type: application/json; charset=utf-8');

    try {
        // Only works for usernames, this is by design
        $userInfo = $users->getUser((string)filter_input(INPUT_GET, 'name'), 'name');
    } catch(Exception $ex) {
        echo json_encode([
            'id' => 0,
            'name' => '',
            'avatar' => $urls->format('user-avatar', ['res' => 200, 'user' => 0]),
        ]);
        return;
    }

    echo json_encode([
        'id' => (int)$userInfo->getId(),
        'name' => $userInfo->getName(),
        'avatar' => $urls->format('user-avatar', ['user' => $userInfo->getId(), 'res' => 200]),
    ]);
    return;
}

$notices = [];
$ipAddress = $_SERVER['REMOTE_ADDR'];
$countryCode = $_SERVER['COUNTRY_CODE'] ?? 'XX';
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';

$remainingAttempts = $loginAttempts->countRemainingAttempts($ipAddress);

$siteIsPrivate = $cfg->getBoolean('private.enable');
if($siteIsPrivate) {
    [
        'private.perm.cat' => $loginPermCat,
        'private.perm.val' => $loginPermVal,
        'private.msg' => $sitePrivateMessage,
        'private.allow_password_reset' => $canResetPassword,
    ] = $cfg->getValues([
        'private.perm.cat:s',
        'private.perm.val:i',
        'private.msg:s',
        ['private.allow_password_reset:b', true],
    ]);
} else {
    $loginPermCat = '';
    $loginPermVal = 0;
    $sitePrivateMessage = '';
    $canResetPassword = true;
}

while(!empty($_POST['login']) && is_array($_POST['login'])) {
    if(!CSRF::validateRequest()) {
        $notices[] = 'Was unable to verify the request, please try again!';
        break;
    }

    $loginRedirect = empty($_POST['login']['redirect']) || !is_string($_POST['login']['redirect']) ? '' : $_POST['login']['redirect'];

    if(empty($_POST['login']['username']) || empty($_POST['login']['password'])
        || !is_string($_POST['login']['username']) || !is_string($_POST['login']['password'])) {
        $notices[] = "You didn't fill in a username and/or password.";
        break;
    }

    if($remainingAttempts < 1) {
        $notices[] = "There are too many failed login attempts from your IP address, please try again later.";
        break;
    }

    $clientInfo = ClientInfo::fromRequest();

    $attemptsRemainingError = sprintf(
        "%d attempt%s remaining",
        $remainingAttempts - 1,
        $remainingAttempts === 2 ? '' : 's'
    );
    $loginFailedError = "Invalid username or password, {$attemptsRemainingError}.";

    try {
        $userInfo = $users->getUser($_POST['login']['username'], 'login');
    } catch(Exception $ex) {
        $loginAttempts->recordAttempt(false, $ipAddress, $countryCode, $userAgent, $clientInfo);
        $notices[] = $loginFailedError;
        break;
    }

    if(!$userInfo->hasPasswordHash()) {
        $notices[] = 'Your password has been invalidated, please reset it.';
        break;
    }

    if($userInfo->isDeleted() || !$userInfo->verifyPassword($_POST['login']['password'])) {
        $loginAttempts->recordAttempt(false, $ipAddress, $countryCode, $userAgent, $clientInfo, $userInfo);
        $notices[] = $loginFailedError;
        break;
    }

    if($userInfo->passwordNeedsRehash())
        $users->updateUser($userInfo, password: $_POST['login']['password']);

    if(!empty($loginPermCat) && $loginPermVal > 0 && !$msz->getPerms()->checkPermissions($loginPermCat, $loginPermVal, $userInfo)) {
        $notices[] = "Login succeeded, but you're not allowed to browse the site right now.";
        $loginAttempts->recordAttempt(true, $ipAddress, $countryCode, $userAgent, $clientInfo, $userInfo);
        break;
    }

    if($userInfo->hasTOTPKey()) {
        $tfaToken = $authCtx->getTwoFactorAuthSessions()->createToken($userInfo);
        Tools::redirect($urls->format('auth-two-factor', ['token' => $tfaToken]));
        return;
    }

    $loginAttempts->recordAttempt(true, $ipAddress, $countryCode, $userAgent, $clientInfo, $userInfo);

    try {
        $sessionInfo = $sessions->createSession($userInfo, $ipAddress, $countryCode, $userAgent, $clientInfo);
    } catch(Exception $ex) {
        $notices[] = "Something broke while creating a session for you, please tell an administrator or developer about this!";
        break;
    }

    $tokenBuilder = $authInfo->getTokenInfo()->toBuilder();
    $tokenBuilder->setUserId($userInfo);
    $tokenBuilder->setSessionToken($sessionInfo);
    $tokenBuilder->removeImpersonatedUserId();
    $tokenInfo = $tokenBuilder->toInfo();

    AuthTokenCookie::apply($tokenPacker->pack($tokenInfo));

    if(!Tools::isLocalURL($loginRedirect))
        $loginRedirect = $urls->format('index');

    Tools::redirect($loginRedirect);
    return;
}

$welcomeMode = !empty($_GET['welcome']);
$loginUsername = !empty($_POST['login']['username']) && is_string($_POST['login']['username']) ? $_POST['login']['username'] : (
    !empty($_GET['username']) && is_string($_GET['username']) ? $_GET['username'] : ''
);
$loginRedirect = $welcomeMode ? $urls->format('index') : (!empty($_GET['redirect']) && is_string($_GET['redirect']) ? $_GET['redirect'] : null) ?? $_SERVER['HTTP_REFERER'] ?? $urls->format('index');
$canRegisterAccount = !$siteIsPrivate;

Template::render('auth.login', [
    'login_notices' => $notices,
    'login_username' => $loginUsername,
    'login_redirect' => $loginRedirect,
    'login_can_reset_password' => $canResetPassword,
    'login_can_register' => $canRegisterAccount,
    'login_attempts_remaining' => $remainingAttempts,
    'login_welcome' => $welcomeMode,
    'login_private' => [
        'enabled' => $siteIsPrivate,
        'message' => $sitePrivateMessage,
    ],
]);
