<?php
/**
 * POST /api/auth/verify-otp
 *
 * Body: email, otp, purpose (signup | login | forget)
 *
 * - signup: pulls payload from publisher_otp, creates publisher_user, logs in
 * - login : creates session for existing publisher (used when 2FA on)
 * - forget: returns a short-lived reset token (rarely used; reset uses link)
 */

declare(strict_types=1);
require_once dirname(__DIR__, 2) . '/config/bootstrap.php';

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    jsonError('Method not allowed', 405);
}

$ip = Logger::clientIp();
RateLimit::hit('otp:verify:ip:' . $ip, 30, 3600, 'Too many OTP attempts. Try later.');

$email   = strtolower(Validator::clean((string)($_POST['email'] ?? ''), 190));
$otp     = Validator::clean((string)($_POST['otp'] ?? ''), 10);
$purpose = strtolower(Validator::clean((string)($_POST['purpose'] ?? 'signup'), 20));

if (!Validator::email($email)) jsonError('Invalid email.');
if (!Validator::otp($otp))     jsonError('Invalid OTP format.');
if (!in_array($purpose, ['signup', 'login', 'forget'], true)) {
    jsonError('Invalid purpose.');
}

RateLimit::hit("otp:verify:{$purpose}:{$email}", 10, 1800, 'Too many attempts for this email.');

$pdo = db();
$stmt = $pdo->prepare("
    SELECT id, otp_hash, attempts, expires_at, payload
    FROM publisher_otp
    WHERE identifier = ? AND purpose = ? AND verified = 0
    ORDER BY id DESC LIMIT 1
");
$stmt->execute([$email, $purpose]);
$row = $stmt->fetch();

if (!$row) {
    jsonError('No active OTP for this email. Please request a new one.', 404);
}

if (time() > (int)$row['expires_at']) {
    jsonError('OTP has expired. Please request a new one.', 410);
}

$maxAttempts = (int)env('OTP_MAX_ATTEMPTS', 5);
if ((int)$row['attempts'] >= $maxAttempts) {
    jsonError('Too many wrong attempts. Please request a new OTP.', 429);
}

if (!password_verify($otp, $row['otp_hash'])) {
    $pdo->prepare("UPDATE publisher_otp SET attempts = attempts + 1 WHERE id = ?")
        ->execute([$row['id']]);
    $remaining = $maxAttempts - ((int)$row['attempts'] + 1);
    jsonError("Wrong OTP. {$remaining} attempts left.");
}

// ───────────────────── OTP correct — mark verified ─────────────────────
$pdo->prepare("UPDATE publisher_otp SET verified = 1 WHERE id = ?")->execute([$row['id']]);
$payload = $row['payload'] ? json_decode($row['payload'], true) : [];

// ───────────────────── Branch on purpose ─────────────────────
if ($purpose === 'signup') {
    $pdo->beginTransaction();
    try {
        $existingId = $payload['existing_id'] ?? null;
        if ($existingId) {
            $pdo->prepare("UPDATE publisher_user SET email_verified = 1 WHERE id = ?")
                ->execute([(int)$existingId]);
            $publisherId = (int)$existingId;
        } else {
            $uuid = bin2hex(random_bytes(8)) . '-' . bin2hex(random_bytes(4)) . '-' . bin2hex(random_bytes(4)) . '-' . bin2hex(random_bytes(4)) . '-' . bin2hex(random_bytes(6));
            $pdo->prepare("
                INSERT INTO publisher_user
                  (uuid, name, email, phone, country_code, country, password_hash, email_verified)
                VALUES (?, ?, ?, ?, ?, ?, ?, 1)
            ")->execute([
                $uuid,
                $payload['name'] ?? '',
                $email,
                $payload['phone'] ?? null,
                $payload['country_code'] ?? '+91',
                $payload['country'] ?? 'India',
                $payload['password_hash'] ?? '',
            ]);
            $publisherId = (int)$pdo->lastInsertId();
        }
        $pdo->commit();
    } catch (Throwable $e) {
        $pdo->rollBack();
        Logger::error('Signup finalize failed', ['error' => $e->getMessage()]);
        jsonError('Could not create account. Please try again.', 500);
    }

    Session::create($publisherId);
    RateLimit::reset('signup:email:' . $email);
    Logger::audit($publisherId, 'signup_verified');

    jsonSuccess('Account verified.', ['redirect' => '/dashboard']);
}

if ($purpose === 'login') {
    // Existing user, password already validated in /api/auth/login (2FA mode)
    $pubId = (int)($payload['publisher_id'] ?? 0);
    if ($pubId <= 0) jsonError('Invalid login OTP context.', 400);

    Session::create($pubId, (bool)($payload['remember'] ?? false));
    Logger::audit($pubId, 'login_otp_verified');

    jsonSuccess('Login successful.', ['redirect' => '/dashboard']);
}

if ($purpose === 'forget') {
    // Forget OTP path is rarely used (we prefer link), but support it.
    $stmt = $pdo->prepare("SELECT id FROM publisher_user WHERE email = ? LIMIT 1");
    $stmt->execute([$email]);
    $u = $stmt->fetch();
    if (!$u) jsonError('No such account.', 404);

    $token = bin2hex(random_bytes(32));
    $tokenHash = hash('sha256', $token);
    $expiry = date('Y-m-d H:i:s', time() + (int)env('RESET_TOKEN_EXPIRY_MINUTES', 30) * 60);
    $pdo->prepare("
        INSERT INTO publisher_password_reset (publisher_id, token_hash, expires_at, ip_address)
        VALUES (?, ?, ?, ?)
    ")->execute([(int)$u['id'], $tokenHash, $expiry, $ip]);

    jsonSuccess('OTP verified. Use the reset token below.', [
        'reset_token' => $token,
        'redirect'    => '/reset-password?token=' . $token,
    ]);
}
