<?php
/**
 * Brute Force Protection Controller
 *
 * @package VMPFence
 * @since 2.0.0
 * @since 2.2.5 Added enhanced Audit Log
 */

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

/**
 * VMPFence Brute Force Controller
 * 
 * Handles WordPress authentication integration and failed login tracking
 * Hooks into WordPress login system to detect and prevent brute force attacks
 */
class VMPFence_Brute_Force_Controller {

    /**
     * Brute Force Model instance
     *
     * @var VMPFence_Brute_Force_Model
     */
    private $model;

    /**
     * Constructor
     */
    public function __construct() {
        $this->model = new VMPFence_Brute_Force_Model();
        $this->init_hooks();
    }

    /**
     * Initialize WordPress hooks
     */
    private function init_hooks() {
        // CRITICAL: Check for blocked IPs first (before everything else)
        add_action('init', array($this, 'check_blocked_ip'), 1);
        
        // Hook into WordPress authentication system
        add_filter('authenticate', array($this, 'block_authentication_if_ip_blocked'), 1, 1);
        add_filter('authenticate', array($this, 'check_invalid_username_on_login'), 20, 3);
        add_action('wp_login_failed', array($this, 'handle_failed_login'), 10, 1);
        add_action('wp_login', array($this, 'handle_successful_login'), 10, 2);
        add_action('login_form', array($this, 'check_invalid_username'), 10);
        add_action('retrieve_password', array($this, 'handle_forgot_password_attempt'), 10, 1);
    }

    /**
     * Check if IP is blocked and block access completely
     * This runs on 'init' with priority 1 (before everything else)
     */
    public function check_blocked_ip() {
        $ip = $this->get_client_ip();
        
        // Skip if brute force protection is disabled
        if (!VMPFence_Config::get('brute_force_enabled', 1)) {
            return;
        }
        
        global $wpdb;
        
        $blocked_table = $wpdb->prefix . 'vmpfence_blocked_ips';
        
        // Check if IP is currently blocked (using Unix timestamp)
        $current_timestamp = time();
        $blocked_record = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM {$blocked_table} WHERE ip_address = %s AND blocked_until > %d ORDER BY blocked_until DESC LIMIT 1",
            $ip,
            $current_timestamp
        ));
        
        if ($blocked_record) {
            // DIRECT AUDIT LOG: Intentionally bypasses observer system.
            // Reason: Fires during pre-authentication check before observers may be loaded.
            VMPFence_Audit_Log::log_event('blocked_ip_access_attempt', array(
                'ip' => $ip,
                'reason' => $blocked_record->reason,
                'blocked_until' => $blocked_record->blocked_until,
                'severity' => 'high'
            ));
            
            // Return 403 Forbidden response
            status_header(403);
            nocache_headers();
            
            // Display blocked message (blocked_until is Unix timestamp)
            // Use JavaScript to display in user's device timezone
            $blocked_until_timestamp = $blocked_record->blocked_until;
            wp_die(
                '<h1>Access Forbidden</h1>' .
                '<p>Your IP address has been temporarily blocked due to suspicious activity.</p>' .
                '<p><strong>Reason:</strong> ' . esc_html($blocked_record->reason) . '</p>' .
                '<p><strong>Block expires:</strong> <span id="block-expires-time" data-timestamp="' . esc_attr($blocked_until_timestamp) . '">Loading...</span></p>' .
                '<script>
                    (function() {
                        var timestamp = document.getElementById("block-expires-time").getAttribute("data-timestamp");
                        var date = new Date(timestamp * 1000);
                        var options = { year: "numeric", month: "long", day: "numeric", hour: "numeric", minute: "2-digit", hour12: true };
                        var localTime = date.toLocaleString("en-US", options);
                        document.getElementById("block-expires-time").textContent = localTime;
                    })();
                </script>' .
                '<hr><p><small>If you believe this is an error, please contact the site administrator.</small></p>',
                'Access Forbidden - VMPFence Security',
                array('response' => 403)
            );
        }
    }

    /**
     * Block authentication attempts from blocked IPs
     * This prevents login attempts even before they're processed
     *
     * @param null|WP_User|WP_Error $user User object or error
     * @return null|WP_User|WP_Error
     */
    public function block_authentication_if_ip_blocked($user) {
        // Skip if brute force protection is disabled
        if (!VMPFence_Config::get('brute_force_enabled', 1)) {
            return $user;
        }

        $ip = $this->get_client_ip();
        
        global $wpdb;
        
        $blocked_table = $wpdb->prefix . 'vmpfence_blocked_ips';
        
        // Check if IP is currently blocked (using Unix timestamp)
        $current_timestamp = time();
        $is_blocked = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$blocked_table} WHERE ip_address = %s AND blocked_until > %d",
            $ip,
            $current_timestamp
        ));
        
        if ($is_blocked) {
            return new WP_Error(
                'ip_blocked',
                '<strong>ERROR:</strong> Your IP address is temporarily blocked due to suspicious activity.'
            );
        }
        return $user;
    }

    /**
     * Check for invalid username attempts during authentication
     * This hooks into the authenticate filter to catch invalid usernames
     *
     * @param null|WP_User|WP_Error $user User object, or null/WP_Error if login failed
     * @param string $username Username submitted for authentication
     * @param string $password Password submitted for authentication
     * @return null|WP_User|WP_Error
     */
    public function check_invalid_username_on_login($user, $username, $password) {
        // Skip if brute force protection is disabled
        if (!VMPFence_Config::get('brute_force_enabled', 1)) {
            return $user;
        }

        // Skip if we already have a successful authentication
        if (is_a($user, 'WP_User')) {
            return $user;
        }

        // Skip if no username provided
        if (empty($username)) {
            return $user;
        }

        $ip = $this->get_client_ip();


        // FEATURE 1: Check if username is in the blocked usernames list (INDEPENDENT)
        $is_blocked_username = $this->model->is_invalid_username($username);

        if ($is_blocked_username) {
            $this->immediately_block_ip_for_blocked_username($ip, $username);
            
            return new WP_Error(
                'blocked_username_attempt',
                '<strong>ERROR:</strong> Your IP address has been blocked due to attempting to use a blocked username.'
            );
        }

        // FEATURE 2: Check for invalid usernames (if enabled)
        $lock_invalid_usernames = VMPFence_Config::get('lock_invalid_usernames', 0);
        if ($lock_invalid_usernames) {
            // Check if this username exists
            $user_exists = username_exists($username) || email_exists($username);
            
            if (!$user_exists) {
                // Invalid username detected - immediately block the IP
                
                $this->immediately_block_ip_for_invalid_username($ip, $username);
                
                // Return an error to stop the authentication process
                return new WP_Error(
                    'invalid_username_blocked',
                    '<strong>ERROR:</strong> Your IP address has been blocked due to an invalid username attempt.'
                );
            }
        }

        return $user;
    }

    /**
     * Immediately block IP for invalid username attempt
     *
     * @param string $ip IP address
     * @param string $username Invalid username attempted
     */
    private function immediately_block_ip_for_invalid_username($ip, $username) {
        global $wpdb;
        
        $lockout_duration = $this->get_config_value('lockout_duration', 240); // minutes
        
        // Calculate block expiry using Unix timestamps
        $current_timestamp = time();
        $blocked_until_timestamp = $current_timestamp + ($lockout_duration * 60);


        $blocked_until = $wpdb->get_var($wpdb->prepare(
            "SELECT DATE_ADD(NOW(), INTERVAL %d MINUTE)",
            $lockout_duration
        ));
        
        // Get current MySQL time
        $mysql_now = $wpdb->get_var("SELECT NOW()");
        
        // Get latest block_count for this IP
        $blocked_table = $wpdb->prefix . 'vmpfence_blocked_ips';
        $latest_count = $wpdb->get_var($wpdb->prepare(
            "SELECT block_count FROM {$blocked_table} WHERE ip_address = %s ORDER BY id DESC LIMIT 1",
            $ip
        ));
        
        $new_block_count = $latest_count ? ((int)$latest_count + 1) : 1;
        
        // Always INSERT new record
        $result = $wpdb->insert(
            $blocked_table,
            array(
                'ip_address' => $ip,
                'reason' => 'Brute Force - Invalid username attempt: ' . sanitize_text_field($username),
                'blocked_at' => $current_timestamp,
                'blocked_until' => $blocked_until_timestamp,
                'block_count' => $new_block_count,
                'last_attempt' => $mysql_now,
                'is_active' => 1,
                'source' => 'local'
            ),
            array('%s', '%s', '%d', '%d', '%d', '%s', '%d', '%s')
        );
        
                // Log to security attempts table
        $attempts_table = $wpdb->prefix . 'vmpfence_security_attempts';
        $wpdb->insert($attempts_table, array(
            'ip_address' => $ip,
            'username' => sanitize_text_field($username),
            'event_type' => 'invalid_username',
            'attempt_time' => $mysql_now,
            'blocked_until' => $blocked_until,
            'user_agent' => sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'] ?? ''))
        ), array('%s', '%s', '%s', '%s', '%s', '%s'));
        
        // DIRECT AUDIT LOG: Intentionally bypasses observer system.
        // Reason: Security event in authentication filter before observers may be loaded.
        VMPFence_Audit_Log::log_event('brute_force_invalid_username', array(
            'ip' => $ip,
            'username' => $username,
            'lockout_duration' => $lockout_duration . ' minutes',
            'blocked_until' => $blocked_until
        ));
        
        // Log to Live Traffic
        if (class_exists('VMPFence_Live_Traffic_Logger')) {
            $logger = VMPFence_Live_Traffic_Logger::getInstance();
            $logger->setAction('lockedOut', 'IP locked out: Invalid username attempt', array(
                'reason' => 'invalid_username_attempt',
                'username' => $username,
                'lockout_duration_minutes' => $lockout_duration
            ));
            $logger->setStatusCode(503);
        }
    }

    /**
     * Immediately block IP for blocked username attempt
     *
     * @param string $ip IP address
     * @param string $username Blocked username attempted
     */
    private function immediately_block_ip_for_blocked_username($ip, $username) {
        global $wpdb;
        
        $lockout_duration = $this->get_config_value('lockout_duration', 240); // minutes
        
        // Calculate block expiry time using MySQL functions (same as failed_login handler)
        $blocked_until = $wpdb->get_var($wpdb->prepare(
            "SELECT DATE_ADD(NOW(), INTERVAL %d MINUTE)",
            $lockout_duration
        ));
        
        // Get current MySQL time
        $mysql_now = $wpdb->get_var("SELECT NOW()");
        
        // Calculate block expiry using Unix timestamps  
        $current_timestamp = time();
        $blocked_until_timestamp = $current_timestamp + ($lockout_duration * 60);

        // Get latest block_count for this IP
        $blocked_table = $wpdb->prefix . 'vmpfence_blocked_ips';
        $latest_count = $wpdb->get_var($wpdb->prepare(
            "SELECT block_count FROM {$blocked_table} WHERE ip_address = %s ORDER BY id DESC LIMIT 1",
            $ip
        ));
        
        $new_block_count = $latest_count ? ((int)$latest_count + 1) : 1;
        
        // Always INSERT new record
        $result = $wpdb->insert(
            $blocked_table,
            array(
                'ip_address' => $ip,
                'reason' => 'Brute Force - Blocked username attempt: ' . sanitize_text_field($username),
                'blocked_at' => $current_timestamp,
                'blocked_until' => $blocked_until_timestamp,
                'block_count' => $new_block_count,
                'last_attempt' => $mysql_now,
                'is_active' => 1,
                'source' => 'local'
            ),
            array('%s', '%s', '%d', '%d', '%d', '%s', '%d', '%s')
        );
        
        // Log to security attempts table
        $attempts_table = $wpdb->prefix . 'vmpfence_security_attempts';
        $wpdb->insert($attempts_table, array(
            'ip_address' => $ip,
            'username' => sanitize_text_field($username),
            'event_type' => 'blocked_username',
            'attempt_time' => $mysql_now,
            'blocked_until' => $blocked_until,
            'user_agent' => sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'] ?? ''))
        ), array('%s', '%s', '%s', '%s', '%s', '%s'));
        
        // DIRECT AUDIT LOG: Intentionally bypasses observer system.
        // Reason: Security event in authentication filter before observers may be loaded.
        VMPFence_Audit_Log::log_event('brute_force_blocked_username', array(
            'ip' => $ip,
            'username' => $username,
            'lockout_duration' => $lockout_duration . ' minutes',
            'blocked_until' => $blocked_until
        ));
        
        // Log to Live Traffic
        if (class_exists('VMPFence_Live_Traffic_Logger')) {
            $logger = VMPFence_Live_Traffic_Logger::getInstance();
            $logger->setAction('lockedOut', 'IP locked out: Blocked username attempt - ' . sanitize_text_field($username), array(
                'reason' => 'blocked_username_attempt',
                'username' => $username,
                'lockout_duration_minutes' => $lockout_duration
            ));
            $logger->setStatusCode(503);
        }
    }

    /**
     * Handle failed login attempts with dynamic configuration
     *
     * @param string $username Username that failed
     */
    public function handle_failed_login($username) {
        // Skip if brute force protection is disabled
        if (!VMPFence_Config::get('brute_force_enabled', 1)) {
            return;
        }

        global $wpdb;
        $ip = $this->get_client_ip();
        
        // Skip the dummy 2FA username completely - it's not a real login attempt
        if ($username === 'vmpfence_2fa_login') {
            return;
        }
        
        // Check if this IP is in 2FA verification mode for THIS specific username
        // Only bypass if the username matches the 2FA session username
        if (class_exists('VMPFence_Login_2FA_Handler')) {
            if (VMPFence_Login_2FA_Handler::is_2fa_active_for_username($username, $ip)) {
                return; // User is on 2FA page for this username, don't track as brute force
            }
        }
        
        
        // Check if IP was just blocked for invalid/blocked username (within last 5 seconds)
        // If so, skip this handler to avoid overwriting the specific reason
        $blocked_table = $wpdb->prefix . 'vmpfence_blocked_ips';
        $recent_block = $wpdb->get_row($wpdb->prepare(
            "SELECT reason, blocked_at FROM {$blocked_table} 
             WHERE ip_address = %s 
             AND blocked_at > %d 
             AND (reason LIKE %s OR reason LIKE %s)
             ORDER BY blocked_at DESC LIMIT 1",
            $ip,
            time() - 5, // Within last 5 seconds
            '%Invalid username attempt:%',
            '%Blocked username attempt:%'
        ));
        
        // If recently blocked for username issue, skip brute force handler
        if ($recent_block) {
            return;
        }
        
        // Get configuration values
        $max_attempts = $this->get_config_value('max_login_attempts', 20);
        $time_window = $this->get_config_value('count_failures_window', 240); // minutes
        $lockout_duration = $this->get_config_value('lockout_duration', 240); // minutes
        
        // Log failed attempt to the vmpfence_security_attempts table
        $table = $wpdb->prefix . 'vmpfence_security_attempts';
        
        // Count existing failed attempts within the time window
        $cutoff_time = $wpdb->get_var($wpdb->prepare(
            "SELECT DATE_SUB(NOW(), INTERVAL %d MINUTE)",
            $time_window
        ));
        
        $existing_attempts = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM $table WHERE ip_address = %s AND attempt_time > %s AND event_type = 'failed_login'",
            $ip,
            $cutoff_time
        ));
        
        // Include this new attempt
        $total_attempts = $existing_attempts + 1;
        
        // Check if we should block this IP
        $blocked_until = null;
        if ($total_attempts >= $max_attempts) {
            // Calculate block expiry time
            $blocked_until = $wpdb->get_var($wpdb->prepare(
                "SELECT DATE_ADD(NOW(), INTERVAL %d MINUTE)",
                $lockout_duration
            ));
            
            // Calculate block expiry time using Unix timestamps
            $current_timestamp = time();
            $blocked_until_timestamp = $current_timestamp + ($lockout_duration * 60);

            // Add to blocked IPs table
            $blocked_table = $wpdb->prefix . 'vmpfence_blocked_ips';
            $reason = "Brute force attack detected ({$total_attempts} failed attempts in {$time_window} minutes)";
            
            // Get latest block_count for this IP
            $latest_count = $wpdb->get_var($wpdb->prepare(
                "SELECT block_count FROM {$blocked_table} WHERE ip_address = %s ORDER BY id DESC LIMIT 1",
                $ip
            ));
            
            $new_block_count = $latest_count ? ((int)$latest_count + 1) : 1;
            
            // Always INSERT new record
            $block_result = $wpdb->insert($blocked_table, array(
                'ip_address' => $ip,
                'reason' => $reason,
                'blocked_at' => $current_timestamp,
                'blocked_until' => $blocked_until_timestamp,
                'block_count' => $new_block_count,
                'last_attempt' => $wpdb->get_var("SELECT NOW()"),
                'is_active' => 1,
                'source' => 'local'
            ), array('%s', '%s', '%d', '%d', '%d', '%s', '%d', '%s'));
            
            // Fire hook for audit log observer
            do_action('vmpfence_brute_force_detected', $ip, array(
                'username' => $username,
                'total_attempts' => $total_attempts,
                'time_window' => $time_window,
                'blocked_until' => $blocked_until,
                'severity' => 'critical',
                'immediate_alert' => true,
                'auto_block' => true
            ));
            
            // Log to Live Traffic
            if (class_exists('VMPFence_Live_Traffic_Logger')) {
                $logger = VMPFence_Live_Traffic_Logger::getInstance();
                $logger->setAction('lockedOut', $reason, array(
                    'reason' => 'brute_force_failed_login',
                    'failed_attempts' => $total_attempts,
                    'time_window_minutes' => $time_window,
                    'lockout_duration_minutes' => $lockout_duration,
                    'username' => $username
                ));
                $logger->setStatusCode(503);
            }
        }
        
        // Insert failed login record
        $mysql_now = $wpdb->get_var("SELECT NOW()");
        $result = $wpdb->insert($table, array(
            'ip_address' => $ip,
            'username' => $username,
            'event_type' => 'failed_login',
            'attempt_time' => $mysql_now,
            'blocked_until' => $blocked_until,
            'user_agent' => sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'] ?? ''))
        ), array('%s', '%s', '%s', '%s', '%s', '%s'));
    }

    /**
     * Handle successful login - clear failed attempts
     *
     * @param string $user_login Username
     * @param WP_User $user User object
     */
    public function handle_successful_login($user_login, $user) {
        // Skip if brute force protection is disabled
        if (!VMPFence_Config::get('brute_force_enabled', 1)) {
            return;
        }

        $ip = $this->get_client_ip();

        // Don't clear lockout completely, but we could reset failed count
        // For now, let the lockout duration expire naturally to maintain security
    }

    /**
     * Handle forgot password attempts
     *
     * @param string $user_login Username or email
     */
    public function handle_forgot_password_attempt($user_login) {
        // Skip if brute force protection is disabled
        if (!VMPFence_Config::get('brute_force_enabled', 1)) {
            return;
        }

        global $wpdb;
        $ip = $this->get_client_ip();
        
        // Get configuration values
        $max_attempts = $this->get_config_value('max_forgot_password_attempts', 20);
        $time_window = $this->get_config_value('count_failures_window', 240); // minutes
        $lockout_duration = $this->get_config_value('lockout_duration', 240); // minutes
        
        // Count existing forgot password attempts within the time window
        $table = $wpdb->prefix . 'vmpfence_security_attempts';
        $cutoff_time = $wpdb->get_var($wpdb->prepare(
            "SELECT DATE_SUB(NOW(), INTERVAL %d MINUTE)",
            $time_window
        ));
        
        $existing_attempts = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM $table WHERE ip_address = %s AND attempt_time > %s AND event_type = 'forgot_password'",
            $ip,
            $cutoff_time
        ));
        
        // Include this new attempt
        $total_attempts = $existing_attempts + 1;
        
        // Check if we should block this IP
        $blocked_until = null;
        if ($total_attempts >= $max_attempts) {
            // Calculate block expiry time
            $blocked_until = $wpdb->get_var($wpdb->prepare(
                "SELECT DATE_ADD(NOW(), INTERVAL %d MINUTE)",
                $lockout_duration
            ));
            
            // Calculate block expiry using Unix timestamps
            $current_timestamp = time();
            $blocked_until_timestamp = $current_timestamp + ($lockout_duration * 60);
            
            // Add to blocked IPs table
            $blocked_table = $wpdb->prefix . 'vmpfence_blocked_ips';
            $reason = "Brute Force - Too many forgot password attempts ({$total_attempts} attempts in {$time_window} minutes)";
            
            // Get latest block_count for this IP
            $latest_count = $wpdb->get_var($wpdb->prepare(
                "SELECT block_count FROM {$blocked_table} WHERE ip_address = %s ORDER BY id DESC LIMIT 1",
                $ip
            ));
            
            $new_block_count = $latest_count ? ((int)$latest_count + 1) : 1;
            
            // Always INSERT new record
            $wpdb->insert($blocked_table, array(
                'ip_address' => $ip,
                'reason' => $reason,
                'blocked_at' => $current_timestamp,
                'blocked_until' => $blocked_until_timestamp,
                'block_count' => $new_block_count,
                'last_attempt' => $wpdb->get_var("SELECT NOW()"),
                'is_active' => 1,
                'source' => 'local'
            ), array('%s', '%s', '%d', '%d', '%d', '%s', '%d', '%s'));
            
            // Log to Live Traffic
            if (class_exists('VMPFence_Live_Traffic_Logger')) {
                $logger = VMPFence_Live_Traffic_Logger::getInstance();
                $logger->setAction('lockedOut', $reason, array(
                    'reason' => 'forgot_password_attempts_exceeded',
                    'failed_attempts' => $total_attempts,
                    'time_window_minutes' => $time_window,
                    'lockout_duration_minutes' => $lockout_duration,
                    'username' => $user_login
                ));
                $logger->setStatusCode(503);
            }
        }

        // Track the forgot password attempt
        $mysql_now = $wpdb->get_var("SELECT NOW()");
        $wpdb->insert($table, array(
            'ip_address' => $ip,
            'username' => $user_login,
            'event_type' => 'forgot_password',
            'attempt_time' => $mysql_now,
            'blocked_until' => $blocked_until,
            'user_agent' => sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'] ?? ''))
        ), array('%s', '%s', '%s', '%s', '%s', '%s'));
        
        // DIRECT AUDIT LOG: Intentionally bypasses observer system.
        // Reason: Forgot-password security event, unique context.
        VMPFence_Audit_Log::log_event('brute_force_forgot_password', array(
            'ip' => $ip,
            'username' => $user_login,
            'attempt_count' => $total_attempts
        ));
    }

    /**
     * Check for invalid username attempts on login form
     */
    public function check_invalid_username() {
        // Skip if brute force protection is disabled
        if (!VMPFence_Config::get('brute_force_enabled', 1)) {
            return;
        }

        global $wpdb;
        $ip = $this->get_client_ip();

        // Check if IP is currently blocked (using Unix timestamp)
        $blocked_table = $wpdb->prefix . 'vmpfence_blocked_ips';
        $current_timestamp = time();
        $is_blocked = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$blocked_table} WHERE ip_address = %s AND blocked_until > %d",
            $ip,
            $current_timestamp
        ));

        if ($is_blocked) {
            // Add a notice that the IP is locked out
            add_action('login_message', array($this, 'show_lockout_message'));
        }
    }

    /**
     * Show lockout message on login form
     *
     * @param string $message Existing message
     * @return string Modified message
     */
    public function show_lockout_message($message) {
        $custom_text = VMPFence_Config::get('custom_block_text', '');
        
        if (!empty($custom_text)) {
            $lockout_msg = esc_html($custom_text);
        } else {
            $lockout_msg = 'Your IP address has been temporarily locked due to too many failed login attempts. Please try again later.';
        }

        return '<div id="login_error">' . $lockout_msg . '</div>' . $message;
    }

    /**
     * Get configuration value with validation and fallback
     *
     * @param string $key Configuration key
     * @param mixed $default Default value if not set or invalid
     * @return mixed Validated configuration value
     */
    private function get_config_value($key, $default) {
        $value = VMPFence_Config::get($key, $default);
        
        // Validate specific configuration keys
        switch ($key) {
            case 'max_login_attempts':
                // Must be between 1 and 500
                if (!is_numeric($value) || $value < 1 || $value > 500) {
                    return $default;
                }
                return (int)$value;
                
            case 'max_forgot_password_attempts':
                // Must be between 1 and 500
                if (!is_numeric($value) || $value < 1 || $value > 500) {
                    return $default;
                }
                return (int)$value;
                
            case 'count_failures_window':
                // Must be between 5 and 1440 minutes (5 minutes to 24 hours)
                if (!is_numeric($value) || $value < 5 || $value > 1440) {
                    return $default;
                }
                return (int)$value;
                
            case 'lockout_duration':
                // Must be between 5 and 86400 minutes (5 minutes to 60 days)
                if (!is_numeric($value) || $value < 5 || $value > 86400) {
                    return $default;
                }
                return (int)$value;
                
            default:
                return $value;
        }
    }

    /**
     * Get client IP address
     *
     * @return string Client IP address
     */
    private function get_client_ip() {
        // Check for various headers that might contain the real IP
        $headers = array(
            'HTTP_CF_CONNECTING_IP',     // Cloudflare
            'HTTP_X_FORWARDED_FOR',      // Load balancers/proxies
            'HTTP_X_FORWARDED',          // Proxies
            'HTTP_X_CLUSTER_CLIENT_IP',  // Cluster
            'HTTP_FORWARDED_FOR',        // Proxies
            'HTTP_FORWARDED',            // Proxies
            'HTTP_CLIENT_IP',            // Proxies
            'REMOTE_ADDR'                // Standard
        );

        foreach ($headers as $header) {
            if (!empty($_SERVER[$header])) {
                $ip = $_SERVER[$header];
                
                // Handle comma-separated list (X-Forwarded-For can have multiple IPs)
                if (strpos($ip, ',') !== false) {
                    $ip = trim(explode(',', $ip)[0]);
                }
                
                // Validate IP
                if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
                    return $ip;
                }
            }
        }

        // Fallback to REMOTE_ADDR
        return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1';
    }
}