<?php
/**
 * Basic bot detection logic for BotFend Anti-Bot Firewall
 *
 * @package BotFend\Detection
 * @since 4.4.1
 * @version 4.8.3 - Rebranded from SafeBot, external services opt-in, database alignment
 */

declare(strict_types=1);

namespace BotFend\Detection;

use BotFend\Core\Config;
use BotFend\Utilities\Utilities;
use BotFend\Database\Database;

defined('ABSPATH') || exit;

/**
 * Class Detection
 *
 * Generic bot detection engine.
 * Handles: User-Agent, Headers, Geographic, Proxy/VPN, TLS, Known Attackers, Crawler Impersonation, CTR Anomaly
 * Does NOT handle: TOR (handled by TorSystem), 404 (handled by Advanced404)
 */
class Detection {
    
    private Config $config;
    private Utilities $utilities;
    private Database $database;
    
    /**
     * Cache for scoring configuration
     *
     * @var array<string, int>
     */
    private array $score_config = [];
    
    /**
     * Cache for country lookups
     *
     * @var array<string, string|null>
     */
    private static array $country_cache = [];
    
    /**
     * Cache for IP validation results
     *
     * @var array<string, bool>
     */
    private static array $valid_ip_cache = [];
    
    /**
     * Cached high-risk country map for O(1) lookups
     *
     * @var array<string, bool>|null
     */
    private static ?array $high_risk_country_map = null;
    
    /**
     * Cache for crawler impersonation detection
     *
     * @var array<string, bool>
     */
    private static array $crawler_impersonation_cache = [];
    
    /**
     * Runtime cache limits (prevent memory leaks)
     */
    private const MAX_RUNTIME_CACHE_SIZE = 500;
    private const CACHE_TRIM_AMOUNT = 100;
    
    private const CACHE_COUNTRY_TTL = WEEK_IN_SECONDS;
    private const CTR_TRACKING_TTL = 3600; // 1 hour
    
    private const MAX_SCORE = 10;
    private const MIN_SCORE = 0;
    
    public function __construct(Utilities $utilities, Database $database) {
        $this->config = Config::get_instance();
        $this->utilities = $utilities;
        $this->database = $database;
        
        $this->preload_score_config();
    }
    
    /**
     * Preload score configuration for performance
     *
     * @return void
     */
    private function preload_score_config(): void {
        $keys = [
            'score_empty_ua',
            'score_suspicious_ua',
            'score_crawler_impersonation',
            'score_proxy',
            'score_suspicious_headers',
            'score_high_risk_country',
            'score_weak_tls',
            'score_known_attacker',
            'score_high_rate',
            'score_ctr_anomaly',
        ];
        
        foreach ($keys as $key) {
            $this->score_config[$key] = $this->config->get_int($key, $this->get_default_score($key));
        }
    }
    
    /**
     * Get default score for configuration key
     *
     * @param string $key
     * @return int
     */
    private function get_default_score(string $key): int {
        $defaults = [
            'score_empty_ua' => 3,
            'score_suspicious_ua' => 2,
            'score_crawler_impersonation' => 9,
            'score_proxy' => 3,
            'score_suspicious_headers' => 2,
            'score_high_risk_country' => 4,
            'score_weak_tls' => 3,
            'score_known_attacker' => 8,
            'score_high_rate' => 3,
            'score_ctr_anomaly' => 3,
        ];
        
        return $defaults[$key] ?? 0;
    }
    
    /**
     * Process request and detect threats
     *
     * @param string $ip IP address to analyze
     * @return array Detection result with score and factors
     */
    public function process_request(string $ip): array {
        static $result_cache = [];
        
        // Check master switch
        if (!$this->config->get_bool('enable_bot_detection', true)) {
            return [
                'score' => 0,
                'factors' => [esc_html__('Bot detection disabled', 'botfend-anti-bot-firewall')],
                'metadata' => ['detection_disabled' => true]
            ];
        }
        
        $this->manage_static_cache_size($result_cache);
        
        $cache_key = md5($ip);
        
        if (isset($result_cache[$cache_key])) {
            return $result_cache[$cache_key];
        }
        
        if (!$this->is_valid_ip($ip)) {
            $result = [
                'score' => 0,
                'factors' => [esc_html__('Invalid IP address', 'botfend-anti-bot-firewall')],
                'metadata' => ['is_valid' => false]
            ];
            $result_cache[$cache_key] = $result;
            return $result;
        }
        
        $result = $this->calculate_detection_score($ip);
        
        $result_cache[$cache_key] = $result;
        return $result;
    }
    
    /**
     * Manage static cache size to prevent memory leaks
     *
     * @param array &$cache
     * @return void
     */
    private function manage_static_cache_size(array &$cache): void {
        if (count($cache) >= self::MAX_RUNTIME_CACHE_SIZE) {
            $cache = array_slice($cache, self::CACHE_TRIM_AMOUNT, null, true);
        }
    }
    
    /**
     * Calculate detection score based on various factors
     *
     * @param string $ip IP address
     * @return array Detection result
     */
    public function calculate_detection_score(string $ip): array {
        static $user_agent = null;
        static $suspicious_ua = null;
        static $request_uri = null;
        
        if (null === $user_agent) {
            $user_agent = $this->utilities->get_user_agent();
            $suspicious_ua = $user_agent && $this->utilities->is_suspicious_ua($user_agent);
            $request_uri = isset($_SERVER['REQUEST_URI']) ? 
                sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])) : '';
        }
        
        $score = 0;
        $factors = [];
        $metadata = [];
        
        // Run all detection modules
        $score += $this->accumulate_user_agent_score($user_agent, (bool)$suspicious_ua, $factors);
        $score += $this->accumulate_crawler_impersonation_score($ip, $user_agent, $factors);
        $score += $this->accumulate_proxy_score($ip, $factors, $metadata);
        $score += $this->accumulate_header_score($factors);
        $score += $this->accumulate_geographic_score($ip, $factors);
        $score += $this->accumulate_weak_tls_score($factors);
        $score += $this->accumulate_known_attacker_score($ip, $factors);
        $score += $this->accumulate_rate_limiting_score($ip, $factors);
        $score += $this->accumulate_ctr_anomaly_score($ip, $request_uri, $user_agent, $factors);
        
        $score = min(self::MAX_SCORE, max(self::MIN_SCORE, $score));
        
        // Check thresholds for action recommendation
        $auto_block_score = $this->config->get_int('auto_block_score', 8);
        $challenge_score = $this->config->get_int('challenge_score', 6);
        
        $action = 'allowed';
        $blocked = false;
        $challenged = false;
        
        if ($score >= $auto_block_score) {
            $action = 'blocked';
            $blocked = true;
        } elseif ($score >= $challenge_score) {
            $action = 'challenged';
            $challenged = true;
        }
        
        return [
            'score' => $score,
            'factors' => $factors,
            'metadata' => $metadata,
            'action' => $action,
            'blocked' => $blocked,
            'challenged' => $challenged,
        ];
    }
    
    /**
     * Accumulate score from User-Agent analysis
     *
     * @param string|null $user_agent
     * @param bool $suspicious_ua
     * @param array &$factors
     * @return int
     */
    private function accumulate_user_agent_score(?string $user_agent, bool $suspicious_ua, array &$factors): int {
        $score = 0;
        
        if (empty($user_agent)) {
            $factors[] = esc_html__('Empty User-Agent', 'botfend-anti-bot-firewall');
            $score += $this->score_config['score_empty_ua'];
        } elseif ($suspicious_ua) {
            $factors[] = esc_html__('Suspicious User-Agent', 'botfend-anti-bot-firewall');
            $score += $this->score_config['score_suspicious_ua'];
        }
        
        return $score;
    }
    
    /**
     * Accumulate score from crawler impersonation detection
     * Detects when User-Agent claims to be a crawler but IP doesn't match
     *
     * @param string $ip
     * @param string|null $user_agent
     * @param array &$factors
     * @return int
     */
    private function accumulate_crawler_impersonation_score(string $ip, ?string $user_agent, array &$factors): int {
        if (empty($user_agent)) {
            return 0;
        }
        
        $cache_key = md5($ip . '_' . $user_agent);
        
        if (isset(self::$crawler_impersonation_cache[$cache_key])) {
            if (self::$crawler_impersonation_cache[$cache_key]) {
                $factors[] = esc_html__('Crawler impersonation detected', 'botfend-anti-bot-firewall');
                return $this->score_config['score_crawler_impersonation'];
            }
            return 0;
        }
        
        $this->manage_static_cache_size(self::$crawler_impersonation_cache);
        
        // Extract claimed crawler from UA
        $crawler_name = $this->utilities->extract_crawler_from_ua($user_agent);
        
        if ($crawler_name) {
            // Verify if IP actually belongs to that crawler
            $ip_verified = $this->utilities->verify_crawler_ip($ip, $crawler_name);
            
            if (!$ip_verified) {
                self::$crawler_impersonation_cache[$cache_key] = true;
                $factors[] = sprintf(
                    esc_html__('Crawler impersonation detected: claimed %s but IP mismatch', 'botfend-anti-bot-firewall'),
                    esc_html($crawler_name)
                );
                return $this->score_config['score_crawler_impersonation'];
            }
        }
        
        self::$crawler_impersonation_cache[$cache_key] = false;
        return 0;
    }
    
    /**
     * Accumulate score from proxy detection
     *
     * @param string $ip
     * @param array &$factors
     * @param array &$metadata
     * @return int
     */
    private function accumulate_proxy_score(string $ip, array &$factors, array &$metadata): int {
        if ($this->config->get_bool('block_proxy', true) && $this->utilities->is_proxy($ip)) {
            $factors[] = esc_html__('Proxy/VPN detected', 'botfend-anti-bot-firewall');
            $metadata['is_proxy'] = true;
            return $this->score_config['score_proxy'];
        }
        
        return 0;
    }
    
    /**
     * Accumulate score from suspicious headers
     *
     * @param array &$factors
     * @return int
     */
    private function accumulate_header_score(array &$factors): int {
        if ($this->utilities->has_suspicious_headers()) {
            $factors[] = esc_html__('Suspicious headers', 'botfend-anti-bot-firewall');
            return $this->score_config['score_suspicious_headers'];
        }
        
        return 0;
    }
    
    /**
     * Accumulate score from geographic location
     * PERFORMANCE: Uses O(1) hash map lookup synced from Utilities
     * PRIORITY: Blocked countries take precedence over high-risk countries
     *
     * @param string $ip
     * @param array &$factors
     * @return int
     */
    private function accumulate_geographic_score(string $ip, array &$factors): int {
        // CRITICAL: Geolocation requires opt-in (disabled by default)
        if (!$this->config->get_bool('enable_geolocation', false)) {
            return 0;
        }
        
        $country = $this->get_country_cached($ip);
        
        if (!$country) {
            return 0;
        }
        
        // PRIORITY 1: Check blocked countries FIRST (immediate block takes precedence)
        if ($this->utilities->is_country_blocked($country)) {
            $factors[] = sprintf(
                esc_html__('Blocked country: %s', 'botfend-anti-bot-firewall'),
                esc_html($country)
            );
            // Blocking is handled by the action system, no score added here
            return 0;
        }
        
        // PRIORITY 2: Check high-risk country (adds to score)
        if ($this->is_high_risk_country($country)) {
            $score = $this->utilities->get_high_risk_country_score();
            $factors[] = sprintf(
                esc_html__('High-risk country (+%d score): %s', 'botfend-anti-bot-firewall'),
                $score,
                esc_html($country)
            );
            return $score;
        }
        
        return 0;
    }
    
    /**
     * Check if country is high-risk using O(1) hash map
     * Synced with Utilities as the single source of truth
     *
     * @param string $country Country code
     * @return bool
     */
    private function is_high_risk_country(string $country): bool {
        if (empty($country)) {
            return false;
        }
        
        // Lazy load the high-risk country map from Utilities
        if (self::$high_risk_country_map === null) {
            $this->sync_high_risk_countries_from_utilities();
        }
        
        return isset(self::$high_risk_country_map[strtoupper($country)]);
    }
    
    /**
     * Sync high-risk country list from Utilities (single source of truth)
     * Converts array to O(1) hash map for performance
     *
     * @return void
     */
    private function sync_high_risk_countries_from_utilities(): void {
        // Get the authoritative list from Utilities
        $high_risk_array = $this->utilities->get_high_risk_countries();
        
        // Convert to O(1) hash map for isset() lookups
        self::$high_risk_country_map = [];
        foreach ($high_risk_array as $country) {
            self::$high_risk_country_map[strtoupper($country)] = true;
        }
    }
    
    /**
     * Public method to get high-risk countries (delegates to Utilities)
     *
     * @return array List of high-risk country codes
     */
    public function get_high_risk_countries(): array {
        return $this->utilities->get_high_risk_countries();
    }
    
    /**
     * Refresh the cached high-risk country map
     * Call this after Utilities high-risk list changes
     *
     * @return void
     */
    public function refresh_high_risk_country_cache(): void {
        self::$high_risk_country_map = null;
        $this->sync_high_risk_countries_from_utilities();
    }
    
    /**
     * Accumulate score from weak TLS detection
     *
     * @param array &$factors
     * @return int
     */
    private function accumulate_weak_tls_score(array &$factors): int {
        if (!$this->config->get_bool('enable_tls_detection', true)) {
            return 0;
        }
        
        if (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] !== 'on') {
            return 0;
        }
        
        $score = 0;
        $tls_version = $this->detect_tls_version();
        
        switch ($tls_version) {
            case 'TLSv1.0':
            case 'TLSv1.1':
                $score += $this->score_config['score_weak_tls'];
                $factors[] = sprintf(
                    esc_html__('Outdated TLS version: %s', 'botfend-anti-bot-firewall'),
                    esc_html($tls_version)
                );
                break;
            case 'SSLv3':
                $score += $this->score_config['score_weak_tls'] + 2;
                $factors[] = esc_html__('SSLv3 detected - known vulnerability (POODLE)', 'botfend-anti-bot-firewall');
                break;
            case 'SSLv2':
                $score += $this->score_config['score_weak_tls'] + 3;
                $factors[] = esc_html__('SSLv2 detected - extremely outdated and insecure', 'botfend-anti-bot-firewall');
                break;
        }
        
        if ($score > 0 && $this->is_modern_browser_ua()) {
            $score += 2;
            $factors[] = esc_html__('Modern browser User-Agent with outdated TLS - possible impersonation', 'botfend-anti-bot-firewall');
        }
        
        return $score;
    }
    
    /**
     * Detect TLS version from server variables
     *
     * @return string
     */
    private function detect_tls_version(): string {
        if (isset($_SERVER['SSL_PROTOCOL'])) {
            return sanitize_text_field(wp_unslash($_SERVER['SSL_PROTOCOL']));
        }
        
        if (isset($_SERVER['SSL_TLS_VERSION'])) {
            return sanitize_text_field(wp_unslash($_SERVER['SSL_TLS_VERSION']));
        }
        
        if (isset($_SERVER['HTTP_SSL_PROTOCOL'])) {
            return sanitize_text_field(wp_unslash($_SERVER['HTTP_SSL_PROTOCOL']));
        }
        
        return 'unknown';
    }
    
    /**
     * Check if User-Agent looks like a modern browser
     *
     * @return bool
     */
    private function is_modern_browser_ua(): bool {
        $user_agent = $this->utilities->get_user_agent();
        if (empty($user_agent)) {
            return false;
        }
        
        $modern_patterns = [
            'Chrome/[6-9][0-9]',
            'Chrome/1[0-9][0-9]',
            'Firefox/[6-9][0-9]',
            'Firefox/1[0-9][0-9]',
            'Safari/[1-9][0-9][0-9]',
            'Edg/[1-9][0-9][0-9]',
            'OPR/[6-9][0-9]',
        ];
        
        foreach ($modern_patterns as $pattern) {
            if (preg_match('/' . $pattern . '/i', $user_agent)) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * Accumulate score from known attacker detection
     *
     * @param string $ip
     * @param array &$factors
     * @return int
     */
    private function accumulate_known_attacker_score(string $ip, array &$factors): int {
        // CRITICAL: Threat intelligence is DISABLED BY DEFAULT (requires opt-in)
        if (!$this->config->get_bool('enable_threat_intel', false)) {
            return 0;
        }
        
        $score = 0;
        $sources = [];
        
        $recidivist_score = $this->check_recidivist_score($ip);
        if ($recidivist_score > 0) {
            $score += $recidivist_score;
            $sources[] = 'repeat_offender';
        }
        
        $threat_score = $this->check_local_threat_database($ip);
        if ($threat_score > 0) {
            $score += $threat_score;
            $sources[] = 'threat_database';
        }
        
        if (!empty($sources)) {
            $factors[] = sprintf(
                esc_html__('Known malicious source detected: %s', 'botfend-anti-bot-firewall'),
                esc_html(implode(', ', $sources))
            );
        }
        
        return min($score, $this->score_config['score_known_attacker']);
    }
    
    /**
     * Check recidivist score based on violation count
     * PERFORMANCE: Uses static cache to prevent repeated database calls
     *
     * @param string $ip
     * @return int
     */
    private function check_recidivist_score(string $ip): int {
        static $recidivist_cache = [];
        
        $cache_key = md5($ip);
        
        if (isset($recidivist_cache[$cache_key])) {
            return $recidivist_cache[$cache_key];
        }
        
        $this->manage_static_cache_size($recidivist_cache);
        
        if (!$this->database || !method_exists($this->database, 'get_violation_count')) {
            $recidivist_cache[$cache_key] = 0;
            return 0;
        }
        
        $count = $this->database->get_violation_count($ip, 30);
        
        if ($count > 50) {
            $score = 8;
        } elseif ($count > 20) {
            $score = 6;
        } elseif ($count > 10) {
            $score = 4;
        } elseif ($count > 5) {
            $score = 2;
        } else {
            $score = 0;
        }
        
        $recidivist_cache[$cache_key] = $score;
        return $score;
    }
    
    /**
     * Check local threat database for known threats
     * PERFORMANCE: Static cache to prevent multiple get_option() calls
     *
     * @param string $ip
     * @return int
     */
    private function check_local_threat_database(string $ip): int {
        static $threats = null;
        
        // Only load the heavy option once per request lifecycle
        if ($threats === null) {
            $threats = get_option('botfend_threat_database', []);
        }
        
        $ip_hash = md5($ip);
        
        if (isset($threats[$ip_hash])) {
            $threat = $threats[$ip_hash];
            $age_hours = (time() - $threat['last_seen']) / 3600;
            
            $decay = max(0, 1 - ($age_hours / 720));
            $score = ceil($threat['severity'] * $decay);
            
            return min((int)$score, 8);
        }
        
        return 0;
    }
    
    /**
     * Accumulate score from rate limiting
     *
     * @param string $ip
     * @param array &$factors
     * @return int
     */
    private function accumulate_rate_limiting_score(string $ip, array &$factors): int {
        if ($this->safe_database_call('is_rate_limited', $ip, false)) {
            $factors[] = esc_html__('High request rate', 'botfend-anti-bot-firewall');
            return $this->score_config['score_high_rate'];
        }
        
        return 0;
    }
    
    /**
     * Accumulate score from CTR anomaly detection
     * Detects view/impression bots and click fraud
     *
     * @param string $ip
     * @param string $request_uri
     * @param string|null $user_agent
     * @param array &$factors
     * @return int
     */
    private function accumulate_ctr_anomaly_score(string $ip, string $request_uri, ?string $user_agent, array &$factors): int {
        // Only track GET requests to frontend pages
        if ($_SERVER['REQUEST_METHOD'] !== 'GET' || is_admin() || wp_doing_ajax() || defined('REST_REQUEST')) {
            return 0;
        }
        
        $tracking_key = 'botfend_ctr_' . md5($ip);
        $tracking = get_transient($tracking_key) ?: [
            'requests' => 0,
            'unique_urls' => [],
            'first_seen' => time(),
            'user_agents' => [],
        ];
        
        $tracking['requests']++;
        $tracking['unique_urls'][md5($request_uri)] = true;
        
        if ($user_agent) {
            $tracking['user_agents'][md5($user_agent)] = true;
        }
        
        set_transient($tracking_key, $tracking, self::CTR_TRACKING_TTL);
        
        $score = 0;
        
        // Check for excessive requests in short time (bot-like behavior)
        $requests_per_minute = $tracking['requests'] / max(1, (time() - $tracking['first_seen']) / 60);
        
        if ($requests_per_minute > 30) {
            $factors[] = sprintf(
                esc_html__('CTR anomaly: %.1f requests/minute (bot-like behavior)', 'botfend-anti-bot-firewall'),
                round($requests_per_minute, 1)
            );
            $score += $this->score_config['score_ctr_anomaly'];
        }
        
        // Check for single URL hammering (view bot)
        if ($tracking['requests'] > 20 && count($tracking['unique_urls']) === 1) {
            $factors[] = esc_html__('CTR anomaly: Single URL hammering (view bot)', 'botfend-anti-bot-firewall');
            $score += $this->score_config['score_ctr_anomaly'];
        }
        
        // Check for User-Agent rotation (sophisticated bot)
        if (count($tracking['user_agents']) > 3 && $tracking['requests'] > 10) {
            $factors[] = esc_html__('CTR anomaly: User-Agent rotation detected', 'botfend-anti-bot-firewall');
            $score += $this->score_config['score_ctr_anomaly'];
        }
        
        return $score;
    }
    
    /**
     * Get country with caching
     * Note: Geolocation opt-in is checked in accumulate_geographic_score
     *
     * @param string $ip
     * @return string|null
     */
    private function get_country_cached(string $ip): ?string {
        $cache_key = md5($ip);
        
        if (isset(self::$country_cache[$cache_key])) {
            return self::$country_cache[$cache_key];
        }
        
        $this->manage_static_cache_size(self::$country_cache);
        
        $transient_key = 'botfend_country_' . $cache_key;
        $cached = get_transient($transient_key);
        
        if (false !== $cached && $cached !== 'N/A') {
            self::$country_cache[$cache_key] = $cached;
            return $cached;
        }
        
        try {
            $country = sanitize_text_field($this->utilities->get_country($ip));
        } catch (\Exception $e) {
            $this->log_debug('Country lookup failed', [
                'ip' => $ip,
                'error' => $e->getMessage()
            ]);
            $country = null;
        }
        
        $cache_value = $country ?: 'N/A';
        self::$country_cache[$cache_key] = $cache_value;
        set_transient($transient_key, $cache_value, self::CACHE_COUNTRY_TTL);
        
        return $country;
    }
    
    /**
     * Safely invoke a method on the database object
     *
     * @param string $method  Method name to call
     * @param string $ip      The primary IP argument
     * @param mixed  $default Fallback value if call fails
     * @return mixed
     */
    private function safe_database_call(string $method, string $ip, $default = false) {
        if ($this->database && method_exists($this->database, $method)) {
            try {
                return $this->database->$method($ip);
            } catch (\ArgumentCountError $e) {
                $this->log_debug("Database method {$method} failed due to mismatched arguments", [
                    'method' => $method,
                    'error' => $e->getMessage()
                ]);
                return $default;
            } catch (\Exception $e) {
                $this->log_debug("Database method {$method} failed", [
                    'ip' => $ip,
                    'error' => $e->getMessage()
                ]);
                return $default;
            }
        }
        
        return $default;
    }
    
    /**
     * Log debug message
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    private function log_debug(string $message, array $context = []): void {
        if (defined('WP_DEBUG') && WP_DEBUG && $this->config->get_bool('debug_mode', false)) {
            $log_message = sprintf(
                '[BotFend Detection] %s | Context: %s',
                $message,
                wp_json_encode($context)
            );
            error_log($log_message);
        }
    }
    
    /**
     * Validate IP address format
     * PERFORMANCE: Removed redundant SQLi pattern loop - filter_var is sufficient
     *
     * @param string $ip
     * @return bool
     */
    private function is_valid_ip(string $ip): bool {
        $this->manage_static_cache_size(self::$valid_ip_cache);
        
        if (isset(self::$valid_ip_cache[$ip])) {
            return self::$valid_ip_cache[$ip];
        }
        
        if (empty($ip) || !is_string($ip)) {
            self::$valid_ip_cache[$ip] = false;
            return false;
        }
        
        // Use PHP's built-in filter first
        if (!filter_var($ip, FILTER_VALIDATE_IP)) {
            self::$valid_ip_cache[$ip] = false;
            return false;
        }

        // Block Octal/Hex encoded IPs that can bypass filter_var
        if (preg_match('/^0[0-9]+|0x[a-fA-F0-9]+/i', $ip)) {
            self::$valid_ip_cache[$ip] = false;
            return false;
        }
        
        self::$valid_ip_cache[$ip] = true;
        return true;
    }
    
    /**
     * Get cache stats for debugging
     *
     * @return array
     */
    public function get_cache_stats(): array {
        return [
            'runtime' => [
                'country_cache_size' => count(self::$country_cache),
                'valid_ip_cache_size' => count(self::$valid_ip_cache),
                'crawler_impersonation_cache_size' => count(self::$crawler_impersonation_cache),
            ],
            'config' => [
                'score_config_loaded' => count($this->score_config),
                'config_keys' => array_keys($this->score_config),
            ],
            'high_risk' => [
                'map_loaded' => self::$high_risk_country_map !== null,
                'country_count' => self::$high_risk_country_map ? count(self::$high_risk_country_map) : 0,
                'source' => 'Utilities (synced)',
            ],
            'thresholds' => [
                'auto_block_score' => $this->config->get_int('auto_block_score', 8),
                'challenge_score' => $this->config->get_int('challenge_score', 6),
            ],
        ];
    }
    
    /**
     * Clear all runtime caches
     *
     * @return void
     */
    public static function clear_runtime_caches(): void {
        self::$country_cache = [];
        self::$valid_ip_cache = [];
        self::$high_risk_country_map = null;
        self::$crawler_impersonation_cache = [];
    }
    
    /**
     * Clear IP transients
     *
     * @param string $ip
     * @return void
     */
    public function clear_ip_transients(string $ip): void {
        $cache_key = md5($ip);
        delete_transient('botfend_country_' . $cache_key);
        delete_transient('botfend_ctr_' . $cache_key);
    }
}