<?php
namespace Misuzu;

use RuntimeException;
use Misuzu\TOTPGenerator;
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();
$tfaSessions = $authCtx->getTwoFactorAuthSessions();
$loginAttempts = $authCtx->getLoginAttempts();

$ipAddress = $_SERVER['REMOTE_ADDR'];
$countryCode = $_SERVER['COUNTRY_CODE'] ?? 'XX';
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
$twofactor = !empty($_POST['twofactor']) && is_array($_POST['twofactor']) ? $_POST['twofactor'] : [];
$notices = [];

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

$tokenString = !empty($_GET['token']) && is_string($_GET['token']) ? $_GET['token'] : (
    !empty($twofactor['token']) && is_string($twofactor['token']) ? $twofactor['token'] : ''
);

$tokenUserId = $tfaSessions->getTokenUserId($tokenString);
if(empty($tokenUserId)) {
    Tools::redirect($urls->format('auth-login'));
    return;
}

$userInfo = $users->getUser($tokenUserId, 'id');

// checking user_totp_key specifically because there's a fringe chance that
//  there's a token present, but totp is actually disabled
if(!$userInfo->hasTOTPKey()) {
    Tools::redirect($urls->format('auth-login'));
    return;
}

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

    $userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
    $redirect = !empty($twofactor['redirect']) && is_string($twofactor['redirect']) ? $twofactor['redirect'] : '';

    if(empty($twofactor['code']) || !is_string($twofactor['code'])) {
        $notices[] = 'Code field was empty.';
        break;
    }

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

    $clientInfo = ClientInfo::fromRequest();
    $totp = new TOTPGenerator($userInfo->getTOTPKey());

    if(!in_array($twofactor['code'], $totp->generateRange())) {
        $notices[] = sprintf(
            "Invalid two factor code, %d attempt%s remaining",
            $remainingAttempts - 1,
            $remainingAttempts === 2 ? '' : 's'
        );
        $loginAttempts->recordAttempt(false, $ipAddress, $countryCode, $userAgent, $clientInfo, $userInfo);
        break;
    }

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

    try {
        $sessionInfo = $sessions->createSession($userInfo, $ipAddress, $countryCode, $userAgent, $clientInfo);
    } catch(RuntimeException $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($redirect))
        $redirect = $urls->format('index');

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

Template::render('auth.twofactor', [
    'twofactor_notices' => $notices,
    'twofactor_redirect' => !empty($_GET['redirect']) && is_string($_GET['redirect']) ? $_GET['redirect'] : $urls->format('index'),
    'twofactor_attempts_remaining' => $remainingAttempts,
    'twofactor_token' => $tokenString,
]);
