<?php
/**
 * Add an element to fusion-builder.
 *
 * @package fusion-builder
 * @since 3.8
 */

if ( ! class_exists( 'AWB_Woo_Filters' ) ) {
	/**
	 * Shortcode class.
	 *
	 * @since 3.8
	 */
	class AWB_Woo_Filters extends Fusion_Component {
		/**
		 * The one, true instance of this object.
		 *
		 * @static
		 * @access private
		 * @since 3.3
		 * @var object
		 */
		private static $instance;

		/**
		 * The counter.
		 *
		 * @access private
		 * @since 3.8
		 * @var int
		 */
		private $element_counter = 1;

		/**
		 * Whether we are requesting from editor.
		 *
		 * @access public
		 * @since 3.8
		 * @var bool
		 */
		protected $live_request = false;

		/**
		 * Cache for value.
		 *
		 * @var mixed
		 */
		protected $original_queried_object;

		/**
		 * Cache for value.
		 *
		 * @var bool
		 */
		protected $original_is_tax;

		/**
		 * Cache for value.
		 *
		 * @var bool
		 */
		protected $original_is_archive;

		/**
		 * Term map cache for product brands.
		 *
		 * @var array
		 */		
		private $term_map = null;

		/**
		 * Constructor.
		 *
		 * @access public
		 * @since 3.8
		 */
		public function __construct() {
			parent::__construct( $this->shortcode_handle );

			add_filter( "fusion_attr_{$this->shortcode_handle}-shortcode", [ $this, 'attr' ] );

			// Ajax mechanism for query related part.
			add_action( "wp_ajax_get_{$this->shortcode_handle}", [ $this, 'ajax_render' ] );

			// Prevent WooCommerce canonical category archive redirects during direct AJAX filter clicks.
			add_action( 'pre_get_posts', [ $this, 'intercept_ajax_category_query' ], 1 );
		}

		/**
		 * Check if component should render
		 *
		 * @access public
		 * @since 3.3
		 * @return boolean
		 */
		public function should_render() {
			return is_shop() || is_product_taxonomy();
		}

		/**
		 * Creates or returns an instance of this class.
		 *
		 * @static
		 * @access public
		 * @since 3.8
		 */
		public static function get_instance() {
			static $instances = [];

			$called_class = get_called_class();

			if ( ! isset( $instances[ $called_class ] ) ) {
				$instances[ $called_class ] = new $called_class();
			}

			return $instances[ $called_class ];
		}

		/**
		 * Gets the default values.
		 *
		 * @static
		 * @access public
		 * @since 3.8
		 * @return array
		 */
		public static function get_element_defaults() {
			$fusion_settings = awb_get_fusion_settings();
			return [
				'hide_on_mobile'                 => fusion_builder_default_visibility( 'string' ),
				'class'                          => '',
				'id'                             => '',
				'title'                          => '',
				'title_size'                     => '4',
				'margin_bottom'                  => '',
				'margin_left'                    => '',
				'margin_right'                   => '',
				'margin_top'                     => '',
				'fusion_font_family_title_font'  => '',
				'fusion_font_variant_title_font' => '',
				'title_font_size'                => '',
				'title_line_height'              => '',
				'title_letter_spacing'           => '',
				'title_text_transform'           => '',
				'title_color'                    => $fusion_settings->get( 'h4_typography', 'color' ),
				'collapsible_color'              => $fusion_settings->get( 'h4_typography', 'color' ),
				'show_title'                     => 'yes',
				'is_collapsible'                 => 'no',
				'collapsed_on_load'              => 'no',
				'title_margin_bottom'            => '',
				'title_margin_top'               => '',
				'animation_type'                 => '',
				'animation_direction'            => 'down',
				'animation_speed'                => '0.1',
				'animation_delay'                => '',
				'animation_offset'               => $fusion_settings->get( 'animation_offset' ),
			];
		}

		/**
		 * Gets the widget instance options.
		 *
		 * @static
		 * @access public
		 * @since 3.8
		 * @return array
		 */
		public function get_widget_instance() {
			return [
				'title' => $this->args['title'],
			];
		}

		/**
		 * Validate the arguments into correct format.
		 *
		 * @access public
		 * @since 3.8
		 * @return void
		 */
		public function validate_args() {
			// Validate margin values.
			foreach ( [ 'top', 'bottom' ] as $direction ) {
				$margin_key                = 'title_margin_' . $direction;
				$this->args[ $margin_key ] = fusion_library()->sanitize->get_value_with_unit( $this->args[ $margin_key ] );
			}

			if ( ! $this->is_default( 'title_font_size' ) ) {
				$this->args['title_font_size'] = fusion_library()->sanitize->get_value_with_unit( $this->args['title_font_size'] );
			}

			if ( ! $this->is_default( 'title_letter_spacing' ) ) {
				$this->args['title_letter_spacing'] = fusion_library()->sanitize->get_value_with_unit( $this->args['title_letter_spacing'] );
			}

			if ( ! $this->is_default( 'title_color' ) ) {
				$this->args['title_color'] = fusion_library()->sanitize->color( $this->args['title_color'] );
			}
		}

		/**
		 * Intercepts the product_cat query variable during direct AJAX filtering on the Shop page.
		 * Moves it into the tax_query and unsets the main variable, preventing WordPress from
		 * treating the request as a Category Archive (which would break sidebar widgets).
		 * 
		 * @access public
		 * @since 3.11.1
		 * @param WP_Query $query The WP_Query instance.
		 */
		public function intercept_ajax_category_query( $query ) {
			if ( ! is_admin() && $query->is_main_query() && isset( $_SERVER['HTTP_X_REQUESTED_WITH'] ) && 'XMLHttpRequest' === $_SERVER['HTTP_X_REQUESTED_WITH'] ) {
				
				// Verify that AJAX filtering features are explicitly enabled.
				$ajax_filters = fusion_library()->get_option( 'woocommerce_ajax_filters' );
				$ajax_categories = fusion_library()->get_option( 'woocommerce_ajax_categories' );
				
				if ( 'no' === $ajax_filters || 'filter_directly' !== $ajax_categories ) {
					return;
				}

				// Only intercept if product_cat is present and we are querying products.
				$product_cat = $query->get( 'product_cat' );
				$post_type   = $query->get( 'post_type' );
				
				if ( ! empty( $product_cat ) && ( 'product' === $post_type || ( is_array( $post_type ) && in_array( 'product', $post_type ) ) || $query->get( 'wc_query' ) === 'product_query' ) ) {
					
					// Get existing tax_query
					$tax_query = $query->get( 'tax_query' );
					if ( ! is_array( $tax_query ) ) {
						$tax_query = [];
					}
					
					// Append our category filter
					$tax_query[] = [
						'taxonomy' => 'product_cat',
						'field'    => 'slug',
						'terms'    => explode( ',', $product_cat ),
						'operator' => 'IN',
					];
					
					// Apply and remove the native query var to prevent is_tax/is_archive flags
					$query->set( 'tax_query', $tax_query );
					$query->set( 'product_cat', '' );
					unset( $query->query_vars['product_cat'] );
					
					// Force it to be treated strictly as a shop/post-type archive
					$query->is_tax              = false;
					$query->is_archive          = true;
					$query->is_post_type_archive= true;
				}
			}
		}

		/**
		 * Gets the query data.
		 *
		 * @static
		 * @access public
		 * @since 3.8
		 * @param array $args An array of args.
		 * @return void
		 */
		public function ajax_render( $args ) {
			check_ajax_referer( 'fusion_load_nonce', 'fusion_load_nonce' );
			$return_data = [];

			// From Ajax Request.
			if ( isset( $_POST['model'] ) && ! apply_filters( 'fusion_builder_live_request', false ) ) { // phpcs:ignore WordPress.Security.NonceVerification
				$this->args         = wp_unslash( $_POST['model']['params'] ); // phpcs:ignore WordPress.Security
				$this->live_request = true;
				add_filter( 'fusion_builder_live_request', '__return_true' );
			}
			do_action( 'awb_before_filter_element_render' );
			$this->emulate_filter();
			$return_data['output']        = $this->get_filter_elements();
			$return_data['msg']           = ( isset( $this->args['attribute'] ) && '' === $this->args['attribute'] ) ? esc_html__( 'Please select attribute.', 'fusion-builder' ) : '';
			$return_data['extra_classes'] = $this->add_extra_classes();
			$this->restore_filter();
			do_action( 'awb_after_filter_element_render' );
			echo wp_json_encode( $return_data );
			wp_die();
		}

		/**
		 * Render the shortcode.
		 *
		 * @access public
		 * @since 3.8
		 * @param  array  $args    Shortcode parameters.
		 * @param  string $content Content between shortcode.
		 * @return string          HTML output
		 */
		public function render( $args, $content = '' ) {
			global $post;

			$this->defaults = static::get_element_defaults();
			$this->args     = FusionBuilder::set_shortcode_defaults( $this->defaults, $args, $this->shortcode_handle );

			$this->validate_args();

			do_action( 'awb_before_filter_element_render' );
			$this->emulate_filter();
			$html = trim( $this->get_filter_elements() );
			$this->restore_filter();
			do_action( 'awb_after_filter_element_render' );

			$is_builder = ( function_exists( 'fusion_is_preview_frame' ) && fusion_is_preview_frame() ) || ( function_exists( 'fusion_is_builder_frame' ) && fusion_is_builder_frame() );

			if ( ! empty( $html ) || ( 'no' !== fusion_library()->get_option( 'woocommerce_ajax_filters' ) && ! $is_builder ) ) {
				$html = '<div ' . FusionBuilder::attributes( $this->shortcode_handle . '-shortcode' ) . '>' . $html . '</div>';
			}

			$this->element_counter++;

			$this->on_render();

			return apply_filters( $this->shortcode_handle . '_content', $html, $args, $this->shortcode_handle );
		}

		/**
		 * Builds HTML for filter elements.
		 *
		 * @since 3.8
		 * @return string
		 */
		public function get_filter_elements() {
			$content        = '';
			$widgets        = [
				'fusion_tb_woo_filters_active'     => 'WC_Widget_Layered_Nav_Filters',
				'fusion_tb_woo_filters_price'      => 'WC_Widget_Price_Filter',
				'fusion_tb_woo_filters_rating'     => 'WC_Widget_Rating_Filter',
				'fusion_tb_woo_filters_brands'     => 'WC_Widget_Brand_Nav',
				'fusion_tb_woo_filters_categories' => 'WC_Widget_Product_Categories',
			];
			$widget         = isset( $widgets[ $this->shortcode_handle ] ) ? $widgets[ $this->shortcode_handle ] : 'WC_Widget_Layered_Nav';
			$widget         = isset( $this->args['attribute'] ) && 'product_brand' === $this->args['attribute'] ? 'WC_Widget_Brand_Nav' : $widget;
			$title_tag      = 'div' === $this->args['title_size'] ? 'div' : 'h' . $this->args['title_size'];
			$toggle         = '';
			$replace_anchor = '';

			if ( 'yes' === $this->args['is_collapsible'] ) {
				if ( 'yes' === $this->args['collapsed_on_load'] ) {
					$toggle         = '<button type="button" class="awb-toggle-filter awb-rotate" aria-expanded="false" aria-label="' . esc_attr__( 'Toggle Filter', 'fusion-builder' ) . '"><i class="awb-icon-angle-up"></i></button>';
					$replace_anchor = '<span class="awb-replace-anchor"></span>';
				} else {
					$toggle = '<button type="button" class="awb-toggle-filter" aria-expanded="true" aria-label="' . esc_attr__( 'Toggle Filter', 'fusion-builder' ) . '"><i class="awb-icon-angle-up"></i></button>';
				}
			}

			$args = [
				'before_title' => '<div class="heading"><' . $title_tag . ' class="widget-title">',
				'after_title'  => '</' . $title_tag . '>' . $toggle . '</div>' . $replace_anchor,
			];

			ob_start();
			the_widget( $widget, $this->get_widget_instance(), $args );
			$content .= ob_get_clean();

			if ( 'yes' === $this->args['is_collapsible'] && 'yes' === $this->args['collapsed_on_load'] ) {
				$content = preg_replace( '/(<span class="awb-replace-anchor"><\/span>\s*)(<[^>]+)/', '$1$2 style="display:none;"', $content, 1 );
				$content = str_replace( '<span class="awb-replace-anchor"></span>', '', $content );
			}

			$content = apply_filters( $this->shortcode_handle . '_widget_content', $content, $this->args, $this->shortcode_handle );

			return $content;
		}

		/**
		 * Emulate filter for LE.
		 *
		 * @access public
		 * @since 3.8
		 * @return void
		 */
		public function emulate_filter() {
			global $post;

			$option = isset( $post->ID ) ? fusion_get_page_option( 'dynamic_content_preview_type', $post->ID ) : '';
			$value  = 'product_cat';

			if ( $this->live_request ) {
				if ( isset( $_POST['fusion_meta'] ) && isset( $_POST['post_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput
					$meta   = fusion_string_to_array( $_POST['fusion_meta'] ); // phpcs:ignore WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput
					$option = is_array( $meta['_fusion'] ) && isset( $meta['_fusion']['dynamic_content_preview_type'] ) && in_array( $meta['_fusion']['dynamic_content_preview_type'], [ 'search', 'archives', 'term' ], true ) ? $meta['_fusion']['dynamic_content_preview_type'] : '';
				}
			}

			if ( ( ( fusion_is_preview_frame() || $this->live_request ) && in_array( $option, [ 'search', 'archives', 'term' ], true ) ) ) {
				$term_args = [
					'taxonomy'   => $value,
					'hide_empty' => true,
					'number'     => 1,
				];
				$terms     = get_terms( $term_args );

				$this->original_queried_object = $GLOBALS['wp_query']->queried_object;
				$this->original_is_tax         = $GLOBALS['wp_query']->is_tax;
				$this->original_is_archive     = $GLOBALS['wp_query']->is_archive;

				// Re-index array.
				if ( is_array( $terms ) && ! empty( $terms ) ) {
					$terms = array_values( $terms );

					$GLOBALS['wp_query']->is_tax               = true;
					$GLOBALS['wp_query']->is_archive           = true;
					$GLOBALS['wp_query']->queried_object       = $terms[0];
					$GLOBALS['wp_query']->query_vars[ $value ] = $terms[0]->slug;

					if ( 0 === $GLOBALS['wp_query']->post_count ) {
						$GLOBALS['wp_query']->post_count = 1;
					}

					$this->emulate_filter_element();
				}
			}
		}

		/**
		 * Restore filter for LE.
		 *
		 * @access public
		 * @since 3.8
		 * @return void
		 */
		public function restore_filter() {
			global $post;

			$option = isset( $post->ID ) ? fusion_get_page_option( 'dynamic_content_preview_type', $post->ID ) : '';
			$value  = isset( $post->ID ) ? fusion_get_page_option( 'preview_' . $option, $post->ID ) : '';

			if ( $this->live_request ) {
				if ( isset( $_POST['fusion_meta'] ) && isset( $_POST['post_id'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput
					$meta   = fusion_string_to_array( $_POST['fusion_meta'] ); //phpcs:ignore WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput
					$option = is_array( $meta['_fusion'] ) && isset( $meta['_fusion']['dynamic_content_preview_type'] ) && in_array( $meta['_fusion']['dynamic_content_preview_type'], [ 'search', 'archives', 'term' ], true ) ? $meta['_fusion']['dynamic_content_preview_type'] : $option;
					$value  = is_array( $meta['_fusion'] ) && isset( $meta['_fusion'][ 'preview_' . $option ] ) ? $meta['_fusion'][ 'preview_' . $option ] : $value;
				}
			}

			if ( ( ( fusion_is_preview_frame() || $this->live_request ) && in_array( $option, [ 'search', 'archives', 'term' ], true ) ) ) {
				// Restore global data.
				$GLOBALS['wp_query']->is_tax         = $this->original_is_tax;          // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
				$GLOBALS['wp_query']->is_archive     = $this->original_is_archive;      // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
				$GLOBALS['wp_query']->queried_object = $this->original_queried_object;  // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited

				$this->restore_filter_element();
			}
		}

		/**
		 * Emulate filter element for LE.
		 *
		 * @access public
		 * @since 3.8
		 * @return void
		 */
		public function emulate_filter_element() {}

		/**
		 * Restore filter element for LE.
		 *
		 * @access public
		 * @since 3.8
		 * @return void
		 */
		public function restore_filter_element() {}

		/**
		 * Add extra classes to the element div.
		 *
		 * @access public
		 * @since 3.8.1
		 * @return string
		 */
		public function add_extra_classes() {
			return '';
		}

		/**
		 * Builds the attributes array.
		 *
		 * @access public
		 * @since 3.8
		 * @return array
		 */
		public function attr() {

			$attr = fusion_builder_visibility_atts(
				$this->args['hide_on_mobile'],
				[
					'class'         => 'awb-woo-filters awb-woo-filters-' . esc_attr( str_replace( 'fusion_tb_woo_filters_', '', $this->shortcode_handle ) ) . ' awb-woo-filters-' . esc_attr( $this->element_counter ),
					'style'         => '',
					'data-instance' => esc_attr( $this->element_counter ),
				]
			);

			if ( 'yes' === $this->args['is_collapsible'] && 'yes' === $this->args['collapsed_on_load'] ) {
				$attr['class'] .= ' awb-load-collapsed';
			}

			if ( $this->args['animation_type'] ) {
				$attr = Fusion_Builder_Animation_Helper::add_animation_attributes( $this->args, $attr );
			}

			$attr['style'] .= $this->get_style_variables( [] );

			if ( $this->args['class'] ) {
				$attr['class'] .= ' ' . $this->args['class'];
			}

			if ( $this->args['id'] ) {
				$attr['id'] = $this->args['id'];
			}

			if ( 'yes' !== $this->args['show_title'] ) {
				$attr['class'] .= ' hide-show_title';
			}

			return $attr;
		}

		/**
		 * Get the style variables.
		 *
		 * @access protected
		 * @since 3.8
		 * @param  array $custom_vars The custom CSS vars array.
		 * @return array
		 */
		protected function get_style_variables( $custom_vars ) {

			// Title Typo.
			$custom_vars = $this->get_typo_variables(
				[
					'title_font'           => 'font',
					'title_font_size'      => 'size',
					'title_line_height'    => 'line_height',
					'title_letter_spacing' => 'letter_spacing',
					'title_text_transform' => 'text_transform',
					'title_color'          => 'color',
				],
				$custom_vars
			);

			$custom_vars['collapsible_color'] = fusion_library()->sanitize->color( $this->args['collapsible_color'] );

			// Validate margin values.
			foreach ( [ 'top', 'bottom' ] as $direction ) {
				$margin_key = 'title_margin_' . $direction;

				if ( ! $this->is_default( $margin_key ) ) {
					$custom_vars[ 'title-margin-' . $direction ] = $this->args[ $margin_key ];
				}
			}

			$margin_sides = [
				'margin_top',
				'margin_right',
				'margin_bottom',
				'margin_left',
			];

			foreach ( $margin_sides as $margin_side ) {
				if ( isset( $this->args[ $margin_side ] ) && $this->args[ $margin_side ] ) {
					$custom_vars[ $margin_side ] = fusion_library()->sanitize->get_value_with_unit( $this->args[ $margin_side ] );
				}
			}

			return $custom_vars;
		}

		/**
		 * Get the typography variables.
		 *
		 * @access protected
		 * @since 3.8
		 * @param array $params      The parameters.
		 * @param array $custom_vars The custom CSS vars array.
		 * @return array
		 */
		protected function get_typo_variables( $params, $custom_vars ) {

			foreach ( $params as $key => $type ) {

				switch ( $type ) {
					case 'font':
						$font_styles = Fusion_Builder_Element_Helper::get_font_styling( $this->args, $key, 'array' );

						foreach ( $font_styles as $rule => $value ) {
							$custom_vars[ $key . '_' . $rule ] = $value;
						}
						break;

					default:
						if ( ! $this->is_default( $key ) ) {
							$custom_vars[ $key ] = $this->args[ $key ];
						}
						break;
				}
			}

			return $custom_vars;
		}

		/**
		 * Get the dimension variables.
		 *
		 * @access protected
		 * @since 3.8
		 * @param string $option_name  The option name.
		 * @param array  $custom_vars  The custom CSS vars array.
		 * @return array
		 */
		protected function get_dimension_variables( $option_name, $custom_vars ) {
			foreach ( [ 'top', 'right', 'bottom', 'left' ] as $direction ) {
				$key = $option_name . '_' . $direction;

				if ( ! $this->is_default( $key ) ) {
					$custom_vars[ $key ] = $this->args[ $key ];
				}
			}
			return $custom_vars;
		}

		/**
		 * Load base CSS.
		 *
		 * @access public
		 * @since 3.8
		 * @return void
		 */
		public function add_css_files() {
			FusionBuilder()->add_element_css( FUSION_BUILDER_PLUGIN_DIR . 'assets/css/components/woo-filters.min.css' );
		}

		/**
		 * Sets the necessary scripts.
		 *
		 * @access public
		 * @since 3.3
		 * @return void
		 */
		public function on_first_render() {
			if ( class_exists( 'WooCommerce' ) && ( is_shop() || is_product_taxonomy() ) ) {
				Fusion_Dynamic_JS::enqueue_script(
					'fusion-js-woo-filters',
					FusionBuilder::$js_folder_url . '/general/fusion-woo-filters.js',
					FusionBuilder::$js_folder_path . '/general/fusion-woo-filters.js',
					[ 'jquery' ],
					FUSION_BUILDER_VERSION,
					true
				);

				$ajax_mode = fusion_library()->get_option( 'woocommerce_ajax_filters' );

				Fusion_Dynamic_JS::localize_script(
					'fusion-js-woo-filters',
					'fusionWooFiltersVars',
					[
						'woo_ajax_filters'    => $ajax_mode,
					]
				);

				if ( 'no' !== $ajax_mode ) {
					Fusion_Dynamic_JS::enqueue_script(
						'fusion-js-woo-filters-ajax',
						FusionBuilder::$js_folder_url . '/general/fusion-woo-filters-ajax.js',
						FusionBuilder::$js_folder_path . '/general/fusion-woo-filters-ajax.js',
						[ 'jquery', 'fusion-js-fusion_post_cards' ],
						FUSION_BUILDER_VERSION,
						true
					);

					if ( 'submit' === $ajax_mode ) {
						Fusion_Dynamic_JS::enqueue_script(
							'fusion-js-woo-filters-ajax-submit',
							FusionBuilder::$js_folder_url . '/general/fusion-woo-filters-ajax-submit.js',
							FusionBuilder::$js_folder_path . '/general/fusion-woo-filters-ajax-submit.js',
							[ 'fusion-js-woo-filters-ajax' ],
							FUSION_BUILDER_VERSION,
							true
						);

						Fusion_Dynamic_JS::localize_script(
							'fusion-js-woo-filters-ajax-submit',
							'fusionWooFiltersAjaxVars',
							[
								'woo_ajax_categories' => fusion_library()->get_option( 'woocommerce_ajax_categories' ),
							]
						);
					} else {
						Fusion_Dynamic_JS::enqueue_script(
							'fusion-js-woo-filters-ajax-instant',
							FusionBuilder::$js_folder_url . '/general/fusion-woo-filters-ajax-instant.js',
							FusionBuilder::$js_folder_path . '/general/fusion-woo-filters-ajax-instant.js',
							[ 'fusion-js-woo-filters-ajax' ],
							FUSION_BUILDER_VERSION,
							true
						);

						Fusion_Dynamic_JS::localize_script(
							'fusion-js-woo-filters-ajax-instant',
							'fusionWooFiltersAjaxVars',
							[
								'woo_ajax_categories' => fusion_library()->get_option( 'woocommerce_ajax_categories' ),
							]
						);
					}

					Fusion_Dynamic_JS::localize_script(
						'fusion-js-woo-filters-ajax',
						'fusionWooFiltersAjaxVars',
						[
							'woo_ajax_filters'    => $ajax_mode,
							'woo_ajax_sorting'    => fusion_library()->get_option( 'woocommerce_ajax_sorting' ),
							'woo_ajax_categories' => fusion_library()->get_option( 'woocommerce_ajax_categories' ),
						]
					);
				}
			}
		}

		/**
		 * Fetch general options.
		 *
		 * @access public
		 * @since 3.8
		 * @return array
		 */
		public function fetch_general_options() {
			return [
				[
					'type'        => 'radio_button_set',
					'heading'     => esc_attr__( 'Collapsible Filter', 'fusion-builder' ),
					'description' => esc_attr__( 'Choose if the filter should be collapsible.', 'fusion-builder' ),
					'param_name'  => 'is_collapsible',
					'default'     => 'no',
					'value'       => [
						'yes' => esc_attr__( 'Yes', 'fusion-builder' ),
						'no'  => esc_attr__( 'No', 'fusion-builder' ),
					],				
				],
				[
					'type'        => 'radio_button_set',
					'heading'     => esc_attr__( 'Collapsed On Page Load', 'fusion-builder' ),
					'description' => esc_attr__( 'Choose if the filter should be collapsed on page load.', 'fusion-builder' ),
					'param_name'  => 'collapsed_on_load',
					'default'     => 'no',
					'value'       => [
						'yes' => esc_attr__( 'Yes', 'fusion-builder' ),
						'no'  => esc_attr__( 'No', 'fusion-builder' ),
					],
					'dependency'  => [
						[
							'element'  => 'is_collapsible',
							'value'    => 'yes',
							'operator' => '=',
						],
					],					
				],				
				[
					'type'        => 'radio_button_set',
					'heading'     => esc_attr__( 'Show Title', 'fusion-builder' ),
					'description' => esc_attr__( 'Choose if title should show or not.', 'fusion-builder' ),
					'param_name'  => 'show_title',
					'default'     => 'yes',
					'value'       => [
						'yes' => esc_attr__( 'Yes', 'fusion-builder' ),
						'no'  => esc_attr__( 'No', 'fusion-builder' ),
					],
				],
				[
					'type'        => 'textfield',
					'heading'     => esc_attr__( 'Title', 'fusion-builder' ),
					'param_name'  => 'title',
					'value'       => esc_html__( 'Title', 'fusion-builder' ),
					'description' => esc_attr__( 'Add the title that will display on element.', 'fusion-builder' ),
					'callback'    => [
						'function' => 'fusion_ajax',
						'action'   => "get_{$this->shortcode_handle}",
						'ajax'     => true,
					],
					'dependency'  => [
						[
							'element'  => 'show_title',
							'value'    => 'no',
							'operator' => '!=',
						],
					],
				],
				[
					'type'        => 'radio_button_set',
					'heading'     => esc_attr__( 'Title Tag', 'fusion-builder' ),
					'description' => esc_attr__( 'Choose HTML tag of the title, either div or the heading tag, h1-h6.', 'fusion-builder' ),
					'param_name'  => 'title_size',
					'value'       => [
						'1'   => 'H1',
						'2'   => 'H2',
						'3'   => 'H3',
						'4'   => 'H4',
						'5'   => 'H5',
						'6'   => 'H6',
						'div' => 'DIV',
					],
					'default'     => '4',
					'callback'    => [
						'function' => 'fusion_ajax',
						'action'   => "get_{$this->shortcode_handle}",
						'ajax'     => true,
					],
					'dependency'  => [
						[
							'element'  => 'show_title',
							'value'    => 'no',
							'operator' => '!=',
						],
					],
				],
				[
					'type'        => 'checkbox_button_set',
					'heading'     => esc_attr__( 'Element Visibility', 'fusion-builder' ),
					'param_name'  => 'hide_on_mobile',
					'value'       => fusion_builder_visibility_options( 'full' ),
					'default'     => fusion_builder_default_visibility( 'array' ),
					'description' => esc_attr__( 'Choose to show or hide the element on small, medium or large screens. You can choose more than one at a time.', 'fusion-builder' ),
				],
				[
					'type'        => 'textfield',
					'heading'     => esc_attr__( 'CSS Class', 'fusion-builder' ),
					'description' => esc_attr__( 'Add a class to the wrapping HTML element.', 'fusion-builder' ),
					'param_name'  => 'class',
					'value'       => '',
				],
				[
					'type'        => 'textfield',
					'heading'     => esc_attr__( 'CSS ID', 'fusion-builder' ),
					'description' => esc_attr__( 'Add an ID to the wrapping HTML element.', 'fusion-builder' ),
					'param_name'  => 'id',
					'value'       => '',
				],
			];
		}

		/**
		 * Fetch design options.
		 *
		 * @access public
		 * @since 3.8
		 * @return array
		 */
		public function fetch_design_options() {
			$fusion_settings = awb_get_fusion_settings();
			return [
				[
					'type'             => 'dimension',
					'remove_from_atts' => true,
					'heading'          => esc_attr__( 'Margin', 'fusion-builder' ),
					'description'      => esc_attr__( 'In pixels or percentage, ex: 10px or 10%.', 'fusion-builder' ),
					'param_name'       => 'margin',
					'value'            => [
						'margin_top'    => '',
						'margin_right'  => '',
						'margin_bottom' => '',
						'margin_left'   => '',
					],
					'group'            => esc_attr__( 'Design', 'fusion-builder' ),
				],
				[
					'type'             => 'typography',
					'heading'          => esc_attr__( 'Title Typography', 'fusion-builder' ),
					'description'      => esc_html__( 'Controls the typography of the title. Leave empty for the global font family.', 'fusion-builder' ),
					'param_name'       => 'title_fonts',
					'choices'          => [
						'font-family'    => 'title_font',
						'font-size'      => 'title_font_size',
						'text-transform' => 'title_text_transform',
						'line-height'    => 'title_line_height',
						'letter-spacing' => 'title_letter_spacing',
						'color'          => 'title_color',
						'margin-top'     => 'title_margin_top',
						'margin-bottom'  => 'title_margin_bottom',
					],
					'default'          => [
						'font-family'    => '',
						'variant'        => '400',
						'font-size'      => '',
						'text-transform' => '',
						'line-height'    => '',
						'letter-spacing' => '',
						'color'          => $fusion_settings->get( 'h4_typography', 'color' ),
						'margin-top'     => '',
						'margin-bottom'  => '',
					],
					'remove_from_atts' => true,
					'global'           => true,
					'group'            => esc_attr__( 'Design', 'fusion-builder' ),
					'dependency'       => [
						[
							'element'  => 'show_title',
							'value'    => 'no',
							'operator' => '!=',
						],
					],
				],
				[
					'type'        => 'colorpickeralpha',
					'heading'     => esc_attr__( 'Collapse Arrow Color', 'fusion-builder' ),
					'description' => esc_attr__( 'Controls the color of vollapse arrow.', 'fusion-builder' ),
					'param_name'  => 'collapsible_color',
					'value'       => '',
					'default'     => $fusion_settings->get( 'h4_typography', 'color' ),
					'group'       => esc_html__( 'Design', 'fusion-builder' ),
					'dependency'  => [
						[
							'element'  => 'is_collapsible',
							'value'    => 'yes',
							'operator' => '=',
						],
					],
				],
			];
		}

		/**
		 * Get element subparams.
		 *
		 * @access public
		 * @since 3.8
		 * @return array
		 */
		public function get_element_subparams() {
			return [
				'fusion_font_family_title_font'  => 'title_fonts',
				'fusion_font_variant_title_font' => 'title_fonts',
				'title_font_size'                => 'title_fonts',
				'title_line_height'              => 'title_fonts',
				'title_letter_spacing'           => 'title_fonts',
				'title_text_transform'           => 'title_fonts',
				'title_color'                    => 'title_fonts',
			];
		}

		/**
		 * Get element params.
		 *
		 * @access public
		 * @since 3.8
		 * @return array
		 */
		public function get_element_params() {
			$general_options = $this->fetch_general_options();
			$design_options  = $this->fetch_design_options();

			$params = [];

			foreach ( $general_options as $general_opt ) {
				$params[] = $general_opt;
			}

			foreach ( $design_options as $design_opt ) {
				$params[] = $design_opt;
			}

			$params['fusion_conditional_render_placeholder'] = [];

			$params['fusion_animation_placeholder'] = [
				'preview_selector' => '.awb-woo-filters',
			];

			return $params;
		}

		/**
		 * Gets the brand term map.
		 *
		 * @access public
		 * @since 3.15.0
		 * @return array The term map.
		 */
		public function get_brand_term_map() {
			if ( null !== $this->term_map ) {
				return $this->term_map;
			}

			$terms = get_terms( [
				'taxonomy'   => 'product_brand',
				'hide_empty' => false,
			] );

			if ( is_wp_error( $terms ) ) {
				$this->term_map = [];
				return $this->term_map;
			}

			$this->term_map = $terms;

			foreach ( $terms as $term ) {
				$this->term_map[ strtolower( $term->name ) ] = $term;
			}

			return $this->term_map;
		}

		/**
		 * Adjusts the widget content, by injecting brand thumbnails, brand name and/or count.
		 *
		 * @access public
		 * @since 3.15.0
		 * @param string $content The Widget content.
		 * @param array $args The element args.
		 * @param string $element_name The name of the current element.
		 * @return string The adjusted widget content.
		 */
		public function adjust_woo_brand_filter_html( $content, $args, $element_name ) {
			$is_attribute_filter = 'fusion_tb_woo_filters_attribute' === $element_name && isset( $args['attribute'] ) && 'product_brand' === $args['attribute'];
			$is_brand_filter     = 'fusion_tb_woo_filters_brands' === $element_name && isset( $this->args['columns' ] );

			if ( ( $is_attribute_filter || $is_brand_filter ) && class_exists( 'DOMDocument' ) && function_exists( 'libxml_use_internal_errors' ) ) {

				if ( false === strpos( $content, '<a' ) ) {
					return $content;
				}

				$term_map = $this->get_brand_term_map();

				if ( empty( $term_map ) ) {
					return $content;
				}

				libxml_use_internal_errors( true );

				$dom = new DOMDocument();
				$dom->loadHTML(
					'<div id="awb-root">' . $content . '</div>',
					LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD
				);

				$xpath   = new DOMXPath( $dom );
				$anchors = $xpath->query( '//a' );

				// Loop through all anchors.
				foreach ( $anchors as $a ) {

					$label = trim( $a->textContent );
					if ( ! $label ) {
						continue;
					}

					// Find existing count span if it exists outside.
					$count_node = null;
					$next       = $a->nextSibling;

					// Skip text nodes (whitespace).
					while ( $next && XML_TEXT_NODE === $next->nodeType ) {
						$next = $next->nextSibling;
					}

					if ( $next instanceof DOMElement && 'span' === $next->tagName && 'count' === $next->getAttribute( 'class' ) ) {
						$count_node = $next;
					}

					// Wrap image and label together.
					$attr_wrapper = $dom->createElement( 'span' );
					$attr_wrapper->setAttribute( 'class', 'awb-woo-attr-wrapper' );

					$display_thumbs = $is_attribute_filter ? 'yes' === $this->args['display_brand_thumbs'] : true;
					if ( $display_thumbs ) {
						$key      = strtolower( $label );
						$img_html = '';

						if ( ! empty( $term_map[ $key ] ) ) {
							$img_html = wc_get_brand_thumbnail_image( $term_map[ $key ] );
						}

						if ( $img_html ) {
							$img_frag = $dom->createDocumentFragment();
							@$img_frag->appendXML( $img_html );
							$attr_wrapper->appendChild( $img_frag );
						}
					}

					$display_names = $is_brand_filter ? 'yes' === $this->args['display_names'] : true;
					if ( $display_names ) {
						$label_span = $dom->createElement( 'span' );
						$label_span->setAttribute( 'class', 'awb-brand-label' );
						$label_span->appendChild( $dom->createTextNode( $label ) );
						$attr_wrapper->appendChild( $label_span );
					}

					// Clear existing anchor contents.
					while ( $a->firstChild ) {
						$a->removeChild( $a->firstChild );
					}

					// Inject the wrapper.
					$a->appendChild( $attr_wrapper );

					// Include count into anchor if present and enabled.
					$display_count = $is_brand_filter ? 'yes' === $this->args['display_count'] : true;
					if ( $count_node ) {
						if ( $display_count ) {
							$a->appendChild( $dom->createTextNode( ' ' ) );
							$a->appendChild( $count_node );
						} else if ( $count_node->parentNode ) {
							$count_node->parentNode->removeChild( $count_node );
						}
					}
				}

				$root = $dom->getElementById( 'awb-root' );

				$output = '';
				foreach ( $root->childNodes as $child ) {
					$output .= $dom->saveHTML( $child );
				}

				return $output;
			}

			return $content;
		}
	}
}
