<?php

declare(strict_types=1);

/**
 * Community-branded two-factor challenge — "/two-factor". Reached after the
 * password step when a member has 2FA enabled (login.php sets the pending marker
 * and redirects here). Reuses the platform's TwoFactor backend verbatim
 * (TOTP / emailed code / backup codes) with the community's own chrome. The
 * generated app's runtime 2FA gate lets this clean URL through because the build
 * ships a root `twofactor-route.php` naming it (see public-routes.php).
 */

require __DIR__ . '/_shell.php';

use PpPhp\Core\Security;
use PpPhp\Core\Auth\TwoFactor;
use PpPhp\Core\Events;

$b = forum_base();

$pending = TwoFactor::pending();
if ($pending === null) {
    header('Location: ' . $b . '/login');
    exit;
}
$uid      = (int) ($pending['uid'] ?? 0);
$username = (string) ($pending['username'] ?? '');
$remember = !empty($pending['remember']);

$method = TwoFactor::activeMethod($uid);
if ($method === '') {
    // 2FA marker but no active method: nothing to challenge — finish the login.
    TwoFactor::clearPending();
    Security::LoginAs($username);
    header('Location: ' . $b . '/');
    exit;
}

$appName = 'PPPHP Community';
$emailField = defined('PP_2FA_EMAIL_FIELD') ? PP_2FA_EMAIL_FIELD : 'email';
$phoneField = defined('PP_2FA_PHONE_FIELD') ? PP_2FA_PHONE_FIELD : 'phone';
$contact = $method === 'email' ? TwoFactor::contact($uid, $emailField)
    : ($method === 'sms' ? TwoFactor::contact($uid, $phoneField) : '');

$mask = static function (string $s, bool $isEmail): string {
    if ($isEmail) {
        $at = strpos($s, '@');
        if ($at === false) { return $s; }
        return substr($s, 0, min(2, $at)) . str_repeat('*', 3) . substr($s, $at);
    }
    return strlen($s) > 4 ? str_repeat('*', max(0, strlen($s) - 4)) . substr($s, -4) : $s;
};

$error = ''; $notice = ''; $view = 'code';
$channelSent = !empty($_SESSION['pp_2fa_otp']);

if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST') {
    $view = in_array(($_POST['view'] ?? ''), ['code', 'send', 'backup'], true) ? (string) $_POST['view'] : 'code';
    if (!Security::CSRF()->Check((string) ($_POST['_token'] ?? ''))) {
        $error = 'Your session expired — please sign in again.';
    } elseif (($_POST['do'] ?? '') === 'send') {
        if ($method === 'totp') {
            $view = 'code';
        } elseif ($contact !== '' && TwoFactor::sendOtp($uid, $method, $contact, $appName)) {
            $view = 'code'; $channelSent = true;
            $notice = ($method === 'sms' ? 'We texted a code to ' : 'We emailed a code to ') . $mask($contact, $method === 'email') . '.';
        } else {
            $error = 'Could not send your code right now. Try a backup code instead.'; $view = 'backup';
        }
    } elseif (($_POST['do'] ?? '') === 'show-backup') {
        $view = 'backup';
    } else {
        $code = (string) ($_POST['code'] ?? '');
        $ok = ($view === 'backup') ? TwoFactor::consumeBackupCode($uid, $code)
            : ($method === 'totp' ? TwoFactor::verifyTotp($uid, $code) : TwoFactor::verifyOtp($uid, $code));
        if ($ok) {
            TwoFactor::clearPending();
            Security::LoginAs($username);
            if (class_exists('\PpPhp\Core\Auth\AuditLog')) {
                \PpPhp\Core\Auth\AuditLog::log('login', null, null, null, null, 'user: ' . $username . ' (2FA)');
            }
            if (class_exists('\PpPhp\Core\Auth\SessionGuard')) {
                \PpPhp\Core\Auth\SessionGuard::track();
            }
            if ($remember && $uid > 0) { Security::SetRememberMeCookie($uid); }
            $_SESSION['_flash'] = ['message' => 'Welcome back, ' . $username . '!', 'type' => 'success'];
            $dest = Events::fire('global.auth.afterLogin', ['user' => Security::CurrentUserData()]);
            header('Location: ' . ((is_string($dest) && $dest !== '') ? $dest : $b . '/'));
            exit;
        }
        $error = $view === 'backup' ? 'That backup code isn\'t valid (or was already used).' : 'That code isn\'t right. Please try again.';
    }
}
if ($view === 'code' && $method !== 'totp' && !$channelSent && $error === '') { $view = 'send'; }

$titles = ['totp' => 'Enter your authenticator code', 'email' => 'Enter the emailed code', 'sms' => 'Enter the texted code'];
$title = $view === 'backup' ? 'Enter a backup code' : ($titles[$method] ?? 'Two-step verification');
$subs = [
    'totp'  => 'Open your authenticator app and type the current 6-digit code.',
    'email' => 'We sent a 6-digit code to ' . $mask($contact, true) . '.',
    'sms'   => 'We sent a 6-digit code to ' . $mask($contact, false) . '.',
];
$sub = $view === 'backup' ? 'Enter one of the one-time backup codes you saved.'
    : ($view === 'send' ? ('Send a one-time code to ' . $mask($contact, $method === 'email') . '.') : ($subs[$method] ?? ''));
$csrf = forum_csrf();

forum_auth_head('Two-step verification — PPPHP Community', '<meta name="robots" content="noindex,follow">');
?>
<div class="fx-auth-card">
  <div class="fx-2fa-mark"><i class="bi bi-shield-lock"></i></div>
  <h1><?= forum_e($title) ?></h1>
  <p class="fx-auth-sub"><?= forum_e($sub) ?></p>
  <?php if ($notice !== ''): ?><div class="fx-formok"><i class="bi bi-check-circle-fill"></i> <?= forum_e($notice) ?></div><?php endif; ?>
  <?php if ($error !== ''): ?><div class="fx-formerr"><i class="bi bi-exclamation-circle"></i> <?= forum_e($error) ?></div><?php endif; ?>

  <?php if ($view === 'send'): ?>
    <form method="post" action="<?= $b ?>/two-factor">
      <input type="hidden" name="_token" value="<?= forum_e($csrf) ?>">
      <input type="hidden" name="do" value="send">
      <button class="fx-submit full" type="submit"><i class="bi <?= $method === 'sms' ? 'bi-chat-dots' : 'bi-envelope' ?>"></i> Send my code</button>
    </form>
  <?php else: ?>
    <form method="post" action="<?= $b ?>/two-factor" autocomplete="off">
      <input type="hidden" name="_token" value="<?= forum_e($csrf) ?>">
      <input type="hidden" name="view" value="<?= forum_e($view) ?>">
      <div class="fx-field">
        <label><?= $view === 'backup' ? 'Backup code' : '6-digit code' ?></label>
        <input class="fx-input fx-2fa-input" name="code" <?= $view === 'backup' ? 'maxlength="9"' : 'inputmode="numeric" pattern="[0-9]*" maxlength="6"' ?> autocomplete="one-time-code" placeholder="<?= $view === 'backup' ? 'XXXX-XXXX' : '000000' ?>" required autofocus>
      </div>
      <button class="fx-submit full" type="submit"><i class="bi bi-check2"></i> Verify &amp; sign in</button>
    </form>
  <?php endif; ?>

  <div class="fx-2fa-alt">
    <?php if ($view !== 'code' && $method === 'totp'): ?>
      <a href="<?= $b ?>/two-factor"><i class="bi bi-phone"></i> Use my authenticator</a>
    <?php endif; ?>
    <?php if ($method !== 'totp' && $view !== 'send'): ?>
      <form method="post" action="<?= $b ?>/two-factor"><input type="hidden" name="_token" value="<?= forum_e($csrf) ?>"><input type="hidden" name="do" value="send"><button type="submit"><i class="bi bi-arrow-repeat"></i> Send a new code</button></form>
    <?php endif; ?>
    <?php if ($view !== 'backup'): ?>
      <form method="post" action="<?= $b ?>/two-factor"><input type="hidden" name="_token" value="<?= forum_e($csrf) ?>"><input type="hidden" name="do" value="show-backup"><button type="submit" formnovalidate><i class="bi bi-key"></i> Use a backup code</button></form>
    <?php endif; ?>
    <a class="fx-2fa-quit" href="<?= $b ?>/generated/auth/logout.php"><i class="bi bi-x-lg"></i> Cancel and sign out</a>
  </div>
</div>
<p class="fx-auth-note"><i class="bi bi-shield-check"></i> Protected by two-factor authentication</p>
<?php forum_auth_foot(); ?>
