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

/**
 * Lightweight Scan Monitoring System
 * 
 * This file provides functions to monitor scan performance with minimal overhead
 * to prevent timeouts and ensure scans complete successfully.
 */

/**
 * Track scan performance for a specific file
 * 
 * @param string $scan_id The scan ID
 * @param string $file_path The file being scanned
 * @param string $operation The operation being performed (started, completed, error)
 * @param array $metrics Optional performance metrics
 */
function wpsec_track_scan_file($scan_id, $file_path, $operation, $metrics = []) {
    if (!$scan_id || !$file_path) {
        return;
    }
    
    // Skip detailed tracking for most files to reduce overhead
    // Only track 1 in every 100 files to maintain performance
    static $track_counter = 0;
    $track_counter++;
    
    // Always track errors regardless of sampling
    $is_error = ($operation === 'error');
    $should_track = $is_error || ($track_counter % 100 === 0);
    
    if (!$should_track) {
        // For completed operations, just update the last progress timestamp
        if ($operation === 'completed') {
            update_option('wpsec_scan_' . $scan_id . '_last_progress_update', time());
            
            // Store the last scanned file path
            update_option('wpsec_scan_' . $scan_id . '_last_scanned_file', $file_path);
        }
        return;
    }
    
    // Simplified metrics to reduce memory usage
    $simple_metrics = [
        'time' => time(),
        'file_size' => file_exists($file_path) ? filesize($file_path) : 0,
        'file_type' => pathinfo($file_path, PATHINFO_EXTENSION)
    ];
    
    // Merge with provided metrics but only keep essential ones
    if (isset($metrics['duration'])) {
        $simple_metrics['duration'] = $metrics['duration'];
    }
    
    // For errors, keep the error message
    if ($is_error && isset($metrics['error'])) {
        $simple_metrics['error'] = $metrics['error'];
    }
    
    // Lightweight performance tracking - only store the last 50 entries
    $performance_log = json_decode(get_option('wpsec_scan_' . $scan_id . '_performance_log', '[]'), true);
    
    // Add entry using much less data
    $performance_log[] = [
        'f' => basename($file_path), // Just store filename not full path
        'o' => $operation[0],        // Use single letter code: s=started, c=completed, e=error
        'm' => $simple_metrics       // Simplified metrics
    ];
    
    // Keep log size very small - only keep the most recent 50 entries
    if (count($performance_log) > 50) {
        $performance_log = array_slice($performance_log, -50);
    }
    
    // Store updated log - use a smaller option name to reduce DB overhead
    update_option('wpsec_scan_' . $scan_id . '_perf_log', json_encode($performance_log));
    
    // If this is a completed operation, update the last completed time
    if ($operation === 'completed') {
        update_option('wpsec_scan_' . $scan_id . '_last_progress_update', time());
    }
    
    // Keep track of largest files scanned
    if ($operation === 'started' && isset($metrics['file_size']) && $metrics['file_size'] > 0) {
        $large_files = json_decode(get_option('wpsec_scan_' . $scan_id . '_large_files', '[]'), true);
        
        // Add to large files list if it's in the top 20 by size
        $large_files[] = [
            'file' => $file_path,
            'size' => $metrics['file_size'],
            'type' => $metrics['file_type']
        ];
        
        // Sort by size descending
        usort($large_files, function($a, $b) {
            return $b['size'] <=> $a['size'];
        });
        
        // Keep only top 20
        $large_files = array_slice($large_files, 0, 20);
        
        update_option('wpsec_scan_' . $scan_id . '_large_files', json_encode($large_files));
    }
    
    // Track files by type for performance analysis
    if ($operation === 'completed' && isset($metrics['duration']) && isset($metrics['file_type'])) {
        $file_type = empty($metrics['file_type']) ? 'unknown' : $metrics['file_type'];
        $file_type_stats = json_decode(get_option('wpsec_scan_' . $scan_id . '_file_type_stats', '{}'), true);
        
        if (!isset($file_type_stats[$file_type])) {
            $file_type_stats[$file_type] = [
                'count' => 0,
                'total_size' => 0,
                'total_time' => 0,
                'max_time' => 0,
                'max_time_file' => '',
                'avg_time' => 0
            ];
        }
        
        // Update stats for this file type
        $file_type_stats[$file_type]['count']++;
        $file_type_stats[$file_type]['total_size'] += $metrics['file_size'];
        $file_type_stats[$file_type]['total_time'] += $metrics['duration'];
        
        // Track file with maximum processing time
        if ($metrics['duration'] > $file_type_stats[$file_type]['max_time']) {
            $file_type_stats[$file_type]['max_time'] = $metrics['duration'];
            $file_type_stats[$file_type]['max_time_file'] = $file_path;
        }
        
        // Calculate average
        $file_type_stats[$file_type]['avg_time'] = 
            $file_type_stats[$file_type]['total_time'] / $file_type_stats[$file_type]['count'];
            
        update_option('wpsec_scan_' . $scan_id . '_file_type_stats', json_encode($file_type_stats));
    }
}

/**
 * Analyze scan performance and detect potential issues
 * 
 * @param string $scan_id The scan ID
 * @return array Analysis results
 */
function wpsec_analyze_scan_performance($scan_id) {
    if (!$scan_id) {
        return [
            'status' => 'error',
            'message' => 'Invalid scan ID'
        ];
    }
    
    // Get scan data
    $status = get_option('wpsec_scan_' . $scan_id . '_status', '');
    $progress = get_option('wpsec_scan_' . $scan_id . '_progress', 0);
    $files_scanned = get_option('wpsec_scan_' . $scan_id . '_files_scanned', 0);
    $total_files = get_option('wpsec_scan_' . $scan_id . '_total_files', 0);
    $start_time = get_option('wpsec_scan_' . $scan_id . '_start', 0);
    $current_time = time();
    $elapsed_time = $current_time - $start_time;
    
    // Get performance data
    $large_files = json_decode(get_option('wpsec_scan_' . $scan_id . '_large_files', '[]'), true);
    $file_type_stats = json_decode(get_option('wpsec_scan_' . $scan_id . '_file_type_stats', '{}'), true);
    $performance_log = json_decode(get_option('wpsec_scan_' . $scan_id . '_performance_log', '[]'), true);
    $timeout_diagnostics = json_decode(get_option('wpsec_scan_' . $scan_id . '_timeout_diagnostics', '{}'), true);
    
    // Analyze performance
    $slowest_file_types = [];
    
    foreach ($file_type_stats as $type => $stats) {
        if ($stats['count'] >= 5) { // Only consider types with enough samples
            $slowest_file_types[] = [
                'type' => $type,
                'avg_time' => $stats['avg_time'],
                'count' => $stats['count'],
                'max_time' => $stats['max_time'],
                'max_time_file' => $stats['max_time_file']
            ];
        }
    }
    
    // Sort by average time descending
    usort($slowest_file_types, function($a, $b) {
        return $b['avg_time'] <=> $a['avg_time'];
    });
    
    // Get just the top 5 slowest types
    $slowest_file_types = array_slice($slowest_file_types, 0, 5);
    
    // Calculate scan rate
    $scan_rate = $elapsed_time > 0 ? round($files_scanned / $elapsed_time, 2) : 0;
    $estimated_completion = $scan_rate > 0 ? round(($total_files - $files_scanned) / $scan_rate) : 'unknown';
    
    // Detect if scan has been interrupted multiple times at same file
    $stall_patterns = [];
    $last_scanned_file = get_option('wpsec_scan_' . $scan_id . '_last_scanned_file', '');
    
    if (!empty($last_scanned_file) && !empty($performance_log)) {
        // Count occurrences of the last scanned file in the log
        $file_occurrences = array_filter($performance_log, function($entry) use ($last_scanned_file) {
            return $entry['file'] === $last_scanned_file;
        });
        
        if (count($file_occurrences) > 1) {
            $stall_patterns[] = [
                'file' => $last_scanned_file,
                'occurrences' => count($file_occurrences),
                'type' => pathinfo($last_scanned_file, PATHINFO_EXTENSION),
                'size' => file_exists($last_scanned_file) ? filesize($last_scanned_file) : 0
            ];
        }
    }
    
    // Get recommendations based on analysis
    $recommendations = [];
    
    // Timeout diagnostics
    if (!empty($timeout_diagnostics)) {
        // Check for memory issues
        if (isset($timeout_diagnostics['memory_usage']) && isset($timeout_diagnostics['memory_limit'])) {
            $memory_usage = floatval(str_replace('MB', '', $timeout_diagnostics['memory_usage']));
            $memory_limit = wp_convert_hr_to_bytes($timeout_diagnostics['memory_limit']) / 1024 / 1024;
            
            if ($memory_usage > 0.8 * $memory_limit) {
                $recommendations[] = [
                    'type' => 'memory',
                    'severity' => 'high',
                    'message' => 'Memory usage is very high (' . $memory_usage . 'MB/' . $memory_limit . 'MB). Consider increasing memory_limit in php.ini or wp-config.php.'
                ];
            }
        }
        
        // Check for large file issues
        if (isset($timeout_diagnostics['file_size']) && $timeout_diagnostics['file_size'] !== 'Unknown') {
            $file_size = floatval(str_replace('KB', '', $timeout_diagnostics['file_size']));
            
            if ($file_size > 5000) { // 5MB+
                $recommendations[] = [
                    'type' => 'large_file',
                    'severity' => 'medium',
                    'message' => 'Scan stalled on a very large file (' . $timeout_diagnostics['file_size'] . '). Consider adding it to the exclusion list.'
                ];
            }
        }
    }
    
    // Check for problematic file types
    if (!empty($slowest_file_types)) {
        $slowest_type = $slowest_file_types[0];
        if ($slowest_type['avg_time'] > 2.0) { // More than 2 seconds average processing time
            $recommendations[] = [
                'type' => 'slow_file_type',
                'severity' => 'medium',
                'message' => "Files with extension '." . $slowest_type['type'] . "' are taking excessive time to scan (" . round($slowest_type['avg_time'], 2) . "s average). Consider excluding this file type."
            ];
        }
    }
    
    return [
        'scan_id' => $scan_id,
        'status' => $status,
        'progress' => $progress,
        'files_scanned' => $files_scanned,
        'total_files' => $total_files,
        'elapsed_time' => $elapsed_time,
        'scan_rate' => $scan_rate . ' files/sec',
        'estimated_completion' => is_numeric($estimated_completion) ? human_time_diff(0, $estimated_completion) : $estimated_completion,
        'large_files' => array_slice($large_files, 0, 10),
        'slowest_file_types' => $slowest_file_types,
        'stall_patterns' => $stall_patterns,
        'timeout_diagnostics' => $timeout_diagnostics,
        'recommendations' => $recommendations
    ];
}

/**
 * Get the scan monitor status as an admin dashboard widget
 */
function wpsec_get_scan_monitor_status() {
    $scan_id = get_option('wpsec_current_scan_id', '');
    
    if (!$scan_id) {
        return '<p>No recent scan data available.</p>';
    }
    
    $status = get_option('wpsec_scan_' . $scan_id . '_status', '');
    $progress = get_option('wpsec_scan_' . $scan_id . '_progress', 0);
    $start_time = get_option('wpsec_scan_' . $scan_id . '_start', 0);
    
    $html = '<div class="wpsec-scan-monitor">';
    $html .= '<h3>Latest Scan: ' . esc_html($scan_id) . '</h3>';
    $html .= '<p>Status: <strong>' . esc_html(ucfirst($status)) . '</strong></p>';
    $html .= '<p>Progress: <strong>' . esc_html($progress) . '%</strong></p>';
    
    if ($status === 'error') {
        $error_message = get_option('wpsec_scan_' . $scan_id . '_error', '');
        $html .= '<p>Error: <strong>' . esc_html($error_message) . '</strong></p>';
        
        $html .= '<p><a href="' . esc_url(admin_url('admin.php?page=wpsec-scan-diagnostics&scan_id=' . $scan_id)) . '" class="button">View Diagnostics</a></p>';
    }
    
    $html .= '</div>';
    
    return $html;
}

// Hook this function into our scan process
function wpsec_inject_scan_monitoring() {
    // Add to the scan.php file
    add_action('wpsec_before_scan_file', 'wpsec_track_scan_file_start', 10, 2);
    add_action('wpsec_after_scan_file', 'wpsec_track_scan_file_complete', 10, 3);
    add_action('wpsec_scan_file_error', 'wpsec_track_scan_file_error', 10, 3);
}

/**
 * Track when a file scan begins
 */
function wpsec_track_scan_file_start($scan_id, $file_path) {
    wpsec_track_scan_file($scan_id, $file_path, 'started');
}

/**
 * Track when a file scan completes
 */
function wpsec_track_scan_file_complete($scan_id, $file_path, $metrics) {
    wpsec_track_scan_file($scan_id, $file_path, 'completed', $metrics);
}

/**
 * Track when a file scan encounters an error
 */
function wpsec_track_scan_file_error($scan_id, $file_path, $error) {
    wpsec_track_scan_file($scan_id, $file_path, 'error', ['error' => $error]);
}

// Initialize scan monitoring
wpsec_inject_scan_monitoring();
