<?php

declare(strict_types=1);

/**
 * HVAC Community Events Security Helper
 *
 * Provides security utilities and validation methods
 *
 * @package    HVAC_Community_Events
 * @subpackage Includes
 * @since      1.1.0
 */

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

/**
 * Class HVAC_Security
 */
class HVAC_Security {

	/**
	 * Verify a nonce with proper error handling
	 *
	 * @param string $nonce      The nonce to verify
	 * @param string $action     The nonce action
	 * @param bool   $die_on_fail Whether to die on failure
	 * @return bool
	 */
	public static function verify_nonce( $nonce, $action, $die_on_fail = false ) {
		$is_valid = wp_verify_nonce( $nonce, $action );

		if ( ! $is_valid ) {
			HVAC_Logger::warning( 'Nonce verification failed', 'Security', [
				'action' => $action,
				'user_id' => get_current_user_id(),
			] );

			if ( $die_on_fail ) {
				wp_die( __( 'Security check failed. Please refresh the page and try again.', 'hvac-community-events' ) );
			}
		}

		return $is_valid;
	}

	/**
	 * Check if current user has required capability
	 *
	 * @param string $capability The capability to check
	 * @param bool   $die_on_fail Whether to die on failure
	 * @return bool
	 */
	public static function check_capability( $capability, $die_on_fail = false ) {
		$has_cap = current_user_can( $capability );

		if ( ! $has_cap ) {
			HVAC_Logger::warning( 'Capability check failed', 'Security', [
				'capability' => $capability,
				'user_id' => get_current_user_id(),
			] );

			if ( $die_on_fail ) {
				wp_die( __( 'You do not have permission to perform this action.', 'hvac-community-events' ) );
			}
		}

		return $has_cap;
	}

	/**
	 * Check if user is logged in with optional redirect
	 *
	 * @param string $redirect_to URL to redirect if not logged in
	 * @return bool
	 */
	public static function require_login( $redirect_to = '' ) {
		if ( ! is_user_logged_in() ) {
			if ( ! empty( $redirect_to ) ) {
				wp_safe_redirect( $redirect_to );
				exit;
			}
			return false;
		}
		return true;
	}

	/**
	 * Sanitize and validate email
	 *
	 * @param string $email Email to validate
	 * @return string|false Sanitized email or false if invalid
	 */
	public static function sanitize_email( $email ) {
		$email = sanitize_email( $email );
		return is_email( $email ) ? $email : false;
	}

	/**
	 * Sanitize and validate URL
	 *
	 * @param string $url URL to validate
	 * @return string|false Sanitized URL or false if invalid
	 */
	public static function sanitize_url( $url ) {
		$url = esc_url_raw( $url );
		return filter_var( $url, FILTER_VALIDATE_URL ) ? $url : false;
	}

	/**
	 * Sanitize array of values
	 *
	 * @param array  $array Array to sanitize
	 * @param string $type  Type of sanitization (text|email|url|int)
	 * @return array
	 */
	public static function sanitize_array( $array, $type = 'text' ) {
		if ( ! is_array( $array ) ) {
			return [];
		}

		$sanitized = [];
		foreach ( $array as $key => $value ) {
			switch ( $type ) {
				case 'email':
					$clean = self::sanitize_email( $value );
					if ( $clean ) {
						$sanitized[ $key ] = $clean;
					}
					break;
				case 'url':
					$clean = self::sanitize_url( $value );
					if ( $clean ) {
						$sanitized[ $key ] = $clean;
					}
					break;
				case 'int':
					$sanitized[ $key ] = intval( $value );
					break;
				default:
					$sanitized[ $key ] = sanitize_text_field( $value );
			}
		}

		return $sanitized;
	}

	/**
	 * Escape output based on context
	 *
	 * @param mixed  $value   Value to escape
	 * @param string $context Context (html|attr|url|js)
	 * @return string
	 */
	public static function escape_output( $value, $context = 'html' ) {
		switch ( $context ) {
			case 'attr':
				return esc_attr( $value );
			case 'url':
				return esc_url( $value );
			case 'js':
				return esc_js( $value );
			case 'textarea':
				return esc_textarea( $value );
			default:
				return esc_html( $value );
		}
	}

	/**
	 * Check if request is AJAX
	 *
	 * @return bool
	 */
	public static function is_ajax_request() {
		return defined( 'DOING_AJAX' ) && DOING_AJAX;
	}

	/**
	 * Get user IP address
	 *
	 * SECURITY FIX (C3): Only trust proxy headers when behind a known trusted proxy.
	 * Previous implementation trusted user-controllable headers unconditionally,
	 * allowing attackers to spoof IPs and bypass rate limiting.
	 *
	 * For most deployments, use REMOTE_ADDR directly. Only trust X-Forwarded-For
	 * when behind Cloudflare, AWS ALB, or other known reverse proxies.
	 *
	 * @return string
	 */
	public static function get_user_ip() {
		// Primary: Use REMOTE_ADDR (cannot be spoofed at network level)
		$ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '';

		// Only trust proxy headers if HVAC_TRUSTED_PROXIES is defined
		// Define as comma-separated list of trusted proxy IPs in wp-config.php
		// Example: define('HVAC_TRUSTED_PROXIES', '10.0.0.1,10.0.0.2');
		if (defined('HVAC_TRUSTED_PROXIES') && HVAC_TRUSTED_PROXIES) {
			$trusted_proxies = array_map('trim', explode(',', HVAC_TRUSTED_PROXIES));

			if (in_array($ip, $trusted_proxies, true)) {
				// Behind trusted proxy - check forwarded headers
				if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
					// Take first IP (original client) from comma-separated list
					$forwarded = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
					$client_ip = trim($forwarded[0]);
					if (filter_var($client_ip, FILTER_VALIDATE_IP)) {
						$ip = $client_ip;
					}
				} elseif (!empty($_SERVER['HTTP_CLIENT_IP'])) {
					$client_ip = $_SERVER['HTTP_CLIENT_IP'];
					if (filter_var($client_ip, FILTER_VALIDATE_IP)) {
						$ip = $client_ip;
					}
				}
			}
		}

		return sanitize_text_field($ip);
	}

	/**
	 * Rate limiting check
	 *
	 * @param string $action    Action to limit
	 * @param int    $limit     Number of attempts allowed
	 * @param int    $window    Time window in seconds
	 * @param string $identifier User identifier (defaults to IP)
	 * @return bool True if within limits, false if exceeded
	 */
	public static function check_rate_limit( $action, $limit = 5, $window = 300, $identifier = null ) {
		if ( null === $identifier ) {
			$identifier = self::get_user_ip();
		}

		$key = 'hvac_rate_limit_' . md5( $action . $identifier );
		$attempts = get_transient( $key );

		if ( false === $attempts ) {
			set_transient( $key, 1, $window );
			return true;
		}

		if ( $attempts >= $limit ) {
			HVAC_Logger::warning( 'Rate limit exceeded', 'Security', [
				'action' => $action,
				'identifier' => $identifier,
				'attempts' => $attempts,
			] );
			return false;
		}

		set_transient( $key, $attempts + 1, $window );
		return true;
	}
}