<?php
/**
 * TraderAlign — Reset Password Page
 * Handles password reset with token validation and security measures
 */

require_once __DIR__ . '/../config/config.php';
require_once __DIR__ . '/../src/PasswordPolicy.php';

// Start session for CSRF protection — session_name(SESSION_NAME) matters
// here: without it, a direct (router-bypassing) hit on this file would
// start a session under PHP's default name instead of the app's own
// cookie, splitting this page's session state from the rest of the app.
if (session_status() === PHP_SESSION_NONE) {
    session_name(SESSION_NAME);
    session_start();
}

// Generate CSRF token if not exists
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

$token = $_GET['token'] ?? '';
$error = $_GET['error'] ?? '';
$success = false;
$validToken = false;
$tokenExpired = false;
$userId = null;
$userEmail = null;

// Custom error messages
$errorMessages = [
    'password_weak' => 'Password does not meet the requirements below.',
    'password_mismatch' => 'Passwords do not match.',
    'token_expired' => 'This reset link has expired. Please request a new one.',
    'token_used' => 'This reset link has already been used.',
    'invalid_token' => 'Invalid reset link. Please request a new one.',
    'system_error' => 'A system error occurred. Please try again.',
    'rate_limit' => 'Too many attempts. Please try again in 15 minutes.',
];

if ($token) {
    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::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4",
            PDO::ATTR_EMULATE_PREPARES => false,
        ]);
        
        // Token lookup with SHA-256 hashing if tokens are stored hashed
        $tokenHash = hash('sha256', $token);
        
        $stmt = $pdo->prepare(
            'SELECT at.user_id, at.expires_at, at.used_at, u.email 
             FROM auth_tokens at
             JOIN users u ON u.id = at.user_id
             WHERE at.token = :token 
             AND at.type = :type 
             AND u.deleted_at IS NULL'
        );
        $stmt->execute([':token' => $tokenHash, ':type' => 'password_reset']);
        $row = $stmt->fetch();
        
        if ($row) {
            // Check if token is expired
            if (strtotime($row['expires_at']) < time()) {
                $tokenExpired = true;
                $validToken = false;
            } elseif ($row['used_at'] !== null) {
                // Token already used
                $validToken = false;
                $error = 'token_used';
            } else {
                $validToken = true;
                $userId = $row['user_id'];
                $userEmail = $row['email'];
            }
        }
    } catch (PDOException $e) {
        error_log("Reset password page error: " . $e->getMessage());
        $error = 'system_error';
    }
}

if ($_SERVER['REQUEST_METHOD'] === 'POST' && $validToken) {
    // CSRF Protection
    if (!isset($_POST['csrf_token']) || !isset($_SESSION['csrf_token']) || 
        !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
        error_log("CSRF token validation failed for password reset. User ID: {$userId}");
        $error = 'system_error';
    } else {
        // Regenerate CSRF token
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
        
        $password = $_POST['password'] ?? '';
        $confirm = $_POST['confirm_password'] ?? '';

        // Password strength validation — same policy enforced on Sign Up
        // and Change Password (src/PasswordPolicy.php), never a separate
        // copy of the rules.
        $passwordErrors = PasswordPolicy::validate($password, ['email' => $userEmail ?? '']);

        if (empty($password)) {
            $error = 'Password is required.';
        } elseif (!empty($passwordErrors)) {
            $error = implode(' ', $passwordErrors);
        } elseif ($password !== $confirm) {
            $error = 'Passwords do not match.';
        } else {
            try {
                $pdo->beginTransaction();

                $hash = password_hash($password, PASSWORD_ARGON2ID, PasswordPolicy::hashOptions());
                
                // Update user password and reset failed attempts
                $stmt = $pdo->prepare(
                    'UPDATE users 
                     SET password_hash = :hash, 
                         updated_at = NOW(),
                         failed_login_attempts = 0,
                         last_password_change = NOW()
                     WHERE id = :uid'
                );
                $stmt->execute([':hash' => $hash, ':uid' => $userId]);
                
                // Mark token as used
                $stmt = $pdo->prepare(
                    'UPDATE auth_tokens 
                     SET used_at = NOW(), 
                         ip_address = :ip 
                     WHERE token = :token'
                );
                $stmt->execute([
                    ':token' => $tokenHash,
                    ':ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
                ]);
                
                // Invalidate all other password reset tokens for this user
                $stmt = $pdo->prepare(
                    'UPDATE auth_tokens 
                     SET used_at = NOW() 
                     WHERE user_id = :uid 
                     AND type = :type 
                     AND used_at IS NULL 
                     AND token != :token'
                );
                $stmt->execute([
                    ':uid' => $userId,
                    ':type' => 'password_reset',
                    ':token' => $tokenHash
                ]);
                
                $pdo->commit();
                
                // Log successful password reset
                error_log("Password reset successful for user {$userId} from IP {$_SERVER['REMOTE_ADDR']}");
                
                // Send notification email about password change
                if ($userEmail) {
                    sendPasswordChangedNotification($userEmail, $userId);
                }
                
                $success = true;
                
            } catch (PDOException $e) {
                if ($pdo->inTransaction()) {
                    $pdo->rollBack();
                }
                error_log("Password reset failed: " . $e->getMessage());
                $error = 'system_error';
            }
        }
    }
}

/**
 * Send password changed notification email
 * 
 * @param string $email User's email
 * @param string $userId User ID
 */
function sendPasswordChangedNotification(string $email, string $userId): void
{
    if (!defined('RESEND_API_KEY') || !RESEND_API_KEY) {
        error_log("Password change notification for user {$userId}: Email service not configured");
        return;
    }
    
    $html = <<<HTML
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </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;">
        <h2 style="color: #1a1a1a;">Password Changed Successfully</h2>
        
        <p>Your TraderAlign password has been successfully changed.</p>
        
        <p>If you didn't make this change, please contact support immediately and secure your account.</p>
        
        <hr style="border: none; border-top: 1px solid #e5e7eb; margin: 30px 0;">
        
        <p style="font-size: 12px; color: #9ca3af;">
            Time: {$datetime} UTC<br>
            IP: {$_SERVER['REMOTE_ADDR']}<br>
            User ID: {$userId}
        </p>
    </body>
    </html>
    HTML;
    
    try {
        $ch = curl_init('https://api.resend.com/emails');
        curl_setopt_array($ch, [
            CURLOPT_POST => true,
            CURLOPT_HTTPHEADER => [
                'Authorization: Bearer ' . RESEND_API_KEY,
                'Content-Type: application/json',
            ],
            CURLOPT_POSTFIELDS => json_encode([
                'from' => 'TraderAlign <noreply@yourdomain.com>',
                'to' => $email,
                'subject' => 'Your TraderAlign Password Has Been Changed',
                'html' => $html,
            ]),
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 10,
        ]);
        
        curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        
        if ($httpCode !== 200) {
            error_log("Failed to send password change notification to user {$userId}");
        }
    } catch (\Throwable $e) {
        error_log("Password change notification error: " . $e->getMessage());
    }
}

// Computed here, after both the token-validation block above and the POST
// handler above it have had a chance to overwrite $error — this used to
// run right after reading $_GET['error'], before any POST-time validation
// error was ever assigned, so a rejected password always displayed the
// stale (usually empty) original value instead of the real reason.
$displayError = $errorMessages[$error] ?? $error;
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="robots" content="noindex, nofollow">
<title>Reset Password - TraderAlign</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/traderalign/assets/tokens.css">
<link rel="icon" type="image/png" sizes="32x32" href="/traderalign/assets/favicon/favicon-32.png">
    <link rel="icon" type="image/png" sizes="16x16" href="/traderalign/assets/favicon/favicon-16.png">
    <link rel="apple-touch-icon" sizes="180x180" href="/traderalign/assets/favicon/apple-touch-icon.png">
    <link rel="shortcut icon" href="/traderalign/assets/favicon/favicon.ico">
<link rel="stylesheet" href="/traderalign/assets/css/brand.css">
<style>
.auth-page{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:2rem;background:var(--background)}
.auth-card{width:100%;max-width:26rem;background:var(--background-elevated);border:1px solid var(--border);border-radius:var(--radius-xl);padding:2.5rem;box-shadow:0 4px 6px -1px rgba(0,0,0,0.1),0 2px 4px -2px rgba(0,0,0,0.1)}
.auth-card h1{font-size:1.5rem;font-weight:700;color:var(--foreground);margin-bottom:.5rem}
.auth-card .sub{color:var(--foreground-muted);font-size:.9rem;margin-bottom:2rem;line-height:1.5}
.form-group{margin-bottom:1.25rem;position:relative}
.form-group label{display:block;font-size:.875rem;font-weight:500;color:var(--foreground);margin-bottom:.375rem}
.form-group input{width:100%;height:2.75rem;padding:0 .875rem;background:var(--background-surface);border:1px solid var(--border);border-radius:var(--radius-md);color:var(--foreground);font-family:inherit;font-size:.95rem;transition:border-color 0.2s ease,box-shadow 0.2s ease}
.form-group input:focus{outline:none;border-color:var(--primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--primary) 15%,transparent)}
.form-group input.error{border-color:var(--negative)}
.form-group .field-error{display:none;font-size:.75rem;color:var(--negative);margin-top:.25rem}
.form-group .field-error.show{display:block}
.password-strength{margin-top:.5rem}
.password-strength .strength-bar{height:4px;background:var(--background-surface);border-radius:2px;overflow:hidden;margin-bottom:.25rem}
.password-strength .strength-fill{height:100%;border-radius:2px;transition:width 0.3s ease,background-color 0.3s ease}
.password-strength .strength-text{font-size:.72rem;color:var(--foreground-dim)}
.strength-weak .strength-fill{width:25%;background:var(--negative)}
.strength-fair .strength-fill{width:50%;background:var(--warning)}
.strength-good .strength-fill{width:75%;background:var(--primary)}
.strength-strong .strength-fill{width:100%;background:var(--positive)}
.success-msg{background:color-mix(in srgb,var(--positive) 12%,transparent);border:1px solid color-mix(in srgb,var(--positive) 25%,transparent);color:var(--positive);padding:.75rem 1rem;border-radius:var(--radius-md);font-size:.875rem;margin-bottom:1.5rem;line-height:1.5;display:flex;align-items:flex-start;gap:.5rem}
.success-msg .icon{flex-shrink:0;margin-top:2px}
.error-msg{background:color-mix(in srgb,var(--negative) 12%,transparent);border:1px solid var(--negative);color:var(--negative);padding:.75rem 1rem;border-radius:var(--radius-md);font-size:.875rem;margin-bottom:1.5rem;line-height:1.5}
.warning-msg{background:color-mix(in srgb,var(--warning) 12%,transparent);border:1px solid var(--warning);color:var(--warning);padding:.75rem 1rem;border-radius:var(--radius-md);font-size:.875rem;margin-bottom:1.5rem;line-height:1.5}
.footer-text{text-align:center;margin-top:1.5rem;font-size:.875rem;color:var(--foreground-muted)}
.footer-text a{color:var(--primary);font-weight:500;text-decoration:none;transition:opacity 0.2s ease}
.footer-text a:hover{opacity:0.8}
.btn-primary{position:relative;display:inline-flex;align-items:center;justify-content:center;gap:.5rem;text-decoration:none;transition:all 0.2s ease}
.password-requirements{font-size:.75rem;color:var(--foreground-dim);margin-top:.5rem;line-height:1.4}
.password-requirements .req{display:flex;align-items:center;gap:.25rem}
.password-requirements .req.met{color:var(--positive)}
.password-requirements .req .icon{width:12px}
.auth-brand{display:flex;justify-content:center;margin-bottom:1.75rem}
</style>
</head>
<body class="auth-page">
<div class="auth-card">
    <div class="auth-brand"><a href="/traderalign/"><img src="/traderalign/images/logo.png" alt="TraderAlign" class="ta-logo ta-logo--xl"></a></div>
    <?php if ($success): ?>
    <h1>Password reset complete</h1>
    <div class="success-msg">
        <span class="icon">✓</span>
        <span>Your password has been updated successfully. You can now sign in with your new password.</span>
    </div>
    <a href="/traderalign/sign-in" class="btn btn-primary btn-lg" style="width:100%;text-align:center;text-decoration:none">Sign In to Your Account</a>
    
    <?php elseif ((!$validToken && $token) || $tokenExpired): ?>
    <h1><?= $tokenExpired ? 'Link expired' : 'Invalid or expired link' ?></h1>
    <div class="<?= $tokenExpired ? 'warning-msg' : 'error-msg' ?>">
        <?php if ($tokenExpired): ?>
        This reset link has expired. For security, links are only valid for 1 hour.
        <?php else: ?>
        This reset link is invalid or has already been used. Please request a new one.
        <?php endif; ?>
    </div>
    <a href="/traderalign/forgot-password" class="btn btn-primary btn-lg" style="width:100%;text-align:center;text-decoration:none">Request New Reset Link</a>
    
    <?php else: ?>
    <h1>Set new password</h1>
    <p class="sub">Choose a strong password for <?= htmlspecialchars($userEmail ?? 'your account') ?>.</p>
    
    <?php
    // This branch only renders once the token itself has already been
    // confirmed valid (or, on a bare page load with no token at all,
    // when there's nothing to show yet) — any $error reaching here is
    // about the SUBMITTED PASSWORD (too weak, mismatched, expired CSRF
    // token), never about the reset token, so it must always be shown
    // regardless of $validToken. The old `!$validToken` condition made
    // every password-rejection error silently disappear — the user just
    // saw the blank form again with no explanation.
    ?>
    <?php if ($error): ?>
    <div class="error-msg"><?= htmlspecialchars($displayError) ?></div>
    <?php endif; ?>
    
    <form id="resetPasswordForm" method="POST" novalidate>
        <input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
        
        <div class="form-group">
            <label for="password">New Password</label>
            <input 
                type="password" 
                id="password" 
                name="password" 
                required
                minlength="<?= PasswordPolicy::MIN_LENGTH ?>"
                placeholder="Enter new password"
                autocomplete="new-password"
                aria-describedby="passwordStrength"
                oninput="checkPasswordStrength()"
            >
            <div class="password-strength" id="passwordStrength">
                <div class="strength-bar">
                    <div class="strength-fill" id="strengthFill"></div>
                </div>
                <div class="strength-text" id="strengthText"></div>
            </div>
            <div class="password-requirements" id="passwordRequirements">
                <?php foreach (PasswordPolicy::requirementsList() as $reqKey => $reqLabel): ?>
                <div class="req" id="req<?= ucfirst($reqKey) ?>"><span class="icon">○</span> <?= htmlspecialchars($reqLabel) ?></div>
                <?php endforeach; ?>
            </div>
        </div>
        
        <div class="form-group">
            <label for="confirm_password">Confirm Password</label>
            <input 
                type="password" 
                id="confirm_password" 
                name="confirm_password" 
                required 
                placeholder="Re-enter new password"
                autocomplete="new-password"
                oninput="checkPasswordMatch()"
            >
            <div class="field-error" id="confirmError">Passwords do not match.</div>
        </div>
        
        <button type="submit" class="btn btn-primary btn-lg" id="submitBtn" style="width:100%">
            Reset Password
        </button>
    </form>
    <?php endif; ?>
    <p class="footer-text"><a href="/traderalign/sign-in">← Back to sign in</a></p>
</div>

<script>
// Password strength checker
function checkPasswordStrength() {
    const password = document.getElementById('password').value;
    const strengthFill = document.getElementById('strengthFill');
    const strengthText = document.getElementById('strengthText');
    const strengthBar = document.getElementById('passwordStrength');
    
    const MIN_LENGTH = <?= PasswordPolicy::MIN_LENGTH ?>;
    let score = 0;

    // Length check
    if (password.length >= MIN_LENGTH) score++;
    if (password.length >= MIN_LENGTH + 4) score++;

    // Character variety
    if (/[A-Z]/.test(password)) score++;
    if (/[a-z]/.test(password)) score++;
    if (/[0-9]/.test(password)) score++;
    if (/[^A-Za-z0-9]/.test(password)) score++;

    // Update strength bar
    strengthBar.className = 'password-strength';
    if (score <= 2) {
        strengthBar.classList.add('strength-weak');
        strengthText.textContent = 'Weak password';
    } else if (score <= 3) {
        strengthBar.classList.add('strength-fair');
        strengthText.textContent = 'Fair password';
    } else if (score <= 4) {
        strengthBar.classList.add('strength-good');
        strengthText.textContent = 'Good password';
    } else {
        strengthBar.classList.add('strength-strong');
        strengthText.textContent = 'Strong password';
    }

    // Update requirements checklist
    updateRequirement('reqLength', password.length >= MIN_LENGTH);
    updateRequirement('reqUpper', /[A-Z]/.test(password));
    updateRequirement('reqLower', /[a-z]/.test(password));
    updateRequirement('reqNumber', /[0-9]/.test(password));
    updateRequirement('reqSymbol', /[^A-Za-z0-9]/.test(password));
}

function updateRequirement(id, met) {
    const req = document.getElementById(id);
    if (met) {
        req.classList.add('met');
        req.querySelector('.icon').textContent = '✓';
    } else {
        req.classList.remove('met');
        req.querySelector('.icon').textContent = '○';
    }
}

// Password match checker
function checkPasswordMatch() {
    const password = document.getElementById('password').value;
    const confirm = document.getElementById('confirm_password').value;
    const confirmInput = document.getElementById('confirm_password');
    const confirmError = document.getElementById('confirmError');
    
    if (confirm && password !== confirm) {
        confirmInput.classList.add('error');
        confirmError.classList.add('show');
    } else {
        confirmInput.classList.remove('error');
        confirmError.classList.remove('show');
    }
}

// Form submission validation
document.getElementById('resetPasswordForm').addEventListener('submit', function(e) {
    const password = document.getElementById('password').value;
    const confirm = document.getElementById('confirm_password').value;
    let hasError = false;
    
    // Validate password strength
    if (password.length < <?= PasswordPolicy::MIN_LENGTH ?> ||
        !/[A-Z]/.test(password) ||
        !/[a-z]/.test(password) ||
        !/[0-9]/.test(password) ||
        !/[^A-Za-z0-9]/.test(password)) {
        document.getElementById('password').classList.add('error');
        hasError = true;
    }
    
    // Validate password match
    if (password !== confirm) {
        document.getElementById('confirm_password').classList.add('error');
        document.getElementById('confirmError').classList.add('show');
        hasError = true;
    }
    
    if (hasError) {
        e.preventDefault();
        return false;
    }
    
    // Disable button to prevent double submission
    document.getElementById('submitBtn').disabled = true;
    document.getElementById('submitBtn').textContent = 'Resetting...';
});

// Prevent double form submission
let formSubmitted = false;
document.getElementById('resetPasswordForm').addEventListener('submit', function(e) {
    if (formSubmitted) {
        e.preventDefault();
        return false;
    }
    formSubmitted = true;
});
</script>
</body>
</html>