<?php
/**
 * Malware scanner for Security Pack.
 *
 * @package ArkHost_Security_Pack
 */

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

/**
 * Malware scanner with signature and hash-based detection.
 *
 * Uses two detection methods:
 * 1. Pattern-based: Regex signatures for suspicious code patterns
 * 2. Hash-based: Known malware file hashes (100% accurate, no false positives)
 *
 * Only scans plugins, themes, and uploads - NOT WordPress core.
 */
class ARKSP_Malware_Scanner {

	/**
	 * Option key for scan results.
	 *
	 * @var string
	 */
	const RESULTS_OPTION = 'arksp_malware_scan_results';

	/**
	 * Option key for last scan time.
	 *
	 * @var string
	 */
	const LAST_SCAN_OPTION = 'arksp_malware_last_scan';

	/**
	 * Option key for malware hash database.
	 *
	 * @var string
	 */
	const HASH_DB_OPTION = 'arksp_malware_hashes';

	/**
	 * Option key for hash database last update.
	 *
	 * @var string
	 */
	const HASH_DB_UPDATED_OPTION = 'arksp_malware_hashes_updated';

	/**
	 * Malware signatures (patterns to detect).
	 *
	 * @var array
	 */
	private $signatures = array();

	/**
	 * Known malware file hashes (MD5).
	 *
	 * @var array
	 */
	private $malware_hashes = array();

	/**
	 * Paths/patterns to skip (legitimate libraries and tools).
	 *
	 * @var array
	 */
	private $skip_paths = array(
		// This plugin's own files.
		'arkhost-security-pack',

		// Common libraries that legitimately use shell/network functions.
		'phpseclib',
		'php-curl-class',

		// Package managers.
		'/vendor/',
		'/node_modules/',

		// Known backup/security plugins.
		'updraftplus',
		'backwpup',
		'duplicator',
	);

	/**
	 * Constructor.
	 */
	public function __construct() {
		$this->load_signatures();
		$this->load_malware_hashes();

		if ( ! ARKSP_Plugin::get_setting( 'malware_scan_enabled', true ) ) {
			return;
		}

		// Schedule weekly scan.
		add_action( 'arksp_weekly_malware_scan', array( $this, 'run_scheduled_scan' ) );

		if ( ! wp_next_scheduled( 'arksp_weekly_malware_scan' ) ) {
			wp_schedule_event( time(), 'weekly', 'arksp_weekly_malware_scan' );
		}
	}

	/**
	 * Load known malware file hashes.
	 *
	 * Hashes can come from:
	 * 1. Local database (user-added)
	 * 2. Custom filter (arksp_malware_hashes)
	 */
	private function load_malware_hashes() {
		// Load from database (user-added or previously downloaded).
		$this->malware_hashes = get_option( self::HASH_DB_OPTION, array() );

		// Allow adding custom hashes via filter.
		$this->malware_hashes = apply_filters( 'arksp_malware_hashes', $this->malware_hashes );
	}

	/**
	 * Add a hash to the local database.
	 *
	 * @param string $hash MD5 hash.
	 * @param string $name Malware name/description.
	 */
	public function add_hash_to_database( $hash, $name ) {
		$hashes = get_option( self::HASH_DB_OPTION, array() );
		$hashes[ strtolower( $hash ) ] = $name;
		update_option( self::HASH_DB_OPTION, $hashes );
		$this->malware_hashes[ strtolower( $hash ) ] = $name;
	}

	/**
	 * Remove a hash from the local database.
	 *
	 * @param string $hash MD5 hash.
	 */
	public function remove_hash_from_database( $hash ) {
		$hashes = get_option( self::HASH_DB_OPTION, array() );
		$hash   = strtolower( $hash );
		if ( isset( $hashes[ $hash ] ) ) {
			unset( $hashes[ $hash ] );
			update_option( self::HASH_DB_OPTION, $hashes );
		}
		if ( isset( $this->malware_hashes[ $hash ] ) ) {
			unset( $this->malware_hashes[ $hash ] );
		}
	}

	/**
	 * Update malware hash database from remote source.
	 *
	 * @param string $source_url URL to fetch hashes from (JSON format).
	 * @return bool|WP_Error
	 */
	public function update_hash_database( $source_url = '' ) {
		if ( empty( $source_url ) ) {
			// Default: Could be a GitHub raw URL or your own endpoint.
			// For now, just return - users can provide their own source.
			return new WP_Error( 'no_source', __( 'No hash database source URL provided.', 'arkhost-security-pack' ) );
		}

		$response = wp_remote_get( $source_url, array( 'timeout' => 30 ) );

		if ( is_wp_error( $response ) ) {
			return $response;
		}

		$body = wp_remote_retrieve_body( $response );
		$data = json_decode( $body, true );

		if ( ! is_array( $data ) ) {
			return new WP_Error( 'invalid_data', __( 'Invalid hash database format.', 'arkhost-security-pack' ) );
		}

		// Merge with existing hashes.
		$current_hashes = $this->malware_hashes;
		$new_hashes     = array_merge( $current_hashes, $data );

		update_option( self::HASH_DB_OPTION, $new_hashes );
		update_option( self::HASH_DB_UPDATED_OPTION, time() );

		$this->malware_hashes = $new_hashes;

		return true;
	}

	/**
	 * Check if a file matches a known malware hash.
	 *
	 * @param string $file_path Path to file.
	 * @return array|false Malware info if matched, false otherwise.
	 */
	public function check_file_hash( $file_path ) {
		if ( ! file_exists( $file_path ) || ! is_readable( $file_path ) ) {
			return false;
		}

		$md5_hash = md5_file( $file_path );

		if ( isset( $this->malware_hashes[ $md5_hash ] ) ) {
			return array(
				'hash'   => $md5_hash,
				'name'   => $this->malware_hashes[ $md5_hash ],
				'method' => 'hash',
			);
		}

		return false;
	}

	/**
	 * Get hash database info.
	 *
	 * @return array
	 */
	public function get_hash_database_info() {
		return array(
			'count'        => count( $this->malware_hashes ),
			'last_updated' => get_option( self::HASH_DB_UPDATED_OPTION, 0 ),
			'source'       => empty( get_option( self::HASH_DB_OPTION ) ) ? 'built-in' : 'updated',
		);
	}

	/**
	 * Load malware signatures.
	 *
	 * Patterns are organized by category and designed to minimize false positives
	 * while catching real threats. We do NOT scan WordPress core files.
	 */
	private function load_signatures() {
		$this->signatures = array(

			// =====================================================================
			// CRITICAL: Code Execution with Obfuscation
			// These patterns are almost always malicious.
			// =====================================================================
			array(
				'name'        => 'Base64 Decode Execution',
				'pattern'     => '/\beval\s*\(\s*base64_decode\s*\(/i',
				'severity'    => 'critical',
				'description' => 'Executing base64-encoded PHP code',
			),
			array(
				'name'        => 'Gzinflate Execution',
				'pattern'     => '/\beval\s*\(\s*gzinflate\s*\(/i',
				'severity'    => 'critical',
				'description' => 'Executing gzip-compressed PHP code',
			),
			array(
				'name'        => 'Gzuncompress Execution',
				'pattern'     => '/\beval\s*\(\s*gzuncompress\s*\(/i',
				'severity'    => 'critical',
				'description' => 'Executing compressed PHP code',
			),
			array(
				'name'        => 'Str_rot13 Execution',
				'pattern'     => '/\beval\s*\(\s*str_rot13\s*\(/i',
				'severity'    => 'critical',
				'description' => 'Executing ROT13-obfuscated PHP code',
			),
			array(
				'name'        => 'Multiple Decode Layers',
				'pattern'     => '/base64_decode\s*\([^)]*base64_decode/is',
				'severity'    => 'critical',
				'description' => 'Multiple layers of encoding (heavy obfuscation)',
			),
			array(
				'name'        => 'Preg_replace /e Modifier',
				'pattern'     => '/preg_replace\s*\(\s*["\'][^"\']*\/[a-z]*e[a-z]*["\']/',
				'severity'    => 'critical',
				'description' => 'Code execution via deprecated preg_replace /e modifier',
			),

			// =====================================================================
			// CRITICAL: User Input to Code Execution
			// Direct path from user input to code execution.
			// =====================================================================
			array(
				'name'        => 'Eval with User Input',
				'pattern'     => '/\beval\s*\(\s*[\$\.\s]*\$_(POST|GET|REQUEST|COOKIE|SERVER|FILES)/i',
				'severity'    => 'critical',
				'description' => 'Direct code execution from user input',
			),
			array(
				'name'        => 'Assert with User Input',
				'pattern'     => '/\bassert\s*\(\s*[\$\.\s]*\$_(POST|GET|REQUEST|COOKIE)/i',
				'severity'    => 'critical',
				'description' => 'Code execution via assert() from user input',
			),
			array(
				'name'        => 'Create_function with User Input',
				'pattern'     => '/\bcreate_function\s*\([^)]*\$_(POST|GET|REQUEST|COOKIE)/i',
				'severity'    => 'critical',
				'description' => 'Dynamic function creation with user input',
			),
			array(
				'name'        => 'Call_user_func with User Input',
				'pattern'     => '/\bcall_user_func(_array)?\s*\(\s*\$_(POST|GET|REQUEST|COOKIE)/i',
				'severity'    => 'critical',
				'description' => 'Calling arbitrary function from user input',
			),
			array(
				'name'        => 'Variable Function with User Input',
				'pattern'     => '/\$_(POST|GET|REQUEST|COOKIE)\s*\[[^\]]+\]\s*\(/i',
				'severity'    => 'critical',
				'description' => 'Calling function name from user input',
			),

			// =====================================================================
			// CRITICAL: Shell Command Execution with User Input
			// =====================================================================
			array(
				'name'        => 'Shell_exec with User Input',
				'pattern'     => '/\bshell_exec\s*\([^)]*\$_(POST|GET|REQUEST|COOKIE)/i',
				'severity'    => 'critical',
				'description' => 'Shell command execution with user input',
			),
			array(
				'name'        => 'System with User Input',
				'pattern'     => '/\bsystem\s*\([^)]*\$_(POST|GET|REQUEST|COOKIE)/i',
				'severity'    => 'critical',
				'description' => 'System command execution with user input',
			),
			array(
				'name'        => 'Passthru with User Input',
				'pattern'     => '/\bpassthru\s*\([^)]*\$_(POST|GET|REQUEST|COOKIE)/i',
				'severity'    => 'critical',
				'description' => 'Passthru command execution with user input',
			),
			array(
				'name'        => 'Exec with User Input',
				'pattern'     => '/\bexec\s*\([^)]*\$_(POST|GET|REQUEST|COOKIE)/i',
				'severity'    => 'critical',
				'description' => 'Exec command execution with user input',
			),
			array(
				'name'        => 'Popen with User Input',
				'pattern'     => '/\bpopen\s*\([^)]*\$_(POST|GET|REQUEST|COOKIE)/i',
				'severity'    => 'critical',
				'description' => 'Process opened with user-controlled command',
			),
			array(
				'name'        => 'Proc_open with User Input',
				'pattern'     => '/\bproc_open\s*\([^)]*\$_(POST|GET|REQUEST|COOKIE)/i',
				'severity'    => 'critical',
				'description' => 'Process opened with user-controlled command',
			),

			// =====================================================================
			// CRITICAL: File Inclusion Vulnerabilities
			// =====================================================================
			array(
				'name'        => 'Include with User Input',
				'pattern'     => '/\b(include|require|include_once|require_once)\s*\(?\s*[\$\.\s]*\$_(POST|GET|REQUEST|COOKIE)/i',
				'severity'    => 'critical',
				'description' => 'Local/Remote File Inclusion vulnerability',
			),

			// =====================================================================
			// CRITICAL: File Write Vulnerabilities
			// =====================================================================
			array(
				'name'        => 'File_put_contents with User Input',
				'pattern'     => '/\bfile_put_contents\s*\([^,]*\$_(POST|GET|REQUEST|COOKIE)/i',
				'severity'    => 'critical',
				'description' => 'Writing to user-controlled file path',
			),
			array(
				'name'        => 'Fwrite with User Content',
				'pattern'     => '/\bfwrite\s*\([^,]+,\s*\$_(POST|GET|REQUEST|COOKIE)/i',
				'severity'    => 'high',
				'description' => 'Writing user content to file',
			),
			array(
				'name'        => 'Fopen with User Path',
				'pattern'     => '/\bfopen\s*\(\s*\$_(POST|GET|REQUEST|COOKIE)/i',
				'severity'    => 'critical',
				'description' => 'Opening user-controlled file path',
			),
			array(
				'name'        => 'Unrestricted File Upload Path',
				'pattern'     => '/move_uploaded_file\s*\([^,]+,\s*[^)]*\$_(POST|GET|REQUEST)/i',
				'severity'    => 'critical',
				'description' => 'Uploading file to user-controlled path',
			),

			// =====================================================================
			// HIGH: Known Backdoor Patterns
			// Specific patterns that indicate known malware structures.
			// =====================================================================
			array(
				'name'        => 'Web Shell Upload Form',
				'pattern'     => '/<form[^>]*enctype=["\']multipart\/form-data["\'][^>]*>.*<input[^>]*type=["\']file["\'].*\$_FILES/is',
				'severity'    => 'high',
				'description' => 'File upload form with immediate processing (potential backdoor)',
			),
			array(
				'name'        => 'Eval POST/GET Pattern',
				'pattern'     => '/\beval\s*\(\s*\$_(POST|GET)\s*\[\s*["\'][a-z0-9_]+["\']\s*\]\s*\)/i',
				'severity'    => 'critical',
				'description' => 'Classic backdoor pattern: eval($_POST[key])',
			),
			array(
				'name'        => 'Base64 POST Execution',
				'pattern'     => '/\beval\s*\(\s*base64_decode\s*\(\s*\$_(POST|GET|REQUEST)/i',
				'severity'    => 'critical',
				'description' => 'Executing base64-encoded user input',
			),
			array(
				'name'        => 'Gunzip Eval Chain',
				'pattern'     => '/\beval\s*\(\s*gzuncompress\s*\(\s*base64_decode/i',
				'severity'    => 'critical',
				'description' => 'Multi-layer deobfuscation chain',
			),

			// =====================================================================
			// HIGH: Obfuscation Indicators
			// =====================================================================
			array(
				'name'        => 'Very Long Encoded String',
				'pattern'     => '/["\'][A-Za-z0-9+\/=]{1500,}["\']/s',
				'severity'    => 'high',
				'description' => 'Extremely long encoded string (likely obfuscated malware)',
			),
			array(
				'name'        => 'Hex Escape Sequence',
				'pattern'     => '/(\\\\x[0-9a-fA-F]{2}){20,}/i',
				'severity'    => 'high',
				'description' => 'Long hex-encoded string (obfuscation technique)',
			),
			array(
				'name'        => 'Chr() Obfuscation',
				'pattern'     => '/(\bchr\s*\(\s*\d+\s*\)\s*\.?\s*){10,}/i',
				'severity'    => 'high',
				'description' => 'Building string from chr() calls (obfuscation)',
			),
			array(
				'name'        => 'Array Character Building',
				'pattern'     => '/\$\w+\s*=\s*["\'][A-Za-z]+["\'];\s*\$\w+\s*=\s*\$\w+\[\d+\]\s*\.\s*\$\w+\[\d+\]/i',
				'severity'    => 'medium',
				'description' => 'Building function names from array indices (obfuscation)',
			),

			// =====================================================================
			// HIGH: WordPress-Specific Attacks
			// =====================================================================
			array(
				'name'        => 'WP User Creation Backdoor',
				'pattern'     => '/wp_create_user\s*\([^)]*\$_(POST|GET|REQUEST|COOKIE)/i',
				'severity'    => 'critical',
				'description' => 'Creating WordPress user from user input',
			),
			array(
				'name'        => 'WP Insert User Backdoor',
				'pattern'     => '/wp_insert_user\s*\([^)]*\$_(POST|GET|REQUEST|COOKIE)/i',
				'severity'    => 'critical',
				'description' => 'Inserting WordPress user from user input',
			),
			array(
				'name'        => 'WP Option Injection',
				'pattern'     => '/update_option\s*\(\s*\$_(POST|GET|REQUEST)/i',
				'severity'    => 'critical',
				'description' => 'Updating arbitrary WordPress option from user input',
			),
			array(
				'name'        => 'WP Auth Cookie Manipulation',
				'pattern'     => '/wp_set_auth_cookie\s*\([^)]*\$_(POST|GET|REQUEST)/i',
				'severity'    => 'critical',
				'description' => 'Setting auth cookie from user input (authentication bypass)',
			),
			array(
				'name'        => 'WP Role Escalation',
				'pattern'     => '/->set_role\s*\(\s*["\']administrator["\']\s*\)|->add_cap\s*\([^)]*\$_(POST|GET|REQUEST)/i',
				'severity'    => 'critical',
				'description' => 'Privilege escalation attempt',
			),

			// =====================================================================
			// HIGH: Suspicious Patterns with Context
			// These require specific dangerous context to trigger.
			// =====================================================================
			array(
				'name'        => 'Error Suppression with Eval',
				'pattern'     => '/@\s*eval\s*\(/i',
				'severity'    => 'high',
				'description' => 'Eval with error suppression (hiding malicious activity)',
			),
			array(
				'name'        => 'Remote Code Fetch and Execute',
				'pattern'     => '/eval\s*\(\s*file_get_contents\s*\(\s*["\']https?:\/\//i',
				'severity'    => 'critical',
				'description' => 'Fetching and executing remote code',
			),
			array(
				'name'        => 'CURL Fetch and Execute',
				'pattern'     => '/eval\s*\([^)]*curl_exec/is',
				'severity'    => 'critical',
				'description' => 'Executing code fetched via CURL',
			),
			array(
				'name'        => 'Dynamic URL Fetch with User Input',
				'pattern'     => '/file_get_contents\s*\(\s*\$_(POST|GET|REQUEST|COOKIE)/i',
				'severity'    => 'high',
				'description' => 'Fetching content from user-controlled URL',
			),
		);

		// Allow adding custom signatures via filter.
		$this->signatures = apply_filters( 'arksp_malware_signatures', $this->signatures );
	}

	/**
	 * Run scheduled scan.
	 */
	public function run_scheduled_scan() {
		$results = $this->scan_files();

		if ( ! empty( $results ) ) {
			update_option( self::RESULTS_OPTION, $results );
			$this->send_alert( $results );
		}

		update_option( self::LAST_SCAN_OPTION, time() );
	}

	/**
	 * Scan files for malware signatures.
	 *
	 * Only scans plugins, themes, and uploads directories.
	 * Does NOT scan WordPress core to avoid false positives.
	 *
	 * @param array $paths Paths to scan (default: wp-content directories only).
	 * @return array
	 */
	public function scan_files( $paths = array() ) {
		$start_time = microtime( true );

		if ( empty( $paths ) ) {
			// Only scan wp-content directories, NOT WordPress core.
			$paths = array(
				WP_CONTENT_DIR . '/plugins/',
				WP_CONTENT_DIR . '/themes/',
				WP_CONTENT_DIR . '/uploads/',
				WP_CONTENT_DIR . '/mu-plugins/',
			);
		}

		$results    = array();
		$file_count = 0;
		$max_files  = 10000; // Limit to prevent timeout.

		foreach ( $paths as $path ) {
			if ( ! file_exists( $path ) ) {
				continue;
			}

			if ( is_file( $path ) ) {
				if ( ! $this->should_skip_file( $path ) ) {
					$file_results = $this->scan_file( $path );
					if ( ! empty( $file_results ) ) {
						$results[ $path ] = $file_results;
					}
				}
				$file_count++;
			} else {
				$iterator = new RecursiveIteratorIterator(
					new RecursiveDirectoryIterator( $path, RecursiveDirectoryIterator::SKIP_DOTS ),
					RecursiveIteratorIterator::SELF_FIRST
				);

				foreach ( $iterator as $file ) {
					if ( $file_count >= $max_files ) {
						break 2;
					}

					if ( ! $file->isFile() ) {
						continue;
					}

					$file_path = $file->getPathname();
					$ext       = strtolower( $file->getExtension() );

					// Only scan PHP files.
					if ( 'php' !== $ext ) {
						continue;
					}

					// Skip large files (> 2MB).
					if ( $file->getSize() > 2 * 1024 * 1024 ) {
						continue;
					}

					// Skip known safe paths.
					if ( $this->should_skip_file( $file_path ) ) {
						continue;
					}

					$file_results = $this->scan_file( $file_path );
					if ( ! empty( $file_results ) ) {
						$results[ $file_path ] = $file_results;
					}

					$file_count++;
				}
			}
		}

		$duration = microtime( true ) - $start_time;

		// Save scan stats.
		update_option( self::LAST_SCAN_OPTION, time() );
		update_option(
			'arksp_malware_scan_stats',
			array(
				'files_scanned' => $file_count,
				'duration'      => round( $duration, 2 ),
				'issues_found'  => count( $results ),
			)
		);

		return $results;
	}

	/**
	 * Check if a file should be skipped (known legitimate libraries).
	 *
	 * @param string $file_path File path to check.
	 * @return bool True if file should be skipped.
	 */
	private function should_skip_file( $file_path ) {
		foreach ( $this->skip_paths as $skip ) {
			if ( stripos( $file_path, $skip ) !== false ) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Scan a single file.
	 *
	 * @param string $file_path File path.
	 * @return array
	 */
	public function scan_file( $file_path ) {
		if ( ! file_exists( $file_path ) || ! is_readable( $file_path ) ) {
			return array();
		}

		$findings = array();

		// First: Check against known malware hashes (100% accurate).
		$hash_match = $this->check_file_hash( $file_path );
		if ( $hash_match ) {
			$findings[] = array(
				'name'        => 'Known Malware: ' . $hash_match['name'],
				'severity'    => 'critical',
				'description' => 'File matches known malware hash (MD5: ' . $hash_match['hash'] . ')',
				'match'       => 'Hash match - confirmed malware',
				'confirmed'   => true,
			);
			// Hash match is definitive - still scan for patterns but mark as confirmed.
		}

		// Second: Pattern-based detection.
		// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
		$content = file_get_contents( $file_path );

		if ( ! empty( $content ) ) {
			foreach ( $this->signatures as $signature ) {
				if ( preg_match( $signature['pattern'], $content, $matches ) ) {
					$findings[] = array(
						'name'        => $signature['name'],
						'severity'    => $signature['severity'],
						'description' => $signature['description'],
						'match'       => substr( $matches[0], 0, 100 ),
						'confirmed'   => false,
					);
				}
			}
		}

		return $findings;
	}

	/**
	 * Send email alert about scan results.
	 *
	 * @param array $results Scan results.
	 */
	private function send_alert( $results ) {
		if ( ! ARKSP_Plugin::get_setting( 'email_alerts_enabled', false ) ) {
			return;
		}

		$email     = ARKSP_Plugin::get_setting( 'email_alerts_address', get_option( 'admin_email' ) );
		$site_name = get_bloginfo( 'name' );
		$site_url  = home_url();

		// Count by severity.
		$counts = array(
			'critical' => 0,
			'high'     => 0,
			'medium'   => 0,
			'low'      => 0,
		);

		foreach ( $results as $file => $findings ) {
			foreach ( $findings as $finding ) {
				$counts[ $finding['severity'] ]++;
			}
		}

		$subject = sprintf(
			/* translators: %s: Site name */
			__( '[%s] Malware Scan Alert - Suspicious Files Found', 'arkhost-security-pack' ),
			$site_name
		);

		$message = sprintf(
			/* translators: %s: Site URL */
			__( "ArkHost Security Pack malware scan has detected suspicious files on %s.\n\n", 'arkhost-security-pack' ),
			$site_url
		);

		$message .= __( "Summary:\n", 'arkhost-security-pack' );
		/* translators: %d: Number of critical severity issues */
		$message .= sprintf( __( "- Critical: %d\n", 'arkhost-security-pack' ), $counts['critical'] );
		/* translators: %d: Number of high severity issues */
		$message .= sprintf( __( "- High: %d\n", 'arkhost-security-pack' ), $counts['high'] );
		/* translators: %d: Number of medium severity issues */
		$message .= sprintf( __( "- Medium: %d\n", 'arkhost-security-pack' ), $counts['medium'] );
		/* translators: %d: Number of low severity issues */
		$message .= sprintf( __( "- Low: %d\n\n", 'arkhost-security-pack' ), $counts['low'] );

		$message .= __( "Files with issues:\n", 'arkhost-security-pack' );

		$count = 0;
		foreach ( $results as $file => $findings ) {
			if ( $count >= 20 ) {
				$message .= sprintf(
					/* translators: %d: Number of additional files */
					__( "... and %d more files\n", 'arkhost-security-pack' ),
					count( $results ) - 20
				);
				break;
			}

			$relative_path = str_replace( ABSPATH, '', $file );
			$message      .= "\n" . $relative_path . ":\n";

			foreach ( $findings as $finding ) {
				$message .= sprintf( "  - [%s] %s\n", strtoupper( $finding['severity'] ), $finding['name'] );
			}

			$count++;
		}

		$message .= "\n" . __( "Please review these files in your WordPress admin panel under Settings > ArkHost Security Pack.", 'arkhost-security-pack' );
		$message .= "\n\n" . __( "Note: Some detections may be false positives. Review each file carefully before taking action.", 'arkhost-security-pack' );

		wp_mail( $email, $subject, $message );
	}

	/**
	 * Get last scan results.
	 *
	 * @return array
	 */
	public function get_last_scan_results() {
		return array(
			'time'    => get_option( self::LAST_SCAN_OPTION, 0 ),
			'results' => get_option( self::RESULTS_OPTION, array() ),
		);
	}

	/**
	 * Clear scan results.
	 */
	public function clear_results() {
		delete_option( self::RESULTS_OPTION );
	}

	/**
	 * Get severity color.
	 *
	 * @param string $severity Severity level.
	 * @return string
	 */
	public static function get_severity_color( $severity ) {
		$colors = array(
			'critical' => '#dc3545',
			'high'     => '#fd7e14',
			'medium'   => '#ffc107',
			'low'      => '#17a2b8',
		);

		return isset( $colors[ $severity ] ) ? $colors[ $severity ] : '#6c757d';
	}

	/**
	 * Get quarantine directory path.
	 *
	 * @return string
	 */
	public function get_quarantine_dir() {
		$upload_dir = wp_upload_dir();
		$quarantine = $upload_dir['basedir'] . '/arkhost-security-pack/quarantine';

		if ( ! file_exists( $quarantine ) ) {
			wp_mkdir_p( $quarantine );

			// Protect quarantine directory.
			$htaccess = $quarantine . '/.htaccess';
			if ( ! file_exists( $htaccess ) ) {
				// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
				file_put_contents( $htaccess, "Deny from all\n" );
			}

			$index = $quarantine . '/index.php';
			if ( ! file_exists( $index ) ) {
				// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
				file_put_contents( $index, "<?php\n// Silence is golden.\n" );
			}
		}

		return $quarantine;
	}

	/**
	 * Quarantine a suspicious file.
	 *
	 * @param string $file_path Full path to the file.
	 * @return array|WP_Error Result with quarantine info or error.
	 */
	public function quarantine_file( $file_path ) {
		if ( ! file_exists( $file_path ) ) {
			return new WP_Error( 'file_not_found', __( 'File not found.', 'arkhost-security-pack' ) );
		}

		// Security: Only allow quarantining files within wp-content.
		if ( strpos( realpath( $file_path ), realpath( WP_CONTENT_DIR ) ) !== 0 ) {
			return new WP_Error( 'invalid_path', __( 'Can only quarantine files within wp-content.', 'arkhost-security-pack' ) );
		}

		$quarantine_dir = $this->get_quarantine_dir();
		$file_hash      = md5_file( $file_path );
		$timestamp      = time();
		$original_name  = basename( $file_path );
		$relative_path  = str_replace( ABSPATH, '', $file_path );

		// Create unique quarantine filename.
		$quarantine_name = sprintf(
			'%s_%s_%s.quarantined',
			$timestamp,
			$file_hash,
			sanitize_file_name( $original_name )
		);
		$quarantine_path = $quarantine_dir . '/' . $quarantine_name;

		// Store metadata.
		$metadata = array(
			'original_path'  => $file_path,
			'relative_path'  => $relative_path,
			'original_name'  => $original_name,
			'file_hash'      => $file_hash,
			'quarantined_at' => $timestamp,
			'quarantined_by' => get_current_user_id(),
			'file_size'      => filesize( $file_path ),
		);

		// Move file to quarantine.
		// phpcs:ignore WordPress.WP.AlternativeFunctions.rename_rename
		if ( ! rename( $file_path, $quarantine_path ) ) {
			return new WP_Error( 'move_failed', __( 'Failed to move file to quarantine.', 'arkhost-security-pack' ) );
		}

		// Save metadata.
		$meta_file = $quarantine_path . '.meta';
		// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
		file_put_contents( $meta_file, wp_json_encode( $metadata, JSON_PRETTY_PRINT ) );

		// Update quarantine list in options.
		$quarantined = get_option( 'arksp_quarantined_files', array() );
		$quarantined[ $quarantine_name ] = $metadata;
		update_option( 'arksp_quarantined_files', $quarantined );

		// Remove from scan results if present.
		$results = get_option( self::RESULTS_OPTION, array() );
		if ( isset( $results[ $file_path ] ) ) {
			unset( $results[ $file_path ] );
			update_option( self::RESULTS_OPTION, $results );
		}

		return array(
			'success'         => true,
			'quarantine_name' => $quarantine_name,
			'metadata'        => $metadata,
		);
	}

	/**
	 * Restore a file from quarantine.
	 *
	 * @param string $quarantine_name The quarantine filename.
	 * @return array|WP_Error Result or error.
	 */
	public function restore_file( $quarantine_name ) {
		$quarantine_dir  = $this->get_quarantine_dir();
		$quarantine_path = $quarantine_dir . '/' . $quarantine_name;
		$meta_file       = $quarantine_path . '.meta';

		if ( ! file_exists( $quarantine_path ) ) {
			return new WP_Error( 'file_not_found', __( 'Quarantined file not found.', 'arkhost-security-pack' ) );
		}

		// Get metadata.
		$metadata = array();
		if ( file_exists( $meta_file ) ) {
			// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
			$metadata = json_decode( file_get_contents( $meta_file ), true );
		}

		if ( empty( $metadata['original_path'] ) ) {
			return new WP_Error( 'no_metadata', __( 'Cannot restore: original path unknown.', 'arkhost-security-pack' ) );
		}

		$original_path = $metadata['original_path'];

		// Ensure parent directory exists.
		$parent_dir = dirname( $original_path );
		if ( ! file_exists( $parent_dir ) ) {
			wp_mkdir_p( $parent_dir );
		}

		// Restore file.
		// phpcs:ignore WordPress.WP.AlternativeFunctions.rename_rename
		if ( ! rename( $quarantine_path, $original_path ) ) {
			return new WP_Error( 'restore_failed', __( 'Failed to restore file.', 'arkhost-security-pack' ) );
		}

		// Clean up metadata file.
		if ( file_exists( $meta_file ) ) {
			// phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
			unlink( $meta_file );
		}

		// Update quarantine list.
		$quarantined = get_option( 'arksp_quarantined_files', array() );
		if ( isset( $quarantined[ $quarantine_name ] ) ) {
			unset( $quarantined[ $quarantine_name ] );
			update_option( 'arksp_quarantined_files', $quarantined );
		}

		return array(
			'success'       => true,
			'restored_path' => $original_path,
		);
	}

	/**
	 * Delete a quarantined file permanently.
	 *
	 * @param string $quarantine_name The quarantine filename.
	 * @return array|WP_Error Result or error.
	 */
	public function delete_quarantined_file( $quarantine_name ) {
		$quarantine_dir  = $this->get_quarantine_dir();
		$quarantine_path = $quarantine_dir . '/' . $quarantine_name;
		$meta_file       = $quarantine_path . '.meta';

		if ( ! file_exists( $quarantine_path ) ) {
			return new WP_Error( 'file_not_found', __( 'Quarantined file not found.', 'arkhost-security-pack' ) );
		}

		// Delete file.
		// phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
		if ( ! unlink( $quarantine_path ) ) {
			return new WP_Error( 'delete_failed', __( 'Failed to delete file.', 'arkhost-security-pack' ) );
		}

		// Clean up metadata file.
		if ( file_exists( $meta_file ) ) {
			// phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
			unlink( $meta_file );
		}

		// Update quarantine list.
		$quarantined = get_option( 'arksp_quarantined_files', array() );
		if ( isset( $quarantined[ $quarantine_name ] ) ) {
			unset( $quarantined[ $quarantine_name ] );
			update_option( 'arksp_quarantined_files', $quarantined );
		}

		return array( 'success' => true );
	}

	/**
	 * Get list of quarantined files.
	 *
	 * @return array
	 */
	public function get_quarantined_files() {
		return get_option( 'arksp_quarantined_files', array() );
	}

	/**
	 * Get count of quarantined files.
	 *
	 * @return int
	 */
	public function get_quarantine_count() {
		return count( $this->get_quarantined_files() );
	}
}
