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

/**
 * Get a safe, sanitized snippet of code from a file
 * 
 * @param string $file_path Path to the file
 * @param array $detection Detection information from scan
 * @param int $context_lines Number of context lines to include before and after
 * @return array|WP_Error Code snippet information or error
 */
function wpsec_get_code_snippet($file_path, $detection, $context_lines = 3) {
    wpsec_log('Getting code snippet for file: ' . $file_path, 'info');
    
    // Normalize the file path
    $normalized_path = wpsec_normalize_scan_path($file_path);
    
    // Check if file exists
    if (!file_exists($normalized_path)) {
        wpsec_log('File not found for code snippet: ' . $normalized_path, 'error');
        return new WP_Error('file_not_found', 'File not found');
    }
    
    // Check file size before reading
    $file_size = filesize($normalized_path);
    if ($file_size > 5 * 1024 * 1024) { // 5MB limit
        wpsec_log('File too large for code snippet: ' . $normalized_path . ' (' . wpsec_format_file_size($file_size) . ')', 'error');
        return new WP_Error('file_too_large', 'File too large for inspection');
    }
    
    // Get file content
    $file_content = file_get_contents($normalized_path);
    if ($file_content === false) {
        wpsec_log('Failed to read file content for code snippet: ' . $normalized_path, 'error');
        return new WP_Error('file_read_failed', 'Failed to read file content');
    }
    
    // Get file extension
    $file_info = pathinfo($normalized_path);
    $extension = isset($file_info['extension']) ? strtolower($file_info['extension']) : '';
    
    // Initialize snippet data
    $snippet = [
        'file_path' => $file_path,
        'normalized_path' => $normalized_path,
        'file_size' => $file_size,
        'file_size_human' => wpsec_format_file_size($file_size),
        'extension' => $extension,
        'detection_type' => isset($detection['type']) ? $detection['type'] : 'unknown',
        'detection_name' => isset($detection['name']) ? $detection['name'] : 'Unknown Detection',
        'severity' => isset($detection['severity']) ? $detection['severity'] : 'unknown',
        'description' => isset($detection['description']) ? $detection['description'] : '',
        'line_count' => substr_count($file_content, "\n") + 1,
        'snippets' => []
    ];
    
    // Split file content into lines
    $lines = explode("\n", $file_content);
    
    // Find relevant code snippets based on detection type
    switch ($snippet['detection_type']) {
        case 'signature':
            // For signature detections, just show the first few lines as a sample
            $start_line = 0;
            $end_line = min(10, count($lines) - 1);
            $snippet['snippets'][] = wpsec_extract_code_snippet($lines, $start_line, $end_line, 'Signature match - showing first few lines as sample');
            break;
            
        case 'pattern':
            // For pattern detections, try to find the pattern in the code
            if (isset($detection['pattern'])) {
                $matches = wpsec_find_pattern_matches($file_content, $detection['pattern']);
                if (!empty($matches)) {
                    foreach ($matches as $match) {
                        $line_number = $match['line'];
                        $start_line = max(0, $line_number - $context_lines);
                        $end_line = min(count($lines) - 1, $line_number + $context_lines);
                        $snippet['snippets'][] = wpsec_extract_code_snippet($lines, $start_line, $end_line, 'Pattern match on line ' . ($line_number + 1));
                    }
                } else {
                    // If pattern not found, show the first few lines
                    $start_line = 0;
                    $end_line = min(10, count($lines) - 1);
                    $snippet['snippets'][] = wpsec_extract_code_snippet($lines, $start_line, $end_line, 'Pattern not found in current content - showing first few lines');
                }
            }
            break;
            
        case 'anomaly':
            // For anomaly detections, show the beginning, middle and end of the file
            // Beginning
            $start_line = 0;
            $end_line = min(5, count($lines) - 1);
            $snippet['snippets'][] = wpsec_extract_code_snippet($lines, $start_line, $end_line, 'File beginning');
            
            // Middle (if file is large enough)
            if (count($lines) > 20) {
                $middle_line = intval(count($lines) / 2);
                $start_line = max(0, $middle_line - 2);
                $end_line = min(count($lines) - 1, $middle_line + 2);
                $snippet['snippets'][] = wpsec_extract_code_snippet($lines, $start_line, $end_line, 'File middle section');
            }
            
            // End
            if (count($lines) > 10) {
                $start_line = max(0, count($lines) - 5);
                $end_line = count($lines) - 1;
                $snippet['snippets'][] = wpsec_extract_code_snippet($lines, $start_line, $end_line, 'File end');
            }
            break;
            
        case 'heuristic':
            // For heuristic detections, try to find suspicious functions or keywords
            $suspicious_keywords = [
                'eval', 'base64_decode', 'gzinflate', 'str_rot13', 'shell_exec', 
                'exec', 'system', 'passthru', 'popen', 'proc_open', 'assert'
            ];
            
            $found_matches = false;
            foreach ($suspicious_keywords as $keyword) {
                $matches = wpsec_find_pattern_matches($file_content, $keyword);
                if (!empty($matches)) {
                    foreach ($matches as $match) {
                        $line_number = $match['line'];
                        $start_line = max(0, $line_number - $context_lines);
                        $end_line = min(count($lines) - 1, $line_number + $context_lines);
                        $snippet['snippets'][] = wpsec_extract_code_snippet($lines, $start_line, $end_line, 'Suspicious keyword "' . $keyword . '" on line ' . ($line_number + 1));
                        $found_matches = true;
                        
                        // Limit to 3 snippets to avoid overwhelming response
                        if (count($snippet['snippets']) >= 3) {
                            break 2;
                        }
                    }
                }
            }
            
            if (!$found_matches) {
                // If no suspicious keywords found, show the first few lines
                $start_line = 0;
                $end_line = min(10, count($lines) - 1);
                $snippet['snippets'][] = wpsec_extract_code_snippet($lines, $start_line, $end_line, 'No specific suspicious code found - showing first few lines');
            }
            break;
            
        default:
            // For unknown detection types, show the first few lines
            $start_line = 0;
            $end_line = min(10, count($lines) - 1);
            $snippet['snippets'][] = wpsec_extract_code_snippet($lines, $start_line, $end_line, 'Showing first few lines');
            break;
    }
    
    return $snippet;
}

/**
 * Extract a code snippet from an array of lines
 * 
 * @param array $lines Array of code lines
 * @param int $start_line Start line number (0-indexed)
 * @param int $end_line End line number (0-indexed)
 * @param string $description Description of the snippet
 * @return array Snippet information
 */
function wpsec_extract_code_snippet($lines, $start_line, $end_line, $description = '') {
    $snippet_lines = [];
    
    for ($i = $start_line; $i <= $end_line; $i++) {
        if (isset($lines[$i])) {
            // Sanitize the line to prevent XSS
            $sanitized_line = htmlspecialchars($lines[$i], ENT_QUOTES, 'UTF-8');
            $snippet_lines[] = [
                'line_number' => $i + 1, // Convert to 1-indexed for display
                'content' => $sanitized_line
            ];
        }
    }
    
    return [
        'start_line' => $start_line + 1, // Convert to 1-indexed for display
        'end_line' => $end_line + 1, // Convert to 1-indexed for display
        'description' => $description,
        'lines' => $snippet_lines
    ];
}

/**
 * Find pattern matches in file content with line numbers
 * 
 * @param string $content File content
 * @param string $pattern Pattern to search for
 * @return array Matches with line numbers
 */
function wpsec_find_pattern_matches($content, $pattern) {
    $matches = [];
    $lines = explode("\n", $content);
    
    // Handle the pattern correctly
    $regex = $pattern;
    
    // If it doesn't look like a valid regex pattern with delimiters, add them
    if (substr($pattern, 0, 1) !== '/' && substr($pattern, -1) !== '/') {
        // This is a plain string or an undelimited regex pattern
        // Add delimiters and make case insensitive
        $regex = '/' . $pattern . '/i';
    }
    
    wpsec_log('Using regex pattern for search: ' . $regex, 'debug');
    
    // Search each line
    foreach ($lines as $line_number => $line) {
        // Use error suppression to handle invalid regex patterns gracefully
        $match_result = @preg_match($regex, $line);
        
        if ($match_result === false) {
            // Invalid regex pattern
            wpsec_log('Invalid regex pattern: ' . $regex, 'error');
            // Try a literal string search instead
            if (stripos($line, str_replace('\\\\', '\\', $pattern)) !== false) {
                $matches[] = [
                    'line' => $line_number,
                    'content' => $line
                ];
            }
        } elseif ($match_result === 1) {
            // Valid match
            $matches[] = [
                'line' => $line_number,
                'content' => $line
            ];
        }
    }
    
    return $matches;
}

/**
 * Get detailed file information for a malware detection
 * 
 * @param string $file_path Path to the file
 * @param array $detection Detection information
 * @return array|WP_Error File information or error
 */
function wpsec_get_malware_file_info($file_path, $detection = []) {
    // Normalize the file path
    $normalized_path = wpsec_normalize_scan_path($file_path);
    
    // Get basic file info
    $file_info = wpsec_get_file_info($normalized_path);
    
    // Add detection information
    $file_info['detection'] = $detection;
    
    // If we have a pattern or signature, try to get code snippets
    if (!empty($detection) && isset($detection['type'])) {
        $snippet = wpsec_get_code_snippet($normalized_path, $detection);
        if (!is_wp_error($snippet)) {
            $file_info['code_snippets'] = $snippet['snippets'];
        }
    }
    
    return $file_info;
}

/**
 * Get the latest scan results from the database
 * 
 * @return array Latest scan results or empty array
 */
function wpsec_get_latest_scan_results() {
    wpsec_log('Attempting to retrieve latest scan results', 'debug');
    
    // Use the same approach as the results endpoint
    $scan_id = get_option('wpsec_current_scan_id');
    
    if (!$scan_id) {
        wpsec_log('No current scan ID found', 'debug');
        
        // For development/testing, look for sample data
        if (defined('WPSEC_DEBUG') && WPSEC_DEBUG) {
            $sample_json = file_get_contents(dirname(__FILE__) . '/sample-scan-results.json');
            if ($sample_json) {
                $result_data = json_decode($sample_json, true);
                if (json_last_error() === JSON_ERROR_NONE) {
                    wpsec_log('Using sample scan results for development', 'debug');
                    return $result_data;
                }
            }
        }
        
        return [];
    }
    
    wpsec_log('Found current scan ID: ' . $scan_id, 'debug');
    
    // Get scan results from options
    $status = get_option('wpsec_scan_' . $scan_id . '_status', 'unknown');
    $start_time = get_option('wpsec_scan_' . $scan_id . '_start', 0);
    $end_time = get_option('wpsec_scan_' . $scan_id . '_end', 0);
    $duration = get_option('wpsec_scan_' . $scan_id . '_duration', 0);
    $files_scanned = get_option('wpsec_scan_' . $scan_id . '_files_scanned', 0);
    $infected_files_json = get_option('wpsec_scan_' . $scan_id . '_infected_files', '[]');
    $infected_count = get_option('wpsec_scan_' . $scan_id . '_infected_count', 0);
    
    // Decode infected files from JSON
    $infected_files = json_decode($infected_files_json, true);
    if (json_last_error() !== JSON_ERROR_NONE) {
        wpsec_log('Error decoding infected files JSON: ' . json_last_error_msg(), 'error');
        $infected_files = [];
    }
    
    if (empty($infected_files)) {
        wpsec_log('No infected files found in scan results', 'debug');
    } else {
        wpsec_log('Found ' . count($infected_files) . ' infected files in scan results', 'debug');
    }
    
    $result_data = [
        'status' => $status,
        'scan_id' => $scan_id,
        'started_at' => gmdate('Y-m-d H:i:s', $start_time),
        'infected_files' => $infected_files,
        'total_files_scanned' => $files_scanned,
        'infected_count' => $infected_count,
        'completed_at' => $end_time ? gmdate('Y-m-d H:i:s', $end_time) : null,
        'duration' => $duration
    ];
    
    return $result_data;
}

/**
 * Get detections for a specific file from the latest scan results
 * 
 * @param string $file_path Path to the file
 * @return array Array of detections for the file
 */
function wpsec_get_file_detections_from_latest_scan($file_path) {
    // Get the latest scan results from the database
    $scan_results = wpsec_get_latest_scan_results();
    
    if (empty($scan_results) || empty($scan_results['infected_files'])) {
        wpsec_log('No infected files found in scan results', 'debug');
        return [];
    }
    
    wpsec_log('Found ' . count($scan_results['infected_files']) . ' infected files in scan results', 'debug');
    
    // Normalize the input file path for comparison
    $normalized_input_path = wpsec_normalize_scan_path($file_path);
    $file_basename = basename($normalized_input_path);
    $file_md5 = @md5_file($normalized_input_path); // Get file hash for more reliable matching
    
    wpsec_log('Looking for file: ' . $normalized_input_path . ' (basename: ' . $file_basename . ')', 'debug');
    
    // Store best match (in case we don't find exact match but get close)
    $best_match = null;
    $best_match_score = 0;
    
    // Look for this file in the infected files list
    foreach ($scan_results['infected_files'] as $infected_file) {
        if (empty($infected_file['file_path'])) {
            continue;
        }
        
        $scan_file_path = $infected_file['file_path'];
        $scan_file_basename = basename($scan_file_path);
        
        // Track match scores to find best possible match
        $match_score = 0;
        
        // METHOD 1: Try exact path match
        if ($scan_file_path === $normalized_input_path) {
            wpsec_log('Found exact path match for: ' . $normalized_input_path, 'debug');
            return isset($infected_file['detections']) ? $infected_file['detections'] : [];
        }
        $match_score += 1; // Found a file to check at least
        
        // METHOD 2: Check if filenames match
        if ($scan_file_basename === $file_basename) {
            $match_score += 5; // Basename match is a strong indicator
            wpsec_log('Basename match: ' . $file_basename, 'debug');
            
            // METHOD 3: Try MD5 hash comparison if available
            if ($file_md5 && isset($infected_file['file_hash']) && $file_md5 === $infected_file['file_hash']) {
                wpsec_log('Found exact hash match for: ' . $file_basename, 'debug');
                return isset($infected_file['detections']) ? $infected_file['detections'] : [];
            }
            
            // METHOD 4: Check file size if available
            $file_size = @filesize($normalized_input_path);
            if ($file_size && isset($infected_file['file_size']) && $file_size == $infected_file['file_size']) {
                $match_score += 3; // Size match is a good indicator
                wpsec_log('File size match: ' . $file_size . ' bytes', 'debug');
            }
            
            // METHOD 5: Check file extension
            $file_ext = pathinfo($normalized_input_path, PATHINFO_EXTENSION);
            $scan_file_ext = pathinfo($scan_file_path, PATHINFO_EXTENSION);
            if ($file_ext === $scan_file_ext) {
                $match_score += 2; // Extension match adds confidence
            }
            
            // METHOD 6: Check common directory names in the path
            $input_parts = explode('/', str_replace('\\', '/', $normalized_input_path));
            $scan_parts = explode('/', str_replace('\\', '/', $scan_file_path));
            
            // Common subdirectories to check for, in order of importance
            $key_dirs = ['wp-content', 'plugins', 'themes', 'uploads'];
            
            foreach ($key_dirs as $dir) {
                $input_has_dir = in_array($dir, $input_parts);
                $scan_has_dir = in_array($dir, $scan_parts);
                
                if ($input_has_dir && $scan_has_dir) {
                    $match_score += 1; // Each matching directory adds confidence
                }
            }
        }
        
        // If this is the best match so far, save it
        if ($match_score > $best_match_score) {
            $best_match_score = $match_score;
            $best_match = $infected_file;
        }
    }
    
    // If we found a reasonably good match (matching at least filename + one other factor)
    if ($best_match && $best_match_score >= 6) { 
        wpsec_log('Found best match with score ' . $best_match_score . ' for file: ' . $file_basename, 'debug');
        return isset($best_match['detections']) ? $best_match['detections'] : [];
    }
    
    // Enable this for very loose matching if needed (just by filename)
    // if ($best_match && $best_match_score >= 5) {
    //     wpsec_log('Found loose match by filename only for: ' . $file_basename, 'debug');
    //     return isset($best_match['detections']) ? $best_match['detections'] : [];
    // }
    
    wpsec_log('No matching detections found for: ' . $file_basename, 'debug');
    return [];
}

/**
 * Get comprehensive file information for all detection types
 * 
 * @param string $file_path Normalized path to the file
 * @param array $detections Array of detections from scan results
 * @return array File information with code snippets
 */
function wpsec_get_comprehensive_file_info($file_path, $detections = []) {
    // Get basic file info
    $file_info = wpsec_get_file_info($file_path);
    
    // Add detection information
    $file_info['detections'] = $detections;
    $file_info['detection_count'] = count($detections);
    
    // Initialize snippets array
    $file_info['code_snippets'] = [];
    
    // If we have detections, try to get code snippets for each type
    if (!empty($detections)) {
        // Group detections by type to avoid duplicate snippets
        $detection_by_type = [];
        
        foreach ($detections as $detection) {
            $type = isset($detection['type']) ? $detection['type'] : 'unknown';
            
            if (!isset($detection_by_type[$type])) {
                $detection_by_type[$type] = [];
            }
            
            $detection_by_type[$type][] = $detection;
        }
        
        // Process each detection type
        foreach ($detection_by_type as $type => $type_detections) {
            // For pattern-based detections, use each pattern
            if ($type === 'potential' || $type === 'pattern') {
                foreach ($type_detections as $detection) {
                    $pattern = isset($detection['name']) ? $detection['name'] : 
                               (isset($detection['pattern']) ? $detection['pattern'] : null);
                    
                    if ($pattern) {
                        $snippet_result = wpsec_get_code_snippet($file_path, [
                            'type' => $type,
                            'pattern' => $pattern,
                            'name' => isset($detection['pattern_id']) ? $detection['pattern_id'] : 
                                     (isset($detection['name']) ? $detection['name'] : 'Unknown Pattern'),
                            'description' => isset($detection['description']) ? $detection['description'] : '',
                            'severity' => isset($detection['severity']) ? $detection['severity'] : 'unknown'
                        ]);
                        
                        if (!is_wp_error($snippet_result) && !empty($snippet_result['snippets'])) {
                            $file_info['code_snippets'] = array_merge(
                                $file_info['code_snippets'], 
                                $snippet_result['snippets']
                            );
                        }
                    }
                }
            } else {
                // For other detection types, just use the first one
                $detection = $type_detections[0];
                $snippet_result = wpsec_get_code_snippet($file_path, [
                    'type' => $type,
                    'name' => isset($detection['name']) ? $detection['name'] : 'Unknown Detection',
                    'description' => isset($detection['description']) ? $detection['description'] : '',
                    'severity' => isset($detection['severity']) ? $detection['severity'] : 'unknown'
                ]);
                
                if (!is_wp_error($snippet_result) && !empty($snippet_result['snippets'])) {
                    $file_info['code_snippets'] = array_merge(
                        $file_info['code_snippets'], 
                        $snippet_result['snippets']
                    );
                }
            }
        }
    } else {
        // If no detections provided, show basic file info with first few lines
        $snippet_result = wpsec_get_code_snippet($file_path, ['type' => 'unknown']);
        
        if (!is_wp_error($snippet_result) && !empty($snippet_result['snippets'])) {
            $file_info['code_snippets'] = $snippet_result['snippets'];
        }
    }
    
    // Add scan metadata if available
    if (!empty($detections) && isset($detections[0]['scan_time'])) {
        $file_info['scan_time'] = $detections[0]['scan_time'];
    }
    
    return $file_info;
}
