<?php
// =============================================================
//  POST /api/v1/auth/login
//
//  Mobile equivalent of login.php's POST handler — same rate limiting,
//  same AccountRepository::authenticate() lookup, same forced-password-
//  change and 2FA gates — but returns JSON + a bearer token instead of
//  redirecting a browser session.
//
//  Body: { "email": "...", "password": "...", "device_label"?: "...", "platform"?: "android"|"ios"|"web" }
//
//  Responses (all HTTP 200 on valid credentials — the follow-up step, if
//  any, is signalled via data.status so the client can branch cleanly):
//    data.status === 'ok'                        → data.token, data.user
//    data.status === 'requires_2fa'               → data.pending_token (→ auth/verify_2fa.php)
//    data.status === 'requires_password_change'   → data.pending_token (→ auth/change_password.php)
// =============================================================

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();
$email    = trim((string)($body['email'] ?? ''));
$password = (string)($body['password'] ?? '');
$deviceLabel = isset($body['device_label']) ? trim((string)$body['device_label']) : null;
$platform    = api_normalize_platform($body['platform'] ?? null);

if ($email === '' || $password === '') {
    Api::error(422, 'ValidationError', 'Email and password are required');
}

$rl_ip  = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
$rl_key = 'api_login:' . $rl_ip . ':' . strtolower($email);
$rl     = rateLimitAttempt($rl_key, 5, 900); // 5 attempts / 15 minutes per IP+email — same budget as the web login

if ($rl['blocked']) {
    $mins = max(1, (int)ceil($rl['retry_after'] / 60));
    Api::error(429, 'TooManyAttempts', "Too many login attempts. Please try again in {$mins} minute(s).", [
        'retry_after' => $rl['retry_after'],
    ]);
}

$account = $account_repo->authenticate($email, $password);

if (!$account) {
    $existing = $account_repo->findByEmailAnyStatus($email);
    if ($existing !== null && $existing->getStatus() === 'Inactive') {
        Api::error(403, 'AccountInactive', 'Your account is inactive. Please contact the administrator.');
    }
    Api::error(401, 'InvalidCredentials', 'Invalid email or password');
}

// ── Forced password change (first login) ────────────────────────────────────
if ($account->isPasswordChangeRequired()) {
    $pending = PendingAuthToken::issue([
        'purpose' => 'password_change',
        'table'   => $account->getTable(),
        'id'      => $account->getId(),
    ], 600);
    Api::success([
        'status'        => 'requires_password_change',
        'pending_token' => $pending,
        'full_name'     => $account->getFullName(),
    ]);
}

// ── Two-factor gate (staff only, opt-in) ─────────────────────────────────────
if ($account->getTable() === 'users' && Totp::isEnabledFor($conn, $account->getId())) {
    $pending = PendingAuthToken::issue([
        'purpose'      => '2fa',
        'table'        => $account->getTable(),
        'id'           => $account->getId(),
        'device_label' => $deviceLabel,
        'platform'     => $platform,
    ], 300);
    Api::success([
        'status'        => 'requires_2fa',
        'pending_token' => $pending,
        'full_name'     => $account->getFullName(),
    ]);
}

$result = api_finalize_login($conn, $perm_manager, $token_manager, $account, $deviceLabel, $platform);
Api::success(array_merge(['status' => 'ok'], $result));
