<?php
require_once __DIR__ . '/../../config/config.php';
require_once __DIR__ . '/../../src/ScopedDb.php';
require_once __DIR__ . '/../../src/PasswordPolicy.php';
require_once __DIR__ . '/../../src/RateLimiter.php';
require_once __DIR__ . '/../../src/Auth.php';

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    exit;
}

if (session_status() === PHP_SESSION_NONE) {
    session_name(SESSION_NAME);
    session_start();
}

function signinFail(string $message, string $email = ''): void
{
    $_SESSION['signin_error'] = $message;
    if ($email !== '') {
        $_SESSION['signin_email'] = $email;
    }
    header('Location: ' . APP_URL . '/sign-in');
    exit;
}

$email = trim($_POST['email'] ?? '');

// IP-scoped throttle — protects against a single source spraying guesses
// across many different accounts. Persists on disk regardless of
// cookies/session, unlike index.php's own session-based global limiter.
// Recorded before CSRF/credential checks so it can't be bypassed by
// intentionally failing an earlier step.
$ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
if (!RateLimiter::attempt('signin_ip_' . $ip, 20, 600)) {
    signinFail('Too many sign-in attempts from this location. Please wait a few minutes and try again.', $email);
}

// CSRF protection
if (!isset($_POST['csrf_token']) || !isset($_SESSION['csrf_token']) ||
    !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
    signinFail('Your session expired. Please try again.', $email);
}
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));

try {
    $dsn = "mysql:host=" . DB_HOST . ";port=" . DB_PORT . ";dbname=" . DB_NAME . ";charset=utf8mb4";
    $pdo = new PDO($dsn, DB_USER, DB_PASS, [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        PDO::ATTR_EMULATE_PREPARES => false,
    ]);
} catch (PDOException $e) {
    http_response_code(500);
    die('Server error.');
}

$auth = new Auth($pdo);
$password = $_POST['password'] ?? '';

$result = $auth->signIn($email, $password);

if (isset($result['error'])) {
    signinFail($result['message'], $email);
}

unset($_SESSION['signin_error'], $_SESSION['signin_email']);

// 2026-08-19 security fix (finding M-3, part of closing the sign-up
// enumeration channel in full): Auth::signUp() no longer auto-logs a new
// account in and redirects straight to /onboarding — that redirect was
// the account-enumeration side channel's other half (see
// src/Auth.php::signUp() and api/auth/sign-up.php for the full account).
// /onboarding was, until now, ONLY ever reached via that auto-login
// redirect — nothing else in the app sends an onboarding-incomplete user
// there — so removing it without replacing it here would have silently
// made onboarding unreachable for every future signup. This is the
// replacement: the first time a user successfully signs in with
// onboarding_context.onboarding_complete still false, send them to
// /onboarding instead of /dashboard; every subsequent sign-in (and every
// already-onboarded user) is unaffected and goes straight to /dashboard,
// exactly as before.
$onboardingComplete = true;
try {
    $stmt = $pdo->prepare('SELECT onboarding_complete FROM user_profiles WHERE user_id = :uid LIMIT 1');
    $stmt->execute([':uid' => $result['user_id']]);
    $row = $stmt->fetch();
    $onboardingComplete = $row === false || (bool)($row['onboarding_complete'] ?? false);
} catch (PDOException $e) {
    // Fail open to /dashboard rather than blocking a real sign-in on a
    // read that's purely a UX nicety — same "don't block sign-up if
    // token generation fails" reasoning already used in sign-up.php.
    error_log('sign-in.php: onboarding_complete check failed: ' . $e->getMessage());
}

header('Location: ' . APP_URL . ($onboardingComplete ? '/dashboard' : '/onboarding'));
exit;
