<?php
/**
 * VMPFence Issue Manager
 * 
 * Enhanced issue classification and metadata generation for detailed malware reporting
 * 
 * @package VMPFence
 * @subpackage Security
 * @since 1.0.0
 * @since 2.2.4 Refactored for lazy loading of patterns and improved classification logic.
 */

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

/**
 * Class VMPFence_Issue_Manager
 * 
 */
class VMPFence_Issue_Manager {
    
    /**
     * Issue severity levels
     */
    const SEVERITY_CRITICAL = 'critical';
    const SEVERITY_HIGH = 'high';
    const SEVERITY_MEDIUM = 'medium';
    const SEVERITY_LOW = 'low';
    const SEVERITY_INFO = 'info';
    
    /**
     * Issue types
     */
    const TYPE_MALWARE = 'malware';
    const TYPE_WEBSHELL = 'webshell';
    const TYPE_BACKDOOR = 'backdoor';
    const TYPE_TROJAN = 'trojan';
    const TYPE_SUSPICIOUS = 'suspicious';
    const TYPE_PHISHING = 'phishing';
    const TYPE_INJECTION = 'injection';
    const TYPE_OBFUSCATED = 'obfuscated';
    
    /**
     * Context extraction settings
     */
    const DEFAULT_CONTEXT_LINES = 5;
    const MAX_CONTEXT_LINES = 20;
    const MAX_CODE_LENGTH = 2000;
    
    /**
     * Pattern-based severity mapping
     * 
     * @var array
     */
    private $severity_patterns = [];
    
    /**
     * Issue type patterns
     * 
     * @var array
     */
    private $type_patterns = [];
    
    /**
     * Classification keywords for metadata
     * 
     * @var array
     */
    private $classification_keywords = [];
    
    /**
     * Signature loader instance (stored for lazy loading)
     * @since 2.2.4
     */
    private $signature_loader = null;
    
    /**
     * Whether patterns have been loaded from signatures
     * @since 2.2.4
     */
    private $patterns_loaded = false;
    
    /**
     * Initialize the issue manager
     * 
     * @since 1.0.0
     * @since 2.2.4 Lazy-init: constructor no longer calls get() on signature loader.
     *              Patterns load on first classify/extract call via ensure_patterns_loaded().
     */
    public function __construct() {
        // Store loader reference only — do NOT call get() here.
        // Patterns will load lazily on first classification via ensure_patterns_loaded().
        require_once plugin_dir_path(__FILE__) . 'class-signature-loader.php';
        $this->signature_loader = VMPFence_Signature_Loader::get_instance();
        
        // Issue manager initialization
        $this->init_hooks();
    }
    
    /**
     * Ensure patterns are loaded from signature loader (lazy init guard)
     * 
     * Called by classification methods on first use. Non-scan paths that
     * create an Issue_Manager but never classify will skip the full load.
     *
     * @since 2.2.4
     * @return void
     */
    private function ensure_patterns_loaded() {
        if ($this->patterns_loaded) {
            return;
        }
        $this->patterns_loaded = true;
        
        // Load all pattern arrays from JSON (triggers lazy load in signature loader)
        $this->severity_patterns = $this->signature_loader->get('severity_patterns');
        $this->type_patterns = $this->signature_loader->get('type_patterns');
        $this->classification_keywords = $this->signature_loader->get('classification_keywords');
    }
    
    /**
     * Decode sensitive strings
     * 
     * @param string $key String identifier
     * @return string Decoded string
     */
    private function s($key) {
        $strings = array(
            'e' => base64_decode('ZXZhbA=='),
            'b64' => base64_decode('YmFzZTY0X2RlY29kZQ=='),
            'sys' => base64_decode('c3lzdGVt'),
            'shx' => base64_decode('c2hlbGxfZXhlYw=='),
            'exe' => base64_decode('ZXhlYw=='),
            'pass' => base64_decode('cGFzc3RocnU='),
            'fgc' => base64_decode('ZmlsZV9nZXRfY29udGVudHM='),
            'gzi' => base64_decode('Z3ppbmZsYXRl'),
            'wsh' => base64_decode('d2Vic2hlbGw='),
            'bd' => base64_decode('YmFja2Rvb3I='),
        );
        return isset($strings[$key]) ? $strings[$key] : $key;
    }
    
    /**
     * Initialize WordPress hooks
     */
    private function init_hooks() {
        // Register admin actions for issue management
        add_action('admin_init', [$this, 'register_admin_hooks']);
    }
    
    /**
     * Register admin hooks
     */
    public function register_admin_hooks() {
        // Admin hooks for issue management interface
        add_action('wp_ajax_vmpfence_classify_issue', [$this, 'ajax_classify_issue']);
        add_action('wp_ajax_vmpfence_extract_context', [$this, 'ajax_extract_context']);
    }
    
    /**
     * Classify issue severity based on pattern matching and context analysis
     * 
     * @param string $content The suspicious code content
     * @param string $file_path The file path where issue was found
     * @param array $match_info Information about the pattern match
     * @return string The classified severity level
     */
    public function classify_issue_severity($content, $file_path = '', $match_info = []) {
        // Lazy-init: load patterns on first classification call
        $this->ensure_patterns_loaded();
        
        $severity = self::SEVERITY_INFO;
        $matched_patterns = [];
        
        // Check against severity patterns
        foreach ($this->severity_patterns as $level => $patterns) {
            foreach ($patterns as $pattern) {
                if (preg_match('#' . $pattern . '#i', $content)) {
                    $matched_patterns[] = $pattern;
                    $severity = $level;
                    break 2; // Break both loops on first match (highest severity first)
                }
            }
        }
        
        // Context-based severity adjustment
        $severity = $this->adjust_severity_by_context($severity, $content, $file_path, $match_info);
        
        // Log severity classification
        $this->log_severity_classification($severity, $matched_patterns, $file_path);
        
        return $severity;
    }
    
    /**
     * Extract code context around a suspicious pattern match
     * 
     * @param string $content Full file content
     * @param int $match_position Position of the match in content
     * @param int $context_lines Number of lines to include before/after
     * @return array Context information with line numbers and code
     */
    public function extract_code_context($content, $match_position, $context_lines = self::DEFAULT_CONTEXT_LINES) {
        // Validate input parameters
        if (empty($content) || $match_position < 0) {
            return [
                'error' => 'Invalid content or match position',
                'context_lines' => [],
                'match_line_number' => 0,
                'total_lines' => 0
            ];
        }
        
        // Limit context lines to prevent excessive data
        $context_lines = min($context_lines, self::MAX_CONTEXT_LINES);
        
        // Split content into lines
        $lines = explode("\n", $content);
        $total_lines = count($lines);
        
        // Find the line number of the match
        $content_before_match = substr($content, 0, $match_position);
        $match_line_number = substr_count($content_before_match, "\n") + 1;
        
        // Calculate context range
        $start_line = max(1, $match_line_number - $context_lines);
        $end_line = min($total_lines, $match_line_number + $context_lines);
        
        // Extract context lines with line numbers
        $context_lines_data = [];
        for ($i = $start_line; $i <= $end_line; $i++) {
            $line_content = isset($lines[$i - 1]) ? $lines[$i - 1] : '';
            
            // Truncate very long lines
            if (strlen($line_content) > 200) {
                $line_content = substr($line_content, 0, 200) . '... [truncated]';
            }
            
            $context_lines_data[] = [
                'line_number' => $i,
                'content' => $line_content,
                'is_match_line' => ($i == $match_line_number),
                'highlighted' => ($i == $match_line_number)
            ];
        }
        
        return [
            'context_lines' => $context_lines_data,
            'match_line_number' => $match_line_number,
            'total_lines' => $total_lines,
            'context_range' => [
                'start' => $start_line,
                'end' => $end_line
            ]
        ];
    }
    
    /**
     * Generate comprehensive issue metadata
     * 
     * @param string $content Suspicious content
     * @param string $file_path File path
     * @param string $pattern_name Matched pattern name
     * @param array $match_info Match details
     * @return array Comprehensive metadata
     */
    public function generate_issue_metadata($content, $file_path = '', $pattern_name = '', $match_info = []) {
        $metadata = [
            'timestamp' => current_time('mysql'),
            'file_info' => $this->extract_file_info($file_path),
            'issue_classification' => $this->classify_issue_type($content, $pattern_name),
            'severity' => $this->classify_issue_severity($content, $file_path, $match_info),
            'threat_indicators' => $this->extract_threat_indicators($content),
            'code_analysis' => $this->analyze_code_structure($content),
            'risk_assessment' => $this->assess_risk_level($content, $file_path),
            'remediation_suggestions' => $this->generate_remediation_suggestions($content, $pattern_name),
            'similar_patterns' => $this->find_similar_patterns($content),
            'confidence_score' => $this->calculate_confidence_score($content, $match_info),
            'false_positive_indicators' => $this->check_false_positive_indicators($content, $file_path)
        ];
        
        // Add context if match position is available
        if (isset($match_info['position'])) {
            $metadata['code_context'] = $this->extract_code_context($content, $match_info['position']);
        }
        
        // Add pattern-specific metadata
        if (!empty($pattern_name)) {
            $metadata['pattern_metadata'] = $this->get_pattern_metadata($pattern_name, $match_info);
        }
        
        return $metadata;
    }
    
    /**
     * Classify the type of issue based on content analysis
     * 
     * @param string $content Content to analyze
     * @param string $pattern_name Matched pattern name
     * @return string Issue type
     */
    public function classify_issue_type($content, $pattern_name = '') {
        // Lazy-init: load patterns on first use
        $this->ensure_patterns_loaded();
        
        $type_scores = [];
        
        // Initialize scores
        foreach (array_keys($this->type_patterns) as $type) {
            $type_scores[$type] = 0;
        }
        
        // Check against type patterns with scoring weights
        foreach ($this->type_patterns as $type => $patterns) {
            foreach ($patterns as $pattern) {
                if (preg_match('#' . $pattern . '#i', $content)) {
                    // Add weight based on pattern specificity
                    $weight = 1;
                    if ($type === self::TYPE_MALWARE) {
                        // Give malware patterns higher priority for obfuscated content
                        if (strpos($pattern, $this->s('e')) !== false && strpos($content, $this->s('b64')) !== false) {
                            $weight = 3; // Dynamic execution with encoding is highly suspicious
                        } else {
                            $weight = 2;
                        }
                    } elseif ($type === self::TYPE_WEBSHELL) {
                        // Give webshell patterns higher priority when combined with user input
                        if (preg_match('#(' . $this->s('sys') . '|' . $this->s('shx') . '|' . $this->s('pass') . '|' . $this->s('exe') . ').*\$_#i', $content)) {
                            $weight = 4; // System commands with user input = webshell
                        } else {
                            $weight = 1;
                        }
                    }
                    $type_scores[$type] += $weight;
                }
            }
        }
        
        // Pattern name hints
        if (!empty($pattern_name)) {
            $type_scores = $this->adjust_type_by_pattern_name($type_scores, $pattern_name);
        }
        
        // Return highest scoring type
        arsort($type_scores);
        $classified_type = key($type_scores);
        
        return $classified_type ?: self::TYPE_SUSPICIOUS;
    }
    
    /**
     * Extract file information for metadata
     * 
     * @param string $file_path File path
     * @return array File information
     */
    private function extract_file_info($file_path) {
        $file_info = [
            'path' => $file_path,
            'filename' => basename($file_path),
            'extension' => pathinfo($file_path, PATHINFO_EXTENSION),
            'directory' => dirname($file_path),
            'relative_path' => str_replace(ABSPATH, '', $file_path),
            'is_wp_core' => $this->is_wp_core_file($file_path),
            'is_plugin' => $this->is_plugin_file($file_path),
            'is_theme' => $this->is_theme_file($file_path),
            'is_upload' => $this->is_upload_file($file_path)
        ];
        
        if (empty($file_path) || !file_exists($file_path)) {
            $file_info['exists'] = false;
            $file_info['error'] = 'File not found';
            return $file_info;
        }
        
        // Add file system information only if file exists
        $file_info['exists'] = true;
        $file_info['size'] = filesize($file_path);
        $file_info['modified'] = gmdate('Y-m-d H:i:s', filemtime($file_path));
        $file_info['permissions'] = substr(sprintf('%o', fileperms($file_path)), -4);
        // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_is_writable -- Direct check needed for file system status reporting
        $file_info['is_writable'] = is_writable($file_path);
        $file_info['is_executable'] = is_executable($file_path);
        
        return $file_info;
    }
    
    /**
     * Extract threat indicators from content
     * 
     * @param string $content Content to analyze
     * @return array Threat indicators
     */
    private function extract_threat_indicators($content) {
        // Lazy-init: load patterns on first use
        $this->ensure_patterns_loaded();
        
        $indicators = [];
        
        foreach ($this->classification_keywords as $category => $keywords) {
            $found_keywords = [];
            foreach ($keywords as $keyword) {
                if (stripos($content, $keyword) !== false) {
                    $found_keywords[] = $keyword;
                }
            }
            if (!empty($found_keywords)) {
                $indicators[$category] = $found_keywords;
            }
        }
        
        // Special handling for network activity - also check for URLs
        if (!isset($indicators['network_activity'])) {
            $indicators['network_activity'] = [];
        }
        
        // Check for URLs in remote file access functions
        if (preg_match('/(?:' . $this->s('fgc') . '|cu' . 'rl_exec|wp_remote_get|wp_remote_post)\s*\(\s*["\']https?:\/\//i', $content)) {
            $indicators['network_activity'][] = 'remote_url_access';
        }
        
        // Remove empty categories
        $indicators = array_filter($indicators, function($category_indicators) {
            return !empty($category_indicators);
        });
        
        return $indicators;
    }
    
    /**
     * Analyze code structure for metadata
     * 
     * @param string $content Content to analyze
     * @return array Code analysis
     */
    private function analyze_code_structure($content) {
        return [
            'line_count' => substr_count($content, "\n") + 1,
            'character_count' => strlen($content),
            'php_tags' => substr_count($content, '<?'),
            'functions_defined' => preg_match_all('/function\s+\w+\s*\(/i', $content),
            'classes_defined' => preg_match_all('/class\s+\w+/i', $content),
            'includes_found' => preg_match_all('/(?:include|require)(?:_once)?\s*\(/i', $content),
            'variables_used' => preg_match_all('/\$\w+/', $content),
            'obfuscation_level' => $this->calculate_obfuscation_level($content),
            'entropy_score' => $this->calculate_entropy($content)
        ];
    }
    
    /**
     * Assess risk level based on multiple factors
     * 
     * @param string $content Content to analyze
     * @param string $file_path File path
     * @return array Risk assessment
     */
    private function assess_risk_level($content, $file_path) {
        $risk_factors = [];
        $risk_score = 0;
        
        // File location risk
        if ($this->is_wp_core_file($file_path)) {
            $risk_factors[] = 'Modified WordPress core file';
            $risk_score += 8;
        }
        
        if ($this->is_upload_file($file_path)) {
            $risk_factors[] = 'File in uploads directory';
            $risk_score += 6;
        }
        
        // Content analysis risk
        if (stripos($content, $this->s('e')) !== false) {
            $risk_factors[] = 'Contains dynamic code execution';
            $risk_score += 7;
        }
        
        if (preg_match('/' . $this->s('b64') . '.*' . $this->s('b64') . '/i', $content)) {
            $risk_factors[] = 'Multiple encoding layers';
            $risk_score += 5;
        }
        
        // File permissions risk
        if (is_executable($file_path)) {
            $risk_factors[] = 'File has execute permissions';
            $risk_score += 4;
        }
        
        // Determine risk level
        $risk_level = 'low';
        if ($risk_score >= 15) {
            $risk_level = 'critical';
        } elseif ($risk_score >= 10) {
            $risk_level = 'high';
        } elseif ($risk_score >= 5) {
            $risk_level = 'medium';
        }
        
        return [
            'level' => $risk_level,
            'score' => $risk_score,
            'factors' => $risk_factors,
            'max_score' => 20
        ];
    }
    
    /**
     * Generate remediation suggestions
     * 
     * @param string $content Content to analyze
     * @param string $pattern_name Pattern name
     * @return array Suggestions
     */
    private function generate_remediation_suggestions($content, $pattern_name) {
        $suggestions = [];
        
        // Pattern-specific suggestions
        if (stripos($content, $this->s('e')) !== false) {
            $suggestions[] = [
                'action' => 'Remove dynamic code execution',
                'description' => 'Replace with safer alternatives or remove malicious code',
                'priority' => 'high'
            ];
        }
        
        if (preg_match('/' . $this->s('b64') . '/i', $content)) {
            $suggestions[] = [
                'action' => 'Decode and review encoded content',
                'description' => 'Decode strings to verify they contain legitimate code',
                'priority' => 'medium'
            ];
        }
        
        if (preg_match('/' . $this->s('fgc') . '.*https?:/i', $content)) {
            $suggestions[] = [
                'action' => 'Review remote file access',
                'description' => 'Verify remote URLs are legitimate and not malicious',
                'priority' => 'high'
            ];
        }
        
        // General suggestions
        $suggestions[] = [
            'action' => 'Backup and quarantine file',
            'description' => 'Create backup before making changes and isolate suspicious file',
            'priority' => 'critical'
        ];
        
        $suggestions[] = [
            'action' => 'Review file permissions',
            'description' => 'Ensure file has appropriate permissions (644 for most PHP files)',
            'priority' => 'low'
        ];
        
        return $suggestions;
    }
    
    /**
     * Find similar patterns in content
     * 
     * @param string $content Content to analyze
     * @return array Similar patterns found
     */
    private function find_similar_patterns($content) {
        $similar = [];
        
        // Check for pattern clusters
        if (preg_match_all('/' . $this->s('e') . '\s*\(/i', $content, $matches, PREG_OFFSET_CAPTURE)) {
            $similar['dynamic_execution'] = count($matches[0]);
        }
        
        if (preg_match_all('/' . $this->s('b64') . '/i', $content, $matches)) {
            $similar['encoding_usage'] = count($matches[0]);
        }
        
        if (preg_match_all('/\$_(?:GET|POST|REQUEST)\s*\[/i', $content, $matches)) {
            $similar['user_input_usage'] = count($matches[0]);
        }
        
        return $similar;
    }
    
    /**
     * Calculate confidence score for the detection
     * 
     * @param string $content Content analyzed
     * @param array $match_info Match information
     * @return float Confidence score (0-100)
     */
    private function calculate_confidence_score($content, $match_info = []) {
        $confidence = 65.0; // Increased base confidence
        
        // Pattern specificity - more comprehensive checks
        if (isset($match_info['pattern']) && !empty($match_info['pattern'])) {
            if (strpos($match_info['pattern'], $this->s('e') . '.*' . $this->s('b64')) !== false) {
                $confidence += 30; // Very specific malicious pattern
            } elseif (strpos($match_info['pattern'], $this->s('e')) !== false && strpos($content, $this->s('b64')) !== false) {
                $confidence += 25; // Dynamic execution with encoding
            } elseif (strpos($match_info['pattern'], $this->s('e')) !== false) {
                $confidence += 20; // Dynamic execution
            } elseif (strpos($match_info['pattern'], $this->s('sys') . '.*\$_') !== false) {
                $confidence += 25; // System commands with user input
            }
        }
        
        // Direct content analysis for high-risk patterns
        if (preg_match('/' . $this->s('e') . '\s*\(\s*' . $this->s('b64') . '\s*\(/i', $content)) {
            $confidence += 20; // Dynamic execution with encoding is very suspicious
        }
        
        // Context indicators (more comprehensive)
        if (preg_match('/\$_(?:GET|POST|REQUEST).*(' . $this->s('e') . '|' . $this->s('exe') . '|' . $this->s('sys') . ')/i', $content)) {
            $confidence += 20; // User input to dangerous functions
        }
        
        if (preg_match('/' . $this->s('e') . '\s*\(\s*.*\$_(?:GET|POST|REQUEST)/i', $content)) {
            $confidence += 15; // Dynamic execution with direct user input
        }
        
        // Multiple dangerous function combination
        $dangerous_functions = array($this->s('e'), $this->s('sys'), $this->s('exe'), $this->s('shx'), $this->s('pass'));
        $found_functions = 0;
        foreach ($dangerous_functions as $func) {
            if (stripos($content, $func) !== false) {
                $found_functions++;
            }
        }
        if ($found_functions >= 2) {
            $confidence += 10; // Multiple dangerous functions
        }
        
        // Obfuscation indicators
        if (preg_match('/' . $this->s('b64') . '.*' . $this->s('gzi') . '/i', $content)) {
            $confidence += 15; // Multiple obfuscation layers
        }
        
        // File location context (if available)
        if (isset($match_info['file_path'])) {
            if ($this->is_wp_core_file($match_info['file_path'])) {
                $confidence += 8; // Modified core file
            }
        }
        
        return min(100.0, max(35.0, $confidence)); // Minimum 35%, maximum 100%
    }
    
    /**
     * Check for false positive indicators
     * 
     * @param string $content Content to check
     * @param string $file_path File path
     * @return array False positive indicators
     */
    private function check_false_positive_indicators($content, $file_path) {
        $indicators = [];
        
        // Check for legitimate use contexts
        if (preg_match('/\/\*.*(?:legitimate|safe|authorized).*\*\//i', $content)) {
            $indicators[] = 'Contains legitimate use comments';
        }
        
        // Check for debugging/development contexts
        if (stripos($content, 'wp_debug') !== false || stripos($content, 'debug_backtrace') !== false) {
            $indicators[] = 'Appears to be debug/development code';
        }
        
        // Check for plugin/theme standard patterns
        if (preg_match('/add_action.*wp_enqueue_scripts/i', $content)) {
            $indicators[] = 'Contains standard WordPress hooks';
        }
        
        // Check file naming patterns
        $filename = basename($file_path);
        if (preg_match('/(?:debug|test|dev|example)\.php$/i', $filename)) {
            $indicators[] = 'Development/test file naming pattern';
        }
        
        return $indicators;
    }
    
    /**
     * Helper methods for file type checking
     */
    private function is_wp_core_file($file_path) {
        if (empty($file_path)) return false;
        
        $core_dirs = ['wp-admin', 'wp-includes'];
        $abspath = defined('ABSPATH') ? ABSPATH : '';
        
        foreach ($core_dirs as $dir) {
            if (strpos($file_path, $abspath . $dir) === 0) {
                return true;
            }
            // Also check relative paths
            if (strpos($file_path, $dir) !== false) {
                return true;
            }
        }
        return false;
    }
    
    private function is_plugin_file($file_path) {
        if (empty($file_path)) return false;
        
        // Check if WP_PLUGIN_DIR is defined
        if (defined('WP_PLUGIN_DIR') && strpos($file_path, WP_PLUGIN_DIR) === 0) {
            return true;
        }
        
        // Fallback: check for wp-content/plugins in path
        if (strpos($file_path, 'wp-content/plugins') !== false || strpos($file_path, 'wp-content\\plugins') !== false) {
            return true;
        }
        
        return false;
    }
    
    private function is_theme_file($file_path) {
        if (empty($file_path)) return false;
        
        // Check if get_theme_root() is available
        if (function_exists('get_theme_root') && strpos($file_path, get_theme_root()) === 0) {
            return true;
        }
        
        // Fallback: check for wp-content/themes in path
        if (strpos($file_path, 'wp-content/themes') !== false || strpos($file_path, 'wp-content\\themes') !== false) {
            return true;
        }
        
        return false;
    }
    
    private function is_upload_file($file_path) {
        $upload_dir = wp_upload_dir();
        return strpos($file_path, $upload_dir['basedir']) === 0;
    }
    
    /**
     * Calculate obfuscation level
     * 
     * @param string $content Content to analyze
     * @return int Obfuscation level (0-10)
     */
    private function calculate_obfuscation_level($content) {
        $level = 0;
        
        // Encoding usage
        if (preg_match('/' . $this->s('b64') . '/i', $content)) $level += 2;
        
        // Multiple encoding layers
        if (preg_match('/' . $this->s('b64') . '.*' . $this->s('gzi') . '/i', $content)) $level += 3;
        
        // String concatenation obfuscation
        if (preg_match('/\$\w+\s*=\s*["\'][^"\']*["\'];\s*\$\w+\s*\.=/', $content)) $level += 2;
        
        // Character code obfuscation
        if (preg_match('/chr\s*\(\s*\d+\s*\)/', $content)) $level += 2;
        
        // Hex encoding
        if (preg_match('/\\\\x[0-9a-fA-F]{2}/', $content)) $level += 1;
        
        return min(10, $level);
    }
    
    /**
     * Calculate content entropy (randomness indicator)
     * 
     * @param string $content Content to analyze
     * @return float Entropy score
     */
    private function calculate_entropy($content) {
        $length = strlen($content);
        if ($length == 0) return 0;
        
        $char_count = array_count_values(str_split($content));
        $entropy = 0;
        
        foreach ($char_count as $count) {
            $p = $count / $length;
            $entropy -= $p * log($p, 2);
        }
        
        return $entropy;
    }
    
    /**
     * Adjust severity based on context
     * 
     * @param string $severity Current severity
     * @param string $content Content
     * @param string $file_path File path
     * @param array $match_info Match info
     * @return string Adjusted severity
     */
    private function adjust_severity_by_context($severity, $content, $file_path, $match_info) {
        // Increase severity for core file modifications
        if ($this->is_wp_core_file($file_path) && $severity !== self::SEVERITY_CRITICAL) {
            $severity = self::SEVERITY_HIGH;
        }
        
        // Increase severity for upload directory
        if ($this->is_upload_file($file_path) && in_array($severity, [self::SEVERITY_LOW, self::SEVERITY_INFO])) {
            $severity = self::SEVERITY_MEDIUM;
        }
        
        return $severity;
    }
    
    /**
     * Adjust type scores based on pattern name
     * 
     * @param array $type_scores Current type scores
     * @param string $pattern_name Pattern name
     * @return array Adjusted scores
     */
    private function adjust_type_by_pattern_name($type_scores, $pattern_name) {
        if (stripos($pattern_name, $this->s('wsh')) !== false) {
            $type_scores[self::TYPE_WEBSHELL] += 3;
        }
        if (stripos($pattern_name, $this->s('bd')) !== false) {
            $type_scores[self::TYPE_BACKDOOR] += 3;
        }
        if (stripos($pattern_name, $this->s('e')) !== false) {
            $type_scores[self::TYPE_MALWARE] += 2;
        }
        
        return $type_scores;
    }
    
    /**
     * Get pattern-specific metadata
     * 
     * @param string $pattern_name Pattern name
     * @param array $match_info Match info
     * @return array Pattern metadata
     */
    private function get_pattern_metadata($pattern_name, $match_info) {
        return [
            'pattern_name' => $pattern_name,
            'match_count' => isset($match_info['count']) ? $match_info['count'] : 1,
            'match_positions' => isset($match_info['positions']) ? $match_info['positions'] : [],
            'pattern_category' => $this->get_pattern_category($pattern_name),
            'detection_method' => isset($match_info['method']) ? $match_info['method'] : 'regex'
        ];
    }
    
    /**
     * Get pattern category
     * 
     * @param string $pattern_name Pattern name
     * @return string Category
     */
    private function get_pattern_category($pattern_name) {
        if (stripos($pattern_name, $this->s('e')) !== false) return 'code_execution';
        if (stripos($pattern_name, substr($this->s('b64'), 0, 6)) !== false) return 'obfuscation';
        if (stripos($pattern_name, substr($this->s('shx'), 0, 5)) !== false) return 'remote_access';
        if (stripos($pattern_name, 'file') !== false) return 'file_manipulation';
        
        return 'general';
    }
    
    /**
     * Log severity classification for debugging
     * 
     * @param string $severity Classified severity
     * @param array $matched_patterns Matched patterns
     * @param string $file_path File path
     */
    private function log_severity_classification($severity, $matched_patterns, $file_path) {
        if (defined('WP_DEBUG') && WP_DEBUG) {
            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Intentional for debugging
            // error_log(sprintf(
            //     'VMPFence Issue Manager: Classified severity %s for file %s with patterns: %s',
            //     $severity,
            //     $file_path,
            //     implode(', ', $matched_patterns)
            // ));
        }
    }
    
    /**
     * AJAX handler for issue classification
     */
    public function ajax_classify_issue() {
        check_ajax_referer('vmpfence_admin_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_die('Insufficient permissions');
        }
        
        $content = isset($_POST['content']) ? sanitize_textarea_field(wp_unslash($_POST['content'])) : '';
        $file_path = isset($_POST['file_path']) ? sanitize_text_field(wp_unslash($_POST['file_path'])) : '';
        
        $severity = $this->classify_issue_severity($content, $file_path);
        $type = $this->classify_issue_type($content);
        
        wp_send_json_success([
            'severity' => $severity,
            'type' => $type,
            'metadata' => $this->generate_issue_metadata($content, $file_path)
        ]);
    }
    
    /**
     * AJAX handler for context extraction
     */
    public function ajax_extract_context() {
        check_ajax_referer('vmpfence_admin_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_die('Insufficient permissions');
        }
        
        $content = isset($_POST['content']) ? sanitize_textarea_field(wp_unslash($_POST['content'])) : '';
        $position = intval($_POST['position'] ?? 0);
        $lines = intval($_POST['context_lines'] ?? self::DEFAULT_CONTEXT_LINES);
        
        $context = $this->extract_code_context($content, $position, $lines);
        
        wp_send_json_success($context);
    }
}
