<?php

/**
 * VMPFence Scan Exclusion Manager
 * 
 * Centralized exclusion logic that all scanners can use to respect
 * user-defined exclusion patterns consistently across the entire system.
 * 
 * This ensures that when a user excludes like wp-content/*, ALL scanners
 * (File Scanner, Malware Scanner, Content Safety Scanner, etc.) 
 * respect this setting.
 */

if (!defined('ABSPATH')) {
    exit;
}

class VMPFence_Scan_Exclusion_Manager {
    
    private static $instance = null;
    private $exclusion_patterns = [];
    private $debug_log = [];
    
    /**
     * Singleton pattern to ensure consistent exclusion logic
     */
    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    /**
     * Initialize exclusion patterns from scan options
     */
    public function initialize($scan_options = null) {
        $this->exclusion_patterns = [];
        
        if (!$scan_options) {
            // Try to get from global options if not provided
            $scan_options = get_option('vmpfence_scan_options', []);
        }
        
        if (isset($scan_options['scan_exclude']) && !empty($scan_options['scan_exclude'])) {
            // Split by any whitespace and filter out empty values
            $patterns = preg_split('/\s+/', trim($scan_options['scan_exclude']));
            $this->exclusion_patterns = array_filter($patterns);
            
            $this->debug_log[] = '[Exclusion Manager] Loaded ' . count($this->exclusion_patterns) . ' exclusion patterns: ' . implode(', ', $this->exclusion_patterns);
        } else {
            $this->debug_log[] = '[Exclusion Manager] No exclusion patterns found';
        }
    }
    
    /**
     * Check if a file path should be excluded from scanning
     */
    public function should_exclude_file($file_path) {
        if (empty($this->exclusion_patterns)) {
            return false;
        }
        
        // Normalize file path
        $normalized_file = str_replace('\\', '/', $file_path);
        $wordpress_root = str_replace('\\', '/', ABSPATH);
        $wordpress_root = rtrim($wordpress_root, '/') . '/';
        
        // Get relative path from WordPress root
        if (strpos($normalized_file, $wordpress_root) === 0) {
            $relative_path = substr($normalized_file, strlen($wordpress_root));
        } else {
            // Fallback: try without trailing slash
            $wordpress_root_no_slash = rtrim($wordpress_root, '/');
            if (strpos($normalized_file, $wordpress_root_no_slash) === 0) {
                $relative_path = substr($normalized_file, strlen($wordpress_root_no_slash));
                $relative_path = ltrim($relative_path, '/');
            } else {
                // Can't determine relative path, don't skip
                return false;
            }
        }
        
        return $this->matches_exclusion_pattern($relative_path);
    }
    
    /**
     * Check if a specific scanner type should be skipped based on exclusion patterns
     * This is a generic method that works for any scanner type or path
     */
    public function should_exclude_scanner_target($target_path) {
        if (empty($this->exclusion_patterns)) {
            return false;
        }
        
        // Normalize the target path (e.g., "wp-content/plugins", "wp-admin", "wp-content/themes/twentytwentythree")
        $normalized_target = str_replace('\\', '/', trim($target_path, '/'));
        
        foreach ($this->exclusion_patterns as $pattern) {
            $pattern = trim($pattern);
            if (empty($pattern)) continue;
            
            // Remove trailing /* if present for comparison
            $clean_pattern = rtrim($pattern, '/*');
            
            // Check if pattern matches or encompasses the target
            if ($clean_pattern === $normalized_target) {
                return true; // Exact match
            }
            
            // Check if target is within excluded pattern (e.g., pattern "wp-content/*" should exclude "wp-content/plugins")
            if (strpos($pattern, '*') !== false) {
                $pattern_prefix = str_replace('*', '', $pattern);
                $pattern_prefix = rtrim($pattern_prefix, '/');
                
                if (!empty($pattern_prefix) && strpos($normalized_target, $pattern_prefix . '/') === 0) {
                    return true; // Target is within excluded directory
                }
                
                if ($pattern_prefix === $normalized_target) {
                    return true; // Exact directory match
                }
            }
        }
        
        return false;
    }
    
    /**
     * Check if a relative path matches any exclusion pattern
     */
    private function matches_exclusion_pattern($relative_path) {
        foreach ($this->exclusion_patterns as $pattern) {
            $pattern = trim($pattern);
            
            if (empty($pattern) || $pattern === '*') {
                continue;
            }
            
            // Convert wildcard pattern to regex
            $regex_pattern = preg_quote($pattern, '/');
            $regex_pattern = str_replace('\*', '.*', $regex_pattern);
            
            // 1. Exact relative path match
            if (preg_match('/^' . $regex_pattern . '$/i', $relative_path)) {
                return true;
            }
            
            // 2. Filename-only match (ONLY if pattern doesn't contain directory separators)
            if (strpos($pattern, '/') === false && strpos($pattern, '*') === false) {
                $filename = basename($relative_path);
                if (preg_match('/^' . $regex_pattern . '$/i', $filename)) {
                    return true;
                }
            }
            
            // 3. Directory prefix match for wildcard patterns
            if (strpos($pattern, '*') !== false) {
                $prefix_pattern = str_replace('*', '', $pattern);
                $prefix_pattern = rtrim($prefix_pattern, '/');
                
                if (!empty($prefix_pattern)) {
                    $dir_prefix = $prefix_pattern . '/';
                    if (strpos($relative_path, $dir_prefix) === 0) {
                        return true;
                    }
                    
                    if ($relative_path === $prefix_pattern) {
                        return true;
                    }
                }
            }
        }
        
        return false;
    }
    
    /**
     * Get debug logs for troubleshooting
     */
    public function get_debug_log() {
        return $this->debug_log;
    }
    
    /**
     * Add debug message
     */
    public function add_debug($message) {
        $this->debug_log[] = $message;
    }
}