<?php
/**
 * YARA Scanner AJAX Handlers
 * 
 * @package VMPFence
 * @since 2.1.0
 */

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

class VMPFence_YARA_Scanner_Ajax {
    
    /**
     * Check if a path is absolute
     */
    private function path_is_absolute($path) {
        if (empty($path)) {
            return false;
        }
        // Windows: C:\ or \\server\share
        if (preg_match('/^[a-zA-Z]:\\\\/', $path) || substr($path, 0, 2) === '\\\\') {
            return true;
        }
        // Unix: /path
        if (substr($path, 0, 1) === '/') {
            return true;
        }
        return false;
    }
    
    public function __construct() {
        add_action('wp_ajax_vmpfence_yara_scan_file', array($this, 'scan_file'));
        add_action('wp_ajax_vmpfence_yara_scan_wp_file', array($this, 'scan_wp_file'));
        add_action('wp_ajax_vmpfence_get_wp_files', array($this, 'get_wp_files'));
        add_action('wp_ajax_vmpfence_get_yara_stats', array($this, 'get_stats'));
        add_action('wp_ajax_vmpfence_get_yara_history', array($this, 'get_history'));
        add_action('wp_ajax_vmpfence_get_yara_session_details', array($this, 'get_session_details'));
        add_action('wp_ajax_vmpfence_get_yara_server_stats', array($this, 'get_server_stats'));
        add_action('wp_ajax_vmpfence_yara_batch_scan', array($this, 'batch_scan'));
        add_action('wp_ajax_vmpfence_yara_batch_status', array($this, 'batch_status'));
        add_action('wp_ajax_vmpfence_yara_batch_results', array($this, 'batch_results'));
        add_action('wp_ajax_vmpfence_yara_cancel_batch', array($this, 'cancel_batch'));
        add_action('wp_ajax_vmpfence_yara_get_batch_sessions', array($this, 'get_batch_sessions'));
        add_action('wp_ajax_vmpfence_yara_whitelist_file', array($this, 'whitelist_file'));
        add_action('wp_ajax_vmpfence_yara_unwhitelist_file', array($this, 'unwhitelist_file'));
        add_action('wp_ajax_vmpfence_yara_delete_threat_file', array($this, 'delete_threat_file'));
        add_action('wp_ajax_vmpfence_yara_get_threats', array($this, 'get_threats'));
        add_action('wp_ajax_vmpfence_yara_get_whitelisted', array($this, 'get_whitelisted'));
        add_action('wp_ajax_vmpfence_yara_mark_fixed', array($this, 'mark_fixed'));
        add_action('wp_ajax_vmpfence_yara_count_total_files', array($this, 'count_total_files'));
        add_action('wp_ajax_vmpfence_integrity_pre_filter', array($this, 'integrity_pre_filter'));
        add_action('wp_ajax_vmpfence_detect_missing_plugins', array($this, 'detect_missing_plugins'));
        add_action('wp_ajax_vmpfence_download_plugin_checksum', array($this, 'download_plugin_checksum'));
        add_action('wp_ajax_vmpfence_yara_get_performance_metrics', array($this, 'get_performance_metrics'));
    }
    
    /**
     * Get performance metrics for scan time estimation
     */
    public function get_performance_metrics() {
        // Verify nonce
        if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'vmpfence_yara_scan')) {
            wp_send_json_error(array('message' => 'Security check failed'));
            return;
        }
        
        // Check permissions
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Unauthorized'));
            return;
        }
        
        $metrics = get_option('vmpfence_yara_performance_metrics', array(
            'total_scans' => 0,
            'total_scan_time' => 0,
            'total_file_size' => 0,
            'avg_seconds_per_mb' => 2.5, // Default baseline
            'last_updated' => current_time('mysql'),
            'samples' => array()
        ));
        
        wp_send_json_success($metrics);
    }
    
    /**
     * Scan uploaded file with YARA
     */
    public function scan_file() {
        // Verify nonce
        if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'vmpfence_yara_scan')) {
            wp_send_json_error(array('message' => 'Security check failed'));
            return;
        }
        
        // Check permissions
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Unauthorized'));
            return;
        }
        
        // Check premium license (use centralized getter)
        $license_type = VMPFence_License::get_license_type();
        $api_key = VMPFence_Config::get('central_api_key', '');
        
        if ($license_type !== VMPFence_Constants::LICENSE_PREMIUM || empty($api_key)) {
            wp_send_json_error(array('message' => 'Premium license required'));
            return;
        }
        
        try {
            $file_name = isset($_POST['file_name']) ? sanitize_text_field(wp_unslash($_POST['file_name'])) : '';
            $file_content = isset($_POST['file_content']) ? wp_unslash($_POST['file_content']) : '';
            $file_hash = isset($_POST['file_hash']) ? sanitize_text_field(wp_unslash($_POST['file_hash'])) : '';
            $scan_reason = isset($_POST['scan_reason']) ? sanitize_text_field(wp_unslash($_POST['scan_reason'])) : 'manual';
            
            if (empty($file_name) || empty($file_content)) {
                throw new Exception('File name and content are required');
            }
            
            // Calculate actual SHA256 hash
            $actual_hash = hash('sha256', base64_decode($file_content));
            
            // Call API client
            require_once VMPFENCE_PLUGIN_DIR . 'lib/class-api-client.php';
            $api_client = new VMPFence_API_Client();
            
            // Create temporary file for scanning
            $temp_file = tempnam(sys_get_temp_dir(), 'yara_');
            file_put_contents($temp_file, base64_decode($file_content));
            
            // Clear file stat cache so filesize() returns correct value
            clearstatcache(true, $temp_file);
            
            // Get file size for performance tracking
            $file_size = filesize($temp_file);
            
            $result = $api_client->yara_deep_scan($api_key, $temp_file, $scan_reason);
            
            // Add file size to result for history tracking
            if (!is_wp_error($result)) {
                $result['file_size_bytes'] = $file_size;
            }
            
            // Clean up temp file
            @unlink($temp_file);
            
            if (is_wp_error($result)) {
                throw new Exception($result->get_error_message());
            }
            
            // Save to history
            $save_result = $this->save_to_history($file_name, $actual_hash, $result);
            
            // Update stats
            $this->update_stats($result);
            
            // Add debug info
            $result['debug_history_saved'] = $save_result;
            
            wp_send_json_success($result);
            
        } catch (Exception $e) {
            wp_send_json_error(array('message' => $e->getMessage()));
        }
    }
    
    /**
     * Scan WordPress file with YARA
     */
    public function scan_wp_file() {
        VMPFence_Secure_Logger::log('debug', 'YARA scan_wp_file() called');
        
        // Verify nonce
        if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'vmpfence_yara_scan')) {
            VMPFence_Secure_Logger::log('warning', 'YARA nonce verification failed');
            wp_send_json_error(array('message' => 'Security check failed'));
            return;
        }
        
        // Check permissions
        if (!current_user_can('manage_options')) {
            VMPFence_Secure_Logger::log('warning', 'YARA permission check failed');
            wp_send_json_error(array('message' => 'Unauthorized'));
            return;
        }
        
        // Check premium license (use centralized getter)
        $license_type = VMPFence_License::get_license_type();
        $api_key = VMPFence_Config::get('central_api_key', '');
        
        VMPFence_Secure_Logger::log('debug', 'YARA license check', ['license_type' => $license_type, 'has_api_key' => !empty($api_key)]);
        
        if ($license_type !== VMPFence_Constants::LICENSE_PREMIUM || empty($api_key)) {
            VMPFence_Secure_Logger::log('warning', 'YARA premium license check failed');
            wp_send_json_error(array('message' => 'Premium license required'));
            return;
        }
        
        try {
            $file_path = isset($_POST['file_path']) ? sanitize_text_field(wp_unslash($_POST['file_path'])) : '';
            
            VMPFence_Secure_Logger::log('info', 'YARA scanning file', ['file_path' => $file_path]);
            
            if (empty($file_path)) {
                throw new Exception('File path is required');
            }
            
            // Validate file exists and is within WordPress directory
            $full_path = ABSPATH . $file_path;
            if (!file_exists($full_path) || !is_readable($full_path)) {
                VMPFence_Secure_Logger::log('error', 'YARA file not found or not readable', ['file_path' => $full_path]);
                throw new Exception('File not found or not readable');
            }
            
            // Security check - ensure file is within WordPress directory
            $real_path = realpath($full_path);
            $base_path = realpath(ABSPATH);
            if (strpos($real_path, $base_path) !== 0) {
                VMPFence_Secure_Logger::log('error', 'YARA security check failed - file outside WordPress', ['real_path' => $real_path]);
                throw new Exception('Invalid file path');
            }
            
            // Call API client
            require_once VMPFENCE_PLUGIN_DIR . 'lib/class-api-client.php';
            $api_client = new VMPFence_API_Client();
            
            // Get file size for performance tracking
            $file_size = filesize($real_path);
            
            VMPFence_Secure_Logger::log('debug', 'YARA calling API for file', ['file_path' => $real_path]);
            $result = $api_client->yara_deep_scan($api_key, $real_path, 'manual_wp_scan');
            
            if (is_wp_error($result)) {
                VMPFence_Secure_Logger::log('error', 'YARA API returned WP_Error', ['error' => $result->get_error_message()]);
                throw new Exception($result->get_error_message());
            }
            
            VMPFence_Secure_Logger::log('info', 'YARA scan successful', ['threats_found' => isset($result['threats_found']) ? $result['threats_found'] : 'unknown']);
            
            // Add file size to result for history tracking
            $result['file_size_bytes'] = $file_size;
            
            // Calculate hash for history
            $file_hash = hash_file('sha256', $real_path);
            
            // Save to history
            $this->save_to_history(basename($file_path), $file_hash, $result);
            
            // Update stats
            $this->update_stats($result);
            
            wp_send_json_success($result);
            
        } catch (Exception $e) {
            VMPFence_Secure_Logger::log('error', 'YARA exception in scan_wp_file()', ['error' => $e->getMessage()]);
            VMPFence_Secure_Logger::log('debug', 'YARA stack trace', ['trace' => $e->getTraceAsString()]);
            wp_send_json_error(array('message' => $e->getMessage()));
        }
    }
    
    /**
     * Get WordPress files for scanning
     */
    public function get_wp_files() {
        // Verify nonce
        if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'vmpfence_yara_scan')) {
            wp_send_json_error(array('message' => 'Security check failed'));
            return;
        }
        
        // Check permissions
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Unauthorized'));
            return;
        }
        
        try {
            $files = array();
            
            // Get plugin files
            $plugins_dir = WP_PLUGIN_DIR;
            $plugin_files = $this->scan_directory($plugins_dir, '*', 99);
            foreach ($plugin_files as $file) {
                $relative_path = str_replace(ABSPATH, '', $file);
                $files[] = array(
                    'name' => basename($file),
                    'path' => $relative_path,
                    'size' => filesize($file),
                    'type' => 'plugin'
                );
            }
            
            // Get theme files
            $themes_dir = get_theme_root();
            $theme_files = $this->scan_directory($themes_dir, '*', 99);
            foreach ($theme_files as $file) {
                $relative_path = str_replace(ABSPATH, '', $file);
                $files[] = array(
                    'name' => basename($file),
                    'path' => $relative_path,
                    'size' => filesize($file),
                    'type' => 'theme'
                );
            }
            
            // Get WordPress core files (wp-includes and wp-admin)
            $core_dirs = array(
                ABSPATH . 'wp-includes',
                ABSPATH . 'wp-admin'
            );
            foreach ($core_dirs as $core_dir) {
                if (is_dir($core_dir)) {
                    $core_files = $this->scan_directory($core_dir, '*', 99);
                    foreach ($core_files as $file) {
                        $relative_path = str_replace(ABSPATH, '', $file);
                        $files[] = array(
                            'name' => basename($file),
                            'path' => $relative_path,
                            'size' => filesize($file),
                            'type' => 'core'
                        );
                    }
                }
            }
            
            // Get uploads directory files (CRITICAL: scan deeply for malware)
            $uploads_dir = wp_upload_dir();
            if (isset($uploads_dir['basedir']) && is_dir($uploads_dir['basedir'])) {
                $upload_files = $this->scan_directory($uploads_dir['basedir'], '*', 99);
                VMPFence_Secure_Logger::log('debug', 'YARA found files in uploads', ['count' => count($upload_files)]);
                foreach ($upload_files as $file) {
                    $relative_path = str_replace(ABSPATH, '', $file);
                    $files[] = array(
                        'name' => basename($file),
                        'path' => $relative_path,
                        'size' => filesize($file),
                        'type' => 'upload'
                    );
                    if (basename($file) === 'suspicious-test.php') {
                        VMPFence_Secure_Logger::log('debug', 'YARA found malware test file in uploads', ['path' => $relative_path]);
                    }
                }
                VMPFence_Secure_Logger::log('debug', 'YARA added upload files to scan list', ['count' => count($upload_files)]);
            }
            
            // No limit - scan everything (can be thousands of files)
            
            wp_send_json_success(array('files' => $files));
            
        } catch (Exception $e) {
            wp_send_json_error(array('message' => $e->getMessage()));
        }
    }
    
    /**
     * Get YARA scan statistics from locally stored sessions (not from API)
     */
    public function get_stats() {
        // Verify nonce
        if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'vmpfence_yara_scan')) {
            wp_send_json_error(array('message' => 'Security check failed'));
            return;
        }
        
        // Get threat files and whitelisted files
        $threat_files = get_option('vmpfence_yara_threat_files', array());
        $whitelisted_files = get_option('vmpfence_yara_whitelisted_files', array());
        
        // VALIDATION: Remove threat files that no longer exist on disk
        // Only remove if we can definitively confirm the file doesn't exist
        $active_threats = array();
        $removed_count = 0;
        foreach ($threat_files as $hash => $threat_data) {
            $should_keep = true;
            
            if (isset($threat_data['file_path'])) {
                $file_path = $threat_data['file_path'];
                
                // Convert relative path to absolute if needed
                if (!$this->path_is_absolute($file_path)) {
                    $file_path = ABSPATH . ltrim($file_path, '/');
                }
                
                // Only remove if file definitively doesn't exist AND path looks valid
                if (!empty($file_path) && strpos($file_path, ABSPATH) === 0) {
                    if (!file_exists($file_path)) {
                        // Double-check: file was deleted
                        $should_keep = false;
                        $removed_count++;
                    }
                }
                // If path is suspicious or empty, keep it (don't accidentally delete valid threats)
            }
            
            if ($should_keep) {
                $active_threats[$hash] = $threat_data;
            }
        }
        
        // Update the option if any files were removed
        if ($removed_count > 0) {
            update_option('vmpfence_yara_threat_files', $active_threats);
            VMPFence_Secure_Logger::log('info', 'YARA removed non-existent threat files from cache', ['count' => $removed_count]);
        }
        
        // Use validated threat list
        $threat_files = $active_threats;
        
        // Count total WordPress files
        $total_files = $this->get_total_file_count();
        
        // Get all batch sessions from WordPress database
        $sessions = get_option('vmpfence_yara_batch_sessions', array());
        
        // Group batches by scan session (batches within 5 minutes of each other)
        // to count each multi-batch scan as ONE scan
        $scan_sessions = array();
        $last_date = null;
        $current_session_index = -1;
        
        if (is_array($sessions) && !empty($sessions)) {
            foreach ($sessions as $session) {
                $session_date = isset($session['date']) ? strtotime($session['date']) : 0;
                
                // If this batch is within 5 minutes of the last one, it's part of the same scan session
                if ($last_date && abs($session_date - $last_date) <= 300) {
                    // Add to current session
                    $scan_sessions[$current_session_index]['batches'][] = $session;
                } else {
                    // Start new session
                    $current_session_index++;
                    $scan_sessions[$current_session_index] = array(
                        'batches' => array($session),
                        'date' => $session_date
                    );
                }
                
                $last_date = $session_date;
            }
        }
        
        // Now calculate stats from grouped scan sessions
        $total_scans = count($scan_sessions);
        
        // Get threat files and whitelisted files for accurate counts
        $threat_files = get_option('vmpfence_yara_threat_files', array());
        $whitelisted_files = get_option('vmpfence_yara_whitelisted_files', array());
        
        // VALIDATION: Remove threat files that no longer exist (same as above)
        $active_threats = array();
        $removed_count = 0;
        foreach ($threat_files as $hash => $threat_data) {
            $should_keep = true;
            
            if (isset($threat_data['file_path'])) {
                $file_path = $threat_data['file_path'];
                
                // Convert relative path to absolute if needed
                if (!$this->path_is_absolute($file_path)) {
                    $file_path = ABSPATH . ltrim($file_path, '/');
                }
                
                // Only remove if file definitively doesn't exist AND path looks valid
                if (!empty($file_path) && strpos($file_path, ABSPATH) === 0) {
                    if (!file_exists($file_path)) {
                        $should_keep = false;
                        $removed_count++;
                    }
                }
            }
            
            if ($should_keep) {
                $active_threats[$hash] = $threat_data;
            }
        }
        
        if ($removed_count > 0) {
            update_option('vmpfence_yara_threat_files', $active_threats);
        }
        
        $threat_files = $active_threats;
        $total_files = $this->get_total_file_count();
        
        // Get threat count: ONLY use cached threat_files (not batch session data)
        // This ensures whitelisted files are properly excluded from the threat count
        $threats_count = count($threat_files);
        
        // Calculate clean files: total files minus (threats + whitelisted)
        // Whitelisted files should not be counted as either threats OR clean
        $clean_files_count = $total_files - $threats_count - count($whitelisted_files);
        
        // Ensure clean files count doesn't go negative
        if ($clean_files_count < 0) {
            $clean_files_count = 0;
        }
        
        $stats = array(
            'totalScans' => $total_scans,
            'threatsFound' => $threats_count,
            'cleanFiles' => $clean_files_count,
            'whitelistedFiles' => count($whitelisted_files),
            'yaraRulesActive' => 996
        );
        
        wp_send_json_success($stats);
    }
    
    /**
     * Get YARA scan history from WordPress database (not from API)
     * Groups multiple batches into single scan sessions
     */
    public function get_history() {
        // Verify nonce
        if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'vmpfence_yara_scan')) {
            wp_send_json_error(array('message' => 'Security check failed'));
            return;
        }
        
        // Get page and page_size from request
        $page = isset($_POST['page']) ? intval($_POST['page']) : 1;
        $page_size = isset($_POST['page_size']) ? intval($_POST['page_size']) : 20;
        
        // Get all sessions from WordPress database
        $all_batches = get_option('vmpfence_yara_batch_sessions', array());
        
        if (!is_array($all_batches)) {
            $all_batches = array();
        }
        
        // Group batches into scan sessions (batches with same session_id = same scan)
        $scan_sessions = array();
        $sessions_by_id = array();
        
        foreach ($all_batches as $batch) {
            $session_id = isset($batch['session_id']) ? $batch['session_id'] : $batch['batch_id'];
            
            if (!isset($sessions_by_id[$session_id])) {
                // Start new session
                $sessions_by_id[$session_id] = array(
                    'session_id' => $session_id,
                    'batch_id' => isset($batch['batch_id']) ? $batch['batch_id'] : '',
                    'batches' => array(),
                    'total_files' => 0,
                    'scanned_files' => 0,
                    'threats_found' => 0,
                    'high_severity_count' => 0,
                    'critical_severity_count' => 0,
                    'status' => 'completed',
                    'started_at' => isset($batch['date']) ? $batch['date'] : '',
                    'completed_at' => isset($batch['date']) ? $batch['date'] : '',
                    'total_batches' => 0,
                    'completed_batches' => 0,
                    'scan_duration_seconds' => null,
                    'threat_files' => array()
                );
            }
            
            // Add to session
            $sessions_by_id[$session_id]['batches'][] = $batch;
            $sessions_by_id[$session_id]['total_files'] += isset($batch['total_files']) ? intval($batch['total_files']) : 0;
            $sessions_by_id[$session_id]['scanned_files'] += isset($batch['completed_files']) ? intval($batch['completed_files']) : 0;
            $sessions_by_id[$session_id]['threats_found'] += isset($batch['threats_found']) ? intval($batch['threats_found']) : 0;
            $sessions_by_id[$session_id]['high_severity_count'] += isset($batch['high_severity']) ? intval($batch['high_severity']) : 0;
            $sessions_by_id[$session_id]['critical_severity_count'] += isset($batch['critical_severity']) ? intval($batch['critical_severity']) : 0;
            $sessions_by_id[$session_id]['total_batches']++;
            $sessions_by_id[$session_id]['completed_batches']++;
            
            // Update completed_at to latest batch
            if (isset($batch['date'])) {
                $batch_date = strtotime($batch['date']);
                $completed_date = strtotime($sessions_by_id[$session_id]['completed_at']);
                if ($batch_date > $completed_date) {
                    $sessions_by_id[$session_id]['completed_at'] = $batch['date'];
                }
            }
        }
        
        // Convert to indexed array and sort by date (newest first)
        $scan_sessions = array_values($sessions_by_id);
        usort($scan_sessions, function($a, $b) {
            return strtotime($b['completed_at']) - strtotime($a['completed_at']);
        });
        
        // Paginate grouped sessions
        $total_count = count($scan_sessions);
        $offset = ($page - 1) * $page_size;
        $paginated_sessions = array_slice($scan_sessions, $offset, $page_size);
        
        $response = array(
            'sessions' => $paginated_sessions,
            'total_count' => $total_count,
            'page' => $page,
            'page_size' => $page_size
        );
        
        wp_send_json_success($response);
    }

    /**
     * Get detailed results for a specific batch scan session
     */
    public function get_session_details() {
        // Verify nonce
        if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'vmpfence_yara_scan')) {
            wp_send_json_error(array('message' => 'Security check failed'));
            return;
        }
        
        $batch_id = isset($_POST['batch_id']) ? sanitize_text_field($_POST['batch_id']) : '';
        if (empty($batch_id)) {
            wp_send_json_error(array('message' => 'Batch ID required'));
            return;
        }
        
        // Get API key
        $api_key = VMPFence_Config::get('central_api_key', '');
        if (empty($api_key)) {
            wp_send_json_error(array('message' => 'API key not configured'));
            return;
        }
        
        // Call API
        require_once VMPFENCE_PLUGIN_DIR . 'lib/class-api-client.php';
        $api_client = new VMPFence_API_Client();
        $response = $api_client->get_yara_batch_results($api_key, $batch_id);
        
        if (is_wp_error($response)) {
            wp_send_json_error(array('message' => $response->get_error_message()));
            return;
        }
        
        wp_send_json_success($response);
    }
    
    /**
     * Save scan to history
     */
    private function save_to_history($file_name, $file_hash, $result) {
        $history = get_option('vmpfence_yara_history', array());
        
        if (!is_array($history)) {
            $history = array();
        }
        
        // Extract numeric scan time for performance tracking
        $scan_time_seconds = 0;
        if (isset($result['scan_time'])) {
            if (is_numeric($result['scan_time'])) {
                $scan_time_seconds = floatval($result['scan_time']);
            } elseif (is_string($result['scan_time']) && preg_match('/([0-9.]+)/', $result['scan_time'], $matches)) {
                $scan_time_seconds = floatval($matches[1]);
            }
        }
        
        $scan_record = array(
            'id' => uniqid('yara_'),
            'file_name' => $file_name,
            'file_hash' => $file_hash,
            'date' => current_time('mysql'),
            'status' => isset($result['threats_found']) && $result['threats_found'] > 0 ? 'Threat Detected' : 'Clean',
            'threats_found' => isset($result['threats_found']) ? $result['threats_found'] : 0,
            'threat_score' => isset($result['threat_score']) ? $result['threat_score'] : 0,
            'confidence' => isset($result['confidence']) ? $result['confidence'] : 'safe',
            'score_breakdown' => isset($result['score_breakdown']) ? $result['score_breakdown'] : array(),
            'reasoning' => isset($result['reasoning']) ? $result['reasoning'] : array(),
            'scan_time' => isset($result['scan_time']) ? $result['scan_time'] : 'N/A',
            'scan_time_seconds' => $scan_time_seconds,
            'file_size_bytes' => isset($result['file_size_bytes']) ? $result['file_size_bytes'] : 0,
            'matches' => isset($result['matches']) ? $result['matches'] : array(),
            'recommendation' => isset($result['recommendation']) ? $result['recommendation'] : ''
        );
        
        array_unshift($history, $scan_record);
        
        // Keep only last 100 scans
        $history = array_slice($history, 0, 100);
        
        $saved = update_option('vmpfence_yara_history', $history, false);
        
        // Update performance metrics for ETA estimation
        if ($scan_time_seconds > 0 && isset($result['file_size_bytes']) && $result['file_size_bytes'] > 0) {
            $this->update_performance_metrics($result['file_size_bytes'], $scan_time_seconds);
        }
        
        return $saved;
    }
    
    /**
     * Update performance metrics for scan time estimation
     * Tracks file size vs scan time to improve ETA predictions
     */
    private function update_performance_metrics($file_size_bytes, $scan_time_seconds) {
        $metrics = get_option('vmpfence_yara_performance_metrics', array(
            'total_scans' => 0,
            'total_scan_time' => 0,
            'total_file_size' => 0,
            'avg_seconds_per_mb' => 2.5, // Default baseline
            'last_updated' => current_time('mysql'),
            'samples' => array()
        ));
        
        // Add new sample
        $file_size_mb = $file_size_bytes / (1024 * 1024);
        $metrics['total_scans']++;
        $metrics['total_scan_time'] += $scan_time_seconds;
        $metrics['total_file_size'] += $file_size_mb;
        $metrics['last_updated'] = current_time('mysql');
        
        // Calculate new average (weighted towards recent scans)
        if ($metrics['total_file_size'] > 0) {
            $metrics['avg_seconds_per_mb'] = $metrics['total_scan_time'] / $metrics['total_file_size'];
        }
        
        // Store sample for detailed analysis (keep last 50)
        $metrics['samples'][] = array(
            'size_mb' => round($file_size_mb, 2),
            'time_s' => round($scan_time_seconds, 2),
            'date' => current_time('mysql')
        );
        $metrics['samples'] = array_slice($metrics['samples'], -50);
        
        update_option('vmpfence_yara_performance_metrics', $metrics, false);
    }
    
    /**
     * Save batch session metadata for persistent history
     */
    private function save_batch_session($batch_id, $results, $scan_session_id = null) {
        $sessions = get_option('vmpfence_yara_batch_sessions', array());
        
        if (!is_array($sessions)) {
            $sessions = array();
        }
        
        // Use scan_session_id to group batches, fallback to batch_id
        $session_id = $scan_session_id ? $scan_session_id : $batch_id;
        
        $session_record = array(
            'session_id' => $session_id,
            'batch_id' => $batch_id,
            'date' => current_time('mysql'),
            'total_files' => $results['total_files'] ?? 0,
            'completed_files' => $results['completed_files'] ?? 0,
            'threats_found' => $results['total_threats_found'] ?? 0,
            'high_severity' => $results['high_severity_count'] ?? 0,
            'critical_severity' => $results['critical_severity_count'] ?? 0,
            'status' => $results['status'] ?? 'completed',
            'safe' => $results['summary']['safe'] ?? 0,
            'quarantine' => $results['summary']['quarantine'] ?? 0,
            'monitor' => $results['summary']['monitor'] ?? 0,
            'delete' => $results['summary']['delete'] ?? 0,
            'error' => $results['summary']['error'] ?? 0,
            'failed_files' => $results['failed_files'] ?? 0,
            'batch_number' => $results['batch_number'] ?? null,
            'total_batches' => $results['total_batches_in_session'] ?? null
        );
        
        array_unshift($sessions, $session_record);
        
        // Keep only last 50 sessions
        $sessions = array_slice($sessions, 0, 50);
        
        $saved = update_option('vmpfence_yara_batch_sessions', $sessions, false);
        
        VMPFence_Secure_Logger::log('debug', 'YARA saved batch session', ['batch_id' => $batch_id, 'session_id' => $session_id, 'saved' => $saved]);
        
        return $saved;
    }
    
    /**
     * Update scan statistics (for batch scans, count actual files not batches)
     */
    private function update_stats($result) {
        $stats = get_option('vmpfence_yara_stats', array(
            'totalScans' => 0,
            'threatsFound' => 0,
            'cleanFiles' => 0
        ));
        
        // For batch scans, add the actual file counts
        $total_scanned = isset($result['total_scans']) ? intval($result['total_scans']) : 1;
        $threats = isset($result['threats_found']) ? intval($result['threats_found']) : 0;
        $clean = isset($result['clean_files']) ? intval($result['clean_files']) : 0;
        
        $stats['totalScans'] += $total_scanned;
        $stats['threatsFound'] += $threats;
        $stats['cleanFiles'] += $clean;
        
        update_option('vmpfence_yara_stats', $stats);
    }
    
    /**
     * Scan directory for files
     */
    private function scan_directory($dir, $pattern, $max_depth = 1, $current_depth = 0) {
        $files = array();
        
        if ($current_depth >= $max_depth) {
            return $files;
        }
        
        // Only exclude truly binary files (fonts, images) - scan archives and SVG for malware
        $excluded_extensions = array('jpg', 'jpeg', 'png', 'gif', 'ico', 'webp', 'woff', 'woff2', 'ttf', 'eot', 'otf');
        
        try {
            $iterator = new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS);
            $filtered = new RecursiveCallbackFilterIterator($iterator, function($current) use ($pattern, $max_depth, $current_depth, $excluded_extensions) {
                if ($current->isDir()) {
                    return $current_depth < $max_depth;
                }
                
                // If pattern is *, scan all files except truly binary extensions
                if ($pattern === '*') {
                    $ext = strtolower(pathinfo($current->getFilename(), PATHINFO_EXTENSION));
                    return !in_array($ext, $excluded_extensions);
                }
                
                return fnmatch($pattern, $current->getFilename());
            });
            
            $iterator = new RecursiveIteratorIterator($filtered, RecursiveIteratorIterator::SELF_FIRST);
            
            foreach ($iterator as $file) {
                if ($file->isFile()) {
                    $file_size = $file->getSize();
                    if ($file_size < 100 * 1024 * 1024) { // Max 100MB
                        $files[] = $file->getPathname();
                    } else {
                        // SECURITY: Log relative path only to prevent server path disclosure
                        $relative_path = str_replace(ABSPATH, '', $file->getPathname());
                        VMPFence_Secure_Logger::log('debug', 'YARA skipping file >100MB', ['size' => size_format($file_size), 'path' => $relative_path]);
                    }
                }
            }
        } catch (Exception $e) {
            // Silently fail if directory is not accessible
        }
        
        return $files;
    }
    
    /**
     * Get YARA server stats (rule counts from API)
     */
    public function get_server_stats() {
        // Verify nonce
        if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'vmpfence_yara_scan')) {
            wp_send_json_error(array('message' => 'Security check failed'));
            return;
        }
        
        // Check for cache clear request
        $clear_cache = isset($_POST['clear_cache']) && $_POST['clear_cache'] === 'true';
        if ($clear_cache) {
            delete_transient('vmpfence_yara_server_stats');
        }
        
        // Try to get cached stats first (1 hour cache)
        $cached_stats = get_transient('vmpfence_yara_server_stats');
        if (!$clear_cache && $cached_stats !== false) {
            $cached_stats['from_cache'] = true;
            wp_send_json_success($cached_stats);
            return;
        }
        
        try {
            $api_key = VMPFence_Config::get('central_api_key', '');
            if (empty($api_key)) {
                throw new Exception('API key not configured');
            }
            
            require_once VMPFENCE_PLUGIN_DIR . 'lib/class-api-client.php';
            $api_client = new VMPFence_API_Client();
            
            $stats = $api_client->get_yara_stats($api_key);
            
            if (is_wp_error($stats)) {
                throw new Exception($stats->get_error_message());
            }
            
            // Map API response to plugin format
            $response = array(
                'total_count' => isset($stats['total_rules']) ? $stats['total_rules'] : 0,
                'active_count' => isset($stats['active_rules']) ? $stats['active_rules'] : 0,
                'categories' => isset($stats['categories']) ? $stats['categories'] : array(),
                'rules_by_category' => isset($stats['rules_by_category']) ? $stats['rules_by_category'] : array(),
                'rules_by_severity' => isset($stats['rules_by_severity']) ? $stats['rules_by_severity'] : array(),
                'last_updated' => isset($stats['last_updated']) ? $stats['last_updated'] : null,
                'current_version' => isset($stats['current_version']) ? $stats['current_version'] : 'unknown',
                'from_cache' => false
            );
            
            // Cache for 1 hour
            set_transient('vmpfence_yara_server_stats', $response, HOUR_IN_SECONDS);
            
            wp_send_json_success($response);
            
        } catch (Exception $e) {
            // Fallback to hardcoded value if API fails
            wp_send_json_success(array(
                'total_count' => 2419,
                'active_count' => 996,
                'error' => 'Using fallback values - API unavailable',
                'from_cache' => false
            ));
        }
    }
    
    /**
     * Submit batch scan request
     */
    public function batch_scan() {
        // Verify nonce
        if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'vmpfence_yara_scan')) {
            wp_send_json_error(array('message' => 'Security check failed'));
            return;
        }
        
        // Check permissions
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Unauthorized'));
            return;
        }
        
        // Check premium license (use centralized getter)
        $license_type = VMPFence_License::get_license_type();
        $api_key = VMPFence_Config::get('central_api_key', '');
        
        if ($license_type !== VMPFence_Constants::LICENSE_PREMIUM || empty($api_key)) {
            wp_send_json_error(array('message' => 'Premium license required'));
            return;
        }
        
        try {
            $files_data = isset($_POST['files']) ? json_decode(wp_unslash($_POST['files']), true) : array();
            $batch_number = isset($_POST['batch_number']) ? intval($_POST['batch_number']) : null;
            $total_batches = isset($_POST['total_batches']) ? intval($_POST['total_batches']) : null;
            $scan_session_id = isset($_POST['scan_session_id']) ? sanitize_text_field(wp_unslash($_POST['scan_session_id'])) : null;
            
            if (empty($files_data)) {
                throw new Exception('No files provided');
            }
            
            // Log initial state
            $initial_files_count = count($files_data);
            $initial_memory = memory_get_usage(true);
            VMPFence_Secure_Logger::log('info', 'YARA batch starting', [
                'batch' => "$batch_number/$total_batches",
                'session_id' => $scan_session_id,
                'files' => $initial_files_count,
                'memory' => size_format($initial_memory)
            ]);
            
            // =====================================================================
            // INTEGRITY PRE-FILTER: Check files against WordPress.org before YARA
            // =====================================================================
            require_once VMPFENCE_PLUGIN_DIR . 'includes/class-wp-integrity-checker.php';
            $integrity_checker = new VMPFence_WP_Integrity_Checker();
            
            // Check if trusted hashes database exists and has data
            if (!VMPFence_Trusted_Hashes_DB::has_data()) {
                VMPFence_Secure_Logger::log('info', 'Building trusted hash database (table empty)');
                $trusted_hashes_array = $this->build_trusted_hash_database($integrity_checker);
                
                // Store hashes in database using batched inserts
                $insert_success = VMPFence_Trusted_Hashes_DB::insert_hashes_batched($trusted_hashes_array, 500);
                
                if ($insert_success) {
                    VMPFence_Secure_Logger::log('info', 'Stored trusted hashes in database', [
                        'count' => count($trusted_hashes_array),
                        'db_count' => VMPFence_Trusted_Hashes_DB::get_hash_count()
                    ]);
                } else {
                    VMPFence_Secure_Logger::log('error', 'Failed to store trusted hashes in database');
                }
            } else {
                VMPFence_Secure_Logger::log('debug', 'Using trusted hashes from database', [
                    'count' => VMPFence_Trusted_Hashes_DB::get_hash_count()
                ]);
            }
            
            // Filter files: separate verified from unverified
            $verified_files = array();
            $files_to_scan = array();
            
            // Load whitelisted files for pre-filtering
            $whitelisted_files = get_option('vmpfence_yara_whitelisted_files', array());
            
            // Debug: Log first 3 paths to understand format
            static $debug_counter = 0;
            
            foreach ($files_data as $file) {
                // Normalize path: convert backslashes to forward slashes and make relative
                $incoming_path = str_replace('\\', '/', $file['path']);
                
                // Detect if path is absolute (Windows C:/ or Unix /)
                $is_absolute = (preg_match('/^[A-Za-z]:/', $incoming_path) || substr($incoming_path, 0, 1) === '/');
                
                if ($is_absolute) {
                    // Convert absolute path to relative
                    $abspath_normalized = str_replace('\\', '/', rtrim(ABSPATH, '/'));
                    if (strpos($incoming_path, $abspath_normalized) === 0) {
                        $relative_path = ltrim(substr($incoming_path, strlen($abspath_normalized)), '/');
                    } else {
                        // Path outside WordPress root - skip it
                        continue;
                    }
                } else {
                    // Already relative, just clean up
                    $relative_path = ltrim($incoming_path, '/');
                }
                
                // Construct full path for file operations
                $full_path = ABSPATH . $relative_path;
                
                // =====================================================================
                // WHITELIST PRE-FILTER: Skip whitelisted files before sending to YARA
                // =====================================================================
                if (file_exists($full_path)) {
                    $file_hash = hash_file('sha256', $full_path);
                    if (isset($whitelisted_files[$file_hash])) {
                        // File is whitelisted - skip YARA scan entirely
                        $verified_files[] = $relative_path;
                        VMPFence_Secure_Logger::log('debug', 'WHITELIST: Skipping whitelisted file', ['path' => $relative_path, 'hash' => $file_hash]);
                        continue;
                    }
                } else {
                    // File doesn't exist - log warning but continue to let YARA process handle it
                    VMPFence_Secure_Logger::log('warning', 'File not found for whitelist check', ['path' => $full_path]);
                }
                
                // Check if file exists in trusted database
                $hash_data = VMPFence_Trusted_Hashes_DB::get_hash($relative_path);
                if ($hash_data !== null) {
                    $expected_hash = $hash_data['hash'];
                    $source = $hash_data['source'];
                    
                    // Special handling for whitelisted premium plugins
                    if ($expected_hash === 'WHITELISTED' && $source === 'premium_plugin_whitelist') {
                        // AUTO-TRUST whitelisted premium plugins without hash verification
                        $verified_files[] = $relative_path;
                        continue;
                    }
                    
                    // Regular checksum verification for WordPress.org plugins
                    if (file_exists($full_path)) {
                        $actual_hash = md5_file($full_path);
                        
                        if ($actual_hash === $expected_hash) {
                            // File verified - skip YARA scan
                            $verified_files[] = $relative_path;
                            continue;
                        }
                    }
                }
                
                // File not in database OR hash mismatch - send to YARA
                $files_to_scan[] = $file;
            }
            
            // Log integrity filter results
            $verified_count = count($verified_files);
            $scan_count = count($files_to_scan);
            $reduction_percent = ($initial_files_count > 0) ? ($verified_count / $initial_files_count * 100) : 0;
            
            VMPFence_Secure_Logger::log('info', 'Integrity pre-filter complete', [
                'total' => $initial_files_count,
                'verified' => $verified_count,
                'verified_percent' => round($reduction_percent, 1),
                'to_scan' => $scan_count
            ]);
            
            // If all files verified, return success immediately
            if (empty($files_to_scan)) {
                VMPFence_Secure_Logger::log('info', 'All files verified by integrity check - skipping YARA scan');
                wp_send_json_success(array(
                    'all_verified' => true,
                    'verified_count' => $verified_count,
                    'message' => 'All files passed integrity check'
                ));
                return;
            }
            
            // Replace files_data with only files that need YARA scanning
            $files_data = $files_to_scan;
            
            // Prepare file metadata for API (with file content)
            $files_metadata = array();
            $skipped_files = 0;
            $processed_files = 0;
            
            foreach ($files_data as $index => $file) {
                $file_path = ABSPATH . ltrim($file['path'], '/');
                
                // DEBUG: Track malware test file
                if (strpos($file['path'], 'suspicious-test.php') !== false) {
                    VMPFence_Secure_Logger::log('debug', 'YARA processing malware test file in batch', [
                        'file_path' => $file['path'],
                        'absolute_path' => $file_path,
                        'exists' => file_exists($file_path)
                    ]);
                }
                
                if (!file_exists($file_path)) {
                    $skipped_files++;
                    VMPFence_Secure_Logger::log('warning', 'YARA file not found', ['file_path' => $file_path]);
                    continue;
                }
                
                // Check memory before processing each file
                $current_memory = memory_get_usage(true);
                $memory_limit = ini_get('memory_limit');
                
                // Get file size first to check if we can safely load it
                $file_size = filesize($file_path);
                if ($file_size === false || $file_size > 100 * 1024 * 1024) {
                    $skipped_files++;
                    // SECURITY: Log relative path only to prevent server path disclosure
                    $relative_path = str_replace(ABSPATH, '', $file_path);
                    VMPFence_Secure_Logger::log('debug', 'YARA skipping file >100MB', ['size' => size_format($file_size), 'path' => $relative_path]);
                    continue;
                }
                
                // Attempt to read file content
                $content = @file_get_contents($file_path);
                if ($content === false) {
                    $skipped_files++;
                    VMPFence_Secure_Logger::log('warning', 'YARA failed to read file', ['file_path' => $file_path]);
                    continue;
                }
                
                $hash = hash('sha256', $content);
                $encoded_content = base64_encode($content);
                
                // DEBUG: Track malware test file content
                if (strpos($file['path'], 'suspicious-test.php') !== false) {
                    VMPFence_Secure_Logger::log('debug', 'YARA malware test file - content read', [
                        'file_size' => $file_size,
                        'sha256' => $hash,
                        'content_preview' => substr($content, 0, 100)
                    ]);
                }
                
                $files_metadata[] = array(
                    'file_name' => basename($file_path),
                    'file_path' => $file['path'],
                    'file_hash' => $hash,
                    'file_size' => $file_size,
                    'file_content' => $encoded_content
                );
                
                $processed_files++;
                
                // Free memory immediately after encoding
                unset($content, $encoded_content);
                
                // Log progress every 100 files
                if ($processed_files % 100 === 0) {
                    $mem_used = memory_get_usage(true);
                    VMPFence_Secure_Logger::log('debug', 'YARA processing files', [
                        'processed' => "$processed_files/{$initial_files_count}",
                        'memory' => size_format($mem_used)
                    ]);
                }
            }
            
            // Log final stats
            $final_memory = memory_get_usage(true);
            VMPFence_Secure_Logger::log('info', 'YARA batch preparation completed', [
                'sent' => $initial_files_count,
                'processed' => $processed_files,
                'skipped' => $skipped_files,
                'memory' => size_format($final_memory)
            ]);
            
            // DEBUG: Check if malware test file is in the batch being sent to API
            $test_file_in_batch = false;
            foreach ($files_metadata as $file_meta) {
                if (strpos($file_meta['file_path'], 'suspicious-test.php') !== false) {
                    $test_file_in_batch = true;
                    VMPFence_Secure_Logger::log('debug', 'YARA malware test file in batch being sent to API', ['batch_size' => count($files_metadata)]);
                    break;
                }
            }
            if (!$test_file_in_batch && $processed_files > 0) {
                VMPFence_Secure_Logger::log('warning', 'YARA malware test file NOT in batch', ['batch_size' => count($files_metadata)]);
            }
            
            if (empty($files_metadata)) {
                throw new Exception('No valid files found after processing');
            }
            
            // Call API client
            require_once VMPFENCE_PLUGIN_DIR . 'lib/class-api-client.php';
            $api_client = new VMPFence_API_Client();
            
            $result = $api_client->yara_batch_scan($api_key, $files_metadata, $batch_number, $total_batches);
            
            if (is_wp_error($result)) {
                throw new Exception($result->get_error_message());
            }
            
            // Store scan_session_id for this batch (for grouping when retrieving results)
            if ($scan_session_id && isset($result['batch_id'])) {
                set_transient('yara_scan_session_' . $result['batch_id'], $scan_session_id, DAY_IN_SECONDS);
                VMPFence_Secure_Logger::log('debug', 'YARA stored session ID for batch', ['session_id' => $scan_session_id, 'batch_id' => $result['batch_id']]);
            }
            
            wp_send_json_success($result);
            
        } catch (Exception $e) {
            wp_send_json_error(array('message' => $e->getMessage()));
        }
    }
    
    /**
     * Get batch scan status
     */
    public function batch_status() {
        // Verify nonce
        if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'vmpfence_yara_scan')) {
            wp_send_json_error(array('message' => 'Security check failed'));
            return;
        }
        
        try {
            $batch_id = isset($_POST['batch_id']) ? sanitize_text_field(wp_unslash($_POST['batch_id'])) : '';
            $api_key = VMPFence_Config::get('central_api_key', '');
            
            if (empty($batch_id) || empty($api_key)) {
                throw new Exception('Missing batch ID or API key');
            }
            
            require_once VMPFENCE_PLUGIN_DIR . 'lib/class-api-client.php';
            $api_client = new VMPFence_API_Client();
            
            $status = $api_client->yara_batch_status($api_key, $batch_id);
            
            if (is_wp_error($status)) {
                throw new Exception($status->get_error_message());
            }
            
            wp_send_json_success($status);
            
        } catch (Exception $e) {
            wp_send_json_error(array('message' => $e->getMessage()));
        }
    }
    
    /**
     * Get batch scan results
     */
    public function batch_results() {
        // Verify nonce
        if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'vmpfence_yara_scan')) {
            wp_send_json_error(array('message' => 'Security check failed'));
            return;
        }
        
        try {
            $batch_id = isset($_POST['batch_id']) ? sanitize_text_field(wp_unslash($_POST['batch_id'])) : '';
            $api_key = VMPFence_Config::get('central_api_key', '');
            
            if (empty($batch_id) || empty($api_key)) {
                throw new Exception('Missing batch ID or API key');
            }
            
            require_once VMPFENCE_PLUGIN_DIR . 'lib/class-api-client.php';
            $api_client = new VMPFence_API_Client();
            
            $results = $api_client->yara_batch_results($api_key, $batch_id);
            
            if (is_wp_error($results)) {
                throw new Exception($results->get_error_message());
            }
            
            // Get session_id from transient
            $scan_session_id = get_transient('yara_scan_session_' . $batch_id);
            
            // Save individual file results to history for detailed view
            if (!empty($results['files'])) {
                $threat_files = get_option('vmpfence_yara_threat_files', array());
                $whitelisted_files = get_option('vmpfence_yara_whitelisted_files', array());
                
                foreach ($results['files'] as $file) {
                    if ($file['scan_status'] === 'completed') {
                        // Parse scan time from duration_ms
                        $scan_time_seconds = 0;
                        if (isset($file['scan_duration_ms']) && is_numeric($file['scan_duration_ms'])) {
                            $scan_time_seconds = $file['scan_duration_ms'] / 1000;
                        }
                        
                        // Get file size from batch metadata
                        $file_size_bytes = isset($file['file_size']) ? intval($file['file_size']) : 0;
                        
                        // Save to history
                        $this->save_to_history(
                            $file['file_name'],
                            $file['file_hash'],
                            array(
                                'threats_found' => $file['threats_found'],
                                'threat_score' => $file['threat_score'] ?? 0,
                                'confidence' => $file['confidence_level'] ?? 'safe',
                                'score_breakdown' => $file['score_breakdown'] ?? array(),
                                'recommendation' => $file['recommendation'],
                                'matched_rules' => $file['matched_rules'] ?? array(),
                                'scan_time' => $scan_time_seconds > 0 ? $scan_time_seconds . 's' : 'N/A',
                                'file_size_bytes' => $file_size_bytes
                            )
                        );
                        
                        // If threat detected (threat_score > 0) and NOT whitelisted, add to threat files
                        // Use threat_score instead of threats_found to exclude false positives
                        if (isset($file['threat_score']) && $file['threat_score'] > 0 && !isset($whitelisted_files[$file['file_hash']])) {
                            // Build threat description from matched rules
                            $threat_desc = '';
                            if (!empty($file['matched_rules'])) {
                                $rules = array();
                                foreach ($file['matched_rules'] as $rule) {
                                    // matched_rules can be either array of strings or array of objects
                                    if (is_string($rule)) {
                                        $rules[] = $rule;  // Simple rule name string
                                    } elseif (is_array($rule)) {
                                        $rules[] = $rule['rule_name'] ?? $rule['rule'] ?? 'Unknown Rule';
                                    }
                                }
                                $threat_desc = implode(', ', $rules);
                            }
                            
                            $threat_files[$file['file_hash']] = array(
                                'file_path' => $file['file_path'] ?? $file['file_name'],
                                'file_name' => $file['file_name'],
                                'threat_description' => $threat_desc,
                                'threat_score' => $file['threat_score'] ?? 0,
                                'confidence' => $file['confidence_level'] ?? 'safe',
                                'score_breakdown' => $file['score_breakdown'] ?? array(),
                                'matched_rules' => $file['matched_rules'] ?? array(),
                                'threats_count' => $file['threats_found'],
                                'detected_at' => current_time('mysql'),
                                'batch_id' => $batch_id,
                                'session_id' => $scan_session_id ? $scan_session_id : 'unknown'
                            );
                        }
                        // If clean (threat_score = 0, even if rules matched) and was previously a threat, remove from threats
                        elseif ((!isset($file['threat_score']) || $file['threat_score'] == 0) && isset($threat_files[$file['file_hash']])) {
                            unset($threat_files[$file['file_hash']]);
                        }
                    }
                }
                
                // Save updated threat files
                update_option('vmpfence_yara_threat_files', $threat_files);
            }
            
            // Save batch session to WordPress database for persistent history
            // Retrieve scan_session_id for grouping batches together
            $scan_session_id = get_transient('yara_scan_session_' . $batch_id);
            $this->save_batch_session($batch_id, $results, $scan_session_id);
            
            // Update stats once per batch with actual file counts (not individual files)
            // Only update if this is the first retrieval (check if already counted)
            $counted_key = 'yara_batch_counted_' . $batch_id;
            if (!get_transient($counted_key)) {
                $this->update_stats(array(
                    'total_scans' => $results['total_files'] ?? 0,
                    'threats_found' => $results['total_threats_found'] ?? 0,
                    'clean_files' => ($results['summary']['safe'] ?? 0)
                ));
                // Mark as counted (expires in 1 hour)
                set_transient($counted_key, true, HOUR_IN_SECONDS);
            }
            
            // Clean up trusted hashes table after scan completes
            // The table is only needed during scanning and can be rebuilt for next scan
            if (isset($results['status']) && $results['status'] === 'completed') {
                VMPFence_Trusted_Hashes_DB::clear_all_hashes();
                VMPFence_Secure_Logger::log('info', 'Cleared trusted hashes table after scan completion', [
                    'batch_id' => $batch_id
                ]);
            }
            
            wp_send_json_success($results);
            
        } catch (Exception $e) {
            wp_send_json_error(array('message' => $e->getMessage()));
        }
    }

    /**
     * Cancel a batch scan
     */
    public function cancel_batch() {
        // Verify nonce
        if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'vmpfence_yara_scan')) {
            wp_send_json_error(array('message' => 'Security check failed'));
            return;
        }
        
        try {
            $batch_id = isset($_POST['batch_id']) ? sanitize_text_field(wp_unslash($_POST['batch_id'])) : '';
            $api_key = VMPFence_Config::get('central_api_key', '');
            
            if (empty($batch_id) || empty($api_key)) {
                throw new Exception('Missing batch ID or API key');
            }
            
            require_once VMPFENCE_PLUGIN_DIR . 'lib/class-api-client.php';
            $api_client = new VMPFence_API_Client();
            
            $result = $api_client->cancel_yara_batch($api_key, $batch_id);
            
            if (is_wp_error($result)) {
                throw new Exception($result->get_error_message());
            }
            
            wp_send_json_success($result);
            
        } catch (Exception $e) {
            wp_send_json_error(array('message' => $e->getMessage()));
        }
    }
    
    /**
     * Get batch sessions from WordPress database
     */
    public function get_batch_sessions() {
        // Verify nonce
        if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'vmpfence_yara_scan')) {
            wp_send_json_error(array('message' => 'Security check failed'));
            return;
        }
        
        try {
            $all_batches = get_option('vmpfence_yara_batch_sessions', array());
            
            if (!is_array($all_batches)) {
                $all_batches = array();
            }
            
            // Group batches into scan sessions (batches with same session_id = same scan)
            $scan_sessions = array();
            $sessions_by_id = array();
            
            // Get threat files to count unique threats per session
            $threat_files = get_option('vmpfence_yara_threat_files', array());
            
            foreach ($all_batches as $batch) {
                $session_id = isset($batch['session_id']) ? $batch['session_id'] : $batch['batch_id'];
                
                if (!isset($sessions_by_id[$session_id])) {
                    // Start new session
                    $sessions_by_id[$session_id] = array(
                        'session_id' => $session_id,
                        'batch_id' => isset($batch['batch_id']) ? $batch['batch_id'] : '',
                        'batches' => array(),
                        'total_files' => 0,
                        'completed_files' => 0,
                        'threats_found' => 0,
                        'high_severity' => 0,
                        'critical_severity' => 0,
                        'safe' => 0,
                        'quarantine' => 0,
                        'monitor' => 0,
                        'delete' => 0,
                        'error' => 0,
                        'failed_files' => 0,
                        'status' => 'completed',
                        'date' => isset($batch['date']) ? $batch['date'] : '',
                        'earliest_date' => isset($batch['date']) ? $batch['date'] : '',
                        'latest_date' => isset($batch['date']) ? $batch['date'] : '',
                        'total_batches' => 0,
                        'completed_batches' => 0,
                        'scan_duration_seconds' => 0
                    );
                }
                
                // Add to session
                $sessions_by_id[$session_id]['batches'][] = $batch;
                $sessions_by_id[$session_id]['total_files'] += isset($batch['total_files']) ? intval($batch['total_files']) : 0;
                $sessions_by_id[$session_id]['completed_files'] += isset($batch['completed_files']) ? intval($batch['completed_files']) : 0;
                // Don't sum threats_found here - we'll count unique threats below
                $sessions_by_id[$session_id]['high_severity'] += isset($batch['high_severity']) ? intval($batch['high_severity']) : 0;
                $sessions_by_id[$session_id]['critical_severity'] += isset($batch['critical_severity']) ? intval($batch['critical_severity']) : 0;
                $sessions_by_id[$session_id]['safe'] += isset($batch['safe']) ? intval($batch['safe']) : 0;
                $sessions_by_id[$session_id]['quarantine'] += isset($batch['quarantine']) ? intval($batch['quarantine']) : 0;
                $sessions_by_id[$session_id]['monitor'] += isset($batch['monitor']) ? intval($batch['monitor']) : 0;
                $sessions_by_id[$session_id]['delete'] += isset($batch['delete']) ? intval($batch['delete']) : 0;
                $sessions_by_id[$session_id]['error'] += isset($batch['error']) ? intval($batch['error']) : 0;
                $sessions_by_id[$session_id]['failed_files'] += isset($batch['failed_files']) ? intval($batch['failed_files']) : 0;
                $sessions_by_id[$session_id]['total_batches']++;
                $sessions_by_id[$session_id]['completed_batches']++;
                
                // Track earliest and latest batch dates to calculate duration
                if (isset($batch['date'])) {
                    $batch_date = $batch['date'];
                    if (strtotime($batch_date) < strtotime($sessions_by_id[$session_id]['earliest_date'])) {
                        $sessions_by_id[$session_id]['earliest_date'] = $batch_date;
                    }
                    if (strtotime($batch_date) > strtotime($sessions_by_id[$session_id]['latest_date'])) {
                        $sessions_by_id[$session_id]['latest_date'] = $batch_date;
                    }
                }
            }
            
            // Calculate scan duration for each session
            foreach ($sessions_by_id as $session_id => &$session) {
                if (isset($session['earliest_date']) && isset($session['latest_date'])) {
                    $start_time = strtotime($session['earliest_date']);
                    $end_time = strtotime($session['latest_date']);
                    $session['scan_duration_seconds'] = max(0, $end_time - $start_time);
                }
            }
            unset($session);
            
            // Count unique threats per session from threat_files database
            foreach ($sessions_by_id as $session_id => &$session) {
                $unique_threats = 0;
                foreach ($threat_files as $threat) {
                    if (isset($threat['session_id']) && $threat['session_id'] === $session_id) {
                        $unique_threats++;
                    }
                }
                $session['threats_found'] = $unique_threats;
            }
            unset($session);
            
            // Convert to indexed array and sort by date (newest first)
            $scan_sessions = array_values($sessions_by_id);
            usort($scan_sessions, function($a, $b) {
                $date_a = isset($a['date']) ? strtotime($a['date']) : 0;
                $date_b = isset($b['date']) ? strtotime($b['date']) : 0;
                return $date_b - $date_a;
            });
            
            wp_send_json_success(array(
                'sessions' => $scan_sessions,
                'total' => count($scan_sessions)
            ));
            
        } catch (Exception $e) {
            wp_send_json_error(array('message' => $e->getMessage()));
        }
    }
    
    /**
     * Get total count of WordPress files (cached for performance)
     */
    private function get_total_file_count() {
        $cached_count = get_transient('vmpfence_total_file_count');
        if ($cached_count !== false) {
            return intval($cached_count);
        }
        
        $count = 0;
        
        // Count using same logic as get_wp_files() for consistency
        // Get plugin files (unlimited depth for thorough scanning)
        $plugins_dir = WP_PLUGIN_DIR;
        $plugin_files = $this->scan_directory($plugins_dir, '*', 99);
        $count += count($plugin_files);
        
        // Get theme files (unlimited depth)
        $themes_dir = get_theme_root();
        $theme_files = $this->scan_directory($themes_dir, '*', 99);
        $count += count($theme_files);
        
        // Get WordPress core files (unlimited depth)
        $core_dirs = array(
            ABSPATH . 'wp-includes',
            ABSPATH . 'wp-admin'
        );
        foreach ($core_dirs as $core_dir) {
            if (is_dir($core_dir)) {
                $core_files = $this->scan_directory($core_dir, '*', 99);
                $count += count($core_files);
            }
        }
        
        // Get uploads directory files (unlimited depth - CRITICAL for malware detection)
        $uploads_dir = wp_upload_dir();
        if (isset($uploads_dir['basedir']) && is_dir($uploads_dir['basedir'])) {
            $upload_files = $this->scan_directory($uploads_dir['basedir'], '*', 99);
            $count += count($upload_files);
        }
        
        // Get cache directory files
        $cache_dir = WP_CONTENT_DIR . '/cache';
        if (is_dir($cache_dir)) {
            $cache_files = $this->scan_directory($cache_dir, '*', 99);
            $count += count($cache_files);
        }
        
        // Get backup directory files
        $backup_dir = WP_CONTENT_DIR . '/backup';
        if (is_dir($backup_dir)) {
            $backup_files = $this->scan_directory($backup_dir, '*', 99);
            $count += count($backup_files);
        }
        
        // Cache for 1 hour
        set_transient('vmpfence_total_file_count', $count, HOUR_IN_SECONDS);
        
        return $count;
    }
    
    /**
     * Count files recursively in a directory
     */
    private function count_files_recursive($dir, $text_only = false) {
        if (!is_dir($dir)) {
            return 0;
        }
        
        $count = 0;
        try {
            $iterator = new RecursiveIteratorIterator(
                new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
                RecursiveIteratorIterator::SELF_FIRST
            );
            
            foreach ($iterator as $file) {
                if ($file->isFile()) {
                    // Always only count scannable text-based files
                    $ext = strtolower($file->getExtension());
                    if (in_array($ext, array('php', 'js', 'css', 'html', 'htm', 'txt', 'json', 'xml'))) {
                        $count++;
                    }
                }
            }
        } catch (Exception $e) {
            // Skip directories we can't read
        }
        
        return $count;
    }
    
    /**
     * Count total WordPress files (AJAX endpoint)
     */
    public function count_total_files() {
        if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'vmpfence_yara_scan')) {
            wp_send_json_error(array('message' => 'Security check failed'));
            return;
        }
        
        $total = $this->get_total_file_count();
        wp_send_json_success(array('total_files' => $total));
    }
    
    /**
     * Add file to whitelist
     */
    public function whitelist_file() {
        if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'vmpfence_yara_scan')) {
            wp_send_json_error(array('message' => 'Security check failed'));
            return;
        }
        
        $file_hash = isset($_POST['file_hash']) ? sanitize_text_field(wp_unslash($_POST['file_hash'])) : '';
        
        if (empty($file_hash)) {
            wp_send_json_error(array('message' => 'File hash required'));
            return;
        }
        
        // Get current threat files and whitelisted files
        $threat_files = get_option('vmpfence_yara_threat_files', array());
        $whitelisted_files = get_option('vmpfence_yara_whitelisted_files', array());
        
        // Find the threat file
        $threat_data = null;
        foreach ($threat_files as $hash => $data) {
            if ($hash === $file_hash) {
                $threat_data = $data;
                break;
            }
        }
        
        if (!$threat_data) {
            wp_send_json_error(array('message' => 'Threat file not found'));
            return;
        }
        
        // Add to whitelist
        $whitelisted_files[$file_hash] = array(
            'file_path' => $threat_data['file_path'],
            'file_name' => $threat_data['file_name'],
            'threat_description' => $threat_data['threat_description'],
            'matched_rules' => $threat_data['matched_rules'],
            'whitelisted_at' => current_time('mysql'),
            'whitelisted_by' => get_current_user_id()
        );
        
        // Remove from threat files
        unset($threat_files[$file_hash]);
        
        // Save both
        update_option('vmpfence_yara_whitelisted_files', $whitelisted_files);
        update_option('vmpfence_yara_threat_files', $threat_files);
        
        wp_send_json_success(array('message' => 'File whitelisted successfully'));
    }
    
    /**
     * Remove file from whitelist (move back to threats)
     */
    public function unwhitelist_file() {
        if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'vmpfence_yara_scan')) {
            wp_send_json_error(array('message' => 'Security check failed'));
            return;
        }
        
        $file_hash = isset($_POST['file_hash']) ? sanitize_text_field(wp_unslash($_POST['file_hash'])) : '';
        
        if (empty($file_hash)) {
            wp_send_json_error(array('message' => 'File hash required'));
            return;
        }
        
        $threat_files = get_option('vmpfence_yara_threat_files', array());
        $whitelisted_files = get_option('vmpfence_yara_whitelisted_files', array());
        
        if (!isset($whitelisted_files[$file_hash])) {
            wp_send_json_error(array('message' => 'Whitelisted file not found'));
            return;
        }
        
        // Move back to threats
        $threat_files[$file_hash] = array(
            'file_path' => $whitelisted_files[$file_hash]['file_path'],
            'file_name' => $whitelisted_files[$file_hash]['file_name'],
            'threat_description' => $whitelisted_files[$file_hash]['threat_description'],
            'matched_rules' => $whitelisted_files[$file_hash]['matched_rules'],
            'detected_at' => current_time('mysql')
        );
        
        unset($whitelisted_files[$file_hash]);
        
        update_option('vmpfence_yara_threat_files', $threat_files);
        update_option('vmpfence_yara_whitelisted_files', $whitelisted_files);
        
        wp_send_json_success(array('message' => 'File removed from whitelist'));
    }
    
    /**
     * Delete threat file from filesystem
     */
    public function delete_threat_file() {
        if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'vmpfence_yara_scan')) {
            wp_send_json_error(array('message' => 'Security check failed'));
            return;
        }
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Unauthorized'));
            return;
        }
        
        $file_hash = isset($_POST['file_hash']) ? sanitize_text_field(wp_unslash($_POST['file_hash'])) : '';
        
        if (empty($file_hash)) {
            wp_send_json_error(array('message' => 'File hash required'));
            return;
        }
        
        $threat_files = get_option('vmpfence_yara_threat_files', array());
        
        if (!isset($threat_files[$file_hash])) {
            wp_send_json_error(array('message' => 'Threat file not found'));
            return;
        }
        
        $threat_data = $threat_files[$file_hash];
        $file_path = $threat_data['file_path'];
        
        // Convert relative path to absolute if needed
        if (!$this->path_is_absolute($file_path)) {
            $file_path = ABSPATH . ltrim($file_path, '/');
        }
        
        // Verify file exists
        if (!file_exists($file_path)) {
            // File already deleted, just remove from tracking
            unset($threat_files[$file_hash]);
            update_option('vmpfence_yara_threat_files', $threat_files);
            wp_send_json_success(array(
                'message' => 'File was already deleted. Removed from threat list.',
                'file_path' => $file_path
            ));
            return;
        }
        
        // Security check: Ensure file is within WordPress directory
        $wp_root = ABSPATH;
        $real_file_path = realpath($file_path);
        $real_wp_root = realpath($wp_root);
        
        if ($real_file_path === false) {
            wp_send_json_error(array(
                'message' => 'Invalid file path',
                'file_path' => $file_path
            ));
            return;
        }
        
        if (strpos($real_file_path, $real_wp_root) !== 0) {
            wp_send_json_error(array(
                'message' => 'Security violation: Cannot delete files outside WordPress directory',
                'attempted_path' => $file_path,
                'resolved_path' => $real_file_path
            ));
            return;
        }
        
        // Attempt to delete the file
        if (@unlink($real_file_path)) {
            // Remove from threat files
            unset($threat_files[$file_hash]);
            update_option('vmpfence_yara_threat_files', $threat_files);
            
            // Log the action
            VMPFence_Secure_Logger::log('info', 'YARA deleted threat file', ['file_path' => $real_file_path, 'hash' => $file_hash]);
            
            wp_send_json_success(array(
                'message' => 'Threat file deleted successfully',
                'file_path' => $real_file_path,
                'file_name' => $threat_data['file_name']
            ));
        } else {
            wp_send_json_error(array(
                'message' => 'Failed to delete file. Check file permissions.',
                'file_path' => $real_file_path,
                'error' => error_get_last()
            ));
        }
    }
    
    /**
     * Mark threat file as fixed
     */
    public function mark_fixed() {
        if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'vmpfence_yara_scan')) {
            wp_send_json_error(array('message' => 'Security check failed'));
            return;
        }
        
        $file_hash = isset($_POST['file_hash']) ? sanitize_text_field(wp_unslash($_POST['file_hash'])) : '';
        
        if (empty($file_hash)) {
            wp_send_json_error(array('message' => 'File hash required'));
            return;
        }
        
        $threat_files = get_option('vmpfence_yara_threat_files', array());
        
        if (!isset($threat_files[$file_hash])) {
            wp_send_json_error(array('message' => 'Threat file not found'));
            return;
        }
        
        // Remove from threats (it's now clean)
        unset($threat_files[$file_hash]);
        update_option('vmpfence_yara_threat_files', $threat_files);
        
        wp_send_json_success(array('message' => 'File marked as fixed'));
    }
    
    /**
     * Get all threat files
     */
    public function get_threats() {
        if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'vmpfence_yara_scan')) {
            wp_send_json_error(array('message' => 'Security check failed'));
            return;
        }
        
        $threat_files = get_option('vmpfence_yara_threat_files', array());
        
        // Optional: filter by session_id
        $session_id = isset($_POST['session_id']) ? sanitize_text_field(wp_unslash($_POST['session_id'])) : '';
        
        if (!empty($session_id)) {
            $filtered_threats = array();
            foreach ($threat_files as $hash => $threat) {
                if (isset($threat['session_id']) && $threat['session_id'] === $session_id) {
                    $filtered_threats[$hash] = $threat;
                }
            }
            wp_send_json_success(array('threats' => $filtered_threats, 'session_id' => $session_id));
        } else {
            wp_send_json_success(array('threats' => $threat_files));
        }
    }
    
    /**
     * Get all whitelisted files
     */
    public function get_whitelisted() {
        if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'vmpfence_yara_scan')) {
            wp_send_json_error(array('message' => 'Security check failed'));
            return;
        }
        
        $whitelisted_files = get_option('vmpfence_yara_whitelisted_files', array());
        wp_send_json_success(array('whitelisted' => $whitelisted_files));
    }
    
    /**
     * Integrity Pre-Filter: Check file integrity against WordPress.org before YARA scan
     * This reduces false positives by skipping verified WordPress core/plugin/theme files
     * 
     * @since 1.0.0
     */
    public function integrity_pre_filter() {
        // Increase timeout for plugin/theme checksum downloads
        set_time_limit(300); // 5 minutes
        @ini_set('max_execution_time', '300');
        
        // Verify nonce
        if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'vmpfence_yara_scan')) {
            wp_send_json_error(array('message' => 'Security check failed'));
            return;
        }
        
        // Check permissions
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Unauthorized'));
            return;
        }
        
        try {
            $files_data = isset($_POST['files']) ? json_decode(wp_unslash($_POST['files']), true) : array();
            
            if (empty($files_data)) {
                throw new Exception('No files provided');
            }
            
            // Use existing integrity checker
            require_once VMPFENCE_PLUGIN_DIR . 'includes/class-wp-integrity-checker.php';
            $integrity_checker = new VMPFence_WP_Integrity_Checker();
            
            // Ensure trusted hashes database exists
            if (!VMPFence_Trusted_Hashes_DB::has_data()) {
                $trusted_hashes_array = $this->build_trusted_hash_database($integrity_checker);
                VMPFence_Trusted_Hashes_DB::insert_hashes_batched($trusted_hashes_array, 500);
            }
            
            $trusted_files = array();
            $untrusted_files = array();
            $integrity_issues = array();
            
            // Filter files
            foreach ($files_data as $file) {
                $file_path = isset($file['path']) ? $file['path'] : '';
                
                if (empty($file_path)) {
                    continue;
                }
                
                // Normalize path: convert backslashes to forward slashes (Windows compatibility)
                $incoming_path = str_replace('\\', '/', $file_path);
                
                // Detect if path is absolute (Windows C:/ or Unix /)
                $is_absolute = (preg_match('/^[A-Za-z]:/', $incoming_path) || substr($incoming_path, 0, 1) === '/');
                
                if ($is_absolute) {
                    // Convert absolute path to relative
                    $abspath_normalized = str_replace('\\', '/', rtrim(ABSPATH, '/'));
                    if (strpos($incoming_path, $abspath_normalized) === 0) {
                        $relative_path = ltrim(substr($incoming_path, strlen($abspath_normalized)), '/');
                    } else {
                        // Path outside WordPress root - skip it
                        continue;
                    }
                } else {
                    // Already relative, just clean up
                    $relative_path = ltrim($incoming_path, '/');
                }
                
                $full_path = ABSPATH . $relative_path;
                
                if (!file_exists($full_path)) {
                    continue;
                }
                
                // Check if file is in trusted database
                $hash_data = VMPFence_Trusted_Hashes_DB::get_hash($relative_path);
                if ($hash_data !== null) {
                    $expected_hash = $hash_data['hash'];
                    $source = $hash_data['source'];
                    
                    // Special handling for whitelisted premium plugins
                    if ($expected_hash === 'WHITELISTED' && $source === 'premium_plugin_whitelist') {
                        // AUTO-TRUST whitelisted premium plugins without hash verification
                        $trusted_files[] = array(
                            'path' => $relative_path,
                            'source' => $source,
                            'version' => isset($hash_data['version']) ? $hash_data['version'] : '',
                            'status' => 'whitelisted_premium',
                            'plugin' => isset($hash_data['plugin']) ? $hash_data['plugin'] : 'unknown'
                        );
                    } else {
                        // Regular checksum verification for WordPress.org plugins
                        $actual_hash = md5_file($full_path);
                        
                        if ($actual_hash === $expected_hash) {
                            // TRUSTED - Skip YARA
                            $trusted_files[] = array(
                                'path' => $relative_path,
                                'source' => $source,
                                'version' => isset($hash_data['version']) ? $hash_data['version'] : '',
                                'status' => 'verified_clean'
                            );
                        } else {
                            // MODIFIED - Send to YARA
                            $untrusted_files[] = $file;
                            $integrity_issues[] = array(
                                'path' => $relative_path,
                                'type' => 'hash_mismatch',
                                'source' => $source,
                                'severity' => 'medium',
                                'expected_hash' => $expected_hash,
                                'actual_hash' => $actual_hash
                            );
                        }
                    }
                } else {
                    // UNKNOWN SOURCE - Send to YARA
                    $untrusted_files[] = $file;
                }
            }
            
            $total_files = count($files_data);
            $trusted_count = count($trusted_files);
            $reduction_percentage = $total_files > 0 ? round(($trusted_count / $total_files) * 100, 2) : 0;
            
            wp_send_json_success(array(
                'trusted_files' => $trusted_files,
                'untrusted_files' => $untrusted_files,
                'integrity_issues' => $integrity_issues,
                'total_files' => $total_files,
                'trusted_count' => $trusted_count,
                'untrusted_count' => count($untrusted_files),
                'reduction_percentage' => $reduction_percentage,
                'message' => sprintf(
                    'Verified %d/%d files (%.1f%% reduction)',
                    $trusted_count,
                    $total_files,
                    $reduction_percentage
                )
            ));
            
        } catch (Exception $e) {
            wp_send_json_error(array('message' => $e->getMessage()));
        }
    }
    
    /**
     * Build trusted file hash database from WordPress.org
     * Uses official checksums for WordPress core, plugins, and themes
     * PLUS whitelisted premium/commercial plugins to prevent false positives
     * 
     * @param VMPFence_WP_Integrity_Checker $integrity_checker
     * @return array Associative array of file paths => hash data
     */
    private function build_trusted_hash_database($integrity_checker) {
        $trusted_hashes = array();
        
        // Load premium plugin whitelist
        if (!class_exists('VMPFence_Premium_Plugin_Whitelist')) {
            require_once VMPFENCE_PLUGIN_DIR . 'includes/class-premium-plugin-whitelist.php';
        }
        
        // Get WordPress Core checksums
        $wp_version = get_bloginfo('version');
        $core_checksums = $integrity_checker->get_official_checksums('core', $wp_version);
        
        if ($core_checksums && is_array($core_checksums)) {
            // WordPress.org API returns checksums nested by version: {"6.8.3": {"file.php": "hash", ...}}
            // Extract the actual file checksums from the version key
            if (isset($core_checksums[$wp_version]) && is_array($core_checksums[$wp_version])) {
                VMPFence_Secure_Logger::log('debug', 'Extracting core checksums from version key', ['version' => $wp_version, 'count' => count($core_checksums[$wp_version])]);
                $core_checksums = $core_checksums[$wp_version];
            }
            
            foreach ($core_checksums as $file_path => $md5_hash) {
                // Skip invalid entries
                if (!is_string($file_path) || !is_string($md5_hash) || empty($file_path) || empty($md5_hash)) {
                    continue;
                }
                
                $trusted_hashes[$file_path] = array(
                    'hash' => $md5_hash,
                    'source' => 'wordpress_core',
                    'version' => $wp_version
                );
            }
        }
        
        // Get Plugin checksums
        // Note: Plugin checksums are pre-downloaded by background job (WP-Cron)
        // This ensures instant loading without blocking the integrity check
        if (!function_exists('get_plugins')) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }
        
        $all_plugins = get_plugins();
        $plugin_count = 0;
        $plugin_file_count = 0;
        $plugins_pending = array(); // Track plugins that need background refresh
        
        foreach ($all_plugins as $plugin_path => $plugin_data) {
            $plugin_slug = dirname($plugin_path);
            
            // Skip plugins in root (single-file plugins)
            if ($plugin_slug === '.') {
                $plugin_slug = basename($plugin_path, '.php');
            }
            
            $plugin_version = isset($plugin_data['Version']) ? $plugin_data['Version'] : '';
            
            if (empty($plugin_version)) {
                continue;
            }
            
            // Try to get from cache FIRST (fast path)
            $cache_key = 'vmpfence_plugin_checksums_' . $plugin_slug . '_' . $plugin_version;
            $plugin_checksums = get_transient($cache_key);
            
            // If not in cache, mark for background download (don't block here)
            if ($plugin_checksums === false) {
                $plugins_pending[] = array('slug' => $plugin_slug, 'version' => $plugin_version);
                
                // Fallback: Try to download synchronously only if there are very few files to scan
                // This prevents the first scan from being completely ineffective
                if (count($all_plugins) <= 3) { // Only if 3 or fewer plugins
                    VMPFence_Secure_Logger::log('debug', 'Cache miss for plugin, downloading synchronously (small plugin count)', ['slug' => $plugin_slug, 'version' => $plugin_version]);
                    $plugin_checksums = $integrity_checker->get_official_checksums('plugin', $plugin_slug, $plugin_version);
                } else {
                    VMPFence_Secure_Logger::log('debug', 'Cache miss for plugin, will download in background', ['slug' => $plugin_slug, 'version' => $plugin_version]);
                    continue; // Skip this plugin for now, background job will cache it
                }
            }
            
            if ($plugin_checksums && is_array($plugin_checksums)) {
                $plugin_count++;
                foreach ($plugin_checksums as $file_path => $md5_hash) {
                    if (!is_string($file_path) || !is_string($md5_hash) || empty($file_path) || empty($md5_hash)) {
                        continue;
                    }
                    
                    $trusted_hashes[$file_path] = array(
                        'hash' => $md5_hash,
                        'source' => 'wordpress_plugin',
                        'plugin' => $plugin_slug,
                        'version' => $plugin_version
                    );
                    $plugin_file_count++;
                }
            }
        }
        
        // If we have plugins pending, schedule background refresh
        if (!empty($plugins_pending)) {
            VMPFence_Secure_Logger::log('debug', 'Plugins need checksum download, scheduling background job', ['count' => count($plugins_pending)]);
            
            // Trigger immediate background refresh if not already scheduled
            if (!wp_next_scheduled('vmpfence_refresh_integrity_cache')) {
                wp_schedule_single_event(time() + 30, 'vmpfence_refresh_integrity_cache');
            }
        }
        
        VMPFence_Secure_Logger::log('info', 'Loaded checksums for plugins', [
            'plugins' => $plugin_count,
            'files' => $plugin_file_count,
            'pending' => count($plugins_pending)
        ]);
        
        // =====================================================================
        // PREMIUM PLUGIN WHITELIST: Trust all files from known premium plugins
        // =====================================================================
        $whitelisted_count = 0;
        $whitelisted_file_count = 0;
        
        foreach ($all_plugins as $plugin_path => $plugin_data) {
            $plugin_slug = dirname($plugin_path);
            if ($plugin_slug === '.') {
                $plugin_slug = basename($plugin_path, '.php');
            }
            
            // Check if plugin is in whitelist
            if (VMPFence_Premium_Plugin_Whitelist::is_whitelisted($plugin_slug)) {
                $whitelisted_count++;
                
                // Get all files from this plugin and mark as trusted
                $plugin_files = VMPFence_Premium_Plugin_Whitelist::get_plugin_files($plugin_slug);
                
                foreach ($plugin_files as $file_path) {
                    // Mark file as trusted WITHOUT requiring checksum verification
                    // Use a special hash value to indicate whitelist-based trust
                    $trusted_hashes[$file_path] = array(
                        'hash' => 'WHITELISTED',
                        'source' => 'premium_plugin_whitelist',
                        'plugin' => $plugin_slug,
                        'version' => $plugin_data['Version'] ?? 'unknown'
                    );
                    $whitelisted_file_count++;
                }
            }
        }
        
        VMPFence_Secure_Logger::log('info', 'Whitelisted premium plugins auto-trusted', [
            'plugins' => $whitelisted_count,
            'files' => $whitelisted_file_count
        ]);
        
        // Get Theme checksums (same fast-path approach)
        $current_theme = wp_get_theme();
        if ($current_theme->exists()) {
            $theme_slug = $current_theme->get_stylesheet();
            $theme_version = $current_theme->get('Version');
            
            if (!empty($theme_version)) {
                // Try cache first
                $theme_cache_key = 'vmpfence_theme_checksums_' . $theme_slug . '_' . $theme_version;
                $theme_checksums = get_transient($theme_cache_key);
                
                // If not cached and we have few plugins, download sync as fallback
                if ($theme_checksums === false && count($all_plugins) <= 3) {
                    VMPFence_Secure_Logger::log('debug', 'Cache miss for theme, downloading synchronously', ['slug' => $theme_slug, 'version' => $theme_version]);
                    $theme_checksums = $integrity_checker->get_official_checksums('theme', $theme_slug, $theme_version);
                } else if ($theme_checksums === false) {
                    VMPFence_Secure_Logger::log('debug', 'Cache miss for theme, will download in background', ['slug' => $theme_slug, 'version' => $theme_version]);
                }
                
                if ($theme_checksums && is_array($theme_checksums)) {
                    $theme_file_count = 0;
                    foreach ($theme_checksums as $file_path => $md5_hash) {
                        if (!is_string($file_path) || !is_string($md5_hash) || empty($file_path) || empty($md5_hash)) {
                            continue;
                        }
                        
                        $trusted_hashes[$file_path] = array(
                            'hash' => $md5_hash,
                            'source' => 'wordpress_theme',
                            'theme' => $theme_slug,
                            'version' => $theme_version
                        );
                        $theme_file_count++;
                    }
                    VMPFence_Secure_Logger::log('debug', 'Loaded checksums for theme', ['slug' => $theme_slug, 'files' => $theme_file_count]);
                }
            }
        }
        
        return $trusted_hashes;
    }
    
    /**
     * AJAX endpoint: Detect missing or updated plugins that need checksum caching
     */
    public function detect_missing_plugins() {
        // Verify nonce
        if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'vmpfence_yara_scan')) {
            wp_send_json_error(array('message' => 'Security check failed'));
            return;
        }
        
        // Check permissions
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Unauthorized'));
            return;
        }
        
        try {
            // Load background processor
            if (!class_exists('VMPFence_AJAX_Background_Processor')) {
                require_once VMPFENCE_PLUGIN_DIR . 'includes/class-ajax-background-processor.php';
            }
            
            $processor = VMPFence_AJAX_Background_Processor::get_instance();
            
            // Detect missing plugins
            $missing_plugins = $processor->detect_missing_plugins();
            
            // Get cache statistics
            $stats = $processor->get_cache_statistics();
            
            wp_send_json_success(array(
                'missing_plugins' => $missing_plugins,
                'total_missing' => count($missing_plugins),
                'statistics' => $stats
            ));
            
        } catch (Exception $e) {
            wp_send_json_error(array(
                'message' => 'Error detecting missing plugins: ' . $e->getMessage()
            ));
        }
    }
    
    /**
     * AJAX endpoint: Download and cache checksums for a single plugin
     */
    public function download_plugin_checksum() {
        // Verify nonce
        if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'vmpfence_yara_scan')) {
            wp_send_json_error(array('message' => 'Security check failed'));
            return;
        }
        
        // Check permissions
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Unauthorized'));
            return;
        }
        
        try {
            $plugin_slug = isset($_POST['plugin_slug']) ? sanitize_text_field(wp_unslash($_POST['plugin_slug'])) : '';
            $plugin_version = isset($_POST['plugin_version']) ? sanitize_text_field(wp_unslash($_POST['plugin_version'])) : '';
            
            if (empty($plugin_slug) || empty($plugin_version)) {
                wp_send_json_error(array(
                    'message' => 'Plugin slug and version are required'
                ));
                return;
            }
            
            // Load background processor
            if (!class_exists('VMPFence_AJAX_Background_Processor')) {
                require_once VMPFENCE_PLUGIN_DIR . 'includes/class-ajax-background-processor.php';
            }
            
            $processor = VMPFence_AJAX_Background_Processor::get_instance();
            
            // Download and cache plugin checksums
            $result = $processor->download_single_plugin_checksums($plugin_slug, $plugin_version);
            
            if ($result['success']) {
                wp_send_json_success($result);
            } else {
                wp_send_json_error($result);
            }
            
        } catch (Exception $e) {
            wp_send_json_error(array(
                'message' => 'Error downloading plugin checksums: ' . $e->getMessage(),
                'success' => false
            ));
        }
    }
}

// Initialize AJAX handler
new VMPFence_YARA_Scanner_Ajax();
