<?php
defined('ABSPATH') || exit;

/**
 * WPFort Security - Scan Recovery System
 * 
 * This system monitors active scans and automatically recovers them if they stall
 * due to server issues, timeouts, or other non-file-specific problems. It uses
 * more frequent checkpoints and robust recovery without skipping any files.
 */

/**
 * Check for stalled scans and attempt recovery
 * 
 * @return void
 */
function wpsec_monitor_active_scans() {
    global $wpdb;
    
    // Find all scans marked as running
    $scan_id_options = $wpdb->get_results(
        "SELECT option_name FROM {$wpdb->options} 
         WHERE option_name LIKE 'wpsec_scan_%_status' AND option_value = 'running'",
        ARRAY_A
    );
    
    // Extract scan IDs
    $active_scans = [];
    foreach ($scan_id_options as $option) {
        $scan_id = str_replace(['wpsec_scan_', '_status'], '', $option['option_name']);
        $active_scans[] = $scan_id;
    }
    
    // Monitor each active scan
    foreach ($active_scans as $scan_id) {
        wpsec_check_scan_progress($scan_id);
    }
}

/**
 * Check if a scan is making progress
 * 
 * @param string $scan_id The scan ID to check
 * @return void
 */
function wpsec_check_scan_progress($scan_id) {
    // Get timing data
    $last_update = get_option('wpsec_scan_' . $scan_id . '_last_progress_update', 0);
    $current_time = time();
    $inactivity_time = $current_time - $last_update;
    
    // Get progress data
    $progress = get_option('wpsec_scan_' . $scan_id . '_progress', 0);
    $files_scanned = get_option('wpsec_scan_' . $scan_id . '_files_scanned', 0);
    $checkpoint = get_option('wpsec_scan_' . $scan_id . '_current_checkpoint', 0);
    
    // Set thresholds based on hosting environment
    // Be more aggressive with checks on shared hosting
    $stall_threshold = 180; // 3 minutes
    
    // Check if scan appears stalled
    if ($inactivity_time > $stall_threshold && $last_update > 0) {
        wpsec_debug_log("⚠️ WPFort: Scan {$scan_id} appears stalled (no updates for {$inactivity_time} seconds)", 'warning');
        wpsec_debug_log("📊 WPFort: Progress: {$progress}%, Files: {$files_scanned}, Checkpoint: {$checkpoint}", 'info');
        
        // Log for diagnostics
        wpsec_log_stalled_scan($scan_id, $inactivity_time, $progress, $files_scanned);
        
        // Attempt to restart the scan batch
        wpsec_restart_scan_batch($scan_id);
    }
}

/**
 * Log details about a stalled scan for diagnostics
 * 
 * @param string $scan_id The scan ID
 * @param int $stall_time How long the scan has been stalled
 * @param int $progress Current progress percentage
 * @param int $files_scanned Number of files scanned
 * @return void
 */
function wpsec_log_stalled_scan($scan_id, $stall_time, $progress, $files_scanned) {
    $stalled_scans = get_option('wpsec_stalled_scans', []);
    $stalled_scans[] = [
        'scan_id' => $scan_id,
        'detected_at' => gmdate('Y-m-d H:i:s'),
        'stall_time' => $stall_time,
        'progress' => $progress,
        'files_scanned' => $files_scanned,
        'memory_used' => memory_get_usage(true),
        'memory_limit' => ini_get('memory_limit'),
        'server_load' => function_exists('sys_getloadavg') ? sys_getloadavg() : 'unavailable'
    ];
    
    // Keep only the most recent 20 entries
    if (count($stalled_scans) > 20) {
        $stalled_scans = array_slice($stalled_scans, -20);
    }
    
    update_option('wpsec_stalled_scans', $stalled_scans);
}

/**
 * Restart a stalled scan batch from the last checkpoint
 * 
 * @param string $scan_id The scan ID to restart
 * @return void
 */
function wpsec_restart_scan_batch($scan_id) {
    // Clear any existing scheduled events for this scan
    wp_clear_scheduled_hook('wpsec_run_scheduled_scan_batch', array($scan_id));
    
    // Update the last progress time to avoid repeated restarts
    update_option('wpsec_scan_' . $scan_id . '_last_progress_update', time());
    
    // Log the restart attempt
    wpsec_debug_log("🔄 WPFort: Attempting to restart scan batch for {$scan_id}", 'info');
    
    // Schedule a new scan batch to run immediately
    wp_schedule_single_event(time() + 10, 'wpsec_run_scheduled_scan_batch', array($scan_id));
    
    // Track restart attempts
    $restart_count = get_option('wpsec_scan_' . $scan_id . '_restart_count', 0);
    update_option('wpsec_scan_' . $scan_id . '_restart_count', $restart_count + 1);
    
    // If we've tried restarting too many times, add additional diagnostics
    if ($restart_count > 3) {
        wpsec_debug_log("⚠️ WPFort: Multiple restart attempts for scan {$scan_id}. Adding extra diagnostics.", 'warning');
        
        // Create diagnostics report
        $report = "Scan ID: {$scan_id}\n";
        $report .= "Memory: " . memory_get_usage(true) . " bytes\n";
        $report .= "Memory Peak: " . memory_get_peak_usage(true) . " bytes\n";
        $report .= "Memory Limit: " . ini_get('memory_limit') . "\n";
        $report .= "Max Execution Time: " . ini_get('max_execution_time') . "\n";
        
        // Save diagnostic report
        update_option('wpsec_scan_' . $scan_id . '_diagnostic_report', $report);
    }
}

/**
 * Schedule the scan monitor to run regularly
 */
function wpsec_schedule_scan_monitor() {
    if (!wp_next_scheduled('wpsec_monitor_scans')) {
        // Run every minute to catch stalls faster
        wp_schedule_event(time(), 'one_minute', 'wpsec_monitor_scans');
    }
}

// Add custom schedule intervals
function wpsec_add_cron_intervals($schedules) {
    $schedules['one_minute'] = [
        'interval' => 60,
        'display'  => __('Every Minute', 'wpfortai-security')
    ];
    return $schedules;
}
add_filter('cron_schedules', 'wpsec_add_cron_intervals');

// Hook the scan monitor
add_action('wpsec_monitor_scans', 'wpsec_monitor_active_scans');

// Initialize the scheduler
add_action('init', 'wpsec_schedule_scan_monitor');
