<?php
/**
 * UAGB Admin Helper.
 *
 * @package UAGB
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

use \ZipAI\Classes\Module as Zip_Ai_Module;

if ( ! class_exists( 'UAGB_Admin_Helper' ) ) {

	/**
	 * Class UAGB_Admin_Helper.
	 */
	final class UAGB_Admin_Helper {

		/**
		 * Member Variable
		 *
		 * @since 0.0.1
		 * @var instance
		 */
		private static $instance;

		/**
		 *  Initiator
		 *
		 * @since 0.0.1
		 */
		public static function get_instance() {
			if ( ! isset( self::$instance ) ) {
				self::$instance = new self();
			}
			return self::$instance;
		}

		/**
		 * Get all data from the admin settings page.
		 *
		 * @return mixed
		 * @since 2.0.8
		 */
		public static function get_admin_settings_shareable_data() {

			// Prepare to get the Zip AI Co-pilot modules.
			$zip_ai_modules = array();

			// If the Zip AI Helper is available, get the required modules and their states.
			if ( class_exists( '\ZipAI\Classes\Module' ) ) {
				$zip_ai_modules = Zip_Ai_Module::get_all_modules();
			}

			$content_width = self::get_global_content_width();

			$options = array(
				'uag_enable_legacy_blocks'          => self::get_admin_settings_option( 'uag_enable_legacy_blocks' ),
				'_uagb_allow_file_generation'       => self::get_admin_settings_option( '_uagb_allow_file_generation', 'enabled' ),
				'uag_enable_templates_button'       => self::get_admin_settings_option( 'uag_enable_templates_button', 'yes' ),
				'uag_enable_on_page_css_button'     => self::get_admin_settings_option( 'uag_enable_on_page_css_button', 'yes' ),
				'uag_enable_block_condition'        => self::get_admin_settings_option( 'uag_enable_block_condition', 'disabled' ),
				'uag_enable_masonry_gallery'        => self::get_admin_settings_option( 'uag_enable_masonry_gallery', 'enabled' ),
				'uag_enable_quick_action_sidebar'   => self::get_admin_settings_option( 'uag_enable_quick_action_sidebar', 'enabled' ),
				'uag_enable_animations_extension'   => self::get_admin_settings_option( 'uag_enable_animations_extension', 'enabled' ),
				'uag_enable_gbs_extension'          => self::get_admin_settings_option( 'uag_enable_gbs_extension', 'enabled' ),
				'uag_enable_block_responsive'       => self::get_admin_settings_option( 'uag_enable_block_responsive', 'enabled' ),
				'uag_select_font_globally'          => self::get_admin_settings_option( 'uag_select_font_globally', array() ),
				'uag_load_select_font_globally'     => self::get_admin_settings_option( 'uag_load_select_font_globally', 'disabled' ),
				'uag_load_gfonts_locally'           => self::get_admin_settings_option( 'uag_load_gfonts_locally', 'disabled' ),
				'uag_collapse_panels'               => self::get_admin_settings_option( 'uag_collapse_panels', 'enabled' ),
				'uag_copy_paste'                    => self::get_admin_settings_option( 'uag_copy_paste', 'enabled' ),
				'uag_preload_local_fonts'           => self::get_admin_settings_option( 'uag_preload_local_fonts', 'disabled' ),
				'uag_visibility_mode'               => self::get_admin_settings_option( 'uag_visibility_mode', 'disabled' ),
				'uag_container_global_padding'      => self::get_admin_settings_option( 'uag_container_global_padding', 'default' ),
				'uag_container_global_elements_gap' => self::get_admin_settings_option( 'uag_container_global_elements_gap', 20 ),
				'uag_btn_inherit_from_theme'        => self::get_admin_settings_option( 'uag_btn_inherit_from_theme', 'disabled' ),
				'uag_blocks_editor_spacing'         => apply_filters( 'uagb_default_blocks_editor_spacing', self::get_admin_settings_option( 'uag_blocks_editor_spacing', 0 ) ),
				'uag_load_font_awesome_5'           => self::get_admin_settings_option( 'uag_load_font_awesome_5' ),
				'uag_auto_block_recovery'           => self::get_admin_settings_option( 'uag_auto_block_recovery' ),
				'uag_enable_bsf_analytics_option'   => self::get_admin_settings_option( 'spectra_usage_optin', 'no' ),
				'uag_content_width'                 => $content_width,
				'spectra_core_blocks'               => apply_filters(
					'spectra_core_blocks',
					array(
						'container',
						'advanced-heading',
						'image',
						'icon',
						'buttons',
						'info-box',
						'call-to-action',
						'countdown',
						'popup-builder',
					)
				),
				'wp_is_block_theme'                 => self::is_block_theme(),
				'zip_ai_modules'                    => $zip_ai_modules,
			);

			return $options;
		}

		/**
		 * Update all data from the admin settings page.
		 *
		 * @param array $data All settings of Admin.
		 * @return mixed
		 * @since 2.0.8
		 */
		public static function update_admin_settings_shareable_data( $data = array() ) {

			foreach ( $data as $key => $value ) {
				self::update_admin_settings_option( $key, $value );
			}
		}

		/**
		 * Returns an option from the database for
		 * the admin settings page.
		 *
		 * @param  string  $key     The option key.
		 * @param  mixed   $default Option default value if option is not available.
		 * @param  boolean $network_override Whether to allow the network admin setting to be overridden on subsites.
		 * @return mixed            Return the option value.
		 * @since 0.0.1
		 */
		public static function get_admin_settings_option( $key, $default = false, $network_override = false ) {
			// Get the site-wide option if we're in the network admin.
			return $network_override && is_multisite() ? get_site_option( $key, $default ) : get_option( $key, $default );
		}

		/**
		 * Deletes an option from the database for
		 * the admin settings page.
		 *
		 * @param  string  $key     The option key.
		 * @param  boolean $network_override Whether to allow the network admin setting to be overridden on subsites.
		 * @since 2.8.0
		 * @return void            Return the option value.
		 */
		public static function delete_admin_settings_option( $key, $network_override = false ) {
			// Get the site-wide option if we're in the network admin.
			if ( $network_override && is_multisite() ) {
				delete_site_option( $key );
			} else {
				delete_option( $key );
			}
		}

		/**
		 * Provide Widget settings.
		 *
		 * @return array()
		 * @since 0.0.1
		 */
		public static function get_block_options() {

			$blocks       = UAGB_Helper::$block_list;
			$saved_blocks = self::get_admin_settings_option( '_uagb_blocks' );

			if ( is_array( $blocks ) ) {
				foreach ( $blocks as $slug => $data ) {
					$_slug = str_replace( 'uagb/', '', $slug );

					if ( isset( $saved_blocks[ $_slug ] ) ) {
						if ( 'disabled' === $saved_blocks[ $_slug ] ) {
							$blocks[ $slug ]['is_activate'] = false;
						} else {
							$blocks[ $slug ]['is_activate'] = true;
						}
					} else {
						$blocks[ $slug ]['is_activate'] = ( isset( $data['default'] ) ) ? $data['default'] : false;
					}
				}
			}

			UAGB_Helper::$block_list = $blocks;

			return apply_filters( 'uagb_enabled_blocks', UAGB_Helper::$block_list );
		}

		/**
		 * Updates an option from the admin settings page.
		 *
		 * @param string $key       The option key.
		 * @param mixed  $value     The value to update.
		 * @param bool   $network   Whether to allow the network admin setting to be overridden on subsites.
		 * @return mixed
		 * @since 0.0.1
		 */
		public static function update_admin_settings_option( $key, $value, $network = false ) {

			// Update the site-wide option since we're in the network admin.
			if ( $network && is_multisite() ) {
				update_site_option( $key, $value );
			} else {
				update_option( $key, $value );
			}
		}

		/**
		 *  Get Specific Stylesheet
		 *
		 * @since 1.13.4
		 */
		public static function create_specific_stylesheet() {

			$saved_blocks         = self::get_admin_settings_option( '_uagb_blocks' );
			$combined             = array();
			$is_already_post      = false;
			$is_already_timeline  = false;
			$is_already_column    = false;
			$is_already_icon_list = false;
			$is_already_button    = false;
			$is_already_faq       = false;
			$is_already_tabs      = false;
			$blocks_info          = UAGB_Block_Module::get_blocks_info();

			foreach ( $blocks_info as $key => $block ) {

				$block_name = str_replace( 'uagb/', '', $key );

				if ( isset( $saved_blocks[ $block_name ] ) && 'disabled' === $saved_blocks[ $block_name ] ) {
					continue;
				}

				switch ( $block_name ) {

					case 'post-grid':
					case 'post-carousel':
					case 'post-masonry':
					case 'post-title':
					case 'post-image':
					case 'post-button':
					case 'post-excerpt':
					case 'post-meta':
						if ( ! $is_already_post ) {
							$combined[]      = 'post';
							$is_already_post = true;
						}
						break;

					case 'columns':
					case 'column':
						if ( ! $is_already_column ) {
							$combined[]        = 'column';
							$combined[]        = 'columns';
							$is_already_column = true;
						}
						break;

					case 'icon-list':
					case 'icon-list-child':
						if ( ! $is_already_icon_list ) {
							$combined[]           = 'icon-list';
							$combined[]           = 'icon-list-child';
							$is_already_icon_list = true;
						}
						break;
					case 'buttons-child':
					case 'buttons':
						if ( ! $is_already_button ) {
							$combined[]        = 'buttons';
							$combined[]        = 'buttons-child';
							$is_already_button = true;
						}
						break;

					case 'post-timeline':
					case 'content-timeline':
						if ( ! $is_already_timeline ) {
							$combined[]          = 'timeline';
							$is_already_timeline = true;
						}
						break;

					case 'restaurant-menu':
						$combined[] = 'price-list';
						break;

					case 'faq-child':
					case 'faq':
						if ( ! $is_already_faq ) {
							$combined[]     = 'faq';
							$combined[]     = 'faq-child';
							$is_already_faq = true;
						}
						break;
					
					case 'tabs-child':
					case 'tabs':
						if ( ! $is_already_tabs ) {
							$combined[]      = 'tabs';
							$combined[]      = 'tabs-child';
							$is_already_tabs = true;
						}
						break;

					default:
						$combined[] = $block_name;
						break;
				}
			}

			// Load common CSS for all the blocks.
			$combined[] = 'extensions';

			$wp_upload_dir = UAGB_Helper::get_uag_upload_dir_path();
			$combined_path = $wp_upload_dir . 'custom-style-blocks.css';

			$style = '';

			$wp_filesystem = uagb_filesystem();

			foreach ( $combined as $key => $c_block ) {

				if ( false !== strpos( $c_block, '-pro' ) ) {
					$style_file = SPECTRA_PRO_DIR . 'assets/css/blocks/' . $c_block . '.css';
				} else {
					$style_file = UAGB_DIR . 'assets/css/blocks/' . $c_block . '.css';
				}

				if ( file_exists( $style_file ) ) {
					$style .= $wp_filesystem->get_contents( $style_file );
				}
			}

			$wp_filesystem->put_contents( $combined_path, $style, FS_CHMOD_FILE );
		}

		/**
		 * Get Rollback versions.
		 *
		 * @since 1.23.0
		 * @return array
		 * @access public
		 */
		public function get_rollback_versions() {

			$rollback_versions = get_transient( 'uag_rollback_versions_' . UAGB_VER );

			if ( empty( $rollback_versions ) ) {

				$max_versions = 10;

				require_once ABSPATH . 'wp-admin/includes/plugin-install.php';

				$plugin_information = plugins_api(
					'plugin_information',
					array(
						'slug' => 'ultimate-addons-for-gutenberg',
					)
				);

				if ( empty( $plugin_information->versions ) || ! is_array( $plugin_information->versions ) ) {
					return array();
				}

				krsort( $plugin_information->versions );

				$rollback_versions = array();

				foreach ( $plugin_information->versions as $version => $download_link ) {

					$lowercase_version = strtolower( $version );

					$is_valid_rollback_version = ! preg_match( '/(trunk|beta|rc|dev)/i', $lowercase_version );

					if ( ! $is_valid_rollback_version ) {
						continue;
					}

					if ( version_compare( $version, UAGB_VER, '>=' ) ) {
						continue;
					}

					$rollback_versions[] = $version;
				}

				usort( $rollback_versions, array( $this, 'sort_rollback_versions' ) );

				$rollback_versions = array_slice( $rollback_versions, 0, $max_versions, true );

				set_transient( 'uag_rollback_versions_' . UAGB_VER, $rollback_versions, WEEK_IN_SECONDS );
			}

			return $rollback_versions;
		}
		/**
		 * Sort Rollback versions.
		 *
		 * @param string $prev Previous Version.
		 * @param string $next Next Version.
		 *
		 * @since 1.23.0
		 * @return array
		 * @access public
		 */
		public function sort_rollback_versions( $prev, $next ) {

			if ( version_compare( $prev, $next, '==' ) ) {
				return 0;
			}

			if ( version_compare( $prev, $next, '>' ) ) {
				return -1;
			}

			return 1;
		}

		/**
		 * Checks if assets should be excluded for a given Custom Post Type (CPT).
		 *
		 * This static method determines if assets should be excluded based on the given CPT and
		 * any additional exclusions provided via a filter.
		 *
		 * @since 2.16.0
		 * @return bool True if assets should be excluded for the given CPT, false otherwise.
		 */
		public static function should_exclude_assets_for_cpt() {
			// Define the default CPTs to always exclude.
			$default_excluded_cpts = array( 'sureforms_form' );

			// Get the filtered CPT(s) that should not load assets.
			$filtered_excluded_cpts = apply_filters( 'exclude_uagb_assets_for_cpts', array() );

			// If the filtered value is not an array, set it to an empty array.
			if ( ! is_array( $filtered_excluded_cpts ) ) {
				$filtered_excluded_cpts = array();
			}

			// Merge default and filtered excluded CPTs.
			$excluded_cpts = array_merge( $default_excluded_cpts, $filtered_excluded_cpts );

			// Pass the excluded CPTs to the 'ast_block_templates_exclude_post_types' filter.
			add_filter(
				'ast_block_templates_exclude_post_types',
				function() use ( $excluded_cpts ) {
					return $excluded_cpts;
				}
			);

			// Pass the excluded CPTs to the 'zipwp_images_excluded_post_types' filter.
			add_filter(
				'zipwp_images_excluded_post_types',
				function() use ( $excluded_cpts ) {
					return $excluded_cpts;
				}
			);

			// Initialize post type variable.
			$post_type = '';

			// Check if we're in the admin/editor context.
			if ( is_admin() ) {
				// Get the current screen and retrieve the post type.
				$screen    = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
				$post_type = ( $screen instanceof WP_Screen ) ? $screen->post_type : ''; // Admin: use WP_Screen.
			} else {
				// On frontend: get the post type from the queried object.
				$queried_object = get_queried_object();
				$post_type      = ( $queried_object instanceof WP_Post ) ? get_post_type( $queried_object ) : ''; // Frontend: use WP_Post.
			}

			// Return true if the post type matches any in the excluded CPTs list.
			return in_array( $post_type, $excluded_cpts );
		}


		/**
		 * Get Global Content Width
		 *
		 * @since 2.0.0
		 * @return int
		 * @access public
		 */
		public static function get_global_content_width() {
			$content_width                = self::get_admin_settings_option( 'uag_content_width', '' );
			$uag_content_width_set_by     = 'Spectra';
			$get_uag_content_width_set_by = self::get_admin_settings_option( 'uag_content_width_set_by', '' );

			if ( '' === $content_width ) {
				$content_width_third_party = apply_filters( 'spectra_global_content_width', 'default' );
				$astra_content_width       = function_exists( 'astra_get_option' ) ? astra_get_option( 'site-content-width' ) : false;

				if ( self::is_block_theme() ) {
					$settings                 = wp_get_global_settings();
					$content_width            = intval( $settings['layout']['wideSize'] );
					$uag_content_width_set_by = __( "Full Site Editor's Global Styles", 'ultimate-addons-for-gutenberg' );
				} elseif ( 'default' !== $content_width_third_party ) {
					$content_width            = intval( $content_width_third_party );
					$uag_content_width_set_by = __( 'Filter added through any 3rd Party Theme/Plugin.', 'ultimate-addons-for-gutenberg' );
				} elseif ( $astra_content_width ) {
					$content_width            = intval( $astra_content_width );
					$ast_theme_name           = function_exists( 'astra_get_theme_name' ) ? astra_get_theme_name() : 'Astra';
					$uag_content_width_set_by = $ast_theme_name . ' ' . __( 'Theme', 'ultimate-addons-for-gutenberg' );
				}
			}

			// Update admin settings option uag_content_width_set_by if $get_uag_content_width_set_by and $uag_content_width_set_by are not same.
			if ( $get_uag_content_width_set_by !== $uag_content_width_set_by ) {
				self::update_admin_settings_option( 'uag_content_width_set_by', $uag_content_width_set_by );
			}

			return '' === $content_width ? 1140 : $content_width;
		}

		/**
		 * Function to check if the current theme is a block theme.
		 *
		 * @since 2.7.11
		 * @return boolean
		 */
		public static function is_block_theme() {
			return ( function_exists( 'wp_is_block_theme' ) && wp_is_block_theme() ) ? true : false;
		}

		/**
		 * Get Spectra Pro URL with required params
		 *
		 * @param string $path     Path for the Website URL.
		 * @param string $source   UMM source.
		 * @param string $medium   UTM medium.
		 * @param string $campaign UTM campaign.
		 * @since 2.7.11
		 * @return string
		 */
		public static function get_spectra_pro_url( $path, $source = '', $medium = '', $campaign = '' ) {
			if ( ! defined( 'UAGB_URI' ) ) {
				define( 'UAGB_URI', trailingslashit( 'https://wpspectra.com/' ) );
			}
			$url             = esc_url( UAGB_URI . $path );
			$spectra_pro_url = trailingslashit( $url );

			// Modify the utm_source parameter using the UTM ready link function to include tracking information.
			if ( class_exists( '\BSF_UTM_Analytics\Inc\Utils' ) && is_callable( '\BSF_UTM_Analytics\Inc\Utils::get_utm_ready_link' ) ) {
				$spectra_pro_url = \BSF_UTM_Analytics\Inc\Utils::get_utm_ready_link( $spectra_pro_url, 'ultimate-addons-for-gutenberg' );
			} else {
				if ( ! empty( $source ) ) {
					$spectra_pro_url = add_query_arg( 'utm_source', sanitize_text_field( $source ), $spectra_pro_url );
				}
			}

			// Set up our URL if we have a medium.
			if ( ! empty( $medium ) ) {
				$spectra_pro_url = add_query_arg( 'utm_medium', sanitize_text_field( $medium ), $spectra_pro_url );
			}

			// Set up our URL if we have a campaign.
			if ( ! empty( $campaign ) ) {
				$spectra_pro_url = add_query_arg( 'utm_campaign', sanitize_text_field( $campaign ), $spectra_pro_url );
			}

			$spectra_pro_url = apply_filters( 'spectra_get_pro_url', $spectra_pro_url, $url );
			$spectra_pro_url = remove_query_arg( 'bsf', is_string( $spectra_pro_url ) ? $spectra_pro_url : '' );

			$ref = get_option( 'spectra_partner_url_param', '' );
			if ( ! empty( $ref ) && is_string( $ref ) ) {
				$spectra_pro_url = add_query_arg( 'bsf', sanitize_text_field( $ref ), $spectra_pro_url );
			}

			return $spectra_pro_url;
		}

		/**
		 * Prepare user country code.
		 *
		 * Returns the user's country code.
		 * Checks the cookie first, then the Cloudflare IP Country header if available,
		 * and finally detects the IP address country if the header is not available.
		 *
		 * @since 2.19.8
		 * @return string The user's country code.
		 */
		public static function prepare_user_country_code() {
			static $currency_code = 'null';

			$default_currency_code = 'US'; // Default currency.
			$user_id               = get_current_user_id();

			// If user is logged in and currency is already stored.
			if ( $user_id ) {
				$stored_code = get_user_meta( $user_id, 'pse_country_code', true );
				if ( is_string( $stored_code ) && ! empty( $stored_code ) ) {
					$currency_code = sanitize_text_field( $stored_code );
					return $currency_code;
				}
			}

			// Prefer Cloudflare IP Country header if available.
			if ( isset( $_SERVER['HTTP_CF_IPCOUNTRY'] ) ) {
				$default_currency_code = sanitize_text_field( $_SERVER['HTTP_CF_IPCOUNTRY'] );

				if ( $user_id && $default_currency_code ) {
					update_user_meta( $user_id, 'pse_country_code', $default_currency_code );
					$currency_code = $default_currency_code;
					return $default_currency_code;
				}
			}

			/**
			 * Token lookup for the ipinfo.io fallback.
			 *
			 * Tokens are no longer hardcoded in source. Site owners can supply one via the
			 * `uagb_ipinfo_token` WP option or the matching filter; with no token configured,
			 * the outbound request is skipped entirely and the default currency code is returned.
			 *
			 * @since 2.19.28
			 */
			$token = apply_filters( 'uagb_ipinfo_token', get_option( 'uagb_ipinfo_token', '' ) );

			// Filters are typed `mixed` — narrow defensively. A non-string value (e.g. a hostile mu-plugin
			// returning an array) skips the outbound request entirely.
			if ( ! is_string( $token ) || '' === $token ) {
				return $default_currency_code;
			}

			$user_ip = static::get_user_ip();
			if ( ! empty( $user_ip ) ) {

				$url     = 'https://ipinfo.io/' . rawurlencode( $user_ip ) . '?token=' . rawurlencode( $token );
				$request = wp_remote_get( $url );
				if ( ! is_wp_error( $request ) && wp_remote_retrieve_response_code( $request ) === 200 ) {
					$response = json_decode( wp_remote_retrieve_body( $request ), true );

					if ( is_array( $response ) && ! empty( $response['country'] ) ) {
						$default_currency_code = sanitize_text_field( $response['country'] );
					}

					if ( $user_id ) {
						update_user_meta( $user_id, 'pse_country_code', $default_currency_code );
					}
					$currency_code = $default_currency_code;
					return $default_currency_code;
				}
			}

			return $default_currency_code;
		}

		/**
		 * Retrieves the user's IP address.
		 *
		 * This function works by following the order of preference:
		 * 1. Cloudflare's `HTTP_CF_CONNECTING_IP`.
		 * 2. `HTTP_X_FORWARDED_FOR` (first IP in case of multiple proxies).
		 * 3. `HTTP_CLIENT_IP`.
		 * 4. `REMOTE_ADDR`.
		 *
		 * @since 2.19.8
		 * @return string The user's IP address.
		 */
		public static function get_user_ip() {
			if ( ! empty( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ) {
				return sanitize_text_field( $_SERVER['HTTP_CF_CONNECTING_IP'] ); // Cloudflare real IP.
			} elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
				return explode( ',', sanitize_text_field( $_SERVER['HTTP_X_FORWARDED_FOR'] ) )[0]; // First IP in case of multiple proxies.
			} elseif ( ! empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
				return sanitize_text_field( $_SERVER['HTTP_CLIENT_IP'] );
			} elseif ( ! empty( $_SERVER['REMOTE_ADDR'] ) ) {
				return sanitize_text_field( $_SERVER['REMOTE_ADDR'] );
			}
			return '';
		}

		/**
		 * Get the user's country code and return a pricing region
		 *
		 * Returns a pricing region based on the user's country code.
		 * The pricing regions are based on the country codes.
		 * The default pricing region is 'US'.
		 *
		 * @since 2.19.8
		 * @return string The pricing region.
		 */
		public static function get_user_country_code() {
			$country_code   = self::prepare_user_country_code();
			$pricing_region = 'US'; // Default fallback.

			switch ( $country_code ) {
				case 'IN':
					$pricing_region = 'IN';
					break;

				// Add more cases as needed.

				default:
					$pricing_region = 'US';
					break;
			}

			return $pricing_region;
		}

		/**
		 * Sanitize inline css.
		 *
		 * @param string $css User-provided CSS input.
		 *
		 * @since 2.19.15
		 * @return string Sanitized CSS.
		 */
		public static function sanitize_inline_css( $css ) {
			if ( empty( $css ) || ! is_string( $css ) ) {
				return '';
			}

			// 1. Strip all HTML/Script tags.
			$css = wp_strip_all_tags( $css );
			$css = is_string( $css ) ? $css : '';

			// 2. Additional XSS prevention.
			$css = str_replace( array( '\\', '<', '&' ), '', $css );

			// 3. Context-aware XSS prevention that preserves valid CSS.
			$css = self::sanitize_css_with_context( $css );

			// Final safety check to ensure we always return a string.
			return is_string( $css ) ? $css : '';
		}

		/**
		 * Context-aware CSS sanitization that preserves quoted content.
		 *
		 * @param string $css CSS content to sanitize.
		 * @return string Sanitized CSS.
		 * @since 2.19.15
		 */
		private static function sanitize_css_with_context( $css ) {
			if ( empty( $css ) || ! is_string( $css ) ) {
				return '';
			}

			// Extract and protect quoted strings (including URLs in quotes).
			$protected_strings  = array();
			$placeholder_prefix = '___PROTECTED_STRING_';
			$counter            = 0;

			// Match quoted strings (single and double quotes).
			$result = preg_replace_callback(
				'/(["\'])((?:\\\\.|(?!\1)[^\\\\])*)(\1)/',
				function( $matches ) use ( &$protected_strings, $placeholder_prefix, &$counter ) {
					$placeholder                       = $placeholder_prefix . $counter . '___';
					$protected_strings[ $placeholder ] = $matches[0];
					$counter++;
					return $placeholder;
				},
				$css
			);
			$css    = is_string( $result ) ? $result : $css;

			// Apply XSS patterns only to unprotected (non-quoted) content.
			$xss_patterns = array(
				// Dangerous CSS functions and protocols.
				'/javascript\s*:/i',
				'/vbscript\s*:/i',
				'/data\s*:\s*[^;]*script/i',

				// CSS expressions (IE specific).
				'/expression\s*\(/i',

				// Event handlers (shouldn't be in CSS but could be injected).
				'/on\w+\s*=/i',

				// Script execution attempts.
				'/alert\s*\(/i',
				'/eval\s*\(/i',

				// @import with potentially dangerous URLs (but preserve normal @import).
				'/@import\s+[^;]*javascript\s*:/i',
				'/@import\s+[^;]*vbscript\s*:/i',
				'/@import\s+[^;]*data\s*:\s*[^;]*script/i',
			);

			foreach ( $xss_patterns as $pattern ) {
				$result = preg_replace( $pattern, '', $css );
				$css    = is_string( $result ) ? $result : $css;
			}

			// Restore protected quoted strings.
			foreach ( $protected_strings as $placeholder => $original ) {
				$result = str_replace( $placeholder, $original, $css );
				$css    = is_string( $result ) ? $result : $css;
			}

			return $css;
		}
	}

	/**
	 *  Prepare if class 'UAGB_Admin_Helper' exist.
	 *  Kicking this off by calling 'get_instance()' method
	 */
	UAGB_Admin_Helper::get_instance();
}

