<?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
use WPNTS\Inc\SlackChannelRouter; // phpcs:ignore

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

	/**
	 * @var array $config Configuration settings for security headers.
	 */
	private $config = [];

	/**
	 * @var string $tor_nodes_file Path to the TOR nodes file.
	 */
	private $tor_nodes_file;

	/**
	 * Option name for storing the last update timestamp.
	 */
	private $last_update_option = 'wpnts_tor_nodes_last_update';

	/**
	 * Option name for storing the TOR nodes list.
	 */
	private $tor_list_option = 'wpnts_tor_nodes_list';

	/**
	 * Update interval in seconds (24 hours).
	 */
	private $update_interval = 86400;

	/**
	 * @var bool $scanner_block Flag to indicate if scanner protection is enabled.
	 */
	private $scanner_block = false;

	/**
	 * List of blocked user agents commonly used by security scanners and bots.
	 */
	private $blocked_user_agents = [
		// Original entries.
		'wpscan',
		'nikto',
		'nuclei',
		'owasp-zap',
		'python-requests',
		'curl',
		'wget',
		'scanbot',
		'bot',
		'spider',
		'crawler',

		// Additional security scanners.
		'acunetix',
		'arachni',
		'burpsuite',
		'gobuster',
		'nessus',
		'qualys',
		'fortify',
		'nmap',
		'sqlmap',
		'dirbuster',
		'metasploit',
		'masscan',
		'hydra',

		// Automated tools.
		'zgrab',
		'rustybot',
		'netcraft',
		'semrush',
		'screaming frog',
		'ahrefsbot',
		'mj12bot',
		'dotbot',

		// Framework-specific.
		'spring-boot',
		'apache-httpclient',
		'go-http-client',
		'node-fetch',
		'axios',

		// Cloud service scanners.
		'aws-security-scanner',
		'azure-security-scan',
		'cloud-mapping',
		'cloudsploit',

		// Generic suspicious identifiers.
		'scanner',
		'vulnerability',
		'pentest',
		'security',
		'recon',
		'exploit',
		'fuzzer',

		// Known malicious.
		'xsspider',
		'shellshock',
		'log4j',
		'probing',
		'harvester',

		// AI/ML crawlers.
		'gptbot',
		'anthropic-ai',
		'claude-web',
		'bard-google',
		'openai',
	];


	/**
	 * Construct method.
	 *
	 * @since 1.0.0
	 */
	public function __construct() {
		$wpnts_db_instance = new DB();
		$schedules_int = get_option( 'wpnts_webhook_site_settings');
		$schedules_interval = json_decode($schedules_int);

		$wpnts_disable_file_editor = $schedules_interval->wpnts_disable_file_editor ?? 'false';
		$suspicious_requests = $schedules_interval->suspicious_requests ?? 'false';
		$rate_limit_ip = $schedules_interval->rate_limit_ip ?? 'false';
		$hide_wordpress_version = $schedules_interval->hide_wordpress_version ?? 'false';
		$hide_http_header_wp_version = $schedules_interval->hide_http_header_wp_version ?? 'false';
		$disable_xml_rpc = $schedules_interval->disable_xml_rpc ?? 'false';
		$create_http_security_header_layer = $schedules_interval->create_http_security_header_layer ?? 'false';
		$x_powered_by = $schedules_interval->x_powered_by ?? 'false';
		$tor_security = $schedules_interval->tor_security ?? 'false';
		$scanner_protection = $schedules_interval->scanner_protection ?? 'false';

		// Disable Themes & Plugins Editor page.
		if ( true === $wpnts_disable_file_editor ) {
			add_action('init', [ $this, 'wpnts_disable_file_editor' ]);
		}

		// Monitoring Suspicious HTTP Requests.
		if ( true === $suspicious_requests ) {
			add_action('init', [ $this, 'wpnts_detect_suspicious_requests' ]);
		}

		// Bot Traffic Detection (Rate-Limiting).
		if ( true === $rate_limit_ip ) {
			add_action('init', [ $this, 'wpnts_rate_limit_ip' ]);
		}

		// Hide WP version.
		if ( true === $hide_wordpress_version ) {
			add_action('init', [ $this, 'wpnts_hide_wordpress_version' ]);
			add_filter('style_loader_src', [ $this, 'wpnts_remove_version_from_assets' ], 9999);
			add_filter('script_loader_src', [ $this, 'wpnts_remove_version_from_assets' ], 9999);
		}
		// Hook into REST API responses to remove X-Powered-By.
		if ( true === $x_powered_by ) {
			add_action('wp', [ $this, 'wpnts_remove_x_powered_by_header' ]);
			add_filter('rest_pre_serve_request', [ $this, 'wpnts_remove_x_powered_by_for_rest' ]);
		}

		// Hide WordPress version from HTTP headers.
		if ( true === $hide_http_header_wp_version ) {
			add_action('init', [ $this, 'hide_http_header_wp_version' ]);
		}

		// Disable XML-RPC.
		if ( true === $disable_xml_rpc ) {
			add_action('init', [ $this, 'disable_xmlrpc_complete' ]);
			add_filter('wp_headers', [ $this, 'remove_xmlrpc_headers' ]);
			add_action('wp', [ $this, 'block_xmlrpc_requests' ]);
		}

		// HTTP Header security layer.
		if ( true === $create_http_security_header_layer ) {
			// Set default configuration.
			$this->set_default_config();
			 // Add action hooks.
			 add_action('send_headers', [ $this, 'add_security_headers' ]);
			 add_action('init', [ $this, 'remove_security_risks' ]);
		}

		// TOR nodes file to track if owner site visit using any tour browser.
		if ( true === $tor_security ) {
			// Set path for TOR nodes file.
			$this->tor_nodes_file = WP_CONTENT_DIR . '/uploads/tor_nodes.txt';

			// Initialize hooks.
			add_action('wp', [ $this, 'initialize_tor_protection' ]);
			add_action('wp_ajax_wpnts_update_tor_list', [ $this, 'update_tor_nodes_list' ]);
			add_action('wp_ajax_nopriv_wpnts_update_tor_list', [ $this, 'update_tor_nodes_list' ]);
		}

		// Scanner block to prevent hacking.
		if ( true === $scanner_protection ) {
			add_action('init', [ $this, 'wpnts_block_scanners' ]);
		}
	}


	/**
	 * Disable Themes and Plugins Editor.
	 * Disables the built-in WordPress theme and plugin editor to prevent unauthorized changes to core files from the admin dashboard.
	 */
	public function wpnts_disable_file_editor() {
		if ( ! defined('DISALLOW_FILE_EDIT') ) {
			define('DISALLOW_FILE_EDIT', true);
		}
	}

	/**
	 * Detect suspicious requests (basic example for SQL injection patterns).
	 *
	 * Add suspicious strings to the URL query parameters that match the patterns in your function:
	 * SQL Injection example: http://localhost/SiteName/notifier/?id=union+select+1,2,3
	 * XSS example: http://localhost/SiteName/notifier/?q=<script>alert('XSS')</script>
	 * SQL Dropping example: http://localhost/SiteName/notifier/?cmd=drop+table+users
	 * Directory traversal example: http://localhost/SiteName/notifier/?file=../../etc/passwd
	 *
	 * Scans incoming URL requests for potential malicious patterns like SQL injection, XSS, and directory traversal. If a suspicious pattern is detected, it logs the activity and sends an alert to Slack.
	 */
	public function wpnts_detect_suspicious_requests() {
		// Sanitize the data.
		$request_uri = isset($_SERVER['REQUEST_URI']) ? esc_url_raw(wp_unslash($_SERVER['REQUEST_URI'])) : '';
		$query_string = isset($_SERVER['QUERY_STRING']) ? esc_url_raw(wp_unslash($_SERVER['QUERY_STRING'])) : '';

		// Get the site URL, including child site for multisite.
		$attachmentHandler = new SlackAttachment();
		$site_info = $attachmentHandler->getSiteURLAddOption();

		// Enhanced patterns for various types of attacks.
		$patterns = [
			// SQL Injection patterns.
			'/(\'|%27|--|\#|\/\*)[^\n]*((information|into|from|select|union|where|and|or|\|\||&|\&\&)[^\n]*)+/ix',
			'/union.*select/i',
			'/drop.*table/i',

			// XSS patterns.
			'/<[^\w<>]*(?:[^<>"\'\s]*:)?[^\w<>]*(?:\W*s\W*c\W*r\W*i\W*p\W*t|\W*f\W*o\W*r\W*m|\W*s\W*t\W*y\W*l\W*e|\W*s\W*v\W*g|\W*m\W*a\W*r\W*q\W*u\W*e\W*e)/i',
			'/<script.*?>/i',

			// Command injection patterns.
			'/;\s*(?:\/\*.*?\*\/\s*)?(?:\|\||&&)?\s*[\n\r]*|`.*?`|;?\s*shutdown\s*(?:-r|-h)?\s*(?:now|0)?\b/i',
			'/(\b(?:exec|xp_cmdshell|sp_executesql)\b|\|\||&&|\bping\b)|\s*\bnet\b\s*(?:user|group|localgroup)\b/i',

			// Directory traversal patterns.
			'/\.\.\/\.\.\//',
			'#\.{2};?\/|\/var\/|\/usr\/|\/etc\/#',
		];

		$is_suspicious = false;

		foreach ( $patterns as $pattern ) {
			// Apply sanitization before matching patterns.
			if ( preg_match($pattern, $request_uri) || preg_match($pattern, $query_string) ) {
				$is_suspicious = true;
				break;
			}
		}

		if ( $is_suspicious ) {
			// Log the event.
			$severity = 'High';
			$event_type = 'Suspicious';

			// Sanitize and escape author data.
			$current_user = wp_get_current_user();
			$author = ( $current_user && ! empty($current_user->display_name) ) ? esc_html($current_user->display_name) : 'Unknown';

			// Get timestamp and IP.
			$time = current_time('mysql');
			$ip = $attachmentHandler->get_user_ip();

			// Default template for suspicious request alert
			$default_template = '⚠️ Suspicious activity detected from ➡️IP {ip_address}. Possible hacking attempt. Request URI: {request_uri} Query String: {query_string} {site_url}';
			
			// Template data for placeholders
			$template_data = [
				'ip_address' => $ip,
				'request_uri' => $request_uri,
				'query_string' => $query_string,
			];

			// Send templated notification with custom message support
			$attachmentHandler->send_templated_notification( 
				$this->get_webhook_url(), 
				$default_template,
				$template_data,
				$severity, 
				$event_type,
				'wpnts_webhook_site_settings', // Settings option name
				'suspicious_requests_message', // Template key
				$author
			);

			// Prepare log message for database (using processed template)
			$log_message = $attachmentHandler->process_message_template(
				$default_template,
				$template_data,
				'wpnts_webhook_site_settings',
				'suspicious_requests_message'
			);

			// Log to database.
			$attachmentHandler->log_activity_to_db($severity, $time, $ip, $author, $event_type, $log_message);

			// Show admin notice.
			add_action('admin_notices', function () use ( $log_message ) {
				echo "<div class='notice notice-error'><p>" . wp_kses_post($log_message) . '</p></div>';
			});
		}
	}

	/**
	 * Bot Detection and Rate Limiting.
	 * Detects excessive requests from bots and applies rate-limiting.
	 * If an IP makes more than 200 requests per minute, it is temporarily blocked for 30 minutes, and a Slack notification is triggered.
	 */
	public function wpnts_rate_limit_ip() {
		$current_user = wp_get_current_user();

		// Bypass rate limiting for logged-in users or admins.
		if ( is_user_logged_in() ) {
			return;
		}

		// Exclude requests for certain file types from rate-limiting.
		$excluded_file_types = array_flip([ 'js', 'css', 'jpg', 'jpeg', 'png', 'gif', 'svg', 'woff', 'woff2', 'ttf' ]);

		$path = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_parse_url( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ), PHP_URL_PATH ) ) : '';

		$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));

		if ( isset($excluded_file_types[ $extension ]) ) {
			return;
		}

		// Retrieve dynamic rate limit settings.
		$schedules_int = get_option('wpnts_webhook_site_settings');
		$schedules_interval = json_decode($schedules_int);

		// Use dynamic values for blocked requests and block duration.
		$blockedRequests = ! empty($schedules_interval->blockedRequests) ? intval($schedules_interval->blockedRequests) : 200;
		$blockedminutes = ! empty($schedules_interval->blockedminutes) ? intval($schedules_interval->blockedminutes) : 30;

		// Get the site URL, including child site for multisite.
		$attachmentHandler = new SlackAttachment();
		$site_url = $attachmentHandler->getSiteURLAddOption();

		$ip_address = $attachmentHandler->get_user_ip();

		// Check if the IP is currently blocked (for dynamic minutes).
		if ( get_transient("wpnts_bots_blocked_ip_$ip_address") ) {
			header('HTTP/1.1 429 Too Many Requests');
			exit('You are temporarily blocked due to excessive requests.');
		}

		// Count the number of requests within 1 minute.
		$request_count = get_transient("wpnts_request_count_$ip_address") ?: 0;
		set_transient("wpnts_request_count_$ip_address", $request_count + 1, 60); // Track requests per minute.

		// Check if the request count exceeds the dynamic limit.
		if ( $request_count > $blockedRequests ) {
			// Default template for rate limiting alert
			$default_template = ':robot: Suspicious bot detected with more than {request_count} requests per minute from IP {ip_address}. Rate-limiting applied and blocked for {blocked_minutes} minutes. {site_url}';
			
			// Template data for placeholders
			$template_data = [
				'ip_address' => $ip_address,
				'request_count' => $blockedRequests,
				'blocked_minutes' => $blockedminutes,
			];

			// Send templated notification with custom message support
			$attachmentHandler->send_templated_notification( 
				$this->get_webhook_url(), 
				$default_template,
				$template_data,
				'Critical', 
				'Security',
				'wpnts_webhook_site_settings', // Settings option name
				'rate_limit_ip_message', // Template key
				'System'
			);

			// Prepare log message for database (using processed template)
			$log_message = $attachmentHandler->process_message_template(
				$default_template,
				$template_data,
				'wpnts_webhook_site_settings',
				'rate_limit_ip_message'
			);

			// Log the event.
			$current_user = wp_get_current_user();
			$author = ( $current_user && ! empty($current_user->display_name) ) ? $current_user->display_name : 'Unknown';
			$severity = 'Critical';
			$event_type = 'Security';

			$ip = $attachmentHandler->get_user_ip();
			$attachmentHandler->log_activity_to_db($severity, current_time('mysql'), $ip, $author, $event_type, $log_message);

			// Block the IP for the dynamic time (in minutes).
			set_transient("wpnts_bots_blocked_ip_$ip_address", true, 60 * $blockedminutes); // Block for dynamic minutes.

			// Set the HTTP 429 header (Rate Limit Exceeded).
			header('HTTP/1.1 429 Too Many Requests');

			/* translators: 1: %d is the number of minutes the user is blocked for */
			$message = sprintf(
				esc_html__( 'Rate limit exceeded. You are blocked for %d minutes. Please try again later.', 'notifier-to-slack' ),
				intval( $blockedminutes )
			);

			wp_die(
				esc_html( $message ),
				esc_html__( 'Too Many Requests', 'notifier-to-slack' ),
				[ 'response' => 429 ]
			);
		}
	}



	 /**
	  * Hide WordPress Version from Meta Tags and Feeds.
	  * Removes the WordPress version from the HTML meta tags and RSS feeds to reduce exposure to potential vulnerabilities.
	  *
	  * @since 1.0.0
	  */
	public function wpnts_hide_wordpress_version() {
		// Remove WordPress version from meta tag in head.
		remove_action('wp_head', 'wp_generator');
		// Remove WordPress version from RSS feeds.
		add_filter('the_generator', '__return_null');
	}


	/**
	 * Remove WordPress Version from Enqueued Scripts and Styles.
	 *
	 * @param string $src The source URL of the asset.
	 * @return string
	 * @since 1.0.0
	 */
	public function wpnts_remove_version_from_assets( $src ) {
		if ( strpos($src, 'ver=') !== false ) {
			$src = remove_query_arg('ver', $src);
		}
		return $src;
	}

	/**
	 * Hide PHP version from HTTP headers.
	 * Removes the X-Powered-By header from HTTP responses to hide the PHP version, reducing potential security risks.
	 *
	 * @since 1.0.0
	 */
	public function wpnts_remove_x_powered_by_header() {
		if ( function_exists('header_remove') ) {
			header_remove('X-Powered-By'); // For Apache.
		}
	}

	/**
	 * Remove the X-Powered-By header for REST API responses.
	 *
	 * @param mixed $result The result to send to the client.
	 * @return mixed
	 */
	public function wpnts_remove_x_powered_by_for_rest( $result ) {
		if ( function_exists('header_remove') ) {
			// Remove X-Powered-By for REST API responses.
			header_remove('X-Powered-By');
		}
		return $result;
	}

	/**
	 * Hide WordPress version from HTTP headers.
	 * Removes the WordPress version from the HTTP headers to reduce exposure to potential vulnerabilities.
	 *
	 * @since 1.0.0
	 */
	public function hide_http_header_wp_version() {
		if ( ! is_admin() ) {
			add_filter('the_generator', '__return_false', PHP_INT_MAX, 1);
			remove_action('template_redirect', 'rest_output_link_header', 11);
			remove_action('xmlrpc_rsd_apis', 'rest_output_rsd');
			remove_action('wp_head', 'wp_generator');
			remove_action('wp_head', 'wlwmanifest_link');
			remove_action('wp_head', 'rsd_link');
			remove_action('wp_head', 'feed_links', 2);
			remove_action('wp_head', 'index_rel_link');
			remove_action('wp_head', 'feed_links_extra', 3);
			remove_action('wp_head', 'start_post_rel_link', 10, 0);
			remove_action('wp_head', 'parent_post_rel_link', 10, 0);
			remove_action('wp_head', 'adjacent_posts_rel_link', 10, 0);
			remove_action('wp_head', 'adjacent_posts_rel_link_wp_head', 10, 0);
			remove_action('wp_head', 'wp_shortlink_wp_head', 10, 0);
			remove_action('wp_head', 'feed_links', 2);

		}
	}

	// END



	/**
	 * Completely disable XML-RPC functionality.
	 * Disables the XML-RPC interface to prevent potential attacks and brute force attempts.
	 *
	 * @since 1.0.0
	 */
	public function disable_xmlrpc_complete() {
		// Disable XML-RPC completely.
		add_filter('xmlrpc_enabled', '__return_false');

		// Remove XML-RPC methods.
		add_filter('xmlrpc_methods', function ( $methods ) {
			return array();
		});

		// Remove XML-RPC capabilities.
		remove_action('xmlrpc_rsd_apis', 'rest_output_rsd');
		remove_action('wp_head', 'rsd_link');
		remove_action('wp_head', 'wlwmanifest_link');

		// Disable pingbacks.
		add_filter('xmlrpc_methods', function ( $methods ) {
			unset($methods['pingback.ping']);
			unset($methods['pingback.extensions.getPingbacks']);
			return $methods;
		});
	}

	/**
	 * Remove XML-RPC related headers.
	 */
	public function remove_xmlrpc_headers( $headers ) {
		if ( isset($headers['X-Pingback']) ) {
			unset($headers['X-Pingback']);
		}
		return $headers;
	}

	/**
	 * Block direct access to xmlrpc.php.
	 */
	public function block_xmlrpc_requests() {
		if ( $this->is_xmlrpc_request() ) {
			$this->send_forbidden_response();
		}
	}

	/**
	 * Check if current request is for XML-RPC.
	 */
	private function is_xmlrpc_request() {
		return (
			strpos(sanitize_url(wp_unslash($_SERVER['REQUEST_URI'] ?? '')), 'xmlrpc.php') !== false ||
			strpos(sanitize_url(wp_unslash($_SERVER['REQUEST_URI'] ?? '')), 'wp-cron.php') !== false ||
			strpos(sanitize_url(wp_unslash($_SERVER['REQUEST_URI'] ?? '')), '/wp/v2/users/') !== false
		);
	}

	/**
	 * Send forbidden response for blocked requests.
	 */
	private function send_forbidden_response() {
		status_header(403);
		header('HTTP/1.1 403 Forbidden');
		wp_die(
			'XML-RPC services are disabled on this site.',
			'XML-RPC Disabled',
			array( 'response' => 403 )
		);
	}

	/**
	 * Set default security configuration.
	 * HTTP Header security layer configuration.
	 */
	private function set_default_config() {
		$this->config = [
			'xss_protection' => true,
			'content_type_options' => true,
			'frame_options' => true,
			'hsts' => true,
			'permissions_policy' => true,
			'referrer_policy' => true,
			'cross_domain_policy' => true,
			'access_control' => true,
			'csp' => false,  // Disabled by default as it needs custom configuration.
		];
	}

	/**
	 * Add security headers to the response
	 */
	public function add_security_headers() {
		// XSS Protection
		if ( $this->config['xss_protection'] ) {
			header('X-XSS-Protection: 1; mode=block');
		}

		// Prevent MIME-type sniffing
		if ( $this->config['content_type_options'] ) {
			header('X-Content-Type-Options: nosniff');
		}

		// Clickjacking protection
		if ( $this->config['frame_options'] ) {
			header('X-Frame-Options: SAMEORIGIN');
		}

		// HSTS (HTTP Strict Transport Security)
		if ( $this->config['hsts'] && is_ssl() ) {
			header('Strict-Transport-Security: max-age=31536000; includeSubDomains; preload');
		}

		// Permissions Policy
		if ( $this->config['permissions_policy'] ) {
			header('Permissions-Policy: accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=(), interest-cohort=(), battery=(), display-capture=(), fullscreen=(), publickey-credentials-get=()');
		}

		// Referrer Policy
		if ( $this->config['referrer_policy'] ) {
			header('Referrer-Policy: strict-origin-when-cross-origin');
		}

		// Cross Domain Policy
		if ( $this->config['cross_domain_policy'] ) {
			header('X-Permitted-Cross-Domain-Policies: none');
		}

		// Access Control
		if ( $this->config['access_control'] ) {
			header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
			header('Access-Control-Allow-Headers: Authorization, Content-Type');
			header('Access-Control-Max-Age: 600');
		}

		// Content Security Policy (disabled by default)
		if ( $this->config['csp'] ) {
			header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;");
		}
	}

	/**
	 * Remove potential security risks
	 */
	public function remove_security_risks() {
		// Remove WordPress version from various locations
		remove_action('wp_head', 'wp_generator');
		add_filter('the_generator', '__return_empty_string');

		// Remove Windows Live Writer manifest
		remove_action('wp_head', 'wlwmanifest_link');

		// Remove RSD link
		remove_action('wp_head', 'rsd_link');

		// Remove REST API link
		remove_action('wp_head', 'rest_output_link_wp_head');
	}

	// End ----


	// Tor Security ----------------
	/**
	 * Initialize TOR protection
	 */
	public function initialize_tor_protection() {
		if ( $this->should_update_list() ) {
			$this->update_tor_nodes_list();
		}

		$this->check_tor_access();
	}

	/**
	 * Get visitor's IP address safely
	 */
	private function get_visitor_ip() {
		$ip_sources = [
			'HTTP_CLIENT_IP',
			'HTTP_X_FORWARDED_FOR',
			'HTTP_X_FORWARDED',
			'HTTP_X_CLUSTER_CLIENT_IP',
			'HTTP_FORWARDED_FOR',
			'HTTP_FORWARDED',
			'REMOTE_ADDR',
		];

		foreach ( $ip_sources as $source ) {
			if ( ! empty($_SERVER[ $source ]) ) {
				$ip = filter_var(wp_unslash($_SERVER[ $source ]), FILTER_VALIDATE_IP);
				if ( $ip !== false ) {
					return $ip;
				}
			}
		}

		return '0.0.0.0'; // Return safe default if no valid IP found
	}

	/**
	 * Check if the current access is from TOR
	 */
	private function check_tor_access() {
		try {
			$visitor_ip = $this->get_visitor_ip();
			if ( $this->is_tor_node($visitor_ip) ) {
				$this->log_blocked_access($visitor_ip);
				$this->block_access();
			}
		} catch ( Exception $e ) {
			// error_log('TOR Blocker Error: ' . $e->getMessage());
		}
	}

	/**
	 * Check if IP is a TOR node
	 */
	private function is_tor_node( $ip ) {
		$tor_nodes = get_option($this->tor_list_option, []);
		return in_array($ip, $tor_nodes, true);
	}

	/**
	 * Update TOR nodes list
	 */
	public function update_tor_nodes_list() {
		try {
			// Get TOR exit nodes list from official source
			$response = wp_remote_get('https://check.torproject.org/exit-addresses');

			if ( is_wp_error($response) ) {
				throw new Exception('Failed to fetch TOR nodes list');
			}

			$body = wp_remote_retrieve_body($response);
			$nodes = [];

			// Parse the response and extract IP addresses
			preg_match_all('/ExitAddress\s(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/', $body, $matches);

			if ( ! empty($matches[1]) ) {
				$nodes = array_unique($matches[1]);

				// Update the stored list
				update_option($this->tor_list_option, $nodes, false);
				update_option($this->last_update_option, time(), false);

				// Save to file as backup
				$this->save_nodes_to_file($nodes);
			}
		} catch ( Exception $e ) {
			// error_log('TOR Nodes Update Error: ' . $e->getMessage());
		}
	}

	/**
	 * Save nodes to backup file
	 */
	private function save_nodes_to_file( $nodes ) {
		$upload_dir = wp_upload_dir();
		$file_path = $upload_dir['basedir'] . '/tor_nodes.txt';

		file_put_contents($file_path, implode(PHP_EOL, $nodes));
	}

	/**
	 * Check if nodes list should be updated
	 */
	private function should_update_list() {
		$last_update = get_option($this->last_update_option, 0);
		return ( time() - $last_update ) >= $this->update_interval;
	}

	/**
	 * Log blocked access attempt
	 */
	private function log_blocked_access( $ip ) {
		// Get REQUEST_URI with proper validation, unslashing, and sanitization
		$request_uri = isset($_SERVER['REQUEST_URI']) ? sanitize_url(wp_unslash($_SERVER['REQUEST_URI'])) : '';

		// Create base message
		$message = sprintf(
			'Blocked TOR access attempt from IP: %s, URL: %s, Time: %s',
			$ip,
			$request_uri,
			current_time('mysql')
		);

		// Get site URL for logging context
		$attachmentHandler = new SlackAttachment();
		$site_info = $attachmentHandler->getSiteURLAddOption();

		// Default template for TOR network block alert
		$default_template = '🚫 TOR Network Access Blocked! ➡️ IP: {ip_address} 🌐 URL: {request_url} 📍 Site: {site_url}';
		
		// Template data for placeholders
		$template_data = [
			'ip_address' => $ip,
			'request_url' => $request_uri,
		];

		// Send templated notification with custom message support
		$attachmentHandler->send_templated_notification( 
			$this->get_webhook_url(), 
			$default_template,
			$template_data,
			'Medium', 
			'TOR_ACCESS',
			'wpnts_webhook_site_settings', // Settings option name
			'tor_security_message', // Template key
			'System'
		);

		// Log to database
		if ( function_exists('wpnts_add_security_log') ) {
			wpnts_add_security_log('TOR_BLOCKED', $message);
		}

		// Log to activity database with more details
		$severity = 'Medium';
		$time = current_time('mysql');
		$current_user = wp_get_current_user();
		$author = ( $current_user && ! empty($current_user->display_name) )
			? esc_html($current_user->display_name)
			: 'Unknown';
		$event_type = 'TOR_ACCESS';

		$attachmentHandler->log_activity_to_db(
			$severity,
			$time,
			$ip,
			$author,
			$event_type,
			$message
		);
	}


	/**
	 * Block access with appropriate response
	 */
	private function block_access() {
		// Add an admin notice if accessed from admin area.
		if ( is_admin() ) {
			add_action('admin_notices', function () {
				echo '<div class="notice notice-error"><p>TOR network access attempt has been blocked and logged.</p></div>';
			});
		}

		status_header(403);
		header('HTTP/1.1 403 Forbidden');
		wp_die(
			'Access through TOR network is not allowed on this site.',
			'Access Denied',
			[ 'response' => 403 ]
		);
	}

	// END ---

	/**
	 * Block scanners and malicious bots
	 * Scanner block start
	 */
	public function wpnts_block_scanners() {
		$ip = $this->get_client_ip();

		// Skip localhost
		if ( $ip === '127.0.0.1' || $ip === '::1' ) {
			return;
		}

		// Check if it's a scanner
		if ( $this->is_scanner_user_agent() ) {
			// Get the site URL
			$attachmentHandler = new SlackAttachment();
			$site_info = $attachmentHandler->getSiteURLAddOption();

			// Prepare log data
			$severity = 'High';
			$event_type = 'Scanner Detection';
			$time = current_time('mysql');
			$current_user = wp_get_current_user();
			$author = ( $current_user && ! empty($current_user->display_name) ) ?
					 esc_html($current_user->display_name) : 'Unknown';

			// Properly sanitize and validate user agent and request URI
			$user_agent = isset($_SERVER['HTTP_USER_AGENT']) ?
				sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])) : 'None';

			$request_uri = isset($_SERVER['REQUEST_URI']) ?
				esc_url(wp_unslash($_SERVER['REQUEST_URI'])) : ''; // phpcs:ignore

			// Default template for scanner protection alert
			$default_template = '⚠️ Scanner/Bot detected from IP: {ip_address} User Agent: {user_agent} URL: {request_url} Site: {site_url}';
			
			// Template data for placeholders
			$template_data = [
				'ip_address' => $ip,
				'user_agent' => $user_agent,
				'request_url' => $request_uri,
			];

			// Send templated notification with custom message support
			$attachmentHandler->send_templated_notification( 
				$this->get_webhook_url(), 
				$default_template,
				$template_data,
				$severity, 
				$event_type,
				'wpnts_webhook_site_settings', // Settings option name
				'scanner_protection_message', // Template key
				$author
			);

			// Prepare log message for database (using processed template)
			$log_message = $attachmentHandler->process_message_template(
				$default_template,
				$template_data,
				'wpnts_webhook_site_settings',
				'scanner_protection_message'
			);

			// Log to database.
			$attachmentHandler->log_activity_to_db(
				$severity,
				$time,
				$ip,
				$author,
				$event_type,
				$log_message
			);

			// Show admin notice.
			add_action('admin_notices', function () use ( $log_message ) {
				echo "<div class='notice notice-error'><p>" . wp_kses_post($log_message) . '</p></div>';
			});

			// Block the request.
			nocache_headers();
			status_header(403);
			wp_die(
				'Access Denied - Scanner/Bot Detected',
				'Access Denied',
				[ 'response' => 403 ]
			);
		}
	}


	 /**
	  * Check if the current request is from a known scanner/bot
	  *
	  * @return bool
	  */
	private function is_scanner_user_agent() {
		if ( ! isset($_SERVER['HTTP_USER_AGENT']) ) {
			return true;
		}

		$user_agent = strtolower(sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])));

		$regex_patterns = [
			'/scan(ner)?/i',
			'/bot(?!strap)/i',  // matches 'bot' but not 'bootstrap'.
			'/crawl(er)?/i',
			'/spider/i',
			'/pentest/i',
			'/security/i',
		];

		foreach ( $this->blocked_user_agents as $bot ) {
			if ( strpos($user_agent, $bot) !== false ) {
				return true;
			}
		}

		foreach ( $regex_patterns as $pattern ) {
			if ( preg_match($pattern, $user_agent) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Get webhook URL from security settings
	 *
	 * @return string
	 */
	private function get_webhook_url() {
		$schedules_int = get_option('wpnts_webhook_site_settings');
		$schedules_interval = json_decode($schedules_int);
		return $schedules_interval->webhook ?? '';
	}

	/**
	 * Get client IP address
	 *
	 * @return string
	 */
	private function get_client_ip() {
		$ip_headers = [
			'HTTP_CLIENT_IP',
			'HTTP_X_FORWARDED_FOR',
			'HTTP_X_FORWARDED',
			'HTTP_X_CLUSTER_CLIENT_IP',
			'HTTP_FORWARDED_FOR',
			'HTTP_FORWARDED',
			'REMOTE_ADDR',
		];

		foreach ( $ip_headers as $header ) {
			if ( ! empty($_SERVER[ $header ]) ) {
				return sanitize_text_field( wp_unslash($_SERVER[ $header ]));
			}
		}

		return '0.0.0.0';
	}



	// End

	/**
	 * Sends a notification message to a configured Slack webhook.
	 *
	 * This function formats a message with an optional emoji and sends it to the Slack webhook URL
	 * specified in the plugin's comment settings.
	 *
	 * @param string $message The message to send to Slack.
	 * @param string $emoji   An optional emoji to prepend to the message.
	 * @return void
	 */
	private function wpnts_notify_slack( $message, $emoji = '' ) {
		$formatted_message = $emoji . ' ' . $message;
		
		// Use SlackChannelRouter for channel and user tag support
		SlackChannelRouter::send_notification_with_routing(
			'wpnts_webhook_site_settings',
			$formatted_message
		);
	}
}
