<?php
namespace Misuzu;

use RuntimeException;
use Misuzu\Users\User;

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

$authCtx = $msz->getAuthContext();
$users = $msz->getUsersContext()->getUsers();
$recoveryTokens = $authCtx->getRecoveryTokens();
$loginAttempts = $authCtx->getLoginAttempts();

$reset = !empty($_POST['reset']) && is_array($_POST['reset']) ? $_POST['reset'] : [];
$forgot = !empty($_POST['forgot']) && is_array($_POST['forgot']) ? $_POST['forgot'] : [];
$userId = !empty($reset['user']) ? (int)$reset['user'] : (
    !empty($_GET['user']) ? (int)$_GET['user'] : 0
);

if($userId > 0)
    try {
        $userInfo = $users->getUser((string)$userId, 'id');
    } catch(RuntimeException $ex) {
        Tools::redirect($urls->format('auth-forgot'));
        return;
    }

$notices = [];
$ipAddress = $_SERVER['REMOTE_ADDR'];
$siteIsPrivate = $cfg->getBoolean('private.enable');
$canResetPassword = $siteIsPrivate ? $cfg->getBoolean('private.allow_password_reset', true) : true;

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

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

        $verifyCode = !empty($reset['verification']) && is_string($reset['verification']) ? $reset['verification'] : '';

        try {
            $tokenInfo = $recoveryTokens->getToken(verifyCode: $verifyCode);
        } catch(RuntimeException $ex) {
            unset($tokenInfo);
        }

        if(empty($tokenInfo) || !$tokenInfo->isValid() || $tokenInfo->getUserId() !== (string)$userInfo->getId()) {
            $notices[] = 'Invalid verification code!';
            break;
        }

        $password = !empty($reset['password']) && is_array($reset['password']) ? $reset['password'] : [];
        $passwordNew = !empty($password['new']) && is_string($password['new']) ? $password['new'] : '';
        $passwordConfirm = !empty($password['confirm']) && is_string($password['confirm']) ? $password['confirm'] : '';

        if(empty($passwordNew) || empty($passwordConfirm)
            || $passwordNew !== $passwordConfirm) {
            $notices[] = "Password confirmation failed!";
            break;
        }

        $passwordValidation = $users->validatePassword($passwordNew);
        if($passwordValidation !== '') {
            $notices[] = $users->validatePasswordText($passwordValidation);
            break;
        }

        // also disables two factor auth to prevent getting locked out of account entirely
        // this behaviour should really be replaced with recovery keys...
        $users->updateUser($userInfo, password: $passwordNew, totpKey: '');

        $msz->createAuditLog('PASSWORD_RESET', [], $userInfo);

        $recoveryTokens->invalidateToken($tokenInfo);

        Tools::redirect($urls->format('auth-login', ['redirect' => '/']));
        return;
    }

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

        if(empty($forgot['email']) || !is_string($forgot['email'])) {
            $notices[] = "You didn't supply an e-mail address.";
            break;
        }

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

        try {
            $forgotUser = $users->getUser($forgot['email'], 'email');
        } catch(RuntimeException $ex) {
            unset($forgotUser);
        }

        if(empty($forgotUser) || $forgotUser->isDeleted()) {
            $notices[] = "This e-mail address is not registered with us.";
            break;
        }

        try {
            $tokenInfo = $recoveryTokens->getToken(userInfo: $forgotUser, remoteAddr: $ipAddress);
        } catch(RuntimeException $ex) {
            $tokenInfo = $recoveryTokens->createToken($forgotUser, $ipAddress);

            $recoveryMessage = Mailer::template('password-recovery', [
                'username' => $forgotUser->getName(),
                'token' => $tokenInfo->getCode(),
            ]);

            $recoveryMail = Mailer::sendMessage(
                [$forgotUser->getEMailAddress() => $forgotUser->getName()],
                $recoveryMessage['subject'], $recoveryMessage['message']
            );

            if(!$recoveryMail) {
                $notices[] = "Failed to send reset email, please contact the administrator.";
                $recoveryTokens->invalidateToken($tokenInfo);
                break;
            }
        }

        Tools::redirect($urls->format('auth-reset', ['user' => $forgotUser->getId()]));
        return;
    }

    break;
}

Template::render(isset($userInfo) ? 'auth.password_reset' : 'auth.password_forgot', [
    'password_notices' => $notices,
    'password_email' => !empty($forget['email']) && is_string($forget['email']) ? $forget['email'] : '',
    'password_attempts_remaining' => $remainingAttempts,
    'password_user' => $userInfo ?? null,
    'password_verification' => $verifyCode ?? '',
]);
