<?php
/**
 * Working connection fingerprinting for BotFend Anti-Bot Firewall
 * Maintains actual functionality with reduced complexity
 *
 * @package BotFend\Security
 * @version 4.8.3 - Rebranded from SafeBot, fixed WPDB LIKE wildcard escaping
 */

namespace BotFend\Security;

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

defined('ABSPATH') || exit;

/**
 * Class ConnectionFingerprint
 *
 * Handles connection fingerprinting for bot detection
 */
class ConnectionFingerprint {
    
    private $config;
    private $utilities;
    private $database;
    private $current_fingerprint_cache = null;
    private static $compiled_patterns = null;
    private static $table_exists_cache = [];
    
    const MAX_SCORE = 10;
    const CACHE_TTL = 300;
    
    public function __construct(Utilities $utilities, Database $database) {
        $this->config = Config::get_instance();
        $this->utilities = $utilities;
        $this->database = $database;
        
        if ($this->is_enabled()) {
            add_action('init', [$this, 'check_fingerprint'], 5);
            
            if (!wp_next_scheduled('botfend_fingerprint_cleanup')) {
                wp_schedule_event(time(), 'weekly', 'botfend_fingerprint_cleanup');
            }
            
            add_action('botfend_fingerprint_cleanup', [$this, 'cleanup_old_data']);
            
            if (is_admin()) {
                add_action('admin_init', [$this, 'verify_cleanup_schedule']);
            }
        }
    }
    
    public function verify_cleanup_schedule() {
        if (!wp_next_scheduled('botfend_fingerprint_cleanup')) {
            wp_schedule_event(time(), 'weekly', 'botfend_fingerprint_cleanup');
        }
    }
    
    public function check_fingerprint() {
        if (null !== $this->current_fingerprint_cache) {
            return;
        }
        
        if (is_admin() || wp_doing_ajax()) {
            return;
        }
        
        if (is_user_logged_in() && current_user_can('manage_options')) {
            return;
        }
        
        $ip = $this->utilities->get_client_ip();
        
        if ($this->utilities->is_ip_whitelisted($ip) || 
            $this->database->is_ip_blocked($ip)) {
            return;
        }
        
        $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? 
                     sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])) : '';
        
        if ($this->utilities->verify_legitimate_crawler($ip, $user_agent)) {
            return; 
        }
        
        try {
            $fingerprint = $this->generate_fingerprint();
            
            if ($fingerprint) {
                $suspicion_score = $this->analyze_fingerprint($fingerprint);
                
                if ($suspicion_score >= 6) {
                    $this->handle_suspicious_fingerprint($ip, $fingerprint, $suspicion_score);
                }
                
                $this->store_fingerprint($ip, $fingerprint);
            }
            
            $this->current_fingerprint_cache = $fingerprint;
        } catch (\Exception $e) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                error_log(sprintf(
                    'BotFend fingerprint error: %s',
                    $e->getMessage()
                ));
            }
        }
    }
    
    private function generate_fingerprint() {
        $components = [];
        
        if (isset($_SERVER['HTTP_USER_AGENT'])) {
            $ua = sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT']));
            $components['ua'] = substr($ua, 0, 100);
        }
        
        if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
            $lang = sanitize_text_field(wp_unslash($_SERVER['HTTP_ACCEPT_LANGUAGE']));
            $components['lang'] = substr($lang, 0, 10);
        }
        
        if (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) {
            $encoding = sanitize_text_field(wp_unslash($_SERVER['HTTP_ACCEPT_ENCODING']));
            $components['encoding'] = substr($encoding, 0, 50);
        }
        
        $components['proxy'] = $this->detect_proxy_headers();
        
        $headers = $this->get_all_headers_safe();
        $components['header_count'] = is_array($headers) ? count($headers) : 0;
        
        if (isset($_SERVER['HTTP_CONNECTION'])) {
            $conn = sanitize_text_field(wp_unslash($_SERVER['HTTP_CONNECTION']));
            $components['connection'] = strtolower($conn);
        }
        
        $components['server_signals'] = [
            'protocol' => $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.1',
            'method' => $_SERVER['REQUEST_METHOD'] ?? 'GET',
            'port' => (int) ($_SERVER['SERVER_PORT'] ?? 80),
            'ssl_cipher' => $_SERVER['SSL_CIPHER'] ?? null,
            'ssl_protocol' => $_SERVER['SSL_PROTOCOL'] ?? null,
            'request_time_float' => $_SERVER['REQUEST_TIME_FLOAT'] ?? microtime(true),
        ];
        
        $components['timing'] = [
            'request_interval' => $this->get_request_interval(),
            'processing_delay' => microtime(true) - ($_SERVER['REQUEST_TIME_FLOAT'] ?? microtime(true))
        ];
        
        $components['ja3'] = null;
        
        return hash('sha256', wp_json_encode($components));
    }
    
    private function get_all_headers_safe() {
        if (function_exists('getallheaders')) {
            return getallheaders();
        } elseif (function_exists('http_get_request_headers')) {
            return http_get_request_headers();
        } else {
            $headers = [];
            foreach ($_SERVER as $name => $value) {
                if (strpos($name, 'HTTP_') === 0) {
                    $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
                }
            }
            return $headers;
        }
    }
    
    private function detect_proxy_headers() {
        $proxy_indicators = 0;
        $proxy_headers = [
            'HTTP_VIA', 
            'HTTP_X_FORWARDED_FOR', 
            'HTTP_X_FORWARDED',
            'HTTP_CLIENT_IP',
            'HTTP_X_REAL_IP',
            'HTTP_CF_CONNECTING_IP'
        ];
        
        foreach ($proxy_headers as $header) {
            if (isset($_SERVER[$header])) {
                $weight = in_array($header, ['HTTP_CF_CONNECTING_IP'], true) ? 0.5 : 1;
                $proxy_indicators += $weight;
            }
        }
        
        return (int) round($proxy_indicators);
    }
    
    private function get_request_interval() {
        global $wpdb;
        $table = $wpdb->prefix . 'botfend_fingerprints';
        $ip = $this->utilities->get_client_ip();
        
        if (!$this->table_exists('botfend_fingerprints')) {
            return PHP_INT_MAX;
        }
        
        $last_request = $wpdb->get_var($wpdb->prepare(
            "SELECT created_at FROM `{$table}` WHERE ip = %s ORDER BY created_at DESC LIMIT 1",
            $ip
        ));
        
        if ($last_request) {
            return time() - strtotime($last_request);
        }
        return PHP_INT_MAX;
    }
    
    private function analyze_fingerprint($fingerprint_hash) {
        $score = 0;
        
        if (!isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
            $score += 2;
        }
        
        if (!isset($_SERVER['HTTP_ACCEPT_ENCODING'])) {
            $score += 1;
        }
        
        if (!isset($_SERVER['HTTP_ACCEPT'])) {
            $score += 1;
        }
        
        $headers = $this->get_all_headers_safe();
        $header_count = is_array($headers) ? count($headers) : 0;
        
        if ($header_count < 5) {
            $score += 2;
        } elseif ($header_count > 25) {
            $score += 1;
        }
        
        $proxy_count = $this->detect_proxy_headers();
        if ($proxy_count > 1) {
            $score += $proxy_count;
        }
        
        if (isset($_SERVER['HTTP_USER_AGENT'])) {
            $ua = strtolower(sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])));
            $ip = $this->utilities->get_client_ip();
            
            if ($this->utilities->verify_legitimate_crawler($ip, $ua)) {
                $score = max(0, $score - 5);
                return $score;
            }
            
            $suspicious_ua = [
                'curl', 'wget', 'python', 'java/', 'go-http', 'httpclient',
                'libwww', 'lwp', 'scrapy', 'mechanize', 'phantomjs', 'selenium'
            ];
            
            foreach ($suspicious_ua as $pattern) {
                if (strpos($ua, $pattern) !== false) {
                    $score += 2;
                    break;
                }
            }
            
            $browsers = ['chrome', 'firefox', 'safari', 'edge', 'opera', 'msie', 'trident'];
            $has_browser = false;
            foreach ($browsers as $browser) {
                if (strpos($ua, $browser) !== false) {
                    $has_browser = true;
                    break;
                }
            }
            
            if (!$has_browser && strlen($ua) > 10) {
                $score += 2;
            }
        }
        
        $ip_count = $this->get_fingerprint_usage_count($fingerprint_hash);
        if ($ip_count > 2) {
            $score += min(3, $ip_count);
        }
        
        $score += $this->analyze_timing_patterns($this->get_request_interval());
        $score += $this->analyze_connection_behavior();
        $score += $this->analyze_ssl_context();
        
        return min(self::MAX_SCORE, max(0, $score));
    }
    
    private function analyze_timing_patterns($interval) {
        if ($interval < 0.5) return 3;
        if ($interval < 1) return 2;
        if ($interval < 3) return 1;
        if ($interval < 5) return 0;
        return 0;
    }
    
    private function analyze_connection_behavior() {
        $score = 0;
        
        $connection_header = $_SERVER['HTTP_CONNECTION'] ?? '';
        $http_version = $_SERVER['SERVER_PROTOCOL'] ?? '';
        
        if (strpos($http_version, 'HTTP/2') === 0 && stripos($connection_header, 'keep-alive') !== false) {
            $score += 2;
        }
        
        return $score;
    }
    
    private function analyze_ssl_context() {
        if (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] !== 'on') {
            return 0;
        }
        
        if (!isset($_SERVER['SSL_CIPHER']) && !empty($_SERVER['HTTP_USER_AGENT'])) {
            return 1;
        }
        
        return 0;
    }
    
    private function get_fingerprint_usage_count($fingerprint_hash) {
        global $wpdb;
        
        $table = $wpdb->prefix . 'botfend_fingerprints';
        
        if (!$this->table_exists('botfend_fingerprints')) {
            return 0;
        }
        
        $cache_key = 'fp_usage_' . md5($fingerprint_hash);
        $count = wp_cache_get($cache_key, 'botfend_defender');
        
        if (false !== $count) {
            return absint($count);
        }
        
        $count = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(DISTINCT ip) 
            FROM `{$table}` 
            WHERE fingerprint_hash = %s 
            AND created_at >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 1 HOUR)",
            sanitize_text_field($fingerprint_hash)
        ));
        
        $count = absint($count ?: 0);
        wp_cache_set($cache_key, $count, 'botfend_defender', 300);
        
        return $count;
    }
    
    private function handle_suspicious_fingerprint($ip, $fingerprint_hash, $score) {
        $country = $this->utilities->get_country($ip);
        $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? 
                     sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])) : '';
        
        $short_hash = substr(sanitize_text_field($fingerprint_hash), 0, 12);
        
        $log_message = esc_html(sprintf(
            esc_html__('Suspicious connection fingerprint (Score: %1$d, Hash: %2$s)', 'botfend-anti-bot-firewall'),
            absint($score),
            $short_hash
        ));
        
        $this->database->log_request(
            sanitize_text_field($ip),
            absint($score),
            'suspicious_fingerprint',
            [$log_message],
            sanitize_text_field($user_agent),
            false,
            sanitize_text_field($country)
        );
        
        if ($score >= 8) {
            $threat_key = 'botfend_threat_' . md5($ip);
            $current_threat = absint(get_transient($threat_key) ?: 0);
            $current_threat += 2;
            set_transient($threat_key, $current_threat, 3600);
        }
    }
    
    private function store_fingerprint($ip, $fingerprint_hash) {
        $rate_key = 'botfend_fp_store_' . md5($ip);
        if (false !== get_transient($rate_key)) {
            return;
        }
        set_transient($rate_key, true, 300);
        
        global $wpdb;
        $table = $wpdb->prefix . 'botfend_fingerprints';
        
        if (!$this->table_exists('botfend_fingerprints')) {
            return;
        }
        
        $exists = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM `{$table}` 
            WHERE ip = %s AND fingerprint_hash = %s AND created_at >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 1 DAY)",
            sanitize_text_field($ip),
            sanitize_text_field($fingerprint_hash)
        ));
        
        if (!$exists) {
            $result = $wpdb->insert($table, [
                'ip' => sanitize_text_field($ip),
                'fingerprint_hash' => sanitize_text_field($fingerprint_hash),
                'user_agent' => isset($_SERVER['HTTP_USER_AGENT']) ? 
                               substr(sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])), 0, 255) : '',
                'country' => sanitize_text_field($this->utilities->get_country($ip)),
                'created_at' => current_time('mysql', true)
            ], [
                '%s', '%s', '%s', '%s', '%s'
            ]);
        }
    }
    
    private function table_exists($table_name) {
        global $wpdb;
        $full_name = $wpdb->prefix . $table_name;
        
        if (isset(self::$table_exists_cache[$full_name])) {
            return self::$table_exists_cache[$full_name];
        }
        
        $cache_key = 'table_exists_' . $table_name;
        $exists = wp_cache_get($cache_key, 'botfend_defender');
        
        if (false !== $exists) {
            self::$table_exists_cache[$full_name] = $exists;
            return $exists;
        }
        
        // FIXED: Escaped the wildcards in the table name prefix for the LIKE clause
        $exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $wpdb->esc_like($full_name))) === $full_name;
        
        wp_cache_set($cache_key, $exists, 'botfend_defender', 300);
        self::$table_exists_cache[$full_name] = $exists;
        
        return $exists;
    }
    
    public static function create_fingerprint_table() {
        global $wpdb;
        
        $table = $wpdb->prefix . 'botfend_fingerprints';
        
        // FIXED: Escaped the wildcards in the table name prefix for the LIKE clause
        if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $wpdb->esc_like($table))) === $table) {
            return true;
        }
        
        $charset_collate = $wpdb->get_charset_collate();
        
        $sql = "CREATE TABLE `{$table}` (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            ip varchar(45) NOT NULL,
            fingerprint_hash varchar(64) NOT NULL,
            user_agent varchar(255) DEFAULT NULL,
            country varchar(2) DEFAULT NULL,
            created_at datetime NOT NULL,
            PRIMARY KEY  (id),
            KEY ip (ip),
            KEY fingerprint_hash (fingerprint_hash),
            KEY created_at (created_at)
        ) {$charset_collate};";
        
        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
        dbDelta($sql);
        
        return true;
    }
    
    public function install_table() {
        return self::create_fingerprint_table();
    }
    
    public function cleanup_old_data() {
        global $wpdb;
        
        $table = $wpdb->prefix . 'botfend_fingerprints';
        
        if (!$this->table_exists('botfend_fingerprints')) {
            return;
        }
        
        $result = $wpdb->query(
            "DELETE FROM `{$table}` WHERE created_at < DATE_SUB(UTC_TIMESTAMP(), INTERVAL 30 DAY)"
        );
        
        if (false === $result && defined('WP_DEBUG') && WP_DEBUG) {
            $error_msg = isset($wpdb->last_error) ? $wpdb->last_error : 'unknown';
            error_log(sprintf(
                'BotFend: Failed to cleanup old fingerprint data. Error: %s',
                $error_msg
            ));
        }
    }
    
    public function get_statistics($hours = 24) {
        global $wpdb;
        
        $table = $wpdb->prefix . 'botfend_fingerprints';
        
        if (!$this->table_exists('botfend_fingerprints')) {
            return [
                'enabled' => $this->is_enabled(),
                'table_exists' => false,
                'message' => esc_html__('Fingerprint table not created yet', 'botfend-anti-bot-firewall')
            ];
        }
        
        $cache_key = 'fp_stats_' . $hours;
        $cached = wp_cache_get($cache_key, 'botfend_defender');
        
        if (false !== $cached) {
            return $cached;
        }
        
        $total = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM `{$table}` WHERE created_at >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL %d HOUR)",
            absint($hours)
        ));
        
        $unique = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(DISTINCT fingerprint_hash) FROM `{$table}` WHERE created_at >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL %d HOUR)",
            absint($hours)
        ));
        
        $multi_ip = $wpdb->get_results($wpdb->prepare(
            "SELECT fingerprint_hash, COUNT(DISTINCT ip) as ip_count
            FROM `{$table}` 
            WHERE created_at >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL %d HOUR)
            GROUP BY fingerprint_hash 
            HAVING ip_count > 1
            ORDER BY ip_count DESC 
            LIMIT 5",
            absint($hours)
        ), ARRAY_A);
        
        $stats = [
            'enabled' => $this->is_enabled(),
            'table_exists' => true,
            'total_fingerprints' => absint($total ?: 0),
            'unique_fingerprints' => absint($unique ?: 0),
            'multi_ip_fingerprints' => $multi_ip ?: [],
            'cleanup_scheduled' => (bool) wp_next_scheduled('botfend_fingerprint_cleanup')
        ];
        
        wp_cache_set($cache_key, $stats, 'botfend_defender', 300);
        
        return $stats;
    }
    
    public function is_enabled() {
        return $this->config->get_bool('enable_connection_fp', true);
    }
    
    public static function clear_caches() {
        self::$compiled_patterns = null;
        self::$table_exists_cache = [];
        
        if (function_exists('wp_cache_flush_group')) {
            wp_cache_flush_group('botfend_defender');
        } else {
            wp_cache_delete('fp_stats_24', 'botfend_defender');
            wp_cache_delete('fp_stats_168', 'botfend_defender');
        }
    }
}