<?php
// =============================================================
//  POST /api/v1/auth/forgot_password
//
//  Mobile equivalent of ajax/forgot_password_handler.php's request_reset
//  action — same OTP-via-SMS-then-email delivery, same account lookup,
//  same "always respond the same way" anti-enumeration behavior — but
//  stateless: the OTP's hash and target account travel inside a signed
//  reset_token (include/PendingAuthToken.php) instead of $_SESSION, since
//  a mobile HTTP client can't be relied on to carry a session cookie
//  between calls the way a browser does. No CSRF token either — that
//  protects a cookie-authenticated browser session from cross-site
//  forgery, which doesn't apply to a native app calling its own API.
//
//  Body: { "email": "..." }
//  Always responds success — never reveals whether the email exists.
// =============================================================

require_once __DIR__ . '/../_bootstrap.php';
require_once __DIR__ . '/../../../include/rate_limiter.php';
require_once __DIR__ . '/../../../include/account_helpers.php';
require_once __DIR__ . '/../../../services/VelstackServiceProvider.php';

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

$body  = Api::body();
$email = trim((string)($body['email'] ?? ''));

if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
    Api::error(422, 'ValidationError', 'A valid email is required');
}

$rl = rateLimitAttempt('api_otp_request:' . ($_SERVER['REMOTE_ADDR'] ?? 'unknown') . ':' . strtolower($email), 5, 900);
if ($rl['blocked']) {
    Api::error(429, 'TooManyAttempts', 'Too many requests. Please wait a few minutes before requesting another code.', [
        'retry_after' => $rl['retry_after'],
    ]);
}

$genericMessage = 'If an account exists with this email, a reset code has been sent.';
$account = $account_repo->findActiveByEmail($email);

// A reset_token is always returned, real account or not — otherwise its
// mere presence/absence in the response would itself be an account-
// enumeration oracle, defeating the point of $genericMessage below. A
// lookup against a nonexistent account fails reset_password.php's OTP
// check the same way a wrong OTP against a real account would.
$otp     = sprintf('%06d', random_int(100000, 999999));
$otpHash = password_hash($otp, PASSWORD_DEFAULT);

$resetToken = PendingAuthToken::issue([
    'purpose'  => 'password_reset',
    'table'    => $account?->getTable() ?? 'users',
    'id'       => $account?->getId()    ?? 0,
    'email'    => $email,
    'otp_hash' => $otpHash,
], 900); // 15 minutes, matching the web flow's OTP lifetime

if (!$account) {
    Api::success(['message' => $genericMessage, 'reset_token' => $resetToken]);
}

$sent = false;
if (!empty($account->getPhone())) {
    try {
        $svc = new VelstackServiceProvider();
        $sr  = $svc->sendQuickSMS($account->getPhone(), "Your STH NRMS password reset OTP is: {$otp}. Valid for 15 minutes.");
        $sent = ($sr['status_code'] == 200);
    } catch (Exception $e) {
        error_log('API password reset SMS error: ' . $e->getMessage());
    }
}
if (!$sent) {
    $sent = sendAccountEmail($email, 'Your STH NRMS Password Reset Code', 'Password Reset Requested', [
        'Hello <strong>' . htmlspecialchars($account->getFirstname()) . '</strong>,',
        "Your one-time reset code is: <strong style=\"font-size:20px;letter-spacing:3px;\">{$otp}</strong>",
        'This code expires in 15 minutes. If you did not request this, you can safely ignore this message.',
    ]);
}
if (!$sent) {
    error_log('API password reset: failed to deliver OTP via SMS or email for ' . $account->getTable() . '#' . $account->getId());
}

Api::success([
    'message'     => $genericMessage,
    'reset_token' => $resetToken,
]);
