<?php
/**
 * VMPFence Login 2FA Handler
 * Integrates Two-Factor Authentication into WordPress login flow
 *
 * @package VMPFence
 * @since 2.1.0
 * @since 2.2.1 Updated text-domain to vmpfence-security
 * @since 2.2.3 Removed license check to allow 2FA for all users
 */

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

/**
 * VMPFence Login 2FA Handler Class
 */
class VMPFence_Login_2FA_Handler {

    /**
     * Session key for storing temp login data
     */
    const SESSION_KEY = 'vmpfence_2fa_user_id';
    const INTERIM_SESSION_KEY = 'vmpfence_2fa_interim';

    /**
     * Initialize the 2FA handler
     */
    public static function init() {
        // Hook into authentication process
        add_filter('authenticate', array(__CLASS__, 'check_2fa_status'), 50, 3);
        
        // Add 2FA verification form to login page
        add_action('login_form', array(__CLASS__, 'add_2fa_field'));
        
        // Process 2FA verification VERY EARLY (before any output)
        add_action('login_head', array(__CLASS__, 'process_2fa_verification'), 1);
        
        // Clear 2FA session when login page is loaded without 2FA submission
        add_action('login_init', array(__CLASS__, 'maybe_clear_2fa_session'), 10);
        
        // Enqueue login page styles
        add_action('login_enqueue_scripts', array(__CLASS__, 'enqueue_login_styles'));
        
        // Add AJAX handler for 2FA verification
        add_action('wp_ajax_nopriv_vmpfence_verify_login_2fa', array(__CLASS__, 'ajax_verify_2fa'));
        
        // Clear 2FA session on logout
        add_action('wp_logout', array(__CLASS__, 'clear_2fa_session'));
        
        // Show error messages on login page
        add_action('login_errors', array(__CLASS__, 'show_2fa_errors'));
    }

    /**
     * Check if user has 2FA enabled during authentication
     *
     * @param WP_User|WP_Error|null $user User object or error
     * @param string $username Username
     * @param string $password Password
     * @return WP_User|WP_Error
     */
    public static function check_2fa_status($user, $username, $password) {
        // Skip if already an error or no user
        if (is_wp_error($user) || !$user || !($user instanceof WP_User)) {
            return $user;
        }

        // PHASE 7: Check IP allowlist FIRST - bypass all 2FA verification
        $user_ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '';
        if (!empty($user_ip) && class_exists('VMPFence_IP_Allowlist') && VMPFence_IP_Allowlist::is_ip_allowlisted($user_ip)) {
            // Clear any existing 2FA session data
            self::clear_2fa_session();
            return $user; // Skip all 2FA verification for allowlisted IPs
        }

        // BUG FIX: Skip XML-RPC requests - they are handled by VMPFence_XMLRPC_Security class
        if (defined('XMLRPC_REQUEST') && XMLRPC_REQUEST) {
            return $user;
        }

        // Skip for interim logins (like iframe embeds)
        if (isset($_POST['interim-login'])) {
            return $user;
        }

        // Get API key
        $api_key = VMPFence_Config::get('central_api_key');

        // 2FA is now available for all users (free and premium)
        // Skip only if no API key configured
        if (empty($api_key)) {
            return $user;
        }

        // Check if user has 2FA enabled
        $api_client = new VMPFence_API_Client();
        $status_response = $api_client->get_2fa_status($api_key, $user->ID);

        if (is_wp_error($status_response)) {
            // If API fails, allow login but log the error
            VMPFence_Secure_Logger::log('error', '2FA: Failed to check 2FA status for user', ['user_id' => $user->ID, 'error' => $status_response->get_error_message()]);
            return $user;
        }

        // If 2FA is not enabled for this user, allow normal login
        if (!isset($status_response['enabled']) || !$status_response['enabled']) {
            return $user;
        }

        // Check for remembered device (before prompting for 2FA)
        $remember_enabled = VMPFence_Config::get('vmpfence_2fa_remember_device', false);
        if ($remember_enabled && isset($_COOKIE['vmpfence_remember_device'])) {
            $cookie_value = sanitize_text_field($_COOKIE['vmpfence_remember_device']);
            
            // Cookie format: user_id|token
            $parts = explode('|', $cookie_value, 2);
            if (count($parts) === 2) {
                $cookie_user_id = intval($parts[0]);
                $token = $parts[1];
                
                // Verify this cookie is for this user
                if ($cookie_user_id === $user->ID) {
                    // Validate token against stored hash
                    $stored_token_hash = get_user_meta($user->ID, 'vmpfence_device_token_hash', true);
                    $token_created = get_user_meta($user->ID, 'vmpfence_device_token_created', true);
                    
                    // Check if token exists and is not expired (30 days)
                    if ($stored_token_hash && $token_created) {
                        $current_timestamp = current_time('timestamp');
                        $token_age = $current_timestamp - intval($token_created);
                        $max_age = 30 * DAY_IN_SECONDS;
                        
                        if ($token_age <= $max_age && hash_equals($stored_token_hash, hash('sha256', $token))) {
                            // Valid and non-expired remembered device, allow login without 2FA
                            return $user;
                        } elseif ($token_age > $max_age) {
                            // Token expired, clean it up
                            VMPFence_2FA_Enforcement::forget_device($user->ID);
                        }
                    }
                }
            }
        }

        // 2FA is enabled - store user ID in session and block login
        self::set_2fa_session($user->ID);
        
        // Set transient to bypass brute force protection during 2FA verification
        // Store username to ensure bypass only works for THIS specific user
        $user_ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '';
        if (!empty($user_ip)) {
            $bypass_data = array(
                'user_id' => $user->ID,
                'username' => $user->user_login,
                'timestamp' => time(),
                'credentials_verified' => true // Password was correct, now on 2FA
            );
            set_transient('vmpfence_2fa_bypass_' . md5($user_ip), $bypass_data, 600); // 10 minutes
        }
        
        // Return error to prevent automatic login
        return new WP_Error('2fa_required', __('Please enter your two-factor authentication code.', 'vmpfence-security'));
    }

    /**
     * Add 2FA input field to login form
     */
    public static function add_2fa_field() {
        $user_id = self::get_2fa_session();
        
        // If no 2FA session, don't show the 2FA field
        if (!$user_id) {
            return;
        }
        
        ?>
        <style type="text/css">
            /* Hide username and password fields during 2FA */
            body.login #loginform #user_login,
            body.login #loginform #user_pass,
            body.login #loginform label[for="user_login"],
            body.login #loginform label[for="user_pass"],
            body.login #loginform .user-pass-wrap {
                display: none !important;
            }
            
            /* Style the 2FA prompt */
            .vmpfence-2fa-container {
                margin-bottom: 20px;
            }
            
            .vmpfence-2fa-container h3 {
                margin-top: 0;
                font-size: 18px;
                font-weight: 600;
                text-align: center;
            }
            
            .vmpfence-2fa-message {
                text-align: center;
                color: #646970;
                margin-bottom: 15px;
            }
            
            .vmpfence-backup-link {
                display: inline-block;
                margin-top: 10px;
                font-size: 13px;
            }
        </style>

        <div id="vmpfence-2fa-prompt" class="vmpfence-2fa-container">
            <h3><?php _e('Two-Factor Authentication', 'vmpfence-security'); ?></h3>
            
            <?php
            // Display error message if 2FA verification failed
            if (!session_id()) {
                session_start();
            }
            if (isset($_SESSION['vmpfence_2fa_error'])) {
                echo '<div class="message" style="border-left: 4px solid #dc3232; padding: 12px; margin: 5px 0 15px; background: #fff;">';
                echo '<strong>' . esc_html($_SESSION['vmpfence_2fa_error']) . '</strong>';
                echo '</div>';
                // Clear the error after displaying
                unset($_SESSION['vmpfence_2fa_error']);
            }
            
            // CRITICAL: Check if backup form should be shown (MUST be before using the variable)
            $show_backup_form = false;
            if (isset($_SESSION['vmpfence_2fa_active_form']) && $_SESSION['vmpfence_2fa_active_form'] === 'backup') {
                $show_backup_form = true;
                unset($_SESSION['vmpfence_2fa_active_form']); // Clear after checking
            }
            ?>
            
            <p class="vmpfence-2fa-message"><?php _e('Enter the 6-digit code from your authenticator app:', 'vmpfence-security'); ?></p>
            
            <p style="display: <?php echo $show_backup_form ? 'none' : 'block'; ?>;">
                <label for="vmpfence_2fa_code"><?php _e('Authentication Code', 'vmpfence-security'); ?></label>
                <input type="text" 
                       name="vmpfence_2fa_code" 
                       id="vmpfence_2fa_code" 
                       class="input vmpfence-2fa-code-input" 
                       value="" 
                       size="20" 
                       maxlength="6" 
                       autocomplete="off" 
                       pattern="[0-9]{6}"
                       inputmode="numeric"
                       <?php echo $show_backup_form ? '' : 'required'; ?> />
            </p>

            <a href="#" id="vmpfence-use-backup-code" class="vmpfence-backup-link" style="display: <?php echo $show_backup_form ? 'none' : 'inline-block'; ?>;">
                <?php _e('Use a backup code instead', 'vmpfence-security'); ?>
            </a>

            <div id="vmpfence-2fa-backup-form" style="display: <?php echo $show_backup_form ? 'block' : 'none'; ?>;">
                <p>
                    <label for="vmpfence_backup_code"><?php _e('Backup Code', 'vmpfence-security'); ?></label>
                    <input type="text" 
                           name="vmpfence_backup_code" 
                           id="vmpfence_backup_code" 
                           class="input" 
                           value="" 
                           size="20" 
                           maxlength="8" 
                           autocomplete="off"
                           <?php echo $show_backup_form ? 'required' : ''; ?> />
                </p>
                <a href="#" id="vmpfence-use-totp-code" class="vmpfence-backup-link">
                    <?php _e('Use authenticator code instead', 'vmpfence-security'); ?>
                </a>
            </div>

            <?php
            // Phase 3: Show "Remember Device" checkbox if enabled
            $remember_device_enabled = VMPFence_Config::get('vmpfence_2fa_remember_device', false);
            if ($remember_device_enabled) {
                ?>
                <p class="vmpfence-2fa-remember">
                    <label>
                        <input type="checkbox" name="vmpfence_remember_device" id="vmpfence_remember_device" value="1" />
                        <?php _e('Remember this device for 30 days', 'vmpfence-security'); ?>
                    </label>
                </p>
                <?php
            }
            ?>

            <input type="hidden" name="vmpfence_2fa_user_id" value="<?php echo esc_attr($user_id); ?>" />
            <input type="hidden" name="vmpfence_2fa_nonce" value="<?php echo esc_attr(wp_create_nonce('vmpfence_2fa_login_' . $user_id)); ?>" />
            
            <!-- Hidden fields to satisfy WordPress login form validation -->
            <input type="hidden" name="log" value="vmpfence_2fa_login" />
            <input type="hidden" name="pwd" value="vmpfence_2fa_verification" />
        </div>

        <script type="text/javascript">
        (function() {
            // Use vanilla JavaScript if jQuery not available
            function ready(fn) {
                if (document.readyState !== 'loading') {
                    fn();
                } else {
                    document.addEventListener('DOMContentLoaded', fn);
                }
            }
            
            ready(function() {
                // Remove username/password fields completely and remove their required attribute
                var userLogin = document.getElementById('user_login');
                var userPass = document.getElementById('user_pass');
                
                if (userLogin) {
                    userLogin.removeAttribute('required');
                    userLogin.removeAttribute('aria-required');
                    var loginParent = userLogin.closest('p');
                    if (loginParent) loginParent.remove();
                }
                
                if (userPass) {
                    userPass.removeAttribute('required');
                    userPass.removeAttribute('aria-required');
                    var passParent = userPass.closest('p');
                    if (passParent) passParent.remove();
                }
                
                // Remove user-pass-wrap
                var passWrap = document.querySelector('.user-pass-wrap');
                if (passWrap) passWrap.remove();
                
                // Remove labels
                var loginLabel = document.querySelector('label[for="user_login"]');
                if (loginLabel && loginLabel.parentElement) loginLabel.parentElement.remove();
                
                var passLabel = document.querySelector('label[for="user_pass"]');
                if (passLabel && passLabel.parentElement) passLabel.parentElement.remove();
                
                // Toggle between TOTP and backup code
                var useBackupLink = document.getElementById('vmpfence-use-backup-code');
                var useTotpLink = document.getElementById('vmpfence-use-totp-code');
                var backupForm = document.getElementById('vmpfence-2fa-backup-form');
                var totpCodeField = document.getElementById('vmpfence_2fa_code');
                var backupCodeField = document.getElementById('vmpfence_backup_code');
                
                if (useBackupLink) {
                    useBackupLink.addEventListener('click', function(e) {
                        e.preventDefault();
                        if (totpCodeField && totpCodeField.parentElement) {
                            totpCodeField.parentElement.style.display = 'none';
                            totpCodeField.removeAttribute('required'); // Remove validation from hidden field
                        }
                        if (useBackupLink) useBackupLink.style.display = 'none';
                        if (backupForm) backupForm.style.display = 'block';
                        if (backupCodeField) {
                            backupCodeField.setAttribute('required', 'required'); // Add validation to backup field
                            backupCodeField.focus();
                        }
                    });
                }
                
                if (useTotpLink) {
                    useTotpLink.addEventListener('click', function(e) {
                        e.preventDefault();
                        if (backupForm) backupForm.style.display = 'none';
                        if (backupCodeField) {
                            backupCodeField.removeAttribute('required'); // Remove validation from hidden backup field
                        }
                        if (totpCodeField && totpCodeField.parentElement) {
                            totpCodeField.parentElement.style.display = 'block';
                            totpCodeField.setAttribute('required', 'required'); // Restore validation to TOTP field
                        }
                        if (useBackupLink) useBackupLink.style.display = 'block';
                        if (totpCodeField) totpCodeField.focus();
                    });
                }
                
                // Auto-submit when 6 digits entered
                if (totpCodeField) {
                    totpCodeField.addEventListener('input', function() {
                        this.value = this.value.replace(/\D/g, '').substr(0, 6);
                        if (this.value.length === 6) {
                            // Auto-submit form after a short delay
                            setTimeout(function() {
                                var form = document.getElementById('loginform');
                                if (form) form.submit();
                            }, 300);
                        }
                    });
                    
                    // Focus on 2FA code input
                    totpCodeField.focus();
                }
            });
        })();
        </script>
        <?php
    }

    /**
     * Process 2FA verification during login
     */
    public static function process_2fa_verification() {
        // Check if 2FA code was submitted
        if (!isset($_POST['vmpfence_2fa_user_id'])) {
            return;
        }
        
        $user_id = intval($_POST['vmpfence_2fa_user_id']);
        $totp_code = isset($_POST['vmpfence_2fa_code']) ? sanitize_text_field($_POST['vmpfence_2fa_code']) : '';
        $backup_code = isset($_POST['vmpfence_backup_code']) ? sanitize_text_field($_POST['vmpfence_backup_code']) : '';
        $nonce = isset($_POST['vmpfence_2fa_nonce']) ? $_POST['vmpfence_2fa_nonce'] : '';

        // Verify session matches
        $session_user_id = self::get_2fa_session();
        if ($user_id !== $session_user_id) {
            self::log_failed_attempt($user_id, 'Session mismatch');
            wp_die(__('Invalid 2FA session. Please try logging in again.', 'vmpfence-security'));
        }
        
        // SECURITY: Verify nonce to prevent CSRF attacks
        if (!wp_verify_nonce($nonce, 'vmpfence_2fa_login_' . $user_id)) {
            self::log_failed_attempt($user_id, 'Invalid nonce (CSRF attempt)');
            wp_die(__('Security check failed. Please try logging in again.', 'vmpfence-security'));
        }

        // Get API key
        $api_key = VMPFence_Config::get('central_api_key');
        if (empty($api_key)) {
            wp_die(__('API key not configured.', 'vmpfence-security'));
        }
        
        // SECURITY: Check rate limiting FIRST (before any validation/API calls)
        $rate_limit_check = self::check_rate_limit($user_id);
        if ($rate_limit_check['locked']) {
            self::log_failed_attempt($user_id, 'Rate limit exceeded - already locked');
            if (!session_id()) {
                session_start();
            }
            $_SESSION['vmpfence_2fa_error'] = sprintf(
                __('Too many failed attempts. Please try again in %d minutes.', 'vmpfence-security'),
                ceil($rate_limit_check['time_remaining'] / 60)
            );
            wp_redirect(wp_login_url());
            exit;
        }

        $api_client = new VMPFence_API_Client();
        $valid = false;
        $error_message = '';

        // Try TOTP code first
        if (!empty($totp_code)) {
            // SECURITY: Validate code format (6 digits)
            if (!preg_match('/^[0-9]{6}$/', $totp_code)) {
                $error_message = __('Invalid code format. Please enter 6 digits.', 'vmpfence-security');
            } else {
                $verify_response = $api_client->verify_2fa_code($api_key, $user_id, $totp_code);
                
                if (is_wp_error($verify_response)) {
                    $error_message = __('Invalid authentication code. Please try again.', 'vmpfence-security');
                } elseif (isset($verify_response['success']) && $verify_response['success'] === true) {
                    $valid = true;
                } else {
                    $error_message = __('Invalid authentication code. Please try again.', 'vmpfence-security');
                }
            }
        }
        // Try backup code
        elseif (!empty($backup_code)) {
            // SECURITY: Validate backup code format (exactly 8 characters: XXXX-XXXX)
            $backup_code_clean = str_replace('-', '', $backup_code);
            if (strlen($backup_code_clean) !== 8 || !ctype_digit($backup_code_clean)) {
                $error_message = __('Invalid backup code format. Must be 8 digits (XXXX-XXXX).', 'vmpfence-security');
            } else {
                $verify_response = $api_client->verify_backup_code($api_key, $user_id, $backup_code);
                
                if (is_wp_error($verify_response)) {
                    $error_message = __('Invalid backup code. Please try again.', 'vmpfence-security');
                } elseif (isset($verify_response['success']) && $verify_response['success'] === true) {
                    $valid = true;
                } else {
                    $error_message = __('Invalid backup code. Please try again.', 'vmpfence-security');
                }
            }
        } else {
            $error_message = __('Please enter an authentication code or backup code.', 'vmpfence-security');
        }

        // If verification failed, show error and stay on 2FA page
        if (!$valid) {
            // SECURITY: Get current attempts BEFORE recording this failure
            $rate_limit_check = self::check_rate_limit($user_id);
            $attempts_remaining = max(0, $rate_limit_check['attempts_remaining'] - 1); // -1 for current attempt
            
            // Record failed attempt for rate limiting
            self::record_failed_attempt($user_id);
            self::log_failed_attempt($user_id, $error_message);
            
            // Add attempt count to error message
            if ($attempts_remaining > 0) {
                $error_message .= ' ' . sprintf(
                    _n('%d attempt remaining.', '%d attempts remaining.', $attempts_remaining, 'vmpfence-security'),
                    $attempts_remaining
                );
            } else {
                // This was the last attempt - now locked
                $error_message = __('Too many failed attempts. Your account has been locked for 15 minutes.', 'vmpfence-security');
            }
            
            // Store error in session to display on 2FA prompt
            if (!session_id()) {
                @session_start();
            }
            $_SESSION['vmpfence_2fa_error'] = $error_message;
            // Remember which form was being used (TOTP or backup code)
            $_SESSION['vmpfence_2fa_active_form'] = !empty($backup_code) ? 'backup' : 'totp';
            // Redirect back to login page, but keep 2FA session active
            wp_redirect(wp_login_url());
            exit;
        }
        
        // SECURITY: Clear rate limit on successful verification
        self::clear_rate_limit($user_id);

        // Verification successful - complete the login
        self::clear_2fa_session();
        
        // SECURITY: Regenerate session ID to prevent session fixation attacks
        if (session_id()) {
            @session_regenerate_id(true);
        }
        
        $user = get_user_by('id', $user_id);
        if ($user) {
            // SECURITY: Log successful 2FA verification
            VMPFence_Secure_Logger::log('info', '2FA: Successful verification', [
                'user_id' => $user_id,
                'username' => $user->user_login,
                'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
            ]);
            
            // Phase 3: Check if user wants to remember device
            if (isset($_POST['vmpfence_remember_device']) && $_POST['vmpfence_remember_device'] === '1') {
                if (class_exists('VMPFence_2FA_Enforcement')) {
                    VMPFence_2FA_Enforcement::remember_device($user_id);
                }
            }
            
            // Check if "remember me" was checked
            $remember = isset($_POST['rememberme']) && $_POST['rememberme'] === 'forever';
            
            // Set current user and auth cookie (wp_set_auth_cookie clears old cookies automatically)
            wp_set_current_user($user_id, $user->user_login);
            wp_set_auth_cookie($user_id, $remember);
            
            // Fire the wp_login action
            do_action('wp_login', $user->user_login, $user);
            
            // Check if enforcement already set a redirect (e.g., forced 2FA setup)
            if (get_transient('vmpfence_redirected_' . $user_id)) {
                // Enforcement is handling redirect, don't override
                exit;
            }
            
            // Get redirect URL
            $redirect_to = admin_url();
            if (isset($_POST['redirect_to']) && !empty($_POST['redirect_to'])) {
                $redirect_to = esc_url_raw($_POST['redirect_to']);
            } elseif (isset($_REQUEST['redirect_to']) && !empty($_REQUEST['redirect_to'])) {
                $redirect_to = esc_url_raw($_REQUEST['redirect_to']);
            }
            
            // Apply WordPress redirect filter
            $redirect_to = apply_filters('login_redirect', $redirect_to, isset($_REQUEST['redirect_to']) ? esc_url_raw($_REQUEST['redirect_to']) : '', $user);
            
            // Validate and safely redirect (prevents open redirect attacks)
            $redirect_to = wp_validate_redirect($redirect_to, admin_url());
            wp_safe_redirect($redirect_to);
            exit;
        } else {
            VMPFence_Secure_Logger::log('error', '2FA: ERROR - User not found for ID', ['user_id' => $user_id]);
            wp_die(__('User not found.', 'vmpfence-security'));
        }
    }

    /**
     * Enqueue styles for login page
     */
    public static function enqueue_login_styles() {
        if (self::get_2fa_session()) {
            wp_enqueue_style('vmpfence-login-2fa', VMPFENCE_PLUGIN_URL . 'admin/assets/css/login-security.css', array(), VMPFENCE_VERSION);
        }
    }

    /**
     * Store user ID in session for 2FA verification
     *
     * @param int $user_id User ID
     */
    private static function set_2fa_session($user_id) {
        if (!session_id()) {
            session_start();
        }
        $_SESSION[self::SESSION_KEY] = $user_id;
        $_SESSION[self::INTERIM_SESSION_KEY] = time();
    }

    /**
     * Get user ID from 2FA session
     *
     * @return int|null User ID or null
     */
    private static function get_2fa_session() {
        if (!session_id()) {
            session_start();
        }
        
        // Check if session exists and hasn't expired (5 minutes for security)
        if (isset($_SESSION[self::SESSION_KEY]) && isset($_SESSION[self::INTERIM_SESSION_KEY])) {
            $session_time = $_SESSION[self::INTERIM_SESSION_KEY];
            if ((time() - $session_time) < 300) { // 5 minutes
                return intval($_SESSION[self::SESSION_KEY]);
            }
        }
        
        return null;
    }

    /**
     * Check if 2FA is active for a specific username from this IP
     * Used by brute force handler to determine if bypass should be applied
     *
     * @param string $username Username to check
     * @param string $ip IP address
     * @return bool True if this username is in active 2FA verification
     */
    public static function is_2fa_active_for_username($username, $ip) {
        if (empty($ip) || empty($username)) {
            return false;
        }
        
        $bypass_key = 'vmpfence_2fa_bypass_' . md5($ip);
        $bypass_data = get_transient($bypass_key);
        
        if ($bypass_data === false) {
            return false;
        }
        
        // Verify this is the same username that triggered 2FA
        if (isset($bypass_data['username']) && $bypass_data['username'] === $username) {
            return true;
        }
        
        // Different username - potential attack during 2FA window
        return false;
    }

    /**
     * Clear 2FA session
     */
    public static function clear_2fa_session() {
        if (!session_id()) {
            session_start();
        }
        unset($_SESSION[self::SESSION_KEY]);
        unset($_SESSION[self::INTERIM_SESSION_KEY]);
        
        // Clear brute force bypass transient
        $user_ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '';
        if (!empty($user_ip)) {
            $bypass_key = 'vmpfence_2fa_bypass_' . md5($user_ip);
            $bypass_data = get_transient($bypass_key);
            
            delete_transient($bypass_key);
        }
    }
    
    /**
     * Clear 2FA session if user navigates away or refreshes without submitting 2FA
     */
    public static function maybe_clear_2fa_session() {
        // Don't clear if we're processing 2FA verification
        if (isset($_POST['vmpfence_2fa_user_id']) || isset($_POST['vmpfence_2fa_code']) || isset($_POST['vmpfence_backup_code'])) {
            VMPFence_Secure_Logger::log('debug', '2FA: Skipping session clear - 2FA submission detected');
            return;
        }
        
        // Don't clear if we just set the session (during authentication)
        // Ignore the dummy values used for form validation
        $is_real_login = isset($_POST['log']) && isset($_POST['pwd']) 
            && $_POST['log'] !== 'vmpfence_2fa_login' 
            && $_POST['pwd'] !== 'vmpfence_2fa_verification';
            
        if ($is_real_login) {
            VMPFence_Secure_Logger::log('debug', '2FA: Skipping session clear - login submission detected');
            return;
        }
        
        // Don't clear if there's a 2FA error waiting to be displayed
        if (!session_id()) {
            session_start();
        }
        if (isset($_SESSION['vmpfence_2fa_error'])) {
            return;
        }
        
        // If we have a 2FA session but user is on login page without submitting credentials or 2FA
        // This means they refreshed or navigated away - clear the session
        if (self::get_2fa_session() && !$is_real_login) {
            self::clear_2fa_session();
        }
    }
    
    /**
     * Show 2FA error messages on login page
     * 
     * @param string $errors Existing login errors
     * @return string Modified errors
     */
    public static function show_2fa_errors($errors) {
        if (isset($_GET['vmpfence_2fa_error'])) {
            $error_message = urldecode(sanitize_text_field(wp_unslash($_GET['vmpfence_2fa_error'])));
            $errors .= '<strong>' . esc_html__('ERROR', 'vmpfence-security') . '</strong>: ' . esc_html($error_message) . '<br />';
        }
        return $errors;
    }
    
    /**
     * Check rate limit for 2FA attempts
     * Uses WordPress transients (no database changes needed)
     * Checks both user-based and IP-based limits
     * 
     * @param int $user_id WordPress user ID
     * @return array ['locked' => bool, 'attempts_remaining' => int, 'time_remaining' => int]
     */
    private static function check_rate_limit($user_id) {
        // Check user-based rate limit
        $user_transient_key = 'vmpfence_2fa_attempts_' . $user_id;
        $user_attempts_data = get_transient($user_transient_key);
        
        $user_locked = false;
        $user_attempts_remaining = 5;
        $user_time_remaining = 0;
        
        if ($user_attempts_data) {
            $failed_count = intval($user_attempts_data['count']);
            $first_attempt_time = intval($user_attempts_data['first_attempt']);
            $time_elapsed = time() - $first_attempt_time;
            $user_time_remaining = (15 * MINUTE_IN_SECONDS) - $time_elapsed;
            
            $user_locked = $failed_count >= 5;
            $user_attempts_remaining = max(0, 5 - $failed_count);
        }
        
        // Check IP-based rate limit (prevent attacks across multiple accounts)
        $ip_address = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
        $ip_transient_key = 'vmpfence_2fa_ip_' . md5($ip_address);
        $ip_attempts_data = get_transient($ip_transient_key);
        
        $ip_locked = false;
        $ip_attempts_remaining = 10; // Allow more attempts from same IP (multiple users)
        $ip_time_remaining = 0;
        
        if ($ip_attempts_data) {
            $failed_count = intval($ip_attempts_data['count']);
            $first_attempt_time = intval($ip_attempts_data['first_attempt']);
            $time_elapsed = time() - $first_attempt_time;
            $ip_time_remaining = (15 * MINUTE_IN_SECONDS) - $time_elapsed;
            
            $ip_locked = $failed_count >= 10; // Lock IP after 10 failures across all accounts
            $ip_attempts_remaining = max(0, 10 - $failed_count);
        }
        
        // Locked if either user OR IP is locked
        $is_locked = $user_locked || $ip_locked;
        $attempts_remaining = min($user_attempts_remaining, $ip_attempts_remaining);
        $time_remaining = max($user_time_remaining, $ip_time_remaining);
        
        return [
            'locked' => $is_locked,
            'attempts_remaining' => $attempts_remaining,
            'time_remaining' => max(0, $time_remaining),
        ];
    }
    
    /**
     * Record a failed 2FA attempt
     * Tracks both user-based and IP-based attempts
     * 
     * @param int $user_id WordPress user ID
     */
    private static function record_failed_attempt($user_id) {
        // Record user-based attempt
        $user_transient_key = 'vmpfence_2fa_attempts_' . $user_id;
        $user_attempts_data = get_transient($user_transient_key);
        
        if (!$user_attempts_data) {
            $user_attempts_data = [
                'count' => 1,
                'first_attempt' => time(),
            ];
        } else {
            $user_attempts_data['count'] = intval($user_attempts_data['count']) + 1;
        }
        
        set_transient($user_transient_key, $user_attempts_data, 15 * MINUTE_IN_SECONDS);
        
        // Record IP-based attempt
        $ip_address = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
        $ip_transient_key = 'vmpfence_2fa_ip_' . md5($ip_address);
        $ip_attempts_data = get_transient($ip_transient_key);
        
        if (!$ip_attempts_data) {
            $ip_attempts_data = [
                'count' => 1,
                'first_attempt' => time(),
                'ip' => $ip_address,
            ];
        } else {
            $ip_attempts_data['count'] = intval($ip_attempts_data['count']) + 1;
        }
        
        set_transient($ip_transient_key, $ip_attempts_data, 15 * MINUTE_IN_SECONDS);
        
        // SECURITY: Set lockout flags if limits exceeded
        if ($user_attempts_data['count'] >= 5) {
            $lockout_key = 'vmpfence_2fa_locked_' . $user_id;
            set_transient($lockout_key, time(), 15 * MINUTE_IN_SECONDS);
        }
        
        if ($ip_attempts_data['count'] >= 10) {
            $ip_lockout_key = 'vmpfence_2fa_ip_locked_' . md5($ip_address);
            set_transient($ip_lockout_key, time(), 15 * MINUTE_IN_SECONDS);
        }
    }
    
    /**
     * Clear rate limit after successful verification
     * Clears both user-based and IP-based limits
     * 
     * @param int $user_id WordPress user ID
     */
    private static function clear_rate_limit($user_id) {
        // Clear user-based limits
        $user_transient_key = 'vmpfence_2fa_attempts_' . $user_id;
        $user_lockout_key = 'vmpfence_2fa_locked_' . $user_id;
        
        delete_transient($user_transient_key);
        delete_transient($user_lockout_key);
        
        // Clear IP-based limits for this IP
        $ip_address = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
        $ip_transient_key = 'vmpfence_2fa_ip_' . md5($ip_address);
        $ip_lockout_key = 'vmpfence_2fa_ip_locked_' . md5($ip_address);
        
        delete_transient($ip_transient_key);
        delete_transient($ip_lockout_key);
    }
    
    /**
     * Log failed 2FA attempt for security monitoring
     * 
     * @param int $user_id WordPress user ID
     * @param string $reason Failure reason
     */
    private static function log_failed_attempt($user_id, $reason) {
        $user = get_user_by('id', $user_id);
        $username = $user ? $user->user_login : 'unknown';
        
        VMPFence_Secure_Logger::log('warning', '2FA: Failed verification attempt', [
            'user_id' => $user_id,
            'username' => $username,
            'reason' => $reason,
            'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown',
        ]);
    }
}

// Initialize the handler on plugins_loaded (priority 25 - same as enforcement)
add_action('plugins_loaded', array('VMPFence_Login_2FA_Handler', 'init'), 25);
