<?php
/**
 * Activity Guard – Real Time Notifier to Slack Rest Route.
 *
 * This class is used to response and all rest route works.
 *
 * @package WPNTS\Inc
 */

namespace WPNTS\Inc;

use WPNTS\Inc\wpnts_nts_fs; // phpcs:ignore
use WPNTS\Inc\Activate; // phpcs:ignore
use WPNTS\Inc\Deactivate; // phpcs:ignore
use WPNTS\Inc\Database\DB; // phpcs:ignore
use WPNTS\Inc\SlackAttachment; // phpcs:ignore

defined('ABSPATH') || die('Hey, what are you doing here? You silly human!');
/**
 * Scanner used to rest route created.
 *
 * @since 1.0.0
 */
class Scanner {

	private $scan_log = [];
	private $security_patterns = [];
	private $batch_size = 5000;
	private $scan_session_key = 'wps_current_scan_session';
	private $scan_history_option = 'wps_security_scan_history';
	private $exclusion_option = 'wps_security_scan_exclusions';

	public function __construct() {
		// Initialize the scan log.
		add_action('wp_ajax_run_security_scan', [ $this, 'run_security_scan' ]);
		// Fetch scan history.
		add_action('wp_ajax_get_scan_history', [ $this, 'get_scan_history' ]);
		// Clear scan history.
		add_action('wp_ajax_download_scan_log', [ $this, 'handle_download_scan_log' ]);

		// Fetch scan exclusions.
		add_action('wp_ajax_get_specific_scan_details', [ $this, 'get_specific_scan_details' ]);
		// Update scan exclusions.
		add_action('wp_ajax_update_scan_exclusions', [ $this, 'update_scan_exclusions' ]);
		// Test API connection.
		add_action('wp_ajax_get_api_settings', [ $this, 'get_api_settings' ]);
		// Update API settings.
		add_action('wp_ajax_update_api_settings', [ $this, 'update_api_settings' ]);
		// Test API connection.
		add_action('wp_ajax_test_api_connection', [ $this, 'test_api_connection' ]);
		
		// Schedule daily security patterns fetch instead of fetching on every load
		add_action('wp', [ $this, 'schedule_security_patterns_fetch' ]);
		add_action('wpnts_fetch_security_patterns', [ $this, 'fetch_security_patterns' ]);
		
		// Clean up scheduled events on plugin deactivation
		register_deactivation_hook(__FILE__, [ $this, 'cleanup_scheduled_events' ]);
	}

	/**
	 * Clean up scheduled events when plugin is deactivated.
	 *
	 * @return void
	 */
	public function cleanup_scheduled_events() {
		$timestamp = wp_next_scheduled('wpnts_fetch_security_patterns');
		if ( $timestamp ) {
			wp_unschedule_event($timestamp, 'wpnts_fetch_security_patterns');
		}
	}

	/**
	 * Clear security patterns cache manually.
	 * Useful for debugging or forcing a refresh.
	 *
	 * @return void
	 */
	public function clear_security_patterns_cache() {
		delete_transient('security_patterns_from_api');
		delete_transient('security_patterns_fetching_lock');
		$this->security_patterns = [];
	}

	/**
	 * Schedule security patterns fetch to run daily instead of on every page load.
	 *
	 * @return void
	 */
	public function schedule_security_patterns_fetch() {
		// Only schedule if not already scheduled
		if ( ! wp_next_scheduled('wpnts_fetch_security_patterns') ) {
			wp_schedule_event(time(), 'daily', 'wpnts_fetch_security_patterns');
		}
		
		// Fetch patterns immediately if cache is empty (first time only)
		$patterns = get_transient('security_patterns_from_api');
		if ( ! $patterns ) {
			$this->fetch_security_patterns();
		}
	}

	/**
	 * Fetch security patterns from the API.
	 *
	 * This function retrieves security patterns from the API and caches them for a day.
	 * If the patterns are already cached, it uses the cached version.
	 *
	 * @return void
	 */
	public function fetch_security_patterns() {
		$patterns = get_transient('security_patterns_from_api');

		if ( $patterns ) {
			$this->security_patterns = $patterns;
			return;
		}

		// Check if we're already fetching to prevent concurrent requests
		$fetching_lock = get_transient('security_patterns_fetching_lock');
		if ( $fetching_lock ) {
			// Wait for the other request to complete, then try to get cached patterns
			sleep(2);
			$patterns = get_transient('security_patterns_from_api');
			if ( $patterns ) {
				$this->security_patterns = $patterns;
				return;
			}
		}

		// Set a lock to prevent concurrent requests
		set_transient('security_patterns_fetching_lock', true, 60); // 1 minute lock

		// Free API URL and key for all users. to load security patterns.
		$api_url = get_option('security_api_url', 'https://wpazleen.com/');
		$api_key = get_option('security_api_key', '890b75e99a1c2fb0a586c5efeae2c30e');

		$base_url = rtrim($api_url, '/');
		$request_url = $base_url . '/wp-json/wpx/v1/security-patterns?api_key=' . urlencode($api_key);

		$args = [
			'timeout' => 30,
			'headers' => [
				'User-Agent' => 'WordPress/' . get_bloginfo('version'),
				'Referer' => site_url(),
			],
		];

		$response = wp_remote_get($request_url, $args);

		// Remove the lock
		delete_transient('security_patterns_fetching_lock');

		if ( ! is_wp_error($response) && 200 === wp_remote_retrieve_response_code($response) ) {
			$data = json_decode(wp_remote_retrieve_body($response), true);

			if ( isset($data['success']) && $data['success'] && ! empty($data['data']) ) {
				$this->security_patterns = $data['data'];
				set_transient('security_patterns_from_api', $this->security_patterns, DAY_IN_SECONDS);
				
			}
		} else {
			$error_message = is_wp_error($response) ? $response->get_error_message() : 'Response code: ' . wp_remote_retrieve_response_code($response);
			
		}
	}


	/**
	 * Get all files in a directory recursively.
	 *
	 * @param string $dir Directory path.
	 * @return array List of files.
	 */
	private $scan_paths = [
		'core' => ABSPATH,
		'wp_config' => ABSPATH . 'wp-config.php',
		'htaccess' => ABSPATH . '.htaccess',
		'uploads' => WP_CONTENT_DIR . '/uploads',
		'plugins' => WP_CONTENT_DIR . '/plugins',
		'themes' => WP_CONTENT_DIR . '/themes',
		'mu_plugins' => WP_CONTENT_DIR . '/mu-plugins',
		'wp_includes' => ABSPATH . WPINC,
		'wp_admin' => ABSPATH . 'wp-admin',
	];

	/**
	 * Map core paths to their respective scan paths.
	 *
	 * @var array
	 */
	private $core_path_mapping = [
		'wp-includes' => 'wp_includes',
		'wp-admin' => 'wp_admin',
		'wp-config.php' => 'wp_config',
		'.htaccess' => 'htaccess',
		'mu-plugins' => 'mu_plugins',
		'uploads' => 'uploads',
		'plugins' => 'plugins',
		'themes' => 'themes',
		'core' => 'core',
	];


	/**
	 * List of security checks to perform.
	 *
	 * @var array
	 */
	private $security_checks = [
		'plugin_vulnerabilities' => [
			'weight' => 10,
			'title' => 'Plugin Vulnerability Check',
		],
		'wordpress_version' => [
			'weight' => 5,
			'title' => 'WordPress Version Check',
		],
	];

	/**
	 * Get all files in a directory recursively.
	 *
	 * @param string $dir Directory path.
	 * @return array List of files.
	 */
	public function get_specific_scan_details() {
		check_ajax_referer('wp_rest', 'nonce');
		$scan_index = isset($_POST['scan_index']) ? intval($_POST['scan_index']) : -1;
		$history = get_option($this->scan_history_option, []);
		if ( $scan_index >= 0 && $scan_index < count($history) ) {
			wp_send_json_success($history[ $scan_index ]);
		} else {
			wp_send_json_error('Scan details not found');
		}
	}

	/**
	 * Get all files in a directory recursively.
	 *
	 * @param string $dir Directory path.
	 * @return array List of files.
	 */
	public function run_security_scan() {
		check_ajax_referer('wp_rest', 'nonce');
		$scan_type = isset($_POST['scan_type']) ? sanitize_text_field(wp_unslash($_POST['scan_type'])) : 'initialize';
		$session_id = isset($_POST['session_id']) ? sanitize_text_field(wp_unslash($_POST['session_id'])) : null;

		// Validate session ID.
		switch ( $scan_type ) {
			case 'initialize':
				$response = $this->initialize_scan();
				break;
			case 'core_files':
				$response = $this->process_core_files_batch($session_id);
				break;
			case 'security_checks':
				$response = $this->process_security_checks_batch($session_id);
				break;
			case 'finalize':
				$response = $this->finalize_scan($session_id);
				break;
			default:
				wp_send_json_error('Invalid scan type');
		}

		wp_send_json_success($response);
	}

	/**
	 * Get all files in a directory recursively.
	 *
	 * @param string $dir Directory path.
	 * @return array List of files.
	 */
	private function initialize_scan() {
		$session_id = uniqid('scan_', true);
		$scan_data = [
			'session_id' => $session_id,
			'start_time' => current_time('mysql'),
			'current_path_index' => 0,
			'current_file_offset' => 0,
			'processed_files' => 0,
			'total_files' => 0,
			'current_security_check_index' => 0,
			'results' => [],
			'issues' => [],
			'completed_paths' => [],
			'completed_checks' => [],
		];

		// Calculate total files.
		foreach ( $this->scan_paths as $path ) {
			if ( file_exists($path) ) {
				$files = $this->get_all_files($path);
				$scan_data['total_files'] += count($files);
			}
		}
		update_option($this->scan_session_key . '_' . $session_id, $scan_data);
		return [
			'type' => 'initialize',
			'session_id' => $session_id,
			'total_files' => $scan_data['total_files'],
			'total_checks' => count($this->security_checks),
			'message' => 'Scan initialized',
		];
	}


	/**
	 * Get all files in a directory recursively.
	 *
	 * @param string $dir Directory path.
	 * @return array List of files.
	 */
	private function process_core_files_batch( $session_id ) {
		// Check if session_id is valid.
		if ( empty($session_id) ) {
			return [
				'type' => 'error',
				'message' => 'Invalid session ID',
			];
		}
		// Retrieve scan data.
		$scan_data = get_option($this->scan_session_key . '_' . $session_id);
		if ( ! $scan_data ) {
			return [
				'type' => 'error',
				'message' => 'Invalid session',
			];
		}
		$paths = array_keys($this->scan_paths);
		$current_path = $paths[ $scan_data['current_path_index'] ];
		$exclusions = $this->get_scan_exclusions();
		// Check if current path type is completely excluded.
		if ( $this->is_path_type_excluded($current_path, $exclusions) ) {
			// Skip this path entirely.
			$scan_data['completed_paths'][] = $current_path;
			$scan_data['current_path_index']++;
			$scan_data['current_file_offset'] = 0;

			$skipped_files = count($this->get_all_files($this->scan_paths[ $current_path ]));
			$scan_data['processed_files'] += $skipped_files;

			update_option($this->scan_session_key . '_' . $session_id, $scan_data);

			$is_core_complete = $scan_data['current_path_index'] >= count($paths);
			$progress = ( $scan_data['processed_files'] / $scan_data['total_files'] ) * 50;

			return [
				'type' => 'progress',
				'phase' => 'core_files',
				'progress' => $progress,
				'current_path' => $current_path,
				'processed_files' => $scan_data['processed_files'],
				'total_files' => $scan_data['total_files'],
				'is_complete' => $is_core_complete,
				'message' => "Skipping excluded path {$current_path}",
			];
		}

		// Get files for current path.
		$files = $this->get_all_files($this->scan_paths[ $current_path ]);
		$batch_end = min($scan_data['current_file_offset'] + $this->batch_size, count($files));

		for ( $i = $scan_data['current_file_offset']; $i < $batch_end; $i++ ) {
			if ( isset($files[ $i ]) ) {
				$file = $files[ $i ];
				if ( ! $this->should_exclude_file($file, $current_path, $exclusions) ) {
					$issues = $this->check_file_security($file, $exclusions);
					if ( ! empty($issues) ) {
						$scan_data['issues'][] = [
							'file' => $file,
							'issues' => $issues,
						];
					}
				}
				$scan_data['processed_files']++;
			}
		}

		// Update progress.
		$scan_data['current_file_offset'] = $batch_end;
		if ( $batch_end >= count($files) ) {
			$scan_data['completed_paths'][] = $current_path;
			$scan_data['current_path_index']++;
			$scan_data['current_file_offset'] = 0;
		}

		update_option($this->scan_session_key . '_' . $session_id, $scan_data);

		$is_core_complete = $scan_data['current_path_index'] >= count($paths);
		$progress = ( $scan_data['processed_files'] / $scan_data['total_files'] ) * 50;

		return [
			'type' => 'progress',
			'phase' => 'core_files',
			'progress' => $progress,
			'current_path' => $current_path,
			'processed_files' => $scan_data['processed_files'],
			'total_files' => $scan_data['total_files'],
			'is_complete' => $is_core_complete,
			'message' => "Scanning {$current_path}",
		];
	}

	/**
	 * Get all files in a directory recursively.
	 *
	 * @param string $dir Directory path.
	 * @return array List of files.
	 */
	private function is_path_type_excluded( $path_type, $exclusions ) {
		$mapped_path = $path_type;

		// Convert path_type to the format used in exclusions if needed.
		foreach ( $this->core_path_mapping as $exclusion_key => $scan_key ) {
			if ( $path_type === $scan_key ) {
				$mapped_path = $exclusion_key;
				break;
			}
		}

		// Check if the path is excluded.
		if ( ! empty($exclusions['core_files']) && in_array($mapped_path, $exclusions['core_files']) ) {
			return true;
		}

		// Special handling for plugins and themes directories.
		switch ( $path_type ) {
			case 'plugins':
				if ( ! empty($exclusions['core_files']) && in_array('plugins', $exclusions['core_files']) ) {
					return true;
				}
				break;
			case 'themes':
				if ( ! empty($exclusions['core_files']) && in_array('themes', $exclusions['core_files']) ) {
					return true;
				}
				break;
		}
		return false;
	}

	/**
	 * Get all files in a directory recursively.
	 *
	 * @param string $dir Directory path.
	 * @return array List of files.
	 */
	private function should_exclude_file( $file, $current_path, $exclusions ) {

		$clean_name = function ( $name ) {
			return preg_replace('/^prefix-/', '', $name);
		};

		if ( ! empty($exclusions['core_files']) ) {
			foreach ( $exclusions['core_files'] as $excluded_dir ) {
				// Map the excluded directory to its scan path.
				$mapped_dir = $this->core_path_mapping[ $excluded_dir ] ?? $excluded_dir;

				if ( isset($this->scan_paths[ $mapped_dir ]) ) {
					$dir_path = $this->scan_paths[ $mapped_dir ];
					if ( strpos($file, $dir_path) === 0 ) {
						return true;
					}
				}
			}
		}

		// Check if file matches any exclusion patterns.
		if ( ! empty($exclusions['file_patterns']) ) {
			foreach ( $exclusions['file_patterns'] as $pattern ) {
				$pattern = trim($pattern);
				if ( ! empty($pattern) ) {
					// Support both glob and regex patterns.
					if ( $this->is_regex_pattern($pattern) ) {
						// For regex patterns.
						if ( @preg_match($pattern, $file) ) {
							return true;
						}
					} else {
						// For glob patterns.
						if ( fnmatch($pattern, $file) ) {
							return true;
						}
					}
				}
			}
		}

		// Check if file is in excluded paths.
		if ( ! empty($exclusions['paths']) ) {
			foreach ( $exclusions['paths'] as $path ) {
				$path = trim($path);
				if ( ! empty($path) ) {
					// Convert relative paths to absolute.
					if ( strpos($path, '/') !== 0 ) {
						$path = ABSPATH . $path;
					}

					// Normalize paths for comparison.
					$path = wp_normalize_path($path);
					$normalized_file = wp_normalize_path($file);

					if ( strpos($normalized_file, $path) === 0 ) {
						return true;
					}
				}
			}
		}

		// Handle plugin-specific exclusions.
		if ( $current_path === 'plugins' && ! empty($exclusions['plugins']) ) {
			$plugin_dir = wp_normalize_path(WP_CONTENT_DIR . '/plugins/');
			$normalized_file = wp_normalize_path($file);

			// Extract plugin folder name from file path.
			$plugin_folder = str_replace($plugin_dir, '', $normalized_file);
			$plugin_folder = explode('/', $plugin_folder)[0];

			foreach ( $exclusions['plugins'] as $plugin ) {
				$plugin = $clean_name(trim($plugin));
				if ( ! empty($plugin) && $plugin === $plugin_folder ) {
					return true;
				}
			}
		}

		// Handle theme-specific exclusions.
		if ( $current_path === 'themes' && ! empty($exclusions['themes']) ) {
			$theme_dir = wp_normalize_path(WP_CONTENT_DIR . '/themes/');
			$normalized_file = wp_normalize_path($file);

			// Extract theme folder name from file path.
			$theme_folder = str_replace($theme_dir, '', $normalized_file);
			$theme_folder = explode('/', $theme_folder)[0];

			foreach ( $exclusions['themes'] as $theme ) {
				$theme = $clean_name(trim($theme));
				if ( ! empty($theme) && $theme === $theme_folder ) {
					return true;
				}
			}
		}

		if ( pathinfo($file, PATHINFO_EXTENSION) === 'php' && ! empty($exclusions['php_functions']) ) {
			$content = @file_get_contents($file);
			if ( $content !== false ) {
				foreach ( $exclusions['php_functions'] as $function ) {
					$function = trim($function);
					if ( ! empty($function) && stripos($content, $function) !== false ) {
						return true;
					}
				}
			}
		}

		return false;
	}

	/**
	 * Get all files to scan in a directory recursively.
	 *
	 * @param string $dir Directory path.
	 * @return array List of files.
	 */
	private function is_regex_pattern( $pattern ) {
		return @preg_match('/^\/.*\/[a-zA-Z]*$/', $pattern);
	}

	/**
	 * Get all files in a directory recursively.
	 *
	 * @param string $dir Directory path.
	 * @return array List of files.
	 */
	private function process_security_checks_batch( $session_id ) {
		$scan_data = get_option($this->scan_session_key . '_' . $session_id);
		if ( ! $scan_data ) {
			return [
				'type' => 'error',
				'message' => 'Invalid session',
			];
		}

		$security_checks = array_keys($this->security_checks);
		$current_check = $security_checks[ $scan_data['current_security_check_index'] ];

		// Process single security check.
		$check_result = $this->process_security_check($current_check);
		$scan_data['results'][ $current_check ] = $check_result;
		$scan_data['completed_checks'][] = $current_check;
		$scan_data['current_security_check_index']++;

		update_option($this->scan_session_key . '_' . $session_id, $scan_data);

		$is_checks_complete = $scan_data['current_security_check_index'] >= count($security_checks);
		$progress = 50 + ( ( $scan_data['current_security_check_index'] / count($security_checks) ) * 50 ); // Security checks are remaining 50%

		return [
			'type' => 'progress',
			'phase' => 'security_checks',
			'progress' => $progress,
			'current_check' => $this->security_checks[ $current_check ]['title'],
			'completed_checks' => count($scan_data['completed_checks']),
			'total_checks' => count($this->security_checks),
			'is_complete' => $is_checks_complete,
			'message' => "Running {$this->security_checks[$current_check]['title']}",
		];
	}

	/**
	 * Get all files in a directory recursively.
	 *
	 * @param string $dir Directory path.
	 * @return array List of files.
	 */
	private function process_security_check( $check_type ) {
		switch ( $check_type ) {
			case 'plugin_vulnerabilities':
				return $this->check_plugin_vulnerabilities();
			case 'wordpress_version':
				return $this->check_wordpress_version_security();
			default:
				return $this->perform_additional_security_checks();
		}
	}


	/**
	 * Get all files in a directory recursively.
	 *
	 * @param string $dir Directory path.
	 * @return array List of files.
	 */
	private function finalize_scan( $session_id ) {
		$scan_data = get_option($this->scan_session_key . '_' . $session_id);
		if ( ! $scan_data ) {
			return [
				'type' => 'error',
				'message' => 'Invalid session',
			];
		}

		// Initialize counters for different types of issues.
		$issue_summary = [
			'critical' => 0,
			'high' => 0,
			'medium' => 0,
			'low' => 0,
			'info' => 0,
		];

		// Process core scan issues.
		if ( ! empty($scan_data['issues']) ) {
			foreach ( $scan_data['issues'] as $issue ) {
				// Core file issues are typically high severity.
				$issue_summary['high'] += count($issue['issues']);
			}
		}

		// Process security check results.
		if ( ! empty($scan_data['results']) ) {
			foreach ( $scan_data['results'] as $check_type => $check_results ) {
				if ( isset($check_results['issues']) ) {
					$this->categorize_security_issues($check_results['issues'], $issue_summary);
				}
			}
		}

		// Calculate total issues.
		$total_issues = array_sum($issue_summary);

		// Determine overall security status.
		$scan_status = $this->determine_security_status($issue_summary);

		// Save scan results.
		$scan_results = [
			'core_scan' => [ 'issues' => $scan_data['issues'] ],
			'security_checks' => $scan_data['results'],
			'issue_summary' => $issue_summary,
		];

		$this->save_scan_history($scan_status, $scan_results);

		// Cleanup session data.
		delete_option($this->scan_session_key . '_' . $session_id);

		return [
			'type' => 'complete',
			'total_issues' => $total_issues,
			'issue_summary' => $issue_summary,
			'status' => $scan_status,
			'results' => $scan_results,
		];
	}

	/**
	 * Get all files in a directory recursively.
	 *
	 * @param string $dir Directory path.
	 * @return array List of files.
	 */
	private function categorize_security_issues( $issues, &$issue_summary ) {
		foreach ( $issues as $issue ) {
			if ( is_array($issue) && isset($issue['type']) ) {
				switch ( $issue['type'] ) {
					case 'vulnerability':
						$issue_summary['critical']++;
						break;
					case 'security_warning':
						$issue_summary['high']++;
						break;
					case 'warning':
						$issue_summary['medium']++;
						break;
					default:
						$issue_summary['low']++;
				}
			} else {
				// Simple string issues are treated as medium severity.
				$issue_summary['medium']++;
			}
		}
	}

	private function determine_security_status( $issue_summary ) {
		// Site is considered vulnerable only if there are critical or high severity issues.
		if ( $issue_summary['critical'] > 0 ) {
			return 'Critical';
		} elseif ( $issue_summary['high'] > 0 ) {
			return 'Vulnerable';
		} elseif ( $issue_summary['medium'] > 0 ) {
			return 'Warning';
		} elseif ( $issue_summary['low'] > 0 || $issue_summary['info'] > 0 ) {
			return 'Notice';
		}

		return 'Secure';
	}

	/**
	 * Get all files in a directory recursively.
	 *
	 * @param string $dir Directory path.
	 * @return array List of files.
	 */
	private function send_progress_update( $check_type, $title, $status, $progress ) {
		// Ensure progress doesn't exceed 100.
		$progress = min(100, max(0, $progress));

		wp_send_json_success([
			'type' => 'progress',
			'check' => $check_type,
			'title' => $title,
			'status' => $status,
			'progress' => $progress,
		]);
		flush();
	}


	/**
	 * Get all files in a directory recursively.
	 *
	 * @param string $dir Directory path.
	 * @return array List of files.
	 */
	private function deep_security_scan( $path, $path_type, $exclusions ) {
		$exclusions = $this->get_scan_exclusions();
		 // Check if this path type is excluded.
		if ( in_array($path_type, $exclusions['core_files'] ?? []) ||
		 in_array($path_type, $exclusions['plugins'] ?? []) ||
		 in_array($path_type, $exclusions['themes'] ?? []) ) {
			return [
				'path' => $path,
				'issues' => [],
				'files_checked' => 0,
			];
		}

		// Existing deep scan logic, but modified to support exclusions.
		$scan_results = [
			'path' => $path,
			'issues' => [],
			'files_checked' => 0,
		];
		$files = $this->get_all_files($path);
		foreach ( $files as $file ) {
			$scan_results['files_checked']++;
			// Check file against exclusion patterns.
			if ( $this->is_file_excluded($file, $exclusions) ) {
				continue;
			}
			$file_issues = $this->check_file_security($file, $exclusions);
			if ( ! empty($file_issues) ) {
				$scan_results['issues'][] = [
					'file' => $file,
					'issues' => $file_issues,
				];
			}
		}

		return $scan_results;
	}

	/**
	 * Get all files in a directory recursively.
	 *
	 * @param string $dir Directory path.
	 * @return array List of files.
	 */
	private function is_file_excluded( $file, $exclusions ) {
		// Check against file exclusion patterns.
		$file_patterns = $exclusions['file_patterns'] ?? [];
		foreach ( $file_patterns as $pattern ) {
			if ( fnmatch($pattern, $file) ) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Get all files in a directory recursively.
	 *
	 * @param string $dir Directory path.
	 * @return array List of files.
	 */
	private function get_all_files( $directory ) {
		$files = [];

		if ( ! is_dir($directory) ) {
			return $files;
		}

		// Use the fully qualified class name with a backslash.
		$iterator = new \RecursiveIteratorIterator(
			new \RecursiveDirectoryIterator($directory, \RecursiveDirectoryIterator::SKIP_DOTS),
			\RecursiveIteratorIterator::SELF_FIRST
		);

		foreach ( $iterator as $file ) {
			if ( ! $file->isDir() ) {
				$files[] = $file->getPathname();
			}
		}

		return $files;
	}

	/**
	 * Get all files in a directory recursively.
	 *
	 * @param string $dir Directory path.
	 * @return array List of files.
	 */
	private function check_file_security( $file, $exclusions ) {
		$issues = [];

		// Skip checking security pattern files from notifier.
		if ( strpos($file, 'Scanner.php') !== false && basename(dirname(dirname($file))) === 'notifier-to-slack' ) {
			return $issues;
		}

		// Skip non-PHP files.
		if ( pathinfo($file, PATHINFO_EXTENSION) !== 'php' ) {
			return $issues;
		}

		$content = @file_get_contents($file);
		if ( $content === false ) {
			return $issues;
		}

		if ( empty($this->security_patterns) ) {
			$patterns = get_transient('security_patterns_from_api');
			if ( $patterns ) {
				$this->security_patterns = $patterns;
			} else {
				$this->fetch_security_patterns();
			}

			if ( empty($this->security_patterns) || ! isset($this->security_patterns['dangerous_functions']) ) {
				return $issues;
			}
		}

		$excluded_functions = $exclusions['php_functions'] ?? [];

		$is_security_scanner = (
			strpos($file, 'scanner') !== false ||
			strpos($file, 'security') !== false ||
			strpos($file, 'Scanner.php') !== false
		);

		if ( $is_security_scanner ) {
			foreach ( $this->security_patterns['dangerous_functions'] as $function ) {
				$clean_function = rtrim($function, '(');

				if ( in_array($clean_function, $excluded_functions) ) {
					continue;
				}

				$pattern = '/\b' . preg_quote($clean_function, '/') . '\s*\(\s*[\$_\'\"]/i';
				if ( preg_match($pattern, $content) ) {
					$issues[] = "Suspicious dangerous_functions call found: $clean_function";
				}
			}
		} else {
			foreach ( $this->security_patterns['dangerous_functions'] as $function ) {
				if ( stripos($content, $function) !== false && ! in_array($function, $excluded_functions) ) {
					$issues[] = "Suspicious dangerous_functions pattern found: $function";
				}
			}
		}

		return $issues;
	}


	/**
	 * Get all files in a directory recursively.
	 *
	 * @param string $dir Directory path.
	 * @return array List of files.
	 */
	private function perform_additional_security_checks() {
		$additional_checks = [
			'database_security' => $this->check_database_security(),
			'wordpress_config' => $this->check_wordpress_configuration(),
		];

		return $additional_checks;
	}

	/**
	 * Get all files in a directory recursively.
	 *
	 * @param string $dir Directory path.
	 * @return array List of files.
	 */
	private function check_database_security() {
		global $wpdb;
		$issues = [];
		// Check for default table prefix.
		if ( $wpdb->prefix === 'wp_' ) {
			$issues[] = 'Using default database prefix increases vulnerability';
		}

		return $issues;
	}

	/**
	 * Get all files in a directory recursively.
	 *
	 * @param string $dir Directory path.
	 * @return array List of files.
	 */
	private function check_wordpress_configuration() {
		$issues = [];
		// Check debug mode.
		if ( defined('WP_DEBUG') && WP_DEBUG === true ) {
			$issues[] = 'Debug mode is enabled - should be disabled in production';
		}

		return $issues;
	}

	/**
	 * Get all files in a directory recursively.
	 *
	 * @param string $dir Directory path.
	 * @return array List of files.
	 */
	private function log_scanning_file( $file ) {
		// Optional: We may add more detailed logging.
		$this->scan_log[] = [
			'timestamp' => current_time('mysql'),
			'file' => $file,
		];
	}


	/**
	 * It store the scan details when scan complete.
	 *
	 * @param string $dir Directory path.
	 * @return array List of files.
	 */
	private function save_scan_history( $status, $results ) {
		$history = get_option($this->scan_history_option, []);

		// Ensure $history is an array.
		if ( ! is_array($history) ) {
			$history = []; // Reset to an empty array if it's not already an array.
		}

		$new_scan = [
			'date' => current_time('mysql'),
			'status' => $status,
			'details' => $results,
		];

		array_unshift($history, $new_scan);
		$history = array_slice($history, 0, 20);

		update_option($this->scan_history_option, $history);

		// Save detailed logs to a file.
		$log_file = WP_CONTENT_DIR . '/security_scan_logs.txt';
		$log_content = 'Scan Date: ' . $new_scan['date'] . PHP_EOL;
		$log_content .= 'Scan Status: ' . $status . PHP_EOL;
		$log_content .= 'Issues: ' . print_r($results, true) . PHP_EOL; // phpcs:ignore Here print_r used to print the issues in log file.
		$log_content .= '-----------------------------' . PHP_EOL;

		file_put_contents($log_file, $log_content, FILE_APPEND);
	}



	/**
	 * Get all files in a directory recursively.
	 *
	 * @param string $dir Directory path.
	 * @return array List of files.
	 */
	public function get_scan_history() {
		$history = get_option($this->scan_history_option, []);
		wp_send_json_success($history);
	}

	/**
	 * Get all files in a directory recursively.
	 *
	 * @param string $dir Directory path.
	 * @return array List of files.
	 */
	public function handle_download_scan_log() {

		if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wp_rest' ) ) {
			wp_send_json_error( 'Security check failed' );
			return;
		}

		// Get the scan date from the request.
		$scan_date = isset($_POST['scan_date']) ? sanitize_text_field(wp_unslash($_POST['scan_date'])) : '';

		if ( empty($scan_date) ) {
			wp_send_json_error('No scan date provided');
		}

		// Read the scan log file.
		$log_file = WP_CONTENT_DIR . '/security_scan_logs.txt';

		if ( ! file_exists($log_file) ) {
			wp_send_json_error('Log file not found');
		}

		// Read the log file content.
		$file_content = file_get_contents($log_file);

		if ( $file_content === false ) {
			wp_send_json_error('Failed to read log file');
		}

		// Split the log file into individual scan logs.
		$logs = explode('-----------------------------', $file_content);

		// Search for the log with the matching date.
		$matched_log = '';
		foreach ( $logs as $log ) {
			if ( strpos($log, 'Scan Date: ' . $scan_date) !== false ) {
				$matched_log = $log . '-----------------------------'; // Add back the separator.
				break;
			}
		}

		if ( empty($matched_log) ) {
			wp_send_json_error('No log found for the specified date');
		}

		// Return the log content.
		wp_send_json_success([ 'content' => $matched_log ]);
	}


	/**
	 * Exclude from scanning files.
	 */
	public function update_scan_exclusions() {
		// Verify nonce first
		if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wp_rest' ) ) {
			wp_send_json_error( 'Security check failed' );
			return;
		}

		// Sanitize all inputs first before using them.
		$paths = isset($_POST['paths']) ? wp_unslash($_POST['paths']) : ''; // phpcs:ignore
		$file_patterns = isset($_POST['file_patterns']) ? wp_unslash($_POST['file_patterns']) : '';  // phpcs:ignore
		$php_functions = isset($_POST['php_functions']) ? wp_unslash($_POST['php_functions']) : '';  // phpcs:ignore
		$core_files = isset($_POST['core_files']) ? wp_unslash($_POST['core_files']) : '';  // phpcs:ignore
		$plugins = isset($_POST['plugins']) ? wp_unslash($_POST['plugins']) : '';  // phpcs:ignore
		$themes = isset($_POST['themes']) ? wp_unslash($_POST['themes']) : '';  // phpcs:ignore

		// Now process the sanitized inputs.
		$exclusions = [
			'paths' => ! empty($paths) ?
				array_filter(array_map('sanitize_text_field', explode("\n", $paths))) : [],
			'file_patterns' => ! empty($file_patterns) ?
				array_filter(array_map('sanitize_text_field', explode("\n", $file_patterns))) : [],
			'php_functions' => ! empty($php_functions) ?
				array_filter(array_map('sanitize_text_field', explode("\n", $php_functions))) : [],
			'core_files' => ! empty($core_files) ?
				array_filter(array_map('sanitize_text_field', json_decode($core_files, true) ?? [])) : [],
			'plugins' => ! empty($plugins) ?
				array_filter(array_map('sanitize_text_field', json_decode($plugins, true) ?? [])) : [],
			'themes' => ! empty($themes) ?
				array_filter(array_map('sanitize_text_field', json_decode($themes, true) ?? [])) : [],
		];

		update_option($this->exclusion_option, $exclusions);
		wp_send_json_success($exclusions);
	}


	/**
	 * Method to retrieve current exclusions
	 */
	private function get_scan_exclusions() {
		$exclusions = get_option($this->exclusion_option, [
			'paths' => [],
			'file_patterns' => [],
			'php_functions' => [],
			'core_files' => [],
			'plugins' => [],
			'themes' => [],
		]);

		$this->clean_exclusion_names($exclusions);
		return $exclusions;
	}

	/**
	 * Method to clean exclusion names
	 */
	private function clean_exclusion_names( &$exclusions ) {
		if ( ! empty($exclusions['plugins']) ) {
			foreach ( $exclusions['plugins'] as &$plugin ) {
				$plugin = preg_replace('/^prefix-/', '', $plugin);
			}
		}

		if ( ! empty($exclusions['themes']) ) {
			foreach ( $exclusions['themes'] as &$theme ) {
				$theme = preg_replace('/^prefix-/', '', $theme);
			}
		}
	}

	/**
	 * Vulnerability check
	 */
	public function check_plugin_vulnerabilities() {
		$issues = [];
		$exclusions = $this->get_scan_exclusions();
		$plugins = get_plugins();
		$scan_results = [];

		// If 'plugins' is in core_files exclusions, skip the entire plugin scan.
		if ( in_array('plugins', $exclusions['core_files'] ?? []) ) {
			return [
				'issues' => [],
				'files_checked' => 0,
			];
		}

		foreach ( $plugins as $plugin_file => $plugin_data ) {
			$plugin_slug = $this->get_plugin_slug($plugin_file);

			// Skip excluded plugins.
			$excluded_slugs = array_map(function ( $path ) {
				return explode('/', $path)[0]; // Extracts the plugin folder name.
			}, $exclusions['plugins'] ?? []);

			// Skip excluded plugins.
			if ( in_array($plugin_slug, $excluded_slugs) ) {
				continue;
			}

			// Fetch vulnerabilities using the correct slug.
			$plugin_vulnerabilities = $this->fetch_vulnerable_plugins($plugin_slug, $plugin_data['Version']);

			if ( ! empty($plugin_vulnerabilities) ) {
				$scan_results[] = [
					'plugin' => $plugin_data['Name'],
					'slug'   => $plugin_slug,
					'version' => $plugin_data['Version'],
					'vulnerabilities' => $plugin_vulnerabilities,
				];
			}
		}

		return [
			'issues' => $scan_results,
			'files_checked' => count($plugins),
		];
	}


	/**
	 * Vulnerability check.
	 */
	private function fetch_vulnerable_plugins( $name, $version ) {
		$vulnerabilities = [];

		// If the plugin is premium, skip the security check.
		if ( $this->is_premium_plugin($name) ) {
			return [];
		}

		$plugin_slug = sanitize_title($name);

		// 1. Check WordPress.org API for plugin information.
		$api_url = 'https://api.wordpress.org/plugins/info/1.2/?action=plugin_information';
		$response = wp_remote_get(add_query_arg([
			'slug' => $plugin_slug,
			'fields' => [
				'versions' => true,
				'tested' => true,
				'requires' => true,
				'rating' => true,
				'last_updated' => true,
				'downloaded' => true,
				'active_installs' => true,
			],
		], $api_url));

		if ( is_wp_error($response) ) {
			return [
				[
					'type' => 'error',
					'description' => 'Unable to check plugin security status',
					'recommendation' => 'Please try again later or check plugin manually.',
				],
			];
		}

		$plugin_data = json_decode(wp_remote_retrieve_body($response));

		// Security checks based on free data.
		if ( $plugin_data ) {
			// Check 1: Version outdated.
			if ( isset($plugin_data->version) && version_compare($version, $plugin_data->version, '<') ) {
				$vulnerabilities[] = [
					'type' => 'security_warning',
					'description' => sprintf(
						'Running outdated version %s. Latest version is %s',
						esc_html($version),
						esc_html($plugin_data->version)
					),
					'recommendation' => 'Update to the latest version for security patches.',
				];
			}

			// Check 2: Plugin abandoned.
			$last_updated = isset($plugin_data->last_updated) ? strtotime($plugin_data->last_updated) : 0;
			if ( $last_updated && ( time() - $last_updated ) > ( 365 * 24 * 60 * 60 ) ) { // Over 1 year
				$vulnerabilities[] = [
					'type' => 'warning',
					'description' => 'Plugin has not been updated in over a year',
					'recommendation' => 'Consider finding an actively maintained alternative.',
				];
			}

			// Check 3: WordPress version compatibility.
			global $wp_version;
			if ( isset($plugin_data->tested) && version_compare($wp_version, $plugin_data->tested, '>') ) {
				$vulnerabilities[] = [
					'type' => 'warning',
					'description' => sprintf(
						'Plugin only tested up to WordPress %s (you\'re running %s)',
						esc_html($plugin_data->tested),
						esc_html($wp_version)
					),
					'recommendation' => 'Check plugin compatibility with your WordPress version.',
				];
			}

			// Check 4: Low installation count (might indicate unreliable plugin).
			if ( isset($plugin_data->active_installs) && $plugin_data->active_installs < 100 ) {
				$vulnerabilities[] = [
					'type' => 'warning',
					'description' => 'Plugin has very few active installations',
					'recommendation' => 'Verify plugin reliability and consider alternatives with larger user base.',
				];
			}

			// Analyze ratings more thoroughly.
			if ( isset($plugin_data->ratings) && isset($plugin_data->num_ratings) ) {
				$rating_analysis = $this->analyze_plugin_ratings($plugin_data->ratings, $plugin_data->num_ratings);
				if ( $rating_analysis['flag'] ) {
					$vulnerabilities[] = [
						'type' => 'warning',
						'description' => $rating_analysis['message'],
						'recommendation' => 'Review plugin ratings and feedback carefully.',
					];
				}
			}

			// Check 5: Check against our custom API for known vulnerabilities.
			$vulnerabilities = array_merge(
				$vulnerabilities,
				$this->check_custom_vulnerability_api($plugin_slug, $version)
			);

			// Check 6: Check against National Vulnerability Database.
			$vulnerabilities = array_merge(
				$vulnerabilities,
				$this->check_wpvulnerability_net($plugin_slug, $version)
			);

			// Check 7: Check against National Vulnerability Database.
			$vulnerabilities = array_merge(
				$vulnerabilities,
				$this->check_nvd_vulnerabilities($plugin_slug, $version)
			);

		} else {
			$vulnerabilities[] = [
				'type' => 'warning',
				'description' => 'Plugin not found in WordPress.org repository',
				'recommendation' => 'Verify the plugin source and ensure it\'s from a trusted developer.',
			];
		}

		return $vulnerabilities;
	}

	/**
	 * Check plugin vulnerabilities using custom API
	 *
	 * @param string $plugin_slug The plugin slug
	 * @param string $version The plugin version
	 * @return array Array of vulnerability data
	 */
	private function check_custom_vulnerability_api( $plugin_slug, $version ) {
		$vulnerabilities = [];

		$api_key = get_option('security_api_key', '890b75e99a1c2fb0a586c5efeae2c30e');
		$api_url = get_option('security_api_url', 'https://wpazleen.com/');
		$base_url = rtrim($api_url, '/');

		$request_url = $base_url . '/wp-json/wpx/v1/plugin-vulnerabilities';
		$request_url = add_query_arg([
			'api_key' => $api_key,
			'plugin_slug' => $plugin_slug,
			'version' => $version,
		], $request_url);

		$response = wp_remote_get($request_url, [
			'timeout' => 15,
			'headers' => [
				'User-Agent' => 'WordPress/' . get_bloginfo('version'),
				'Referer' => site_url(),
			],
		]);

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

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

		if ( isset($data['success']) && $data['success'] && isset($data['vulnerabilities']) ) {
			foreach ( $data['vulnerabilities'] as $vuln_version => $vuln_details ) {
				if ( version_compare($version, $vuln_version, '<=') ) {
					$vulnerabilities[] = [
						'type' => 'vulnerability',
						'description' => $vuln_details['description'],
						'recommendation' => $vuln_details['fix'],
						'source' => 'Activity Guard Database',
					];
				}
			}
		}

		return $vulnerabilities;
	}


	/**
	 * Check plugin vulnerabilities using wpvulnerability.net API
	 *
	 * @param string $plugin_slug The plugin slug
	 * @param string $version The plugin version
	 * @return array Array of vulnerability data
	 */
	private function check_wpvulnerability_net( $plugin_slug, $version ) {
		$vulnerabilities = [];

		$api_url = "https://www.wpvulnerability.net/plugin/{$plugin_slug}/";

		$response = wp_remote_get($api_url, [
			'timeout' => 15,
			'headers' => [
				'Accept' => 'application/json',
				'User-Agent' => 'WordPress Security Scanner',
			],
		]);

		if ( is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200 ) {
			return [];
		}

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

		if ( empty($data) || isset($data['error']) && $data['error'] !== 0 || ! isset($data['data']['vulnerability']) ) {
			return [];
		}

		foreach ( $data['data']['vulnerability'] as $vulnerability ) {
			// Check if this version is affected by the vulnerability.
			$is_vulnerable = false;

			if ( isset($vulnerability['operator']) ) {
				$operator = $vulnerability['operator'];

				// Check if version is below the max version (when max_operator is "lt" or "le").
				if ( isset($operator['max_version']) && isset($operator['max_operator']) ) {
					if ( $operator['max_operator'] === 'lt' && version_compare($version, $operator['max_version'], '<') ) {
						$is_vulnerable = true;
					} else if ( $operator['max_operator'] === 'le' && version_compare($version, $operator['max_version'], '<=') ) {
						$is_vulnerable = true;
					}
				}

				// Check if version is above the min version (when min_operator is "gt" or "ge").
				if ( isset($operator['min_version']) && isset($operator['min_operator']) ) {
					if ( $operator['min_operator'] === 'gt' && version_compare($version, $operator['min_version'], '>') ) {
						$is_vulnerable = true;
					} else if ( $operator['min_operator'] === 'ge' && version_compare($version, $operator['min_version'], '>=') ) {
						$is_vulnerable = true;
					}
				}

				// Handle the case where there's both min and max version constraints.
				if ( isset($operator['min_version']) && isset($operator['max_version']) &&
					isset($operator['min_operator']) && isset($operator['max_operator']) ) {

					$above_min = true;
					$below_max = true;

					if ( $operator['min_operator'] === 'gt' ) {
						$above_min = version_compare($version, $operator['min_version'], '>');
					} else if ( $operator['min_operator'] === 'ge' ) {
						$above_min = version_compare($version, $operator['min_version'], '>=');
					}

					if ( $operator['max_operator'] === 'lt' ) {
						$below_max = version_compare($version, $operator['max_version'], '<');
					} else if ( $operator['max_operator'] === 'le' ) {
						$below_max = version_compare($version, $operator['max_version'], '<=');
					}

					$is_vulnerable = $above_min && $below_max;
				}

				// If unfixed is set to 1, we need to check if we're above the max version.
				// Only consider unfixed if we don't have both min and max operators already set.
				if ( isset($operator['unfixed']) && $operator['unfixed'] === '1' ) {
					// If max_version is set, only versions up to max_version are vulnerable.
					// For newer versions, we should not mark them as vulnerable.
					if ( isset($operator['max_version']) ) {
						if ( $operator['max_operator'] === 'lt' && version_compare($version, $operator['max_version'], '<') ) {
							$is_vulnerable = true;
						} else if ( $operator['max_operator'] === 'le' && version_compare($version, $operator['max_version'], '<=') ) {
							$is_vulnerable = true;
						}
					} else {
						// If no max_version is set, then all versions are vulnerable.
						$is_vulnerable = true;
					}
				}
			}

			if ( $is_vulnerable ) {
				// Get the most relevant source for the vulnerability details.
				$source = $vulnerability['source'][0] ?? [];
				$cve_source = null;

				// Try to find a CVE source if available.
				foreach ( $vulnerability['source'] as $src ) {
					if ( isset($src['id']) && strpos($src['id'], 'CVE-') === 0 ) {
						$cve_source = $src;
						break;
					}
				}

				// Use CVE source if available.
				if ( $cve_source ) {
					$source = $cve_source;
				}

				$fix_version = isset($vulnerability['operator']['max_version']) ?
					$vulnerability['operator']['max_version'] : null;

				$vulnerabilities[] = [
					'type' => 'vulnerability',
					'description' => $vulnerability['name'] ?? 'Security vulnerability found',
					'recommendation' => $fix_version ?
						"Update to version {$fix_version} or higher" :
						'Update to the latest version available or consider an alternative plugin',
					'source' => 'WPVulnerability',
					'details' => $source['description'] ?? '',
					'reference' => $source['link'] ?? '',
					'cve_id' => isset($source['id']) && strpos($source['id'], 'CVE-') === 0 ? $source['id'] : null,
					'published_date' => $source['date'] ?? '',
				];
			}
		}

		return $vulnerabilities;
	}


	/**
	 * Check plugin vulnerabilities using NVD API
	 *
	 * @param string $plugin_slug The plugin slug
	 * @param string $version The plugin version
	 * @return array Array of vulnerability data
	 */
	private function check_nvd_vulnerabilities( $plugin_slug, $version ) {
		$vulnerabilities = [];

		// Convert WordPress plugin name to CPE format.
		$cpe_name = "cpe:2.3:a:*:{$plugin_slug}:{$version}:*:*:*:*:wordpress:*:*";

		// NVD API URL.
		$api_url = 'https://services.nvd.nist.gov/rest/json/cves/2.0';
		$api_url = add_query_arg([
			'cpeName' => $cpe_name,
			'resultsPerPage' => 10,
		], $api_url);

		$response = wp_remote_get($api_url, [
			'timeout' => 15,
			'headers' => [
				'User-Agent' => 'WordPress Security Scanner',
			],
		]);

		if ( is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200 ) {
			return [];
		}

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

		if ( ! empty($data) && isset($data['vulnerabilities']) ) {
			foreach ( $data['vulnerabilities'] as $vulnerability ) {
				$cve_id = $vulnerability['cve']['id'] ?? '';
				$description = '';

				if ( isset($vulnerability['cve']['descriptions']) ) {
					foreach ( $vulnerability['cve']['descriptions'] as $desc ) {
						if ( $desc['lang'] === 'en' ) {
							$description = $desc['value'];
							break;
						}
					}
				}

				if ( ! empty($description) ) {
					$vulnerabilities[] = [
						'type' => 'vulnerability',
						'description' => "CVE: {$cve_id} - {$description}",
						'recommendation' => 'Update to the latest version or check plugin security advisories',
						'source' => 'National Vulnerability Database',
						'reference' => "https://nvd.nist.gov/vuln/detail/{$cve_id}",
					];
				}
			}
		}

		return $vulnerabilities;
	}


	/**
	 * Get all plugin.
	 */
	private function get_plugin_slug( $plugin_file ) {
		$plugin_path = WP_PLUGIN_DIR . '/' . $plugin_file;

		// Ensure the file exists before attempting to read.
		if ( ! file_exists($plugin_path) ) {
			return false;
		}

		// Read plugin metadata.
		$plugin_data = get_file_data($plugin_path, [
			'TextDomain' => 'Text Domain',
			'Name'       => 'Plugin Name',
			'PluginURI'  => 'Plugin URI',
		]);

		// 1. Use Text Domain first if available.
		if ( ! empty($plugin_data['TextDomain']) ) {
			return sanitize_title($plugin_data['TextDomain']);
		}

		// 2. Extract from Plugin URI if it's a WordPress.org plugin.
		if ( ! empty($plugin_data['PluginURI']) ) {
			if ( preg_match('/wordpress\.org\/plugins\/([^\/]+)/', $plugin_data['PluginURI'], $matches) ) {
				return sanitize_title($matches[1]);
			}
		}

		// 3. Extract from directory name.
		if ( strpos($plugin_file, '/') !== false ) {
			$directory = dirname($plugin_file);
			return sanitize_title(basename($directory));
		}

		// 4. Fallback to filename without .php.
		return sanitize_title(basename($plugin_file, '.php'));
	}

	/**
	 * Check for known vulnerabilities
	 * This is a placeholder function. In a real-world scenario, you would fetch this  data from a reliable source.
	 */
	private function get_known_vulnerable_versions( $plugin_name ) {
		$known_vulnerabilities = [
			'contact-form-7' => [
				'5.3.1' => [
					'description' => 'XSS vulnerability in versions 5.3.1 and below',
					'fix' => 'Update to version 5.3.2 or higher',
				],
			],
			'wordpress-seo' => [
				'17.8' => [
					'description' => 'Authenticated SQLi vulnerability in versions 17.8 and below',
					'fix' => 'Update to version 17.9 or higher',
				],
			],
			// Add more known vulnerabilities here.
		];

		return $known_vulnerabilities[ sanitize_title($plugin_name) ] ?? [];
	}


	/**
	 * Check WordPress version security.
	 */
	public function check_wordpress_version_security() {
		global $wp_version;
		$issues = [];

		// Check current WordPress version against recommended security version.
		$latest_version = $this->get_latest_wordpress_version();

		if ( version_compare($wp_version, $latest_version, '<') ) {
			$issues[] = [
				'current_version' => $wp_version,
				'latest_version' => $latest_version,
				'message' => 'WordPress is outdated. Please update to the latest version.',
			];
		}

		return [
			'issues' => $issues,
			'version_checked' => $wp_version,
		];
	}

	/**
	 * Get the latest WordPress version from the API.
	 */
	private function get_latest_wordpress_version() {
		// Use WordPress core API to check for updates.
		$update_core = get_site_transient('update_core');

		// If transient doesn't exist or is expired, fetch fresh data.
		if ( ! $update_core ) {
			wp_version_check();
			$update_core = get_site_transient('update_core');
		}

		// Parse the update check response.
		if ( $update_core && property_exists($update_core, 'updates') ) {
			foreach ( $update_core->updates as $update ) {
				// Look for the latest stable version.
				if ( $update->response === 'upgrade' && $update->channel === 'stable' ) {
					return $update->version;
				}
			}
		}

		// Fallback: directly check WordPress API.
		$api_url = 'https://api.wordpress.org/core/version-check/1.7/';
		$response = wp_remote_get($api_url);

		if ( ! is_wp_error($response) ) {
			$api_response = json_decode(wp_remote_retrieve_body($response), true);
			if ( isset($api_response['offers']) && is_array($api_response['offers']) ) {
				foreach ( $api_response['offers'] as $offer ) {
					if ( isset($offer['response']) && $offer['response'] === 'upgrade' &&
						isset($offer['current']) && ! empty($offer['current']) ) {
						return $offer['current'];
					}
				}
			}
		}

		// If all checks fail, return current WordPress version.
		global $wp_version;
		return $wp_version;
	}

	/**
	 * Check if a plugin is premium.
	 *
	 * @param string $name Plugin name.
	 * @return bool True if premium, false otherwise.
	 */
	private function is_premium_plugin( $name ) {
		$premium_patterns = [
			'/pro$/i',
			'/premium$/i',
			'/-pro-/i',
			'/-premium-/i',
		];

		foreach ( $premium_patterns as $pattern ) {
			if ( preg_match($pattern, $name) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Analyze plugin ratings and inform user if it has low ratting.
	 *
	 * @param object $ratings Ratings data.
	 * @param int    $total_ratings Total number of ratings.
	 * @return array Analysis result.
	 */
	private function analyze_plugin_ratings( $ratings, $total_ratings ) {
		if ( $total_ratings < 10 ) {
			return [
				'flag' => true,
				'message' => 'Plugin has very few ratings to establish reliability',
			];
		}

		// Convert stdClass to array and handle potential missing keys.
		$ratings_array = (array) $ratings;
		$five_star = isset($ratings_array[5]) ? $ratings_array[5] : 0;
		$one_star = isset($ratings_array[1]) ? $ratings_array[1] : 0;

		// Calculate percentages.
		$five_star_percent = ( $five_star / $total_ratings ) * 100;
		$one_star_percent = ( $one_star / $total_ratings ) * 100;

		if ( $five_star_percent > 90 && $total_ratings < 50 ) {
			return [
				'flag' => true,
				'message' => 'Unusually high proportion of 5-star ratings with limited total ratings',
			];
		}

		if ( $one_star_percent > 30 ) {
			return [
				'flag' => true,
				'message' => 'High proportion of 1-star ratings indicates potential issues',
			];
		}

		return [ 'flag' => false ];
	}

	/**
	 * Get API settings.
	 */
	public function get_api_settings() {
		// Check nonce for security.
		if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wp_rest' ) ) {
			wp_send_json_error( 'Security check failed' );
			return;
		}

		// Get API settings from options.
		$api_url = get_option('security_api_url', 'https://wpazleen.com/');
		$api_key = get_option('security_api_key', '890b75e99a1c2fb0a586c5efeae2c30e');

		wp_send_json_success([
			'api_url' => $api_url,
			'api_key' => $api_key,
		]);
	}

	/**
	 * Update API settings.
	 */
	public function update_api_settings() {
		// Check nonce for security.
		if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wp_rest' ) ) {
			wp_send_json_error( 'Security check failed' );
			return;
		}

		// Get and sanitize input with unslashing.
		$api_url = isset($_POST['api_url']) ? sanitize_url(wp_unslash($_POST['api_url'])) : '';
		$api_key = isset($_POST['api_key']) ? sanitize_text_field(wp_unslash($_POST['api_key'])) : '';

		// Validate URL format.
		if ( ! empty($api_url) && ! filter_var($api_url, FILTER_VALIDATE_URL) ) {
			wp_send_json_error('Invalid API URL format');
			return;
		}

		// Validate API key (customize validation as needed).
		if ( empty($api_key) || strlen($api_key) < 10 ) {
			wp_send_json_error('Invalid API key');
			return;
		}

		// Save settings.
		update_option('security_api_url', $api_url);
		update_option('security_api_key', $api_key);

		wp_send_json_success('API settings saved successfully');
	}

	/**
	 * Test API connection.
	 */
	public function test_api_connection() {
		// Check nonce for security.
		check_ajax_referer('wp_rest', 'nonce');

		// Get API settings from request.
		$api_url = isset($_POST['api_url']) ? sanitize_url(wp_unslash($_POST['api_url'])) : '';
		$api_key = isset($_POST['api_key']) ? sanitize_text_field(wp_unslash($_POST['api_key'])) : '';

		// Use provided values or fall back to saved settings.
		if ( empty($api_url) ) {
			$api_url = get_option('security_api_url', 'https://wpazleen.com/');
		}

		if ( empty($api_key) ) {
			$api_key = get_option('security_api_key', '890b75e99a1c2fb0a586c5efeae2c30e');
		}

		$base_url = rtrim($api_url, '/');
		$request_url = $base_url . '/wp-json/wpx/v1/security-patterns?api_key=' . urlencode($api_key);

		$args = [
			'timeout' => 30,
			'headers' => [
				'User-Agent' => 'WordPress/' . get_bloginfo('version'),
				'Referer' => site_url(),
			],
		];

		$response = wp_remote_get($request_url, $args);

		// Check for errors.
		if ( is_wp_error($response) ) {
			wp_send_json_error($response->get_error_message());
			return;
		}

		$status_code = wp_remote_retrieve_response_code($response);

		// Check response status.
		if ( $status_code !== 200 ) {
			wp_send_json_error('API returned status code: ' . $status_code);
			return;
		}

		// Try to decode JSON response.
		$body = wp_remote_retrieve_body($response);
		$data = json_decode($body, true);

		// Check if response has expected format.
		if ( json_last_error() !== JSON_ERROR_NONE || ! isset($data['success']) || $data['success'] !== true ) {
			wp_send_json_error('Invalid API response format');
			return;
		}

		wp_send_json_success('API connection successful');
	}
}
