<?php
/**
 * Configuration Management Class
 *
 * @package VMPFence
 * @since 1.0.0
 * @since 2.0.0 -- Firewall Config set
 * @since 2.2.4 -- Added clear_cache method, Added default values for allowlisted services, Onboarding state management added, class exist check of audit log
 * @since 2.2.5 -- Added Enhanced Audit Log integration for configuration changes, DB value checked before config update, audit log observer.
 */

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

/**
 * VMPFence Configuration Class
 *
 * @since 1.0.0
 */
class VMPFence_Config {

    /**
     * Configuration cache
     *
     * @var array
     */
    private static $config_cache = array();

    /**
     * Default configuration values
     * @since 2.0.0 -- Firewall Config set
     * @since 2.2.0 -- Country Blocking
     * @since 2.2.4 -- Added default values for allowlisted services
     *
     * @var array
     */
    private static $defaults = array(
        // General settings
        'enabled' => 1,
        'license_key' => '',
        'license_type' => VMPFence_Constants::LICENSE_FREE,

        // WAF settings
        'waf_enabled' => 1,
        'waf_protection_level' => VMPFence_Constants::WAF_PROTECTION_BASIC,
        'waf_learning_mode' => 0,
        'waf_block_attackers' => 1,
        'waf_log_attacks' => 1,
        'waf_blocking_threshold' => 50,  // Lowered from 100 for single-rule blocking
        'waf_enable_caching' => 1,
        'waf_cache_duration' => 3600,
        
        // Scanner settings
        'scanner_enabled' => 1,
        'scan_type' => VMPFence_Constants::SCAN_TYPE_STANDARD,
        'scan_schedule' => 'daily',
        'scan_max_execution_time' => 7200,
        'scan_max_file_size' => 52428800,
        'scan_include_uploads' => 1,
        'scan_exclude_patterns' => array(),
        'scan_force_ipv4_start' => 0,
        'scan_max_resume_attempts' => 2,
        'scanMonitorRemainingResumeAttempts' => 0,
        'scanMonitorLastAttempt' => null,
        'scanMonitorLastSuccess' => null,
        'scanMonitorLastAttemptMode' => null,
        'scanMonitorLastAttemptWasFork' => false,
        
        // Reputation check options
        'scansEnabled_checkGSB' => 0,
        'spamvertizeCheck' => 0,
        'checkSpamIP' => 0,
        
        // Login security settings
        'login_security_enabled' => 1,
        'captcha_enabled' => 0,
        'captcha_provider' => 'recaptcha',
        'captcha_site_key' => '',
        'captcha_secret_key' => '',
        '2fa_enabled' => 0,
        '2fa_required_roles' => array(),
        
        // Phase 2 Login Security Settings
        'vmpfence_show_last_login_column' => 1,
        'vmpfence_delete_on_deactivate' => 0,
        'vmpfence_ntp_enabled' => 1,
        
        // Traffic monitoring settings
        'traffic_monitoring_enabled' => 1,
        'traffic_log_mode' => VMPFence_Constants::TRAFFIC_LOG_SECURITY_ONLY,
        'traffic_retention_days' => 30,
        
        // Blocking settings
        'ip_blocking_enabled' => 1,
        'country_blocking_enabled' => 0,
        'blocked_countries' => array(),
        'rate_limiting_enabled' => 0,
        'realtime_ip_blocklist_enabled' => 1,
        'rate_limit_requests' => 240,
        'rate_limit_window' => 60,
        'rate_limit_404_requests' => 20,
        'allowlisted_404_urls' => array('/favicon.ico', '/apple-touch-icon*.png', '/*@2x.png', '/browserconfig.xml'),
        
        // Blocking Options Page Settings (v2.2.0)
        'blocking_menu_display' => 1,
        'country_block_action' => 'message',
        'country_block_logged_in' => 0,
        
        // Advanced Firewall Options
        'delay_ip_blocking' => 0,
        'whitelisted_ips_bypass' => '',
        'whitelisted_services' => '{"sucuri":true,"facebook":true,"uptime":true,"statuscake":true,"managewp":true,"seznam":true,"google":true}',
        'blocked_urls' => '',
        'ignored_ips_waf' => '',

        // Allowlist settings
        'allowlisted_url_params' => array(),
        
        // Rate limiting detailed settings
        'google_crawler_treatment' => 'verified_google',
        'global_rate_limit' => 'unlimited',
        'global_rate_action' => 'throttle',
        'crawler_rate_limit' => 'unlimited',
        'crawler_rate_action' => 'throttle',
        'crawler_404_limit' => 'unlimited',
        'crawler_404_action' => 'throttle',
        'human_rate_limit' => 'unlimited',
        'human_rate_action' => 'throttle',
        'human_404_limit' => 'unlimited',
        'human_404_action' => 'throttle',
        'rate_block_duration' => 5,
        'throttle_behavior' => 'show_503',
        'throttle_delay_duration' => 5,
        
        // Firewall settings
        'firewall_enabled' => 1,
        'brute_force_enabled' => 1,
        'max_login_attempts' => 20,
        'max_forgot_password_attempts' => 20,
        'count_failures_window' => 240, // 4 hours in minutes
        'lockout_duration' => 240, // 4 hours in minutes
        'lock_invalid_usernames' => 0,
        'blocked_usernames' => array(),
        'prevent_leaked_passwords_enabled' => 0,
        'prevent_leaked_passwords_scope' => 'admin',
        'enforce_strong_passwords' => 1,
        'strong_passwords_scope' => 'recommended',
        'dont_reveal_users' => 1,
        'prevent_admin_username' => 1,
        'prevent_username_discovery' => 1,
        'disable_app_passwords' => 1,
        'block_post_requests' => 0,
        'custom_block_text' => '',
        'check_password_strength' => 1,
        'participate_security_network' => 1,
        'permanent_ban_threshold' => 10, // After how many lockouts to apply permanent ban
        'blocklist_enabled' => 1,
        'advanced_blocking_enabled' => 0,
        'whitelist_enabled' => 1,
        
        // Notification settings
        'email_notifications_enabled' => 1,
        'notification_email' => '',
        'alert_on_scan_completion' => 1,
        'alert_on_threats_found' => 1,
        'alert_on_login_failures' => 1,
        'alert_on_waf_blocks' => 0,
        'activity_report_enabled' => 1,
        'activity_report_frequency' => 'weekly',
        
        // Advanced settings
        'preserve_data_on_uninstall' => 0,
        'enable_audit_log' => 1,
        'audit_log_retention_days' => 90,
        'central_management_enabled' => 0,
        'central_api_key' => '',
        'enable_debug_logging' => 0,
        
        // Performance settings
        'cache_enabled' => 1,
        'cache_duration' => 3600,
        'background_processing' => 1,
        'memory_limit' => '256M',
        
        // Advanced Scheduling Configuration Options (Phase 1.1)
        'scanSched' => array(), // Custom 7x24 schedule grid
        'vmpfenceScanSchedule' => array(), // Automatic schedule timestamps
        'lastScheduleUpdate' => 0, // Timestamp of last schedule update
        'scheduleTimezone' => '', // User's timezone for schedule generation
        'randomOffset' => false, // Random 0-60 minute offset
        'allScansScheduled' => array(), // Currently scheduled cron events
        
        // Onboarding system
        'onboarding_dismissed' => array() // Tracks which page tours have been dismissed
    );

    /**
     * Get configuration value
     *
     * @since 1.0.0
     * @param string $key Configuration key
     * @param mixed $default Default value
     * @return mixed Configuration value
     */
    public static function get($key, $default = null) {
        // Check cache first
        if (isset(self::$config_cache[$key])) {
            return self::$config_cache[$key];
        }

        // Get from database
        $value = self::get_from_db($key);
        
        // Handle missing values - use default or fallback to defaults array
        if ($value === null || $value === '') {
            $value = $default !== null ? $default : (self::$defaults[$key] ?? null);
        }

        // Cache the value
        self::$config_cache[$key] = $value;

        return $value;
    }

    /**
     * Set configuration value
     *
     * @since 1.0.0
     * @param string $key Configuration key
     * @param mixed $value Configuration value
     * @return bool
     */
    public static function set($key, $value) {
        // Validate and sanitize the value
        $value = self::validate_config_value($key, $value);
        
        // Debug logging for scanSched after validation
        if ($key === 'scanSched') {
            // error_log('VMPFence: After validation, scanSched type: ' . gettype($value));
            if (is_array($value)) {
                $total = 0;
                foreach ($value as $day => $hours) {
                    if (is_array($hours)) {
                        foreach ($hours as $hour => $checked) {
                            if ($checked == 1) $total++;
                        }
                    }
                }
                // error_log('VMPFence: After validation, scanSched has ' . $total . ' checked hours');
            } else {
                // error_log('VMPFence: After validation, scanSched value: ' . print_r($value, true));
            }
        }

        // Get current value BEFORE writing to DB
        $old_value = self::get($key);

        // Skip DB write if value hasn't actually changed.
        // The DB stores all scalars as strings via $wpdb->replace with %s format,
        // so we must compare using the same string representation to avoid
        // false positives like "0" (DB) vs 0 (form) being treated as different.
        $norm_new = ($value === false) ? 0 : (($value === true) ? 1 : $value);
        $norm_old = ($old_value === false) ? 0 : (($old_value === true) ? 1 : $old_value);

        if ((string) maybe_serialize($norm_old) === (string) maybe_serialize($norm_new)) {
            return true; // Value unchanged — no DB write, no hooks, no noise
        }

        // Value actually changed — save to database
        $result = self::save_to_db($key, $value);

        if ($result) {
            // Update cache
            self::$config_cache[$key] = $value;

            // Fire centralized config change hook (used by audit log observer)
            do_action('vmpfence_config_changed', $key, $old_value, $value);
        }

        return $result;
    }

    /**
     * Get multiple configuration values
     *
     * @since 1.0.0
     * @param array $keys Configuration keys
     * @return array Configuration values
     */
    public static function get_multiple($keys) {
        $values = array();
        
        foreach ($keys as $key) {
            $values[$key] = self::get($key);
        }

        return $values;
    }

    /**
     * Set multiple configuration values
     *
     * @since 1.0.0
     * @param array $config Configuration array
     * @return bool
     */
    public static function set_multiple($config) {
        $success = true;

        foreach ($config as $key => $value) {
            if (!self::set($key, $value)) {
                $success = false;
            }
        }

        return $success;
    }

    /**
     * Save configuration array
     *
     * @since 1.0.0
     * @param array $config Configuration data
     * @return bool
     */
    public function save_config($config) {
        return self::set_multiple($config);
    }

    /**
     * Delete configuration value
     *
     * @since 1.0.0
     * @param string $key Configuration key
     * @return bool
     */
    public static function delete($key) {
        global $wpdb;

        $table_name = VMPFence_Constants::get_table_name(VMPFence_Constants::TABLE_CONFIG);
        
        $result = $wpdb->delete(
            $table_name,
            array('name' => $key),
            array('%s')
        );

        if ($result !== false) {
            // Remove from cache
            unset(self::$config_cache[$key]);

            // Log configuration deletion (guard for uninstall context)
            // Fire hook for audit log observer
            do_action('vmpfence_config_deleted', $key);
        }

        return $result !== false;
    }

    /**
     * Clear configuration cache for a specific key or all keys
     *
     * @since 2.2.4
     * @param string|null $key Configuration key to clear, or null to clear all
     * @return void
     */
    public static function clear_cache($key = null) {
        if ($key === null) {
            // Clear entire cache
            self::$config_cache = array();
        } else {
            // Clear specific key
            unset(self::$config_cache[$key]);
        }
    }

    /**
     * Set default configuration values
     *
     * @since 1.0.0
     * @since 2.2.0 -- Smart default for country_block_action
     * @return void
     */
    public static function set_defaults() {
        foreach (self::$defaults as $key => $value) {
            // Only set if not already exists
            if (self::get_from_db($key) === null) {
                // Smart default detection for country_block_action
                // If redirect URL is already set, default to 'redirect' mode
                if ($key === 'country_block_action') {
                    $existing_redirect = get_option('vmpfence_country_block_redirect', '');
                    if (!empty($existing_redirect)) {
                        self::save_to_db($key, 'redirect');
                        continue;
                    }
                }
                
                self::save_to_db($key, $value);
            }
        }

        // Set notification email to admin email if not set
        if (empty(self::get('notification_email'))) {
            self::set('notification_email', get_option('admin_email'));
        }
    }

    /**
     * Remove all configuration options
     *
     * @since 1.0.0
     * @since 2.2.2 -- Added more list of options for deletion
     * @return void
     */
    public static function remove_all_options() {
        global $wpdb;

        $table_name = VMPFence_Constants::get_table_name(VMPFence_Constants::TABLE_CONFIG);
        
        // Check if table exists before truncating (for uninstall compatibility)
        $table_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table_name)) === $table_name;
        if ($table_exists) {
            $wpdb->query("TRUNCATE TABLE `{$table_name}`"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
        }

        // Clear cache
        self::$config_cache = array();

        // Remove WordPress options (complete list - prevents partial deletion bug)
        $options = array(
            'vmpfence_version',
            'vmpfence_db_version',
            'vmpfence_activation_time',
            'vmpfence_license_key',
            'vmpfence_license_status',
            // Missing license options (prevents "FREE-XXXXXXX" bug)
            'vmpfence_license_type',
            'vmpfence_central_api_key',
            'vmpfence_license_email',
            'vmpfence_license',
            'vmpfence_firewall_license_tier',
            'vmpfence_scanner_license_tier',
            'vmpfence_needs_setup',
            'vmpfence_setup_completed', // CRITICAL: Remove to trigger fresh install flow
            // NOTE: vmpfence_preserve_data_on_uninstall NOT included here
            // Must persist through deactivation so uninstall.php can read it
            'vmpfence_email_alerts_enabled'
        );

        foreach ($options as $option) {
            delete_option($option);
        }
    }

    /**
     * Get configuration value from database
     *
     * @since 1.0.0
     * @param string $key Configuration key
     * @return mixed Configuration value or null
     */
    private static function get_from_db($key) {
        global $wpdb;

        $table_name = VMPFence_Constants::get_table_name(VMPFence_Constants::TABLE_CONFIG);
        
        // Check if table exists before querying (prevents errors during activation)
        $table_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table_name)) === $table_name;
        if (!$table_exists) {
            return null;
        }
        
        $result = $wpdb->get_var($wpdb->prepare(
            "SELECT val FROM `{$table_name}` WHERE name = %s", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
            $key
        ));

        if ($result === null) {
            return null;
        }

        return maybe_unserialize($result);
    }

    /**
     * Save configuration value to database
     *
     * @since 1.0.0
     * @since 2.1.1 
     * @param string $key Configuration key
     * @param mixed $value Configuration value
     * @return bool
     */
    private static function save_to_db($key, $value) {
        global $wpdb;

        $table_name = VMPFence_Constants::get_table_name(VMPFence_Constants::TABLE_CONFIG);
        
        // Check if table exists before saving (prevents errors during activation)
        $table_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table_name)) === $table_name;
        if (!$table_exists) {
            return false;
        }
        
        // Ensure boolean false is stored as integer 0, not empty string
        if ($value === false) {
            $value = 0;
        } elseif ($value === true) {
            $value = 1;
        }
        
        // Chunk large data if necessary
        $serialized_value = maybe_serialize($value);
        
        if (is_string($serialized_value) && strlen($serialized_value) > 1000000) { // 1MB limit
            return self::chunk_data($key, $value);
        }

        $result = $wpdb->replace(
            $table_name,
            array(
                'name' => $key,
                'val' => $serialized_value,
                'autoload' => 'yes'
            ),
            array('%s', '%s', '%s')
        );

        return $result !== false;
    }

    /**
     * Chunk large configuration data
     *
     * @since 1.0.0
     * @param string $key Configuration key
     * @param mixed $value Configuration value
     * @return bool
     */
    public static function chunk_data($key, $value) {
        global $wpdb;

        $table_name = VMPFence_Constants::get_table_name(VMPFence_Constants::TABLE_CONFIG);
        $serialized_value = maybe_serialize($value);
        $chunk_size = 1000000; // 1MB chunks
        $chunks = str_split($serialized_value, $chunk_size);
        $total_chunks = count($chunks);

        // Start transaction
        $wpdb->query('START TRANSACTION');

        try {
            // Delete existing chunks
            $wpdb->delete(
                $table_name,
                array('name' => $key),
                array('%s')
            );

            $wpdb->delete(
                $table_name,
                array('name' => $key . '_chunk_%'),
                array('%s')
            );

            // Save chunks
            foreach ($chunks as $index => $chunk) {
                $chunk_key = $key . '_chunk_' . $index;
                
                $result = $wpdb->insert(
                    $table_name,
                    array(
                        'name' => $chunk_key,
                        'val' => $chunk,
                        'autoload' => 'no'
                    ),
                    array('%s', '%s', '%s')
                );

                if ($result === false) {
                    throw new Exception('Failed to save chunk');
                }
            }

            // Save metadata
            $metadata = array(
                'chunked' => true,
                'total_chunks' => $total_chunks,
                'original_size' => strlen($serialized_value)
            );

            $result = $wpdb->insert(
                $table_name,
                array(
                    'name' => $key,
                    'val' => maybe_serialize($metadata),
                    'autoload' => 'yes'
                ),
                array('%s', '%s', '%s')
            );

            if ($result === false) {
                throw new Exception('Failed to save metadata');
            }

            $wpdb->query('COMMIT');
            return true;

        } catch (Exception $e) {
            $wpdb->query('ROLLBACK');
            return false;
        }
    }

    /**
     * Retrieve chunked data
     *
     * @since 1.0.0
     * @param string $key Configuration key
     * @return mixed Configuration value or null
     */
    private static function get_chunked_data($key) {
        global $wpdb;

        $table_name = VMPFence_Constants::get_table_name(VMPFence_Constants::TABLE_CONFIG);
        
        // Get metadata
        $metadata = self::get_from_db($key);
        
        if (!is_array($metadata) || !isset($metadata['chunked'])) {
            return null;
        }

        $total_chunks = $metadata['total_chunks'];
        $data = '';

        // Retrieve chunks
        for ($i = 0; $i < $total_chunks; $i++) {
            $chunk_key = $key . '_chunk_' . $i;
            $chunk = $wpdb->get_var($wpdb->prepare(
                "SELECT val FROM `{$table_name}` WHERE name = %s", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
                $chunk_key
            ));

            if ($chunk === null) {
                return null;
            }

            $data .= $chunk;
        }

        return maybe_unserialize($data);
    }

    /**
     * Validate configuration value
     *
     * @since 1.0.0
     * @param string $key Configuration key
     * @param mixed $value Configuration value
     * @return mixed Validated value
     */
    public static function validate_config($key, $value) {
        return self::validate_config_value($key, $value);
    }

    /**
     * Validate configuration value
     *
     * @since 1.0.0
     * @since 2.0.0 -- Firewall Case added
     * @since 2.2.0 -- Country Blocking Cases added
     * @since 2.2.2 -- Notification email supports comma-separated list
     * @param string $key Configuration key
     * @param mixed $value Configuration value
     * @return mixed Validated value
     */
    private static function validate_config_value($key, $value) {
        // Debug logging for scanSched
        // if ($key === 'scanSched') {
        //     error_log('VMPFence: validate_config_value for scanSched - input type: ' . gettype($value));
        //     if (is_array($value)) {
        //         $total = 0;
        //         foreach ($value as $day => $hours) {
        //             if (is_array($hours)) {
        //                 foreach ($hours as $hour => $checked) {
        //                     if ($checked == 1) $total++;
        //                 }
        //             }
        //         }
        //         error_log('VMPFence: validate_config_value for scanSched - input has ' . $total . ' checked hours');
        //     }
        // }
        
        switch ($key) {
            case 'enabled':
            case 'waf_enabled':
            case 'scanner_enabled':
            case 'login_security_enabled':
            case 'traffic_monitoring_enabled':
            case 'ip_blocking_enabled':
            case 'country_blocking_enabled':
            case 'rate_limiting_enabled':
            case 'email_notifications_enabled':
            case 'scheduledScansEnabled':
            case 'firewall_enabled':
            case 'brute_force_enabled':
            case 'blocklist_enabled':
            case 'advanced_blocking_enabled':
            case 'whitelist_enabled':
            case 'lock_invalid_usernames':
            case 'prevent_leaked_passwords_enabled':
            case 'enforce_strong_passwords':
            case 'dont_reveal_users':
            case 'prevent_admin_username':
            case 'prevent_username_discovery':
            case 'disable_app_passwords':
            case 'block_post_requests':
            case 'check_password_strength':
            case 'participate_security_network':
            case 'vmpfence_show_last_login_column':
            case 'vmpfence_delete_on_deactivate':
            case 'vmpfence_ntp_enabled':
            case 'blocking_menu_display':
            case 'country_block_logged_in':
                return (int) (bool) $value;

            case 'max_login_attempts':
            case 'lockout_duration':
            case 'max_forgot_password_attempts':
            case 'count_failures_window':
            case 'permanent_ban_threshold':
            case 'scan_max_execution_time':
            case 'scan_max_file_size':
            case 'traffic_retention_days':
            case 'rate_limit_requests':
            case 'rate_limit_window':
            case 'rate_limit_404_requests':
            case 'audit_log_retention_days':
            case 'cache_duration':
                return max(0, intval($value));

            case 'waf_protection_level':
                $valid_levels = array(
                    VMPFence_Constants::WAF_PROTECTION_BASIC,
                    VMPFence_Constants::WAF_PROTECTION_EXTENDED,
                    VMPFence_Constants::WAF_PROTECTION_LEARNING
                );
                return in_array($value, $valid_levels) ? $value : VMPFence_Constants::WAF_PROTECTION_BASIC;

            case 'scan_type':
                $valid_types = array(
                    VMPFence_Constants::SCAN_TYPE_QUICK,
                    VMPFence_Constants::SCAN_TYPE_STANDARD,
                    VMPFence_Constants::SCAN_TYPE_LIMITED,
                    VMPFence_Constants::SCAN_TYPE_HIGH_SENSITIVITY,
                    VMPFence_Constants::SCAN_TYPE_CUSTOM
                );
                return in_array($value, $valid_types) ? $value : VMPFence_Constants::SCAN_TYPE_STANDARD;

            case 'traffic_log_mode':
                $valid_modes = array(
                    VMPFence_Constants::TRAFFIC_LOG_SECURITY_ONLY,
                    VMPFence_Constants::TRAFFIC_LOG_ALL,
                    VMPFence_Constants::TRAFFIC_LOG_DISABLED
                );
                return in_array($value, $valid_modes) ? $value : VMPFence_Constants::TRAFFIC_LOG_SECURITY_ONLY;

                
            case 'license_type':
                $valid_types = array(
                    VMPFence_Constants::LICENSE_FREE,
                    VMPFence_Constants::LICENSE_PREMIUM
                );
                return in_array($value, $valid_types) ? $value : VMPFence_Constants::LICENSE_FREE;

            case 'notification_email':
                // Support comma-separated emails
                if (empty($value)) {
                    return get_option('admin_email');
                }
                
                // Check if it's a single email or comma-separated list
                if (strpos($value, ',') !== false) {
                    // Validate comma-separated emails
                    $emails = array_map('trim', explode(',', $value));
                    $valid = true;
                    foreach ($emails as $email) {
                        if (!is_email($email)) {
                            $valid = false;
                            break;
                        }
                    }
                    return $valid ? $value : get_option('admin_email');
                } else {
                    // Single email validation
                    return is_email($value) ? $value : get_option('admin_email');
                }

            case 'license_key':
            case 'captcha_site_key':
            case 'captcha_secret_key':
            case 'central_api_key':
            case 'custom_block_text':
                return sanitize_text_field($value);

            case 'prevent_leaked_passwords_scope':
                return in_array($value, array('admin', 'all')) ? $value : 'admin';

            case 'strong_passwords_scope':
                return in_array($value, array('recommended', 'all')) ? $value : 'recommended';
            
            case 'country_block_action':
                return in_array($value, array('message', 'redirect')) ? $value : 'message';

            case 'scan_exclude_patterns':
            case 'blocked_countries':
            case '2fa_required_roles':
            case 'blocked_usernames':
            case 'allowlisted_404_urls':
                return is_array($value) ? array_map('sanitize_text_field', $value) : array();

            case 'allScansScheduled':
            case 'scheduledScanEvents':
            case 'scheduledScanCounters':
            case 'scheduledScanStats':
                // Handle arrays of scheduled scan data - validate as integers/arrays
                if (!is_array($value)) {
                    return array();
                }
                
                // For timestamp arrays, ensure all values are valid integers
                if ($key === 'allScansScheduled') {
                    return array_map('intval', array_filter($value, 'is_numeric'));
                }
                
                // For other scan data arrays, return as-is after basic validation
                return $value;

            case 'scanSched':
                // Handle custom schedule grid - validate structure and sanitize values
                if (!is_array($value)) {
                    return array();
                }
                
                $sanitized_schedule = array();
                for ($day = 0; $day < 7; $day++) {
                    $sanitized_schedule[$day] = array();
                    if (isset($value[$day]) && is_array($value[$day])) {
                        for ($hour = 0; $hour < 24; $hour++) {
                            $sanitized_schedule[$day][$hour] = isset($value[$day][$hour]) ? (int) $value[$day][$hour] : 0;
                        }
                    } else {
                        // Fill with zeros if day data is missing
                        for ($hour = 0; $hour < 24; $hour++) {
                            $sanitized_schedule[$day][$hour] = 0;
                        }
                    }
                }
                return $sanitized_schedule;

            case 'memory_limit':
                // Validate memory limit format
                if (preg_match('/^(\d+)(M|G)?$/i', $value, $matches)) {
                    return $value;
                }
                return '256M';

            case 'allowlisted_url_params':
                // Validate allowlist array structure
                if (!is_array($value)) {
                    return array();
                }
                return $value;

            case 'onboarding_dismissed':
                // Validate onboarding dismissal tracking
                if (!is_array($value)) {
                    return array();
                }
                // Sanitize: only allow known page keys with boolean values
                $allowed_keys = array('dashboard', 'firewall', 'blocking', 'scan', 'login_security', 'live_traffic', 'audit_log');
                $sanitized = array();
                foreach ($allowed_keys as $page_key) {
                    if (isset($value[$page_key])) {
                        $sanitized[$page_key] = (bool) $value[$page_key];
                    }
                }
                return $sanitized;

            default:
                // Debug logging for unhandled keys
                // if ($key === 'scanSched') {
                //     error_log('VMPFence: CRITICAL ERROR - scanSched fell through to default case!');
                //     error_log('VMPFence: This will destroy the data by calling sanitize_text_field on array');
                // }
                return sanitize_text_field($value);
        }
    }

    /**
     * Export configuration
     *
     * @since 1.0.0
     * @return array
     */
    public static function export_config() {
        global $wpdb;

        $table_name = VMPFence_Constants::get_table_name(VMPFence_Constants::TABLE_CONFIG);
        
        $results = $wpdb->get_results(
            "SELECT name, val FROM `{$table_name}` WHERE autoload = 'yes'", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
            ARRAY_A
        );

        $config = array();
        foreach ($results as $row) {
            $config[$row['name']] = maybe_unserialize($row['val']);
        }

        return $config;
    }

    /**
     * Import configuration
     *
     * @since 1.0.0
     * @param array $config Configuration data
     * @return bool
     */
    public static function import_config($config) {
        if (!is_array($config)) {
            return false;
        }

        $success = true;

        foreach ($config as $key => $value) {
            if (!self::set($key, $value)) {
                $success = false;
            }
        }

        // REMOVED: VMPFence_Audit_Log::log_event('config_imported') — now handled by Observer_VMPFence via class-import-export.php hook

        return $success;
    }

    /**
     * Reset configuration to defaults
     *
     * @since 1.0.0
     * @return bool
     */
    public static function reset_to_defaults() {
        global $wpdb;

        $table_name = VMPFence_Constants::get_table_name(VMPFence_Constants::TABLE_CONFIG);
        
        // Clear all configuration
        $wpdb->query("TRUNCATE TABLE `{$table_name}`"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
        
        // Clear cache
        self::$config_cache = array();

        // Set defaults
        self::set_defaults();

        // Fire hook for observer-based audit logging
        do_action('vmpfence_config_reset');

        return true;
    }

    /**
     * Get configuration schema
     *
     * @since 1.0.0
     * @return array
     */
    public static function get_schema() {
        return array(
            'general' => array(
                'enabled' => array('type' => 'boolean', 'default' => true),
                                'license_key' => array('type' => 'string', 'default' => ''),
                'license_type' => array('type' => 'string', 'default' => VMPFence_Constants::LICENSE_FREE)
            ),
            'waf' => array(
                'waf_enabled' => array('type' => 'boolean', 'default' => true),
                'waf_protection_level' => array('type' => 'string', 'default' => VMPFence_Constants::WAF_PROTECTION_BASIC),
                'waf_learning_mode' => array('type' => 'boolean', 'default' => false),
                'waf_block_attackers' => array('type' => 'boolean', 'default' => true),
                'waf_log_attacks' => array('type' => 'boolean', 'default' => true)
            ),
            'scanner' => array(
                'scanner_enabled' => array('type' => 'boolean', 'default' => true),
                'scan_type' => array('type' => 'string', 'default' => VMPFence_Constants::SCAN_TYPE_STANDARD),
                'scan_schedule' => array('type' => 'string', 'default' => 'daily'),
                'scan_max_execution_time' => array('type' => 'integer', 'default' => 7200),
                'scan_max_file_size' => array('type' => 'integer', 'default' => 52428800),
                'scan_include_uploads' => array('type' => 'boolean', 'default' => true),
                'scan_exclude_patterns' => array('type' => 'array', 'default' => array())
            ),
            'login_security' => array(
                'login_security_enabled' => array('type' => 'boolean', 'default' => true),
                'max_login_attempts' => array('type' => 'integer', 'default' => 20),
                'lockout_duration' => array('type' => 'integer', 'default' => 1800),
                'captcha_enabled' => array('type' => 'boolean', 'default' => false),
                '2fa_enabled' => array('type' => 'boolean', 'default' => false),
                'enforce_strong_passwords' => array('type' => 'boolean', 'default' => true)
            ),
            'traffic' => array(
                'traffic_monitoring_enabled' => array('type' => 'boolean', 'default' => true),
                'traffic_log_mode' => array('type' => 'string', 'default' => VMPFence_Constants::TRAFFIC_LOG_SECURITY_ONLY),
                'traffic_retention_days' => array('type' => 'integer', 'default' => 30)
            ),
            'blocking' => array(
                'ip_blocking_enabled' => array('type' => 'boolean', 'default' => true),
                'country_blocking_enabled' => array('type' => 'boolean', 'default' => false),
                'blocked_countries' => array('type' => 'array', 'default' => array()),
                'rate_limiting_enabled' => array('type' => 'boolean', 'default' => true),
                'rate_limit_requests' => array('type' => 'integer', 'default' => 30),
                'rate_limit_window' => array('type' => 'integer', 'default' => 300)
            ),
            'notifications' => array(
                'email_notifications_enabled' => array('type' => 'boolean', 'default' => true),
                'notification_email' => array('type' => 'string', 'default' => ''),
                'alert_on_scan_completion' => array('type' => 'boolean', 'default' => true),
                'alert_on_threats_found' => array('type' => 'boolean', 'default' => true),
                'activity_report_enabled' => array('type' => 'boolean', 'default' => true),
                'activity_report_frequency' => array('type' => 'string', 'default' => 'weekly')
            )
        );
    }

    /**
     * Check if API is available (central API key configured)
     *
     * @since 2.0.0
     * @return bool True if API key is configured and non-empty
     */
    public static function is_api_available() {
        $api_key = self::get('central_api_key', '');
        return !empty($api_key) && is_string($api_key) && strlen(trim($api_key)) > 0;
    }
}
