<?php

/**
 * Restriction REST Controller
 *
 * REST API endpoints for Members Access admin functionality.
 * Handles saving/loading restriction rules and select options.
 *
 * @package ArraySubs\Features\MembersAccess\REST
 * @since 1.0.0
 */

namespace ArraySubs\Features\MembersAccess\REST;

defined('ABSPATH') || exit;

use ArraySubs\Supports\BaseRestController;
use WP_REST_Request;
use WP_REST_Response;

/**
 * RestrictionController class
 */
class RestrictionController extends BaseRestController
{
    /**
     * Enable auto-loading
     *
     * @var bool
     */
    public static $loadable = true;

    /**
     * Constructor
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Register REST routes
     *
     * @return void
     */
    public function registerRoutes(): void
    {
        // Get all restriction settings
        register_rest_route(
            $this->namespace,
            '/members-access/settings',
            [
                [
                    'methods'             => 'GET',
                    'callback'            => [$this, 'getSettings'],
                    'permission_callback' => [$this, 'checkAdminPermission'],
                ],
                [
                    'methods'             => 'POST',
                    'callback'            => [$this, 'saveSettings'],
                    'permission_callback' => [$this, 'checkAdminPermission'],
                ],
            ]
        );

        // Get select options for products
        register_rest_route(
            $this->namespace,
            '/members-access/select/products',
            [
                'methods'             => 'GET',
                'callback'            => [$this, 'getProductOptions'],
                'permission_callback' => [$this, 'checkAdminPermission'],
                'args'                => [
                    'search' => [
                        'type'              => 'string',
                        'sanitize_callback' => 'sanitize_text_field',
                    ],
                    'type' => [
                        'type'              => 'string',
                        'sanitize_callback' => 'sanitize_text_field',
                    ],
                ],
            ]
        );

        // Get select options for variations
        register_rest_route(
            $this->namespace,
            '/members-access/select/variations',
            [
                'methods'             => 'GET',
                'callback'            => [$this, 'getVariationOptions'],
                'permission_callback' => [$this, 'checkAdminPermission'],
                'args'                => [
                    'search' => [
                        'type'              => 'string',
                        'sanitize_callback' => 'sanitize_text_field',
                    ],
                    'product_id' => [
                        'type'              => 'integer',
                        'sanitize_callback' => 'absint',
                    ],
                ],
            ]
        );

        // Get select options for taxonomies
        register_rest_route(
            $this->namespace,
            '/members-access/select/taxonomies',
            [
                'methods'             => 'GET',
                'callback'            => [$this, 'getTaxonomyOptions'],
                'permission_callback' => [$this, 'checkAdminPermission'],
            ]
        );

        // Get content taxonomies for CPT rules.
        register_rest_route(
            $this->namespace,
            '/members-access/select/content-taxonomies',
            [
                'methods'             => 'GET',
                'callback'            => [$this, 'getContentTaxonomyOptions'],
                'permission_callback' => [$this, 'checkAdminPermission'],
            ]
        );

        // Get select options for terms
        register_rest_route(
            $this->namespace,
            '/members-access/select/terms',
            [
                'methods'             => 'GET',
                'callback'            => [$this, 'getTermOptions'],
                'permission_callback' => [$this, 'checkAdminPermission'],
                'args'                => [
                    'taxonomy' => [
                        'required'          => true,
                        'type'              => 'string',
                        'sanitize_callback' => 'sanitize_text_field',
                    ],
                    'search' => [
                        'type'              => 'string',
                        'sanitize_callback' => 'sanitize_text_field',
                    ],
                ],
            ]
        );

        // Get select options for WordPress roles
        register_rest_route(
            $this->namespace,
            '/members-access/select/roles',
            [
                'methods'             => 'GET',
                'callback'            => [$this, 'getRoleOptions'],
                'permission_callback' => [$this, 'checkAdminPermission'],
            ]
        );

        // Get select options for post types
        register_rest_route(
            $this->namespace,
            '/members-access/select/post-types',
            [
                'methods'             => 'GET',
                'callback'            => [$this, 'getPostTypeOptions'],
                'permission_callback' => [$this, 'checkAdminPermission'],
            ]
        );

        // Get select options for posts/pages/CPT entries.
        register_rest_route(
            $this->namespace,
            '/members-access/select/posts',
            [
                'methods'             => 'GET',
                'callback'            => [$this, 'getPostOptions'],
                'permission_callback' => [$this, 'checkAdminPermission'],
                'args'                => [
                    'search' => [
                        'type'              => 'string',
                        'sanitize_callback' => 'sanitize_text_field',
                    ],
                    'post_type' => [
                        'type'              => 'string',
                        'sanitize_callback' => 'sanitize_key',
                    ],
                ],
            ]
        );

        // Get select options for features (from Feature Manager)
        register_rest_route(
            $this->namespace,
            '/members-access/select/features',
            [
                'methods'             => 'GET',
                'callback'            => [$this, 'getFeatureOptions'],
                'permission_callback' => [$this, 'checkAdminPermission'],
            ]
        );

        // Unified select options endpoint (routes based on 'type' param)
        register_rest_route(
            $this->namespace,
            '/members-access/select-options',
            [
                'methods'             => 'GET',
                'callback'            => [$this, 'getSelectOptions'],
                'permission_callback' => [$this, 'checkAdminPermission'],
                'args'                => [
                    'type' => [
                        'required'          => true,
                        'type'              => 'string',
                        'sanitize_callback' => 'sanitize_text_field',
                    ],
                    'search' => [
                        'type'              => 'string',
                        'sanitize_callback' => 'sanitize_text_field',
                    ],
                    'taxonomy' => [
                        'type'              => 'string',
                        'sanitize_callback' => 'sanitize_text_field',
                    ],
                    'product_id' => [
                        'type'              => 'integer',
                        'sanitize_callback' => 'absint',
                    ],
                    'post_type' => [
                        'type'              => 'string',
                        'sanitize_callback' => 'sanitize_key',
                    ],
                ],
            ]
        );
    }

    /**
     * Check admin permission
     *
     * @return bool
     */
    public function checkAdminPermission(): bool
    {
        return current_user_can('manage_woocommerce') || current_user_can('manage_options');
    }

    /**
     * Unified select options endpoint
     *
     * Routes to the appropriate handler based on 'type' parameter.
     *
     * @param WP_REST_Request $request Request object.
     * @return WP_REST_Response
     */
    public function getSelectOptions(WP_REST_Request $request): WP_REST_Response
    {
        $type = $request->get_param('type');

        switch ($type) {
            case 'products':
                return $this->getProductOptions($request);

            case 'variations':
                return $this->getVariationOptions($request);

            case 'taxonomies':
                return $this->getTaxonomyOptions($request);

            case 'content-taxonomies':
                return $this->getContentTaxonomyOptions($request);

            case 'terms':
                return $this->getTermOptions($request);

            case 'roles':
                return $this->getRoleOptions($request);

            case 'post-types':
            case 'postTypes':
                return $this->getPostTypeOptions($request);

            case 'posts':
                return $this->getPostOptions($request);

            case 'features':
                return $this->getFeatureOptions($request);

            default:
                return new WP_REST_Response([
                    'success' => false,
                    'message' => sprintf(
                        /* translators: %s: option type */
                        __('Unknown select option type: %s', 'arraysubs'),
                        $type
                    ),
                ], 400);
        }
    }

    /**
     * Get restriction settings
     *
     * @param WP_REST_Request $request Request object.
     * @return WP_REST_Response
     */
    public function getSettings(WP_REST_Request $request): WP_REST_Response
    {
        $settings = arraysubs_get_setting('members_access', []);

        // Apply defaults
        $defaults = $this->getDefaultSettings();
        $settings = wp_parse_args($settings, $defaults);

        return new WP_REST_Response([
            'success' => true,
            'data'    => $settings,
        ], 200);
    }

    /**
     * Save restriction settings
     *
     * Handles partial updates - only saves the keys that are provided.
     *
     * @param WP_REST_Request $request Request object.
     * @return WP_REST_Response
     */
    public function saveSettings(WP_REST_Request $request): WP_REST_Response
    {
        // Get current settings
        $all_settings = arraysubs_get_settings();
        $current = $all_settings['members_access'] ?? [];

        // Get all JSON body params
        $body = $request->get_json_params();

        // Merge provided settings with current
        // This allows partial updates (e.g., just role_mapping_rules)
        $known_keys = [
            'enabled',
            'default_redirect_url',
            'default_message',
            'role_mapping_rules',
            'url_rules',
            'cpt_rules',
            'download_rules',
            'download_settings',
            'discount_rules',
            'ecommerce_rules',
        ];

        $known_keys = apply_filters('arraysubs_members_access_known_keys', $known_keys);

        foreach ($known_keys as $key) {
            if (isset($body[$key])) {
                $current[$key] = $body[$key];
            }
        }

        // Sanitize the merged settings
        $sanitized = $this->sanitizeSettings($current);

        // Save to options
        $all_settings['members_access'] = $sanitized;
        arraysubs_save_settings($all_settings);

        return new WP_REST_Response([
            'success' => true,
            'message' => __('Settings saved successfully.', 'arraysubs'),
        ], 200);
    }

    /**
     * Get product options for select
     *
     * @param WP_REST_Request $request Request object.
     * @return WP_REST_Response
     */
    public function getProductOptions(WP_REST_Request $request): WP_REST_Response
    {
        $search = $request->get_param('search') ?? '';
        $type = $request->get_param('type') ?? '';

        $args = [
            'status'         => 'publish',
            'limit'          => 20,
            'orderby'        => 'title',
            'order'          => 'ASC',
        ];

        if (!empty($search)) {
            $args['s'] = $search;
        }

        // Filter by subscription type if requested
        if ($type === 'subscription') {
            $args['meta_key'] = '_arraysubs_is_subscription';
            $args['meta_value'] = 'yes';
        }

        $products = wc_get_products($args);
        $options = [];

        foreach ($products as $product) {
            $options[] = [
                'value' => $product->get_id(),
                'label' => $product->get_name(),
            ];
        }

        return new WP_REST_Response([
            'success' => true,
            'data'    => $options,
        ], 200);
    }

    /**
     * Get variation options for select
     *
     * @param WP_REST_Request $request Request object.
     * @return WP_REST_Response
     */
    public function getVariationOptions(WP_REST_Request $request): WP_REST_Response
    {
        $search = $request->get_param('search') ?? '';
        $product_id = $request->get_param('product_id') ?? 0;

        $options = [];

        if ($product_id > 0) {
            // Get variations for specific product
            $product = wc_get_product($product_id);
            if ($product && $product->is_type('variable')) {
                foreach ($product->get_children() as $variation_id) {
                    $variation = wc_get_product($variation_id);
                    if ($variation) {
                        $options[] = [
                            'value' => $variation_id,
                            'label' => $product->get_name() . ' - ' . $variation->get_attribute_summary(),
                        ];
                    }
                }
            }
        } else {
            // Search all variations
            $args = [
                'post_type'      => 'product_variation',
                'post_status'    => 'publish',
                'posts_per_page' => 20,
                'orderby'        => 'title',
                'order'          => 'ASC',
            ];

            if (!empty($search)) {
                $args['s'] = $search;
            }

            $variations = get_posts($args);

            foreach ($variations as $variation_post) {
                $variation = wc_get_product($variation_post->ID);
                if ($variation) {
                    $parent = wc_get_product($variation->get_parent_id());
                    $label = $parent ? $parent->get_name() . ' - ' : '';
                    $label .= $variation->get_attribute_summary();

                    $options[] = [
                        'value' => $variation->get_id(),
                        'label' => $label,
                    ];
                }
            }
        }

        return new WP_REST_Response([
            'success' => true,
            'data'    => $options,
        ], 200);
    }

    /**
     * Get taxonomy options for select
     *
     * @param WP_REST_Request $request Request object.
     * @return WP_REST_Response
     */
    public function getTaxonomyOptions(WP_REST_Request $request): WP_REST_Response
    {
        $taxonomies = get_taxonomies(['object_type' => ['product']], 'objects');
        $options = [];

        foreach ($taxonomies as $taxonomy) {
            $options[] = [
                'value' => $taxonomy->name,
                'label' => $taxonomy->label,
            ];
        }

        return new WP_REST_Response([
            'success' => true,
            'data'    => $options,
        ], 200);
    }

    /**
     * Get content taxonomy options for CPT rules.
     *
     * @param WP_REST_Request $request Request object.
     * @return WP_REST_Response
     */
    public function getContentTaxonomyOptions(WP_REST_Request $request): WP_REST_Response
    {
        $taxonomies = get_taxonomies(['show_ui' => true], 'objects');
        $excluded_taxonomies = [
            'nav_menu',
            'link_category',
            'post_format',
        ];

        $content_taxonomies = [];

        foreach ($taxonomies as $taxonomy) {
            if (in_array($taxonomy->name, $excluded_taxonomies, true)) {
                continue;
            }

            $content_taxonomies[$taxonomy->name] = [
                'value' => $taxonomy->name,
                'label' => $taxonomy->label,
            ];
        }

        uasort(
            $content_taxonomies,
            static function (array $left, array $right): int {
                return strcasecmp($left['label'], $right['label']);
            }
        );

        return new WP_REST_Response([
            'success' => true,
            'data'    => array_values($content_taxonomies),
        ], 200);
    }

    /**
     * Get term options for select
     *
     * @param WP_REST_Request $request Request object.
     * @return WP_REST_Response
     */
    public function getTermOptions(WP_REST_Request $request): WP_REST_Response
    {
        $taxonomy = $request->get_param('taxonomy');
        $search = $request->get_param('search') ?? '';

        $args = [
            'taxonomy'   => $taxonomy,
            'hide_empty' => false,
            'number'     => 50,
        ];

        if (!empty($search)) {
            $args['search'] = $search;
        }

        $terms = get_terms($args);
        $options = [];

        if (!is_wp_error($terms)) {
            foreach ($terms as $term) {
                $options[] = [
                    'value' => $term->term_id,
                    'label' => $term->name,
                ];
            }
        }

        return new WP_REST_Response([
            'success' => true,
            'data'    => $options,
        ], 200);
    }

    /**
     * Get role options for select
     *
     * @param WP_REST_Request $request Request object.
     * @return WP_REST_Response
     */
    public function getRoleOptions(WP_REST_Request $request): WP_REST_Response
    {
        global $wp_roles;
        $options = [];

        foreach ($wp_roles->roles as $slug => $role) {
            $options[] = [
                'value' => $slug,
                'label' => $role['name'],
            ];
        }

        return new WP_REST_Response([
            'success' => true,
            'data'    => $options,
        ], 200);
    }

    /**
     * Get post type options for select
     *
     * @param WP_REST_Request $request Request object.
     * @return WP_REST_Response
     */
    public function getPostTypeOptions(WP_REST_Request $request): WP_REST_Response
    {
        $post_types = get_post_types(['show_ui' => true], 'objects');
        $options = [];

        foreach ($post_types as $post_type) {
            if (in_array($post_type->name, ['attachment', 'revision', 'nav_menu_item'], true)) {
                continue;
            }

            $options[] = [
                'value' => $post_type->name,
                'label' => $post_type->label,
            ];
        }

        usort(
            $options,
            static function (array $left, array $right): int {
                return strcasecmp($left['label'], $right['label']);
            }
        );

        return new WP_REST_Response([
            'success' => true,
            'data'    => $options,
        ], 200);
    }

    /**
     * Get post options for select.
     *
     * @param WP_REST_Request $request Request object.
     * @return WP_REST_Response
     */
    public function getPostOptions(WP_REST_Request $request): WP_REST_Response
    {
        $search = $request->get_param('search') ?? '';
        $post_type = sanitize_key((string) ($request->get_param('post_type') ?? 'post'));

        if (empty($post_type) || !post_type_exists($post_type)) {
            $post_type = 'post';
        }

        $query = new \WP_Query([
            'post_type'              => $post_type,
            'post_status'            => ['publish', 'private'],
            'posts_per_page'         => 20,
            'orderby'                => 'title',
            'order'                  => 'ASC',
            's'                      => $search,
            'ignore_sticky_posts'    => true,
            'no_found_rows'          => true,
            'update_post_meta_cache' => false,
            'update_post_term_cache' => false,
        ]);

        $options = [];

        foreach ($query->posts as $post) {
            if (!$post instanceof \WP_Post) {
                continue;
            }

            $title = get_the_title($post);

            if ('' === $title) {
                /* translators: %d: post ID */
                $title = sprintf(__('(no title) #%d', 'arraysubs'), $post->ID);
            }

            $options[] = [
                'value' => $post->ID,
                'label' => $title,
            ];
        }

        return new WP_REST_Response([
            'success' => true,
            'data'    => $options,
        ], 200);
    }

    /**
     * Get feature options for select (from Feature Manager)
     *
     * @param WP_REST_Request $request Request object.
     * @return WP_REST_Response
     */
    public function getFeatureOptions(WP_REST_Request $request): WP_REST_Response
    {
        // Get all defined features from products
        global $wpdb;

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Aggregate query for unique features
        $features_data = $wpdb->get_col(
            "SELECT DISTINCT meta_value FROM {$wpdb->postmeta}
            WHERE meta_key = '_arraysubs_features'
            AND meta_value != ''
            AND meta_value != '[]'"
        );

        $feature_keys = [];

        foreach ($features_data as $features_json) {
            $features = json_decode($features_json, true);
            if (is_array($features)) {
                foreach ($features as $feature) {
                    if (isset($feature['key']) && !empty($feature['key'])) {
                        $feature_keys[$feature['key']] = $feature['label'] ?? $feature['key'];
                    }
                }
            }
        }

        $options = [];
        foreach ($feature_keys as $key => $label) {
            $options[] = [
                'value' => $key,
                'label' => $label,
            ];
        }

        return new WP_REST_Response([
            'success' => true,
            'data'    => $options,
        ], 200);
    }

    /**
     * Get default settings
     *
     * @return array
     */
    private function getDefaultSettings(): array
    {
        $defaults = [
            'enabled'              => true,
            'default_redirect_url' => '/pricing',
            'default_message'      => __('This content is restricted. Please subscribe to access.', 'arraysubs'),
            'require_login'        => true,
            'cache_compatibility'  => true,
            'role_mapping_rules'   => [],
            'url_rules'            => [],
            'cpt_rules'            => [],
            'download_rules'       => [],
            'discount_rules'       => [],
            'ecommerce_rules'      => [],
            'download_settings'    => [
                'protected_directories' => [],
                'serve_via_php'         => false,
                'signed_url_expiry'     => 3600,
            ],
        ];

        return apply_filters('arraysubs_members_access_default_settings', $defaults);
    }

    /**
     * Sanitize settings
     *
     * @param array $settings Settings to sanitize.
     * @return array
     */
    private function sanitizeSettings(array $settings): array
    {
        $sanitized = [];

        // Boolean settings
        $sanitized['enabled'] = !empty($settings['enabled']);
        $sanitized['require_login'] = !empty($settings['require_login']);
        $sanitized['cache_compatibility'] = !empty($settings['cache_compatibility']);

        // String settings
        $sanitized['default_redirect_url'] = sanitize_text_field($settings['default_redirect_url'] ?? '/pricing');
        $sanitized['default_message'] = wp_kses_post($settings['default_message'] ?? '');

        // Rule arrays (sanitize each rule)
        $sanitized['role_mapping_rules'] = $this->sanitizeRules($settings['role_mapping_rules'] ?? []);
        $sanitized['url_rules'] = $this->sanitizeRules($settings['url_rules'] ?? []);
        $sanitized['cpt_rules'] = $this->sanitizeRules($settings['cpt_rules'] ?? []);
        $sanitized['download_rules'] = $this->sanitizeRules($settings['download_rules'] ?? []);
        $sanitized['discount_rules'] = $this->sanitizeRules($settings['discount_rules'] ?? []);
        $sanitized['ecommerce_rules'] = $this->sanitizeRules($settings['ecommerce_rules'] ?? []);

        // Download settings
        $download_settings = $settings['download_settings'] ?? [];
        $sanitized['download_settings'] = [
            'protected_directories' => array_map('sanitize_text_field', $download_settings['protected_directories'] ?? []),
            'serve_via_php'         => !empty($download_settings['serve_via_php']),
            'signed_url_expiry'     => absint($download_settings['signed_url_expiry'] ?? 3600),
        ];

        return apply_filters('arraysubs_members_access_sanitize_settings', $sanitized, $settings);
    }

    /**
     * Sanitize rules array
     *
     * @param array $rules Rules to sanitize.
     * @return array
     */
    private function sanitizeRules(array $rules): array
    {
        $sanitized = [];

        foreach ($rules as $rule) {
            if (!is_array($rule)) {
                continue;
            }

            $sanitized_rule = [
                'id'      => sanitize_text_field($rule['id'] ?? uniqid('rule_')),
                'enabled' => !empty($rule['enabled']),
            ];

            // Copy other fields based on rule type
            foreach ($rule as $key => $value) {
                if (in_array($key, ['id', 'enabled'], true)) {
                    continue;
                }

                if ($key === 'conditions') {
                    $sanitized_rule[$key] = $this->sanitizeConditions($value);
                } elseif ($key === 'schedule_enabled') {
                    $sanitized_rule[$key] = !empty($value);
                } elseif ($key === 'schedule_value') {
                    $sanitized_rule[$key] = max(0, absint($value));
                } elseif ($key === 'schedule_unit') {
                    $allowed_units = ['days', 'weeks', 'months'];
                    $sanitized_rule[$key] = in_array($value, $allowed_units, true) ? $value : 'days';
                } elseif ($key === 'message') {
                    $sanitized_rule[$key] = wp_kses_post($value);
                } elseif ($key === 'redirect_url') {
                    $sanitized_rule[$key] = esc_url_raw($value);
                } elseif ($key === 'files') {
                    // Handle files array (for download rules)
                    $sanitized_rule[$key] = $this->sanitizeFilesList($value);
                } elseif (is_array($value)) {
                    $sanitized_rule[$key] = array_map(function ($v) {
                        return is_numeric($v) ? absint($v) : sanitize_text_field($v);
                    }, $value);
                } elseif (is_bool($value)) {
                    $sanitized_rule[$key] = $value;
                } elseif (is_numeric($value)) {
                    $sanitized_rule[$key] = is_float($value) ? floatval($value) : absint($value);
                } else {
                    $sanitized_rule[$key] = sanitize_text_field($value);
                }
            }

            $sanitized[] = $sanitized_rule;
        }

        return $sanitized;
    }

    /**
     * Sanitize files list for download rules
     *
     * @param array $files Files array.
     * @return array
     */
    private function sanitizeFilesList(array $files): array
    {
        $sanitized = [];

        foreach ($files as $file) {
            if (!is_array($file)) {
                continue;
            }

            // Skip files without URL (not selected from media library)
            $url = esc_url_raw($file['url'] ?? '');
            if (empty($url)) {
                continue;
            }

            $sanitized[] = [
                'id'       => is_numeric($file['id'] ?? '') ? absint($file['id']) : sanitize_text_field($file['id'] ?? uniqid()),
                'name'     => sanitize_text_field($file['name'] ?? ''),
                'url'      => $url,
                'filename' => sanitize_file_name($file['filename'] ?? basename($url)),
            ];
        }

        return $sanitized;
    }

    /**
     * Sanitize conditions
     *
     * @param array $conditions Conditions to sanitize.
     * @return array
     */
    private function sanitizeConditions(array $conditions): array
    {
        return [
            'logic' => in_array($conditions['logic'] ?? 'and', ['and', 'or'], true) ? $conditions['logic'] : 'and',
            'rules' => $this->sanitizeConditionRules($conditions['rules'] ?? []),
        ];
    }

    /**
     * Sanitize condition rules
     *
     * @param array $rules Condition rules.
     * @return array
     */
    private function sanitizeConditionRules(array $rules): array
    {
        $sanitized = [];

        foreach ($rules as $rule) {
            if (!is_array($rule)) {
                continue;
            }

            $type = sanitize_text_field($rule['type'] ?? '');
            if (empty($type)) {
                continue;
            }

            $sanitized_rule = ['type' => $type];

            // Handle nested groups
            if ($type === 'group') {
                $sanitized_rule['logic'] = in_array($rule['logic'] ?? 'and', ['and', 'or'], true) ? $rule['logic'] : 'and';
                $sanitized_rule['rules'] = $this->sanitizeConditionRules($rule['rules'] ?? []);
                $sanitized[] = $sanitized_rule;
                continue;
            }

            // Copy other fields
            foreach ($rule as $key => $value) {
                if ($key === 'type') {
                    continue;
                }

                if (is_array($value)) {
                    $sanitized_rule[$key] = array_map(function ($v) {
                        return is_numeric($v) ? absint($v) : sanitize_text_field($v);
                    }, $value);
                } elseif (is_numeric($value)) {
                    $sanitized_rule[$key] = is_float($value + 0) ? floatval($value) : absint($value);
                } else {
                    $sanitized_rule[$key] = sanitize_text_field($value);
                }
            }

            $sanitized[] = $sanitized_rule;
        }

        return $sanitized;
    }
}
