<?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 signupFail(string $message, string $email = ''): void
{
    $_SESSION['signup_error'] = $message;
    if ($email !== '') {
        $_SESSION['signup_email'] = $email;
    }
    header('Location: ' . APP_URL . '/sign-up');
    exit;
}

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

// IP-scoped throttle — blocks bulk/scripted account creation regardless
// of whether the client sends cookies. Recorded before any DB work.
$ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
if (!RateLimiter::attempt('signup_ip_' . $ip, 10, 3600)) {
    signupFail('Too many sign-up attempts from this location. Please try again later.', $email);
}

// CSRF protection
if (!isset($_POST['csrf_token']) || !isset($_SESSION['csrf_token']) ||
    !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
    signupFail('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'] ?? '';
$confirmPassword = $_POST['confirm_password'] ?? '';

if ($password !== $confirmPassword) {
    signupFail('Passwords do not match.', $email);
}

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

// 2026-08-19 security fix (pre-launch audit finding M-3, closed in full
// this pass — see src/Auth.php::signUp()'s own comment for the other
// half). This used to pass Auth::signUp()'s own $result['message']
// straight through to the visible error banner — for the 'email_taken'
// case specifically, that message is the literal string "An account with
// this email already exists.", i.e. a direct, unauthenticated account-
// enumeration oracle (confirmed live during the audit: submitting a
// known-registered email here returned that exact text). A first pass
// fixed the TEXT (uniform message on both paths) but left a real,
// confirmed-live residual signal: a genuinely new signup auto-logged the
// submitter in and redirected to /onboarding, while the email_taken case
// did neither — an attacker inspecting the raw HTTP response (Set-Cookie,
// Location header) rather than just the rendered page text could still
// tell the two cases apart. Auth::signUp() no longer auto-logs in at all
// (see its own comment), so BOTH branches below now do the exact same
// three things: no session change, the exact same $_SESSION['signup_notice']
// text, the exact same redirect target — genuinely observation-identical
// over the wire, not just text-identical. The real account (when one was
// created) is fully real and unaffected; the trader verifies their email
// and then signs in normally, same as any returning user — see
// api/auth/sign-in.php for where a first-time signed-in user is now sent
// to /onboarding instead of /dashboard, preserving that flow.
//
// Every OTHER signUp() error ('invalid_email', 'weak_password',
// 'server_error') is about the SUBMITTED input itself, not about whether
// some other account exists, and is still shown verbatim via
// signupFail() below — only 'email_taken' is special-cased.
if (isset($result['error']) && $result['error'] === 'email_taken') {
    // Same random-delay technique already used by forgot-password.php's
    // equivalent not-a-real-account path, so this branch doesn't complete
    // measurably faster than the real signUp() path (which does a real
    // Argon2id hash + 2 INSERTs) in a way a timing-sensitive scraper
    // could key on. A genuinely new signup additionally does a
    // verification-token UPDATE and (in production, once RESEND_API_KEY
    // is configured) a real outbound HTTPS call to Resend before it
    // reaches its own identical redirect below — that network round trip
    // is real added latency this fixed 100-300ms window can't fully mask
    // against a patient, high-precision timing attacker. Flagged
    // honestly rather than claimed as fully closed: the text/cookie/
    // redirect-target signal (the practical, scriptable vector this
    // finding was actually about) is fully closed by this change; a
    // network-timing side channel on top of a real transactional email
    // provider is a materially harder, lower-value attack this fix does
    // not additionally defend against.
    usleep(random_int(100000, 300000));
    $_SESSION['signup_notice'] = 'Check your email to finish setting up your account. If you already have an account with this email, you can sign in instead.';
    header('Location: ' . APP_URL . '/sign-up');
    exit;
}

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

// Generate email verification token
$userId = $result['user_id'];
$verificationToken = generateUUID();

try {
    $stmt = $pdo->prepare('UPDATE users SET verification_token = :token WHERE id = :uid');
    $stmt->execute([':token' => $verificationToken, ':uid' => $userId]);

    $verificationUrl = APP_URL . '/verify?token=' . urlencode($verificationToken);

    // Plaintext, unexpired login-capable tokens must never sit in a
    // web-servable log file in production (logs/ is also now blocked at
    // the web-server level via logs/.htaccess, but this keeps a real
    // token out of that file entirely outside of local development,
    // rather than relying on a single layer of defense).
    if (APP_ENV === 'development') {
        $logDir = __DIR__ . '/../../logs';
        if (!is_dir($logDir)) {
            mkdir($logDir, 0755, true);
        }
        $logEntry = sprintf(
            "[%s] User ID: %s | Email: %s | Token: %s | URL: %s\n",
            date('Y-m-d H:i:s'),
            $userId,
            $email,
            $verificationToken,
            $verificationUrl
        );
        file_put_contents($logDir . '/verification.log', $logEntry, FILE_APPEND | LOCK_EX);
    }

    if (defined('RESEND_API_KEY') && RESEND_API_KEY) {
        sendVerificationEmail($email, $verificationUrl, $userId);
    }

} catch (PDOException $e) {
    error_log("Failed to save verification token for user {$userId}: " . $e->getMessage());
    // Don't block sign-up if token generation fails
}

// 2026-08-19 security fix (finding M-3): same message, same redirect
// target as the 'email_taken' branch above — see that branch's comment.
// No session was started for this request (Auth::signUp() no longer
// auto-logs in), so there is nothing here for an outside observer to
// distinguish from the duplicate-email case.
$_SESSION['signup_notice'] = 'Check your email to finish setting up your account. If you already have an account with this email, you can sign in instead.';
header('Location: ' . APP_URL . '/sign-up');
exit;

/**
 * Generate a UUID v4
 */
function generateUUID(): string
{
    $data = random_bytes(16);
    $data[6] = chr(ord($data[6]) & 0x0f | 0x40); // Version 4
    $data[8] = chr(ord($data[8]) & 0x3f | 0x80); // Variant
    return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}

/**
 * Send email verification email using Resend API
 */
function sendVerificationEmail(string $email, string $verifyUrl, string $userId): bool
{
    $apiKey = RESEND_API_KEY;
    $fromEmail = 'TraderAlign <noreply@yourdomain.com>';

    $html = <<<HTML
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Verify Your Email</title>
    </head>
    <body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f9fafb;">
        <div style="background: linear-gradient(135deg, #2563eb, #1d4ed8); padding: 30px; border-radius: 8px 8px 0 0; text-align: center;">
            <h1 style="color: #ffffff; font-size: 28px; margin: 0;">TraderAlign</h1>
            <p style="color: #bfdbfe; margin: 10px 0 0;">Trading Journal & Analytics</p>
        </div>

        <div style="background: #ffffff; padding: 30px; border-radius: 0 0 8px 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
            <h2 style="color: #1a1a1a; font-size: 22px; margin-top: 0;">Verify Your Email Address</h2>

            <p style="font-size: 16px;">Welcome to TraderAlign! To get started, please verify your email address by clicking the button below:</p>

            <div style="text-align: center; margin: 35px 0;">
                <a href="{$verifyUrl}" style="display: inline-block; background: linear-gradient(135deg, #2563eb, #1d4ed8); color: #ffffff; text-decoration: none; padding: 14px 40px; border-radius: 8px; font-weight: 600; font-size: 16px; box-shadow: 0 2px 4px rgba(37, 99, 235, 0.3);">Verify Email</a>
            </div>

            <p style="font-size: 14px; color: #6b7280;">Or copy and paste this link in your browser:</p>
            <p style="background: #f3f4f6; padding: 12px; border-radius: 6px; word-break: break-all; font-size: 13px; color: #4b5563;">{$verifyUrl}</p>

            <p style="font-size: 14px;">This verification helps us ensure account security and enables all features of TraderAlign.</p>

            <hr style="border: none; border-top: 1px solid #e5e7eb; margin: 30px 0;">

            <p style="font-size: 13px; color: #9ca3af;">If you didn't create this account, you can safely ignore this email.</p>
            <p style="font-size: 12px; color: #d1d5db;">User ID: {$userId}</p>
        </div>
    </body>
    </html>
    HTML;

    $data = [
        'from' => $fromEmail,
        'to' => $email,
        'subject' => 'Verify Your Email - TraderAlign',
        'html' => $html,
        'headers' => [
            'X-Entity-Ref-ID' => 'verify-' . $userId . '-' . time(),
        ],
    ];

    $ch = curl_init('https://api.resend.com/emails');
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . $apiKey,
            'Content-Type: application/json',
        ],
        CURLOPT_POSTFIELDS => json_encode($data),
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 10,
        CURLOPT_CONNECTTIMEOUT => 5,
    ]);

    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $curlError = curl_error($ch);
    curl_close($ch);

    if ($curlError) {
        error_log("Resend API curl error: {$curlError}");
        return false;
    }

    if ($httpCode !== 200) {
        error_log("Resend API error (HTTP {$httpCode}): {$response}");
        return false;
    }

    return true;
}
