<?php
// =============================================================
//  POST /api/v1/auth/verify_2fa
//
//  Second step of login for staff accounts with TOTP enabled — mirrors
//  verify_2fa.php's code/recovery-code check, but reached with the
//  pending_token issued by auth/login.php (data.status === 'requires_2fa')
//  instead of a $_SESSION['pending_2fa_user_id'] a browser redirect set.
//
//  Body: { "pending_token": "...", "code": "123456" }   (code may also be
//         an XXXX-XXXX recovery code)
// =============================================================

require_once __DIR__ . '/../_bootstrap.php';
require_once __DIR__ . '/_helpers.php';
require_once __DIR__ . '/../../../include/rate_limiter.php';
require_once __DIR__ . '/../../../include/Totp.php';

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    Api::error(405, 'MethodNotAllowed', 'Use POST');
}

$body    = Api::body();
$pending = PendingAuthToken::verify((string)($body['pending_token'] ?? ''), '2fa');
$code    = trim((string)($body['code'] ?? ''));

if (!$pending) {
    Api::error(401, 'InvalidPendingToken', 'This verification step has expired. Please log in again.');
}
if ($code === '') {
    Api::error(422, 'ValidationError', 'Code is required');
}

$userId = (int)$pending['id'];

$rl = rateLimitAttempt('api_2fa_verify:' . ($_SERVER['REMOTE_ADDR'] ?? 'unknown') . ':' . $userId, 8, 900); // 8 / 15 min
if ($rl['blocked']) {
    $mins = max(1, (int)ceil($rl['retry_after'] / 60));
    Api::error(429, 'TooManyAttempts', "Too many attempts. Please try again in {$mins} minute(s).", [
        'retry_after' => $rl['retry_after'],
    ]);
}

$verified = false;
$secret   = Totp::getSecretFor($conn, $userId);
if ($secret && Totp::verifyCode($secret, $code)) {
    $verified = true;
} else {
    // Not a valid TOTP code — try it as a recovery code instead.
    foreach (Totp::getRecoveryHashesFor($conn, $userId) ?? [] as $hash) {
        if (password_verify($code, $hash)) {
            Totp::consumeRecoveryHash($conn, $userId, $hash);
            $verified = true;
            break;
        }
    }
}

if (!$verified) {
    Api::error(401, 'InvalidCode', 'Invalid code. Please try again.');
}

$account = $account_repo->findById('users', $userId);
if (!$account) {
    Api::error(404, 'AccountNotFound', 'Account no longer exists.');
}

$result = api_finalize_login($conn, $perm_manager, $token_manager, $account, $pending['device_label'] ?? null, api_normalize_platform($pending['platform'] ?? null));
Api::success(array_merge(['status' => 'ok'], $result));
