<?php
/**
 * Sanitization Engine for WordPress
 *
 * This file contains the SanitizationEngine class which handles data sanitization
 * with enhanced security and maximum performance for the Ultraguard Pro plugin.
 * It implements a singleton pattern with caching, type validation, and extensible
 * sanitizer architecture.
 *
 * The engine supports multiple data types, recursive array sanitization, custom
 * sanitizers, and includes comprehensive security measures against common
 * vulnerabilities.
 *
 * @package    UltraguardPro\core
 * @author     Vikas Bhardwaj <support@ultraguard.net>
 * @copyright  2024 Ultraguard Pro
 * @license    GPL v2 or later
 * @version    1.0.0
 * @link       https://ultraguardpro.com/docs/sanitization-engine
 */

namespace UltraguardPro\core;

defined( 'ABSPATH' ) || exit;

/**
 * Sanitization Engine class
 *
 * Handles all data sanitization operations with security, performance, and
 * extensibility. Implements a singleton pattern with comprehensive caching,
 * type validation, and support for custom sanitizers.
 *
 * The class provides:
 * - Type-specific sanitization (text, email, URL, HTML, JSON, etc.)
 * - Recursive array sanitization with depth control
 * - Custom sanitizer registration
 * - Performance optimization through caching
 * - Strict mode for enhanced security
 * - Comprehensive error handling and logging
 *
 * @package    UltraguardPro\view
 * @author     Vikas Bhardwaj
 * @version    1.0.0
 * @since      1.0.0
 * @see        sanitize_text_field()
 * @see        sanitize_email()
 * @see        wp_kses_post()
 * @see        filter_var()
 *
 * @example
 * $engine = SanitizationEngine::get_instance();
 * $clean_data = $engine->sanitize($_POST, [
 *     'name' => 'text',
 *     'email' => 'email',
 *     'age' => 'int',
 *     'bio' => ['html', ['allowed_tags' => ['p', 'strong']]]
 * ]);
 *
 * @example
 * // Quick sanitization for single values
 * $clean_email = SanitizationEngine::quick_sanitize($email, 'email');
 */
class SanitizationEngine {

    /**
     * Singleton instance
     *
     * Stores the singleton instance of SanitizationEngine class.
     * Ensures only one instance exists throughout the application.
     *
     * @var SanitizationEngine|null
     * @access private
     * @static
     */
    private static $instance = null;

    /**
     * Custom sanitizers cache
     *
     * Associative array of registered sanitizer callbacks indexed by sanitizer name.
     * Contains both built-in and custom registered sanitizers.
     *
     * @var array<string, callable>
     * @access private
     * @static
     */
    private static $sanitizers = array();

    /**
     * Default sanitization rules cache
     *
     * Associative array mapping data types to default sanitization rules.
     * Used when no specific rules are provided for a field.
     *
     * @var array<string, array<string|array>>
     * @access private
     * @static
     */
    private static $default_rules = array();

    /**
     * Sanitization result cache
     *
     * Multi-dimensional cache storing sanitization results to avoid redundant
     * processing of identical data. Uses MD5 hashes for cache keys.
     *
     * @var array<string, mixed>
     * @access private
     * @static
     */
    private static $cache = array();

    /**
     * Pre-compiled regex patterns
     *
     * Cache of pre-compiled regular expression patterns for common validation
     * scenarios to improve performance of pattern-based sanitization.
     *
     * @var array<string, string>
     * @access private
     * @static
     */
    private static $patterns = array();

    /**
     * Type validation cache
     *
     * Cache storing validation results for common data type checks to
     * avoid redundant validation operations.
     *
     * @var array<string, bool>
     * @access private
     * @static
     */
    private static $validation_cache = array();

    /**
     * Default sanitization context
     *
     * Default configuration values for sanitization operations including
     * recursion limits, strict mode, and HTML handling options.
     *
     * @var array{
     *     strict_mode: bool,
     *     recursive: bool,
     *     max_depth: int,
     *     current_depth: int,
     *     allow_html: bool,
     *     allowed_tags: array<string, array<string, bool>>
     * }
     * @access private
     * @const
     */
    private const DEFAULT_CONTEXT = array(
        'strict_mode'   => false,
        'recursive'     => true,
        'max_depth'     => 10,
        'current_depth' => 0,
        'allow_html'    => false,
        'allowed_tags'  => array(),
    );

    /**
     * Get singleton instance
     *
     * Returns the singleton instance of SanitizationEngine, creating it if it
     * doesn't exist. Initializes regex patterns and default sanitizers on
     * first instantiation.
     *
     * @return SanitizationEngine The singleton instance
     * @access public
     * @static
     * @since  1.0.0
     *
     * @example
     * $engine = SanitizationEngine::get_instance();
     */
    public static function get_instance(): self {
        if ( null === self::$instance ) {
            self::$instance = new self();
            self::init_patterns();
            self::init_default_sanitizers();
        }
        return self::$instance;
    }

    /**
     * Private constructor
     *
     * Initializes default sanitization rules. Prevents direct instantiation
     * (singleton pattern).
     *
     * @return void
     * @access private
     */
    private function __construct() {
        if ( empty( self::$default_rules ) ) {
            self::init_default_rules();
        }
    }

    /**
     * Initialize regex patterns
     *
     * Pre-compiles and caches regular expression patterns for common
     * validation scenarios to improve performance.
     *
     * @return void
     * @access private
     * @static
     * @since  1.0.0
     */
    private static function init_patterns(): void {
        if ( empty( self::$patterns ) ) {
            self::$patterns = array(
                'alpha'         => '/[^a-zA-Z]/',
                'alphanumeric'  => '/[^a-zA-Z0-9]/',
                'hex'           => '/[^a-fA-F0-9]/',
                'non_scalar'    => '/^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$/',
            );
        }
    }

    /**
     * Initialize default sanitizers
     *
     * Sets up the default sanitizer callbacks for common data types.
     * Uses closures to properly reference the instance methods.
     * Applies filters to allow customization of sanitizers.
     *
     * @return void
     * @access private
     * @static
     * @since  1.0.0
     *
     * @see    apply_filters()
     */
    private static function init_default_sanitizers(): void {
        if ( ! empty( self::$sanitizers ) ) {
            return;
        }

        // Use closures to properly reference the instance
        $instance = self::$instance;
        self::$sanitizers = array(
            'text'          => function($value, $args, $context) use ($instance) {
                return $instance->sanitize_text($value, $args, $context);
            },
            'textarea'      => function($value, $args, $context) use ($instance) {
                return $instance->sanitize_textarea($value, $args, $context);
            },
            'email'         => function($value, $args, $context) use ($instance) {
                return $instance->sanitize_email($value, $args, $context);
            },
            'url'           => function($value, $args, $context) use ($instance) {
                return $instance->sanitize_url($value, $args, $context);
            },
            'number'        => function($value, $args, $context) use ($instance) {
                return $instance->sanitize_number($value, $args, $context);
            },
            'int'           => function($value, $args, $context) use ($instance) {
                return $instance->sanitize_int($value, $args, $context);
            },
            'float'         => function($value, $args, $context) use ($instance) {
                return $instance->sanitize_float($value, $args, $context);
            },
            'html'          => function($value, $args, $context) use ($instance) {
                return $instance->sanitize_html($value, $args, $context);
            },
            'color'         => function($value, $args, $context) use ($instance) {
                return $instance->sanitize_color($value, $args, $context);
            },
            'slug'          => function($value, $args, $context) use ($instance) {
                return $instance->sanitize_slug($value, $args, $context);
            },
            'filename'      => function($value, $args, $context) use ($instance) {
                return $instance->sanitize_filename($value, $args, $context);
            },
            'key'           => function($value, $args, $context) use ($instance) {
                return $instance->sanitize_key($value, $args, $context);
            },
            'ip_address'    => function($value, $args, $context) use ($instance) {
                return $instance->sanitize_ip_address($value, $args, $context);
            },
            'alpha'         => function($value, $args, $context) use ($instance) {
                return $instance->sanitize_alpha($value, $args, $context);
            },
            'alphanumeric'  => function($value, $args, $context) use ($instance) {
                return $instance->sanitize_alphanumeric($value, $args, $context);
            },
            'hex'           => function($value, $args, $context) use ($instance) {
                return $instance->sanitize_hex($value, $args, $context);
            },
            'bool'          => function($value, $args, $context) use ($instance) {
                return $instance->sanitize_bool($value, $args, $context);
            },
            'json'          => function($value, $args, $context) use ($instance) {
                return $instance->sanitize_json($value, $args, $context);
            },
            'base64'        => function($value, $args, $context) use ($instance) {
                return $instance->sanitize_base64($value, $args, $context);
            },
        );

        // Apply filters with caching
        $cache_key = 'sanitizers_filtered';
        if ( ! isset( self::$cache[ $cache_key ] ) ) {
            self::$sanitizers = apply_filters( 'ultraguard_sanitization_engine_sanitizers', self::$sanitizers );
            self::$cache[ $cache_key ] = true;
        }
    }

    /**
     * Initialize default rules
     *
     * Sets up default sanitization rules for common data types.
     * Applies filters to allow customization of default rules.
     *
     * @return void
     * @access private
     * @static
     * @since  1.0.0
     *
     * @see    apply_filters()
     */
    private static function init_default_rules(): void {
        if ( ! empty( self::$default_rules ) ) {
            return;
        }

        self::$default_rules = array(
            'default'  => array( 'text' ),
            'string'   => array( 'text' ),
            'integer'  => array( 'int' ),
            'boolean'  => array( 'bool' ),
            'array'    => array( 'text' ),
        );

        // Apply filters with caching
        $cache_key = 'default_rules_filtered';
        if ( ! isset( self::$cache[ $cache_key ] ) ) {
            self::$default_rules = apply_filters( 'ultraguard_sanitization_engine_default_rules', self::$default_rules );
            self::$cache[ $cache_key ] = true;
        }
    }

    /**
     * Sanitize scheme data
     *
     * Main sanitization method that processes data arrays according to specified
     * rules. Includes caching, recursion control, and comprehensive error handling.
     *
     * @param  array $data    Data array to sanitize
     * @param  array $rules   Sanitization rules mapping field names to sanitizers
     * @param  array $context Context configuration for sanitization behavior
     * @return array          Sanitized data array
     * @access public
     * @since  1.0.0
     *
     * @example
     * $clean_data = $engine->sanitize($_POST, [
     *     'username' => 'text',
     *     'email' => 'email',
     *     'age' => 'int',
     *     'preferences' => ['array', ['recursive' => true]]
     * ]);
     */
    public function sanitize( array $data, array $rules = array(), array $context = array() ): array {
        // Early return for empty data
        if ( empty( $data ) ) {
            return array();
        }

        // Cache key for this sanitization operation
        $cache_key = $this->generate_cache_key( $data, $rules, $context );

        if ( isset( self::$cache[ $cache_key ] ) ) {
            return self::$cache[ $cache_key ];
        }

        // Prepare context with defaults
        $context = $this->prepare_context( $context );

        // Check recursion depth
        if ( $context['current_depth'] >= $context['max_depth'] ) {
            $result = $this->handle_sanitization_error(
                __( 'Maximum recursion depth exceeded', 'ultraguard-security-suite' ),
                $data,
                $context
            );
            self::$cache[ $cache_key ] = $result;
            return $result;
        }

        $sanitized = array();
        foreach ( $data as $field => $value ) {
            $sanitized_field = sanitize_key( $field );
            $field_rules = $this->get_field_rules( $field, $rules, $context );
            $sanitized[ $sanitized_field ] = $this->sanitize_field( $value, $field_rules, $context );
        }

        // Apply final filter with caching
        $filter_key = 'sanitized_data_' . $cache_key;
        if ( ! isset( self::$cache[ $filter_key ] ) ) {
            $sanitized = apply_filters(
                'ultraguard_sanitization_engine_sanitized_data',
                $sanitized,
                $data,
                $rules,
                $context
            );
            self::$cache[ $filter_key ] = $sanitized;
        }

        self::$cache[ $cache_key ] = $sanitized;
        return $sanitized;
    }

    /**
     * Prepare context with defaults
     *
     * Merges provided context with default values and ensures proper type casting
     * for context parameters.
     *
     * @param  array $context Provided context configuration
     * @return array          Prepared context with defaults applied
     * @access private
     * @since  1.0.0
     *
     * @see    wp_parse_args()
     * @see    wp_validate_boolean()
     * @see    absint()
     */
    private function prepare_context( array $context ): array {
        $context = wp_parse_args( $context, self::DEFAULT_CONTEXT );

        // Type casting with validation
        $context['strict_mode']   = wp_validate_boolean( $context['strict_mode'] );
        $context['recursive']     = wp_validate_boolean( $context['recursive'] );
        $context['max_depth']     = absint( $context['max_depth'] );
        $context['current_depth'] = absint( $context['current_depth'] );
        $context['allow_html']    = wp_validate_boolean( $context['allow_html'] );

        return $context;
    }

    /**
     * Get field rules with defaults
     *
     * Retrieves sanitization rules for a specific field, falling back to
     * default rules if no specific rules are provided.
     *
     * @param  string $field   Field name
     * @param  array  $rules   All sanitization rules
     * @param  array  $context Context configuration
     * @return array           Field-specific sanitization rules
     * @access private
     * @since  1.0.0
     *
     * @see    apply_filters()
     */
    private function get_field_rules( string $field, array $rules, array $context ): array {
        $sanitized_field = sanitize_key( $field );

        if ( isset( $rules[ $sanitized_field ] ) ) {
            $field_rules = $rules[ $sanitized_field ];
            if ( ! is_array( $field_rules ) ) {
                $field_rules = array( $field_rules );
            }
        } else {
            $field_rules = self::$default_rules['default'] ?? array( 'text' );
        }

        // Cache filter result
        $cache_key = 'field_rules_' . $sanitized_field . '_' . md5( serialize( $context ) );
        if ( ! isset( self::$cache[ $cache_key ] ) ) {
            self::$cache[ $cache_key ] = apply_filters(
                'ultraguard_sanitization_engine_field_rules',
                $field_rules,
                $field,
                $context
            );
        }

        return self::$cache[ $cache_key ];
    }

    /**
     * Sanitize single field
     *
     * Applies sanitization rules to a single field value, handling arrays
     * recursively and applying multiple sanitizers in sequence.
     *
     * @param  mixed $value   Field value to sanitize
     * @param  array $rules   Sanitization rules for this field
     * @param  array $context Context configuration
     * @return mixed          Sanitized field value
     * @access private
     * @since  1.0.0
     */
    private function sanitize_field( $value, array $rules, array $context ) {
        // Handle null values early
        if ( is_null( $value ) ) {
            return null;
        }

        // Handle arrays recursively
        if ( is_array( $value ) && $context['recursive'] ) {
            $new_context = array_merge( $context, array( 'current_depth' => $context['current_depth'] + 1 ) );
            return $this->sanitize_array_recursive( $value, $rules, $new_context );
        }

        // Apply each sanitization rule
        foreach ( $rules as $rule ) {
            $value = $this->apply_sanitizer( $value, $rule, $context );
        }

        return $value;
    }

    /**
     * Apply sanitizer to value
     *
     * Applies a specific sanitizer to a value, handling arrays and unknown
     * sanitizers appropriately based on context configuration.
     *
     * @param  mixed  $value   Value to sanitize
     * @param  mixed  $rule    Sanitizer rule (string or array)
     * @param  array  $context Context configuration
     * @return mixed           Sanitized value
     * @access private
     * @since  1.0.0
     *
     * @throws \Exception If sanitizer execution fails
     */
    private function apply_sanitizer( $value, $rule, array $context ) {
        // Extract rule name and arguments
        $rule_name = is_array( $rule ) ? ( $rule['type'] ?? '' ) : $rule;
        $rule_args = is_array( $rule ) ? ( $rule['args'] ?? array() ) : array();

        if ( empty( $rule_name ) || ! is_string( $rule_name ) ) {
            return $value;
        }

        $rule_name = sanitize_key( $rule_name );

        // Handle arrays
        if ( is_array( $value ) ) {
            return $this->sanitize_array( $value, $rule_name, $rule_args, $context );
        }

        // Apply sanitizer
        if ( isset( self::$sanitizers[ $rule_name ] ) ) {
            $cache_key = 'sanitizer_' . $rule_name . '_' . md5( serialize( array( $value, $rule_args, $context ) ) );

            if ( ! isset( self::$cache[ $cache_key ] ) ) {
                try {
                    $sanitized_value = call_user_func( self::$sanitizers[ $rule_name ], $value, $rule_args, $context );

                    // Cache the filter result
                    $filter_key = 'sanitized_value_' . $cache_key;
                    if ( ! isset( self::$cache[ $filter_key ] ) ) {
                        self::$cache[ $filter_key ] = apply_filters(
                            'ultraguard_sanitization_engine_sanitized_value',
                            $sanitized_value,
                            $value,
                            $rule_name,
                            $rule_args,
                            $context
                        );
                    }

                    self::$cache[ $cache_key ] = self::$cache[ $filter_key ];
                } catch ( \Exception $e ) {
                    self::$cache[ $cache_key ] = $this->handle_sanitization_error(
                        /* translators: %1$s: string, %2$s: string */
                        sprintf( __( 'Sanitizer error for rule "%1$s": %2$s', 'ultraguard-security-suite' ), $rule_name, $e->getMessage() ),
                        $value,
                        $context
                    );
                }
            }

            return self::$cache[ $cache_key ];
        }

        // Unknown rule handling
        if ( $context['strict_mode'] ) {
            return $this->handle_sanitization_error(
                /* translators: %s: string */
                sprintf( __( 'Unknown sanitizer rule: %s', 'ultraguard-security-suite' ), $rule_name ),
                $value,
                $context
            );
        }

        // Fallback to text sanitization
        return $this->sanitize_text( $value, $rule_args, $context );
    }

    /**
     * Sanitize array recursively
     *
     * Recursively sanitizes array values while preserving array structure
     * and applying the same sanitization rules to all elements.
     *
     * @param  array $array   Array to sanitize
     * @param  array $rules   Sanitization rules
     * @param  array $context Context configuration
     * @return array          Sanitized array
     * @access private
     * @since  1.0.0
     */
    private function sanitize_array_recursive( array $array, array $rules, array $context ): array {
        $sanitized = array();

        foreach ( $array as $key => $value ) {
            $sanitized_key = $this->sanitize_field( $key, array( 'text' ), $context );

            if ( is_array( $value ) ) {
                $sanitized[ $sanitized_key ] = $this->sanitize_array_recursive( $value, $rules, $context );
            } else {
                $sanitized[ $sanitized_key ] = $this->sanitize_field( $value, $rules, $context );
            }
        }

        return $sanitized;
    }

    // =========================================================================
    // SANITIZER METHODS (INSTANCE METHODS)
    // =========================================================================

    /**
     * Sanitize text field
     *
     * Basic text sanitization using WordPress sanitize_text_field().
     * Handles scalar values and returns empty string for non-scalar values.
     *
     * @param  mixed  $value   Value to sanitize
     * @param  array  $args    Additional sanitizer arguments
     * @param  array  $context Context configuration
     * @return string          Sanitized text
     * @access protected
     * @since  1.0.0
     *
     * @see    sanitize_text_field()
     */
    protected function sanitize_text( $value, array $args = array(), array $context = array() ): string {
        if ( ! is_scalar( $value ) && ! is_null( $value ) ) {
            return '';
        }
        return sanitize_text_field( (string) $value );
    }

    /**
     * Sanitize textarea field
     *
     * Multi-line text sanitization using WordPress sanitize_textarea_field().
     * Preserves line breaks and whitespace formatting.
     *
     * @param  mixed  $value   Value to sanitize
     * @param  array  $args    Additional sanitizer arguments
     * @param  array  $context Context configuration
     * @return string          Sanitized textarea content
     * @access protected
     * @since  1.0.0
     *
     * @see    sanitize_textarea_field()
     */
    protected function sanitize_textarea( $value, array $args = array(), array $context = array() ): string {
        if ( ! is_scalar( $value ) && ! is_null( $value ) ) {
            return '';
        }
        return sanitize_textarea_field( (string) $value );
    }

    /**
     * Sanitize email address
     *
     * Email address sanitization with validation using WordPress sanitize_email()
     * and is_email() validation.
     *
     * @param  mixed  $value   Value to sanitize
     * @param  array  $args    Additional sanitizer arguments
     * @param  array  $context Context configuration
     * @return string          Sanitized email address or empty string if invalid
     * @access protected
     * @since  1.0.0
     *
     * @see    sanitize_email()
     * @see    is_email()
     */
    protected function sanitize_email( $value, array $args = array(), array $context = array() ): string {
        if ( ! is_scalar( $value ) && ! is_null( $value ) ) {
            return '';
        }
        $email = sanitize_email( (string) $value );
        return is_email( $email ) ? $email : '';
    }

    /**
     * Sanitize URL
     *
     * URL sanitization using WordPress esc_url_raw() for safe storage.
     *
     * @param  mixed  $value   Value to sanitize
     * @param  array  $args    Additional sanitizer arguments
     * @param  array  $context Context configuration
     * @return string          Sanitized URL
     * @access protected
     * @since  1.0.0
     *
     * @see    esc_url_raw()
     */
    protected function sanitize_url( $value, array $args = array(), array $context = array() ): string {
        if ( ! is_scalar( $value ) && ! is_null( $value ) ) {
            return '';
        }
        return esc_url_raw( (string) $value );
    }

    /**
     * Sanitize number
     *
     * Generic number sanitization using filter_var() with float allowance.
     * Returns 0 for non-numeric values.
     *
     * @param  mixed  $value   Value to sanitize
     * @param  array  $args    Additional sanitizer arguments
     * @param  array  $context Context configuration
     * @return float|int       Sanitized number
     * @access protected
     * @since  1.0.0
     *
     * @see    filter_var()
     */
    protected function sanitize_number( $value, array $args = array(), array $context = array() ) {
        if ( ! is_numeric( $value ) ) {
            return 0;
        }
        return filter_var( $value, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION );
    }

    /**
     * Sanitize integer
     *
     * Integer sanitization with type casting.
     *
     * @param  mixed  $value   Value to sanitize
     * @param  array  $args    Additional sanitizer arguments
     * @param  array  $context Context configuration
     * @return int             Sanitized integer
     * @access protected
     * @since  1.0.0
     */
    protected function sanitize_int( $value, array $args = array(), array $context = array() ): int {
        return (int) $value;
    }

    /**
     * Sanitize float
     *
     * Float sanitization with type casting.
     *
     * @param  mixed  $value   Value to sanitize
     * @param  array  $args    Additional sanitizer arguments
     * @param  array  $context Context configuration
     * @return float           Sanitized float
     * @access protected
     * @since  1.0.0
     */
    protected function sanitize_float( $value, array $args = array(), array $context = array() ): float {
        return (float) $value;
    }

    /**
     * Sanitize HTML content
     *
     * HTML content sanitization using wp_kses() with configurable allowed tags.
     * Supports context-based tag whitelisting.
     *
     * @param  mixed  $value   Value to sanitize
     * @param  array  $args    Additional sanitizer arguments
     * @param  array  $context Context configuration
     * @return string          Sanitized HTML
     * @access protected
     * @since  1.0.0
     *
     * @see    wp_kses()
     * @see    wp_kses_post()
     */
    protected function sanitize_html( $value, array $args = array(), array $context = array() ): string {
        if ( ! is_scalar( $value ) && ! is_null( $value ) ) {
            return '';
        }

        $allowed_tags = $context['allowed_tags'] ?? array();
        if ( empty( $allowed_tags ) && $context['allow_html'] ) {
            $allowed_tags = 'post';
        }

        if ( ! empty( $allowed_tags ) && $allowed_tags !== 'post' ) {
            return wp_kses( (string) $value, $allowed_tags );
        }

        return wp_kses_post( (string) $value );
    }

    /**
     * Sanitize color value
     *
     * Hex color sanitization using WordPress sanitize_hex_color().
     *
     * @param  mixed  $value   Value to sanitize
     * @param  array  $args    Additional sanitizer arguments
     * @param  array  $context Context configuration
     * @return string          Sanitized hex color
     * @access protected
     * @since  1.0.0
     *
     * @see    sanitize_hex_color()
     */
    protected function sanitize_color( $value, array $args = array(), array $context = array() ): string {
        if ( ! is_scalar( $value ) && ! is_null( $value ) ) {
            return '';
        }
        return sanitize_hex_color( (string) $value );
    }

    /**
     * Sanitize slug
     *
     * URL slug sanitization using WordPress sanitize_title().
     *
     * @param  mixed  $value   Value to sanitize
     * @param  array  $args    Additional sanitizer arguments
     * @param  array  $context Context configuration
     * @return string          Sanitized slug
     * @access protected
     * @since  1.0.0
     *
     * @see    sanitize_title()
     */
    protected function sanitize_slug( $value, array $args = array(), array $context = array() ): string {
        if ( ! is_scalar( $value ) && ! is_null( $value ) ) {
            return '';
        }
        return sanitize_title( (string) $value );
    }

    /**
     * Sanitize filename
     *
     * Filename sanitization using WordPress sanitize_file_name().
     *
     * @param  mixed  $value   Value to sanitize
     * @param  array  $args    Additional sanitizer arguments
     * @param  array  $context Context configuration
     * @return string          Sanitized filename
     * @access protected
     * @since  1.0.0
     *
     * @see    sanitize_file_name()
     */
    protected function sanitize_filename( $value, array $args = array(), array $context = array() ): string {
        if ( ! is_scalar( $value ) && ! is_null( $value ) ) {
            return '';
        }
        return sanitize_file_name( (string) $value );
    }

    /**
     * Sanitize key
     *
     * Key sanitization using WordPress sanitize_key() for database keys,
     * option names, and similar identifiers.
     *
     * @param  mixed  $value   Value to sanitize
     * @param  array  $args    Additional sanitizer arguments
     * @param  array  $context Context configuration
     * @return string          Sanitized key
     * @access protected
     * @since  1.0.0
     *
     * @see    sanitize_key()
     */
    protected function sanitize_key( $value, array $args = array(), array $context = array() ): string {
        if ( ! is_scalar( $value ) && ! is_null( $value ) ) {
            return '';
        }
        return sanitize_key( (string) $value );
    }

    /**
     * Sanitize alphabetic characters
     *
     * Removes all non-alphabetic characters using regex pattern.
     *
     * @param  mixed  $value   Value to sanitize
     * @param  array  $args    Additional sanitizer arguments
     * @param  array  $context Context configuration
     * @return string          String containing only letters
     * @access protected
     * @since  1.0.0
     *
     * @see    preg_replace()
     */
    protected function sanitize_alpha( $value, array $args = array(), array $context = array() ): string {
        if ( ! is_scalar( $value ) && ! is_null( $value ) ) {
            return '';
        }
        return preg_replace( self::$patterns['alpha'], '', (string) $value );
    }

    /**
     * Sanitize alphanumeric characters
     *
     * Removes all non-alphanumeric characters using regex pattern.
     *
     * @param  mixed  $value   Value to sanitize
     * @param  array  $args    Additional sanitizer arguments
     * @param  array  $context Context configuration
     * @return string          String containing only letters and numbers
     * @access protected
     * @since  1.0.0
     *
     * @see    preg_replace()
     */
    protected function sanitize_alphanumeric( $value, array $args = array(), array $context = array() ): string {
        if ( ! is_scalar( $value ) && ! is_null( $value ) ) {
            return '';
        }
        return preg_replace( self::$patterns['alphanumeric'], '', (string) $value );
    }

    /**
     * Sanitize hexadecimal characters
     *
     * Removes all non-hexadecimal characters using regex pattern.
     *
     * @param  mixed  $value   Value to sanitize
     * @param  array  $args    Additional sanitizer arguments
     * @param  array  $context Context configuration
     * @return string          String containing only hex characters (0-9, a-f, A-F)
     * @access protected
     * @since  1.0.0
     *
     * @see    preg_replace()
     */
    protected function sanitize_hex( $value, array $args = array(), array $context = array() ): string {
        if ( ! is_scalar( $value ) && ! is_null( $value ) ) {
            return '';
        }
        return preg_replace( self::$patterns['hex'], '', (string) $value );
    }

    /**
     * Sanitize boolean value
     *
     * Boolean sanitization using filter_var() with FILTER_VALIDATE_BOOLEAN.
     *
     * @param  mixed  $value   Value to sanitize
     * @param  array  $args    Additional sanitizer arguments
     * @param  array  $context Context configuration
     * @return bool            Sanitized boolean
     * @access protected
     * @since  1.0.0
     *
     * @see    filter_var()
     */
    protected function sanitize_bool( $value, array $args = array(), array $context = array() ): bool {
        return filter_var( $value, FILTER_VALIDATE_BOOLEAN );
    }

    /**
     * Sanitize JSON string
     *
     * JSON sanitization with validation. Returns empty object JSON if invalid.
     *
     * @param  mixed  $value   Value to sanitize
     * @param  array  $args    Additional sanitizer arguments
     * @param  array  $context Context configuration
     * @return string          Valid JSON string or '{}' if invalid
     * @access protected
     * @since  1.0.0
     *
     * @see    json_decode()
     * @see    json_last_error()
     */
    protected function sanitize_json( $value, array $args = array(), array $context = array() ): string {
        if ( ! is_scalar( $value ) && ! is_null( $value ) ) {
            return '{}';
        }

        $json_str = (string) $value;

        // Cache JSON validation
        $cache_key = 'json_validate_' . md5( $json_str );
        if ( ! isset( self::$validation_cache[ $cache_key ] ) ) {
            json_decode( $json_str );
            self::$validation_cache[ $cache_key ] = ( json_last_error() === JSON_ERROR_NONE );
        }

        return self::$validation_cache[ $cache_key ] ? $json_str : '{}';
    }

    /**
     * Sanitize Base64 string
     *
     * Base64 sanitization with validation. Returns empty string if invalid.
     *
     * @param  mixed  $value   Value to sanitize
     * @param  array  $args    Additional sanitizer arguments
     * @param  array  $context Context configuration
     * @return string          Valid Base64 string or empty string
     * @access protected
     * @since  1.0.0
     *
     * @see    base64_decode()
     */
    protected function sanitize_base64( $value, array $args = array(), array $context = array() ): string {
        if ( ! is_scalar( $value ) && ! is_null( $value ) ) {
            return '';
        }

        $str_value = (string) $value;
        $decoded = base64_decode( $str_value, true );
        return ( $decoded !== false ) ? $str_value : '';
    }

    /**
     * Sanitize array with specific rule
     *
     * Applies a sanitization rule to all elements of an array.
     *
     * @param  array  $array   Array to sanitize
     * @param  string $rule    Sanitization rule to apply
     * @param  array  $args    Additional sanitizer arguments
     * @param  array  $context Context configuration
     * @return array           Sanitized array
     * @access protected
     * @since  1.0.0
     */
    protected function sanitize_array( array $array, string $rule, array $args, array $context ): array {
        $sanitized = array();

        foreach ( $array as $key => $value ) {
            $sanitized[ $key ] = $this->apply_sanitizer( $value, $rule, $context );
        }

        return $sanitized;
    }

    /**
     * Sanitize IP addresses with validation
     *
     * IP address sanitization with validation using filter_var().
     * Supports comma-separated lists of IP addresses.
     *
     * @param  mixed  $value   Value to sanitize
     * @param  array  $args    Additional sanitizer arguments
     * @param  array  $context Context configuration
     * @return string          Comma-separated list of valid IP addresses
     * @access protected
     * @since  1.0.0
     *
     * @see    filter_var()
     * @see    sanitize_text_field()
     */
    protected function sanitize_ip_address( $value, array $args = array(), array $context = array() ): string {
        if ( empty( $value ) ) {
            return '';
        }

        if ( ! is_scalar( $value ) ) {
            return '';
        }

        $str_value = (string) $value;
        $ips = array_map( 'trim', explode( ',', $str_value ) );
        $sanitized_ips = array();

        foreach ( $ips as $ip ) {
            $sanitized_ip = sanitize_text_field( $ip );

            if ( ! empty( $sanitized_ip ) ) {
                // Cache IP validation
                $cache_key = 'ip_validate_' . md5( $sanitized_ip );
                if ( ! isset( self::$validation_cache[ $cache_key ] ) ) {
                    self::$validation_cache[ $cache_key ] = filter_var( $sanitized_ip, FILTER_VALIDATE_IP );
                }

                if ( self::$validation_cache[ $cache_key ] ) {
                    $sanitized_ips[] = $sanitized_ip;
                }
            }
        }

        return implode( ', ', $sanitized_ips );
    }

    /**
     * Handle sanitization errors securely
     *
     * Logs sanitization errors and returns safe fallback values based on
     * input type. Fires action for error handling integration.
     *
     * @param  string $error   Error message
     * @param  mixed  $value   Original value that caused the error
     * @param  array  $context Context configuration
     * @return mixed           Safe fallback value
     * @access protected
     * @since  1.0.0
     *
     * @see    error_log()
     * @see    do_action()
     */
    protected function handle_sanitization_error( string $error, $value, array $context ) {
        // Log error securely
        if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
            \UltraguardPro\core\Logger::error( 'Sanitization error: ' . $error );
        }

        // Fire error action
        do_action( 'ultraguard_sanitization_engine_error', $error, $value, $context );

        // Return safe fallback
        if ( is_array( $value ) ) {
            return array();
        }
        if ( is_numeric( $value ) ) {
            return 0;
        }
        if ( is_bool( $value ) ) {
            return false;
        }
        return '';
    }

    /**
     * Register custom sanitizer with validation
     *
     * Registers a custom sanitizer callback for use in sanitization rules.
     * Clears caches to ensure new sanitizer is used immediately.
     *
     * @param  string   $name     Sanitizer name (will be sanitized to key format)
     * @param  callable $callback Sanitizer callback function
     * @return bool               True if registration successful, false otherwise
     * @access public
     * @since  1.0.0
     *
     * @see    sanitize_key()
     * @see    do_action()
     */
    public function register_sanitizer( string $name, callable $callback ): bool {
        if ( empty( $name ) ) {
            return false;
        }

        $sanitized_name = sanitize_key( $name );
        self::$sanitizers[ $sanitized_name ] = $callback;

        // Clear cache to ensure new sanitizer is used
        $this->clear_cache();

        // Fire registration action
        do_action( 'ultraguard_sanitization_engine_sanitizer_registered', $sanitized_name, $callback );

        return true;
    }

    /**
     * Unregister custom sanitizer
     *
     * Removes a previously registered custom sanitizer.
     * Clears caches to prevent stale sanitizer usage.
     *
     * @param  string $name Sanitizer name to unregister
     * @return bool         True if sanitizer was unregistered, false if not found
     * @access public
     * @since  1.0.0
     */
    public function unregister_sanitizer( string $name ): bool {
        if ( isset( self::$sanitizers[ $name ] ) ) {
            unset( self::$sanitizers[ $name ] );

            // Clear cache
            $this->clear_cache();

            return true;
        }
        return false;
    }

    /**
     * Get registered sanitizers
     *
     * Returns all currently registered sanitizers including built-in and
     * custom sanitizers.
     *
     * @return array<string, callable> Registered sanitizers
     * @access public
     * @since  1.0.0
     */
    public function get_sanitizers(): array {
        return self::$sanitizers;
    }

    /**
     * Set default rules
     *
     * Updates the default sanitization rules for data types.
     * Merges with existing rules and clears caches.
     *
     * @param  array $rules New default rules to add/update
     * @return void
     * @access public
     * @since  1.0.0
     *
     * @see    wp_parse_args()
     */
    public function set_default_rules( array $rules ): void {
        self::$default_rules = wp_parse_args( $rules, self::$default_rules );
        $this->clear_cache();
    }

    /**
     * Get default rules
     *
     * Returns the current default sanitization rules for data types.
     *
     * @return array<string, array<string|array>> Default sanitization rules
     * @access public
     * @since  1.0.0
     */
    public function get_default_rules(): array {
        return self::$default_rules;
    }

    /**
     * Quick sanitize helper for common use cases
     *
     * Static helper method for quick sanitization of single values.
     * Uses caching for performance optimization.
     *
     * @param  mixed  $value Value to sanitize
     * @param  string $type  Sanitization type (text, email, url, etc.)
     * @param  array  $args  Additional sanitizer arguments
     * @return mixed         Sanitized value
     * @access public
     * @static
     * @since  1.0.0
     *
     * @example
     * $clean = SanitizationEngine::quick_sanitize(wp_unslash( $_GET['id'] ), 'int');
     */
    public static function quick_sanitize( $value, string $type = 'text', array $args = array() ) {
        $instance = self::get_instance();
        $context = array( 'strict_mode' => false );

        $sanitized_type = sanitize_key( $type );

        if ( isset( self::$sanitizers[ $sanitized_type ] ) ) {
            $cache_key = 'quick_' . $sanitized_type . '_' . md5( serialize( array( $value, $args ) ) );

            if ( ! isset( self::$cache[ $cache_key ] ) ) {
                self::$cache[ $cache_key ] = call_user_func(
                    self::$sanitizers[ $sanitized_type ],
                    $value,
                    $args,
                    $context
                );
            }

            return self::$cache[ $cache_key ];
        }

        return $instance->sanitize_text( $value, $args, $context );
    }

    /**
     * Validate if value matches expected type after sanitization
     *
     * Static helper method for type validation. Uses caching for
     * performance optimization.
     *
     * @param  mixed  $value Value to validate
     * @param  string $type  Expected data type
     * @return bool          True if value matches expected type, false otherwise
     * @access public
     * @static
     * @since  1.0.0
     *
     * @example
     * $is_valid = SanitizationEngine::validate_type($email, 'email');
     */
    public static function validate_type( $value, string $type ): bool {
        $cache_key = 'validate_' . $type . '_' . md5( serialize( $value ) );

        if ( isset( self::$validation_cache[ $cache_key ] ) ) {
            return self::$validation_cache[ $cache_key ];
        }

        $result = false;

        switch ( $type ) {
            case 'email':
                $result = is_email( $value ) !== false;
                break;
            case 'url':
                $result = filter_var( $value, FILTER_VALIDATE_URL ) !== false;
                break;
            case 'int':
                $result = filter_var( $value, FILTER_VALIDATE_INT ) !== false;
                break;
            case 'float':
                $result = filter_var( $value, FILTER_VALIDATE_FLOAT ) !== false;
                break;
            case 'bool':
                $result = is_bool( $value );
                break;
            case 'ip':
                $result = filter_var( $value, FILTER_VALIDATE_IP ) !== false;
                break;
            case 'json':
                json_decode( $value );
                $result = json_last_error() === JSON_ERROR_NONE;
                break;
            default:
                $result = true;
                break;
        }

        self::$validation_cache[ $cache_key ] = $result;
        return $result;
    }

    /**
     * Generate cache key for sanitization operation
     *
     * Creates a unique cache key based on data, rules, and context
     * to enable efficient caching of sanitization results.
     *
     * @param  array $data    Data to sanitize
     * @param  array $rules   Sanitization rules
     * @param  array $context Context configuration
     * @return string         Generated cache key
     * @access private
     * @since  1.0.0
     *
     * @see    serialize()
     * @see    md5()
     */
    private function generate_cache_key( array $data, array $rules, array $context ): string {
        return 'sanitize_' . md5( serialize( array( $data, $rules, $context ) ) );
    }

    /**
     * Clear all caches
     *
     * Clears all internal caches including sanitization results and
     * validation results. Useful after configuration changes.
     *
     * @return void
     * @access public
     * @since  1.0.0
     */
    public function clear_cache(): void {
        self::$cache = array();
        self::$validation_cache = array();
    }

    /**
     * Get cache statistics (for debugging)
     *
     * Returns cache statistics including counts and configuration
     * information for debugging and performance monitoring.
     *
     * @return array{
     *     sanitization_cache: int,
     *     validation_cache: int,
     *     sanitizers_count: int,
     *     default_rules: array
     * } Cache statistics
     * @access public
     * @since  1.0.0
     */
    public function get_cache_stats(): array {
        return array(
            'sanitization_cache' => count( self::$cache ),
            'validation_cache'   => count( self::$validation_cache ),
            'sanitizers_count'   => count( self::$sanitizers ),
            'default_rules'      => self::$default_rules,
        );
    }

    /**
     * Prevent cloning
     *
     * Private clone method to prevent cloning of singleton instance.
     *
     * @return void
     * @access private
     */
    private function __clone() {}

    /**
     * Prevent unserialization
     *
     * Prevents unserialization of singleton instance for security.
     * Triggers WordPress doing_it_wrong notice if attempted.
     *
     * @return void
     * @access public
     * @since  1.0.0
     *
     * @see    _doing_it_wrong()
     */
    public function __wakeup() {
        _doing_it_wrong( __FUNCTION__, esc_html__( 'SanitizationEngine cannot be unserialized.', 'ultraguard-security-suite' ), '1.0.0' );
    }
}
