<?php
namespace BricksUltimate;

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

use BricksUltimate\{Plugin, Helpers, Wishlist, Compare};
use BricksUltimate\Trait\UltimateQueryControls;

/**
 * UltimateQuery Class
 * 
 * Extends Bricks query functionality with additional query types and options.
 * Provides custom query builders for WordPress posts, WooCommerce products,
 * and integration with various plugins like ACF, MetaBox, etc.
 * 
 * @since 1.0.0
 */
class UltimateQuery {
	use UltimateQueryControls;

	/**
	 * @var int $start Starting index for query results pagination
	 */
	public $start = 0;

	/**
	 * @var int $end Ending index for query results pagination
	 */
	public $end = 0;

	/**
	 * Constructor
	 * 
	 * Sets up filters and actions for query functionality.
	 * Adds query controls to Bricks elements and registers query handlers.
	 */
	public function __construct() {
		if( Helpers::isBricksBuilderActive() ) {
			$this->register_builder_filters();
		}

		// Add Ultimate Query filter if activated
		if( $this->isActivatedUltimateQuery() ) {
			add_filter( 'bricks/query/run', [ $this, 'run_ultimate_query' ], 15, 2 );
		}

		if( class_exists( 'WooCommerce' ) ) {
			add_filter( 'bricks/query/run', [ $this, 'run_woo_query' ], 10, 2 );
		}

		add_filter( 'bricks/query/loop_object', [ $this, 'bu_loop_object' ], 10, 3 );

		// Handle pagination for query pages
		$this->setup_pagination_handler();
	}

	/**
	 * Register filters specific to Bricks Builder
	 */
	private function register_builder_filters() {
		// Add the supported fields to the Query type control
		add_filter( 'bricks/setup/control_options', [ $this, 'add_control_options' ] );
		
		// Add query controls to container elements
		$elements = ['container', 'div', 'block'];
		foreach ($elements as $element) {
			add_filter( "bricks/elements/{$element}/controls", [ $this, 'add_query_controls' ] );
		}
	}

	/**
	 * Setup pagination handler for query pages
	 */
	private function setup_pagination_handler() {
		add_action( 'bricks/render_query_page/start', function( $request_data ){
			$ori_query_element_id = $request_data['queryElementId'];
			$post_id          = $request_data['postId'];
			$page             = $request_data['page'];

			add_filter(
				'bricks/query/prepare_query_vars_from_settings',
				function( $settings, $element_id ) use ( $page, $ori_query_element_id ) {
					if ( $element_id !== $ori_query_element_id ) {
						return $settings;
					}

					$settings['query']['paged'] = $page;

					return $settings;
				},
				1000,
				2
			);
		});
	}

	/**
	 * Check if Ultimate Query is activated in plugin settings
	 * 
	 * @return bool True if Ultimate Query is enabled, false otherwise
	 */
	public function isActivatedUltimateQuery() {
		return !empty(Plugin::$bu_settings['ultimate_query']) && Plugin::$bu_settings['ultimate_query'] === 'enabled';
	}

	/**
	 * Check if Ultimate WooCommerce Query is activated in plugin settings
	 * 
	 * @return bool True if WooCommerce Query is enabled, false otherwise
	 */
	public function isActivatedUltimateWooQuery() {
		return !empty(Plugin::$bu_settings['woo_query']) && Plugin::$bu_settings['woo_query'] === 'enabled';
	}

	/**
	 * Adds custom query types for query loop
	 * 
	 * Registers Ultimate Query Builder and WooCommerce-specific query types
	 * in the Bricks query control options.
	 * 
	 * @param array $control_options The existing control options
	 * @return array Modified control options with added query types
	 */
	public function add_control_options( $control_options ) {
		if( $this->isActivatedUltimateQuery() ) {
			$control_options['queryTypes'][ 'buQuery' ] = esc_html__("Ultimate Query Builder", 'bricksultimate');
		}

		if( \Bricks\Woocommerce::is_woocommerce_active() ) {
			$control_options['queryTypes'][ 'buwooMiniCart' ] = esc_html__("Mini Cart Contents", 'bricksultimate');
			$control_options['queryTypes'][ 'buprdgallery' ] = esc_html__("Product Galleries", 'bricksultimate');

			if( $this->isActivatedUltimateWooQuery() ) {
				$control_options['queryTypes'][ 'wooQuery' ] = esc_html__("Ultimate Woo Query Builder", 'bricksultimate');
			}
		}
		
		return $control_options;
	}

	/**
	 * Helper function to add query controls to Bricks elements
	 * 
	 * Adds Ultimate Query and WooCommerce query controls to container elements
	 * when the respective features are activated.
	 * 
	 * @param array $controls The existing element controls
	 * @return array Modified controls with added query options
	 */
	public function add_query_controls( $controls ) {
		if( $this->isActivatedUltimateQuery() ) {
			$controls = $this->add_bu_query_controls( $controls );
		}

		if( \Bricks\Woocommerce::is_woocommerce_active() && $this->isActivatedUltimateWooQuery() ) {
			$controls = $this->add_woo_controls( $controls );
		}

		return $controls;
	}

	/**
	 * Sets the maximum number of pages for query pagination
	 * 
	 * Handles custom max_num_pages values for different query types.
	 * 
	 * @param int $max_num_pages The default maximum number of pages
	 * @param object $queryObj The query object
	 * @return int The modified maximum number of pages
	 */
	public function set_max_num_pages( $max_num_pages, $queryObj ) {
		$settings = $queryObj->settings;

		remove_filter( 'bricks/query/result_max_num_pages', [ $this, 'set_max_num_pages' ], 10, 2 );

		// Check if this is a supported Ultimate Query type
		$supported_types = ['customwp', 'wishlist', 'compare', 'termquery', 'mbrel'];
		if (isset($settings['buQueryTpes']) && in_array($settings['buQueryTpes'], $supported_types)) {
			return $queryObj->max_num_pages;
		}

		// Check if this is a WooCommerce query with max_num_pages set
		if (isset($settings['wooQueryTpes']) && isset($queryObj->max_num_pages)) {
			return $queryObj->max_num_pages;
		}

		return $max_num_pages;
	}

	/**
	 * Sets the start index for query results pagination
	 * 
	 * Handles custom start index values for different query types.
	 * 
	 * @param int $start The default start index
	 * @param object $queryObj The query object
	 * @return int The modified start index
	 */
	public function bu_result_start( $start, $queryObj ) {
		$settings = $queryObj->settings;
		remove_filter( 'bricks/query/result_start', [$this, 'bu_result_start'], 10, 2 );
		
		// Check if this is a supported query type that needs custom start index
		$supported_types = ['customwp', 'wishlist', 'compare', 'termquery', 'mbrel'];
		if (isset($settings['buQueryTpes']) && in_array($settings['buQueryTpes'], $supported_types)) {
			return $this->start;
		}

		return $start;
	}

	/**
	 * Sets the end index for query results pagination
	 * 
	 * Handles custom end index values for different query types.
	 * 
	 * @param int $end The default end index
	 * @param object $queryObj The query object
	 * @return int The modified end index
	 */
	public function bu_result_end( $end, $queryObj ) {
		$settings = $queryObj->settings;
		remove_filter( 'bricks/query/result_end', [$this, 'bu_result_end'], 10, 2 );
		
		// Check if this is a supported query type that needs custom end index
		$supported_types = ['customwp', 'wishlist', 'compare', 'termquery', 'mbrel'];
		if (isset($settings['buQueryTpes']) && in_array($settings['buQueryTpes'], $supported_types)) {
			return $this->end;
		}

		return $end;
	}

	/**
	 * Execute the query
	 */
	public function get_query_results( $query, $settings, $results ) {
		try {
			$recs = new \WP_Query( $query->query_vars );

			if( $recs->have_posts() ) {
				$results = $recs->posts;

				// STEP: Populate the total count
				$query->count = empty( $query->query_vars['no_found_rows'] ) ? $recs->found_posts : ( is_array( $recs->posts ) ? count( $recs->posts ) : 0 );

				// STEP : Populate the max number of pages
				$query->max_num_pages = empty( $settings['posts_per_page'] ) ? 1 : ceil( $query->count / $settings['posts_per_page'] );

				add_filter( 'bricks/query/result_max_num_pages', [ $this, 'set_max_num_pages' ], 10, 2 );

				\wp_reset_query();
			}
		} catch ( \ParseError $e ) {
			echo $e->getMessage();
		}

		return $results;
	}

	/**
	 * Run ultimate query builder loop
	 * 
	 * Handles various query types including MetaBox relationships, wishlist, compare,
	 * post years, custom WP queries, term queries, adjacent posts, author boxes,
	 * ACF galleries, ACF post objects, ACF repeaters, MetaBox image advanced fields,
	 * MetaBox cloneable fields, and WordPress menus.
	 * 
	 * @param array $results The default query results
	 * @param object $query The query object
	 * @return array The modified query results
	 */
	public function run_ultimate_query( $results, $query ) {
		// Early return if not our query type
		if ($query->object_type !== 'buQuery') {
			return $results;
		}

		$settings = $query->settings;

		// Early return if no query type specified
		if (empty($settings['buQueryTpes']) || $settings['buQueryTpes'] === 'none') {
			return $results;
		}

		$query->query_vars = array(
			'posts_per_page' 	=>  ! empty( $settings['posts_per_page'] ) ? $settings['posts_per_page'] : 10,
			'post_status' 		=> 'publish',
			'wp_grid_builder' 	=> 'wpgb-content',
			'orderby'			=> ! empty( $settings['orderby'] ) ? $settings['orderby'] : 'date',
			'order' 			=> ! empty( $settings['pyOrder'] ) ? $settings['pyOrder'] : 'DESC',
			'paged'				=> ( get_query_var('paged') ? get_query_var('paged') : ( $query->query_vars['paged'] ?? 1 ) ),
			'objectType' 		=> $query->object_type
		);

		if( $settings['buQueryTpes'] == 'mbrel' ) {

			if( empty( $settings['mbRelationId'] ) || $settings['mbRelationId'] == 'none' )
				return $results;

			$mbRelQueryType = ! empty( $settings['mbRelQueryType'] ) ? $settings['mbRelQueryType'] : 'post';
			$mbRelFecthing 	= ! empty( $settings['mbRelFecthing'] ) ? $settings['mbRelFecthing'] : 'from';
			$relationship 	= [ 'id' => $settings['mbRelationId'] ];
			$relationship[ $mbRelFecthing ] = ! empty( $settings['mbRelObjectId'] ) ? bricks_render_dynamic_data( $settings['mbRelObjectId'] ) : get_the_ID();

			if( $mbRelQueryType == 'post' ) {
				$query->query_vars['relationship'] = $relationship;

				if( ! empty( $settings['ignore_sticky_posts'] ) ) {
					$query->query_vars['ignore_sticky_posts'] = 1;
				}

				$results = $this->get_query_results( $query, $settings, $results );
			}

			return ! empty( $results ) ? $results : [];
		}

		if( $settings['buQueryTpes'] == 'wishlist' || $settings['buQueryTpes'] == 'compare' ) {
			$postType = $settings['pyPostType'] ?? 'post';

			if( $settings['buQueryTpes'] == 'wishlist' ) {
				$cookie_key = Wishlist::get_cookie_key($postType);
				$ids = Wishlist::$wishlist_ids[ $postType ] ?? [];
			} elseif( $settings['buQueryTpes'] == 'compare' ){
				$cookie_key = Compare::get_cookie_key($postType);
				$ids = Compare::$compare_ids[ $postType ] ?? [];
			}

			if( count( $ids ) <= 0 )
				return [];

			if( class_exists('WooCommerce') && $postType == 'product' )
				$postType = [ $postType, 'product_variation' ]; 

			if ( $postType === 'attachment' ) {
				$query_args['post_status'] = 'inherit';
				$query_args['post_mime_type'] = 'image';
			}

			if( ! empty( $settings['ignore_sticky_posts'] ) ) {
				$query_args['ignore_sticky_posts'] = 1;
			}

			$query->query_vars['post_type'] = $postType;
			$query->query_vars['post__in'] = $ids;

			$results = $this->get_query_results( $query, $settings, $results );

			return ! empty( $results ) ? $results : [];
		}

		if( $settings['buQueryTpes'] == 'postyear' ) {
			global $wpdb;
			$limit = $settings['yearLimit'] ?? 999;
			$postType = $settings['pyPostType'] ?? 'post';
			$order = $settings['pyOrder'] ?? 'DESC';
			$years = $wpdb->get_results(
				$wpdb->prepare(
					"SELECT YEAR(p.post_date) year 
					FROM {$wpdb->posts} p
					WHERE p.post_status = 'publish' 
					AND p.post_type = '{$postType}' 
					GROUP BY year 
					ORDER BY year {$order} 
					LIMIT 0,{$limit}"
				),
				ARRAY_N
			);

			if ( is_array( $years ) && count( $years ) > 0 ) {
				foreach ( $years as $year ) {
					$results[] = $year[0];
				}
			}
			return $results;
		}

		if( $settings['buQueryTpes'] == 'customwp' && ! empty( $settings['wp_args'] ) ) {
			$query->query_vars = [];
			$query->query_vars = eval( ' ?>' . $settings['wp_args'] );
			$query->query_vars['paged'] = ( get_query_var('paged') ? get_query_var('paged') : ( $query->query_vars['paged'] ?? 1 ) );
			$settings['posts_per_page'] = ! empty( $query->query_vars['posts_per_page'] ) ? $query->query_vars['posts_per_page'] : '';
			$results = $this->get_query_results($query, $settings, $results);

			return ! empty( $results ) ? $results : [];
		}

		if( $settings['buQueryTpes'] == 'termquery' ) {
			$tax_args = [];

			if( ! empty( $settings['tax_args'] ) ) {
				try {
					$tax_args = eval( ' ?>' . $settings['tax_args'] );
					$offset = ! empty( $tax_args['offset'] ) ? $tax_args['offset'] : 0;
					$terms_query = new \WP_Term_Query( $tax_args );
					$results = $terms_query->get_terms();

					// STEP: Populate the total count
					if ( empty( $tax_args['number'] ) ) {
						$query->count = ! empty( $results ) && is_array( $results ) ? count( $results ) : 0;
					} else {
						$number = $tax_args['number'];
						unset( $tax_args['offset'] );
						unset( $tax_args['number'] );

						// Numeric string containing the number of terms in that taxonomy or WP_Error if the taxonomy does not exist.
						$count = wp_count_terms( $tax_args );

						if ( is_wp_error( $count ) ) {
							$query->count = 0;
						} else {
							$count = (int) $count;

							$query->count = $offset <= $count ? $count - $offset : 0;
						}
					}

					// STEP : Populate the max number of pages
					$query->max_num_pages = empty( $number ) ? 1 : ceil( $count / $number );
					add_filter( 'bricks/query/result_max_num_pages', [ $this, 'set_max_num_pages' ], 10, 2 );

				} catch ( \ParseError $e ) {
					echo $e->getMessage();
				}
			}

			return ! empty( $results ) ? $results : [];
		}

		$post_id = isset( $_POST['postId'] ) ? $_POST['postId'] : ( isset( $_POST['data'] ) && is_numeric( $_POST['data'] ) ? $_POST['data'] : get_the_ID() );
		$preview_id 	= \Bricks\Helpers::get_template_setting( 'templatePreviewPostId', $post_id );

		if( $settings['buQueryTpes'] == 'adjposts' ) {
			$prevpost 		= ( isset( $settings['postPrevNext'] ) && $settings['postPrevNext'] == 'next' ) ? false : true;
			$excl_terms 	= isset( $settings['excludedTerms'] ) ? trim( $settings['excludedTerms'] ) : '';
			$in_same_term 	= ( isset( $settings['inSameTerm'] ) && $settings['inSameTerm'] == 'yes' ) ? true : false;
			$taxonomy 		= $in_same_term && isset( $settings['taxonomy'] ) ? $settings['taxonomy'] : 'category';

			if( ! empty( $preview_id ) ) {
				global $post;

				$post = get_post( $preview_id );
				setup_postdata( $post );
			}

			$adjacent_post = \get_adjacent_post( $in_same_term, $excl_terms, $prevpost, $taxonomy );

			if( ! empty( $preview_id ) ) {
				wp_reset_postdata();
			}

			return \is_a( $adjacent_post, 'WP_Post' ) ? [ $adjacent_post ] : [];
		}

		if( $settings['buQueryTpes'] == 'authbox' ) {
			global $post;

			if( ! empty( $preview_id ) ) {
				$post = get_post( $preview_id );
				setup_postdata( $post );
			}

			$post_author = get_users( array( 'include' => $post->post_auhtor, 'number' => 1 ) );

			if( ! empty( $preview_id ) ) {
				wp_reset_postdata();
			}

			return is_array( $post_author ) ? $post_author : [];
		}

		if( class_exists( 'ACF' ) ) {

			if( $settings['buQueryTpes'] == 'acfgallery' ) {
				$post_id = isset( $query->settings['isOptionsPpage'] ) ? 'option' : ( ! empty( $preview_id ) ? $preview_id : $post_id );

				$image_ids = (array) get_field( $settings['acfGalleryField'], $post_id, false );
				if( isset( $settings['galleryLimit'] ) ) {
					$slice = array_slice( (array) $image_ids, 0, $settings['galleryLimit'] );
					unset( $image_ids );
					return is_array( $slice ) ? $slice : $results;
				}
				return is_array( $image_ids ) ? $image_ids : $results;
			}

			if( $settings['buQueryTpes'] == 'acfpobj' ) {
				$post_id = isset( $settings['isOptionsPpage'] ) ? 'option' : ( ! empty( $preview_id ) ? $preview_id : $post_id );

				$acfPosts = get_field( $settings['acfField'], $post_id, false );
				if( isset( $settings['galleryLimit'] ) ) {
					$slice = array_slice((array) $acfPosts, 0, $settings['galleryLimit'] );
					unset( $acfPosts );
					return is_array( $slice ) ? $slice : $results;
				}

				if( ! empty( $acfPosts ) ) {
					return is_array( $acfPosts ) ? $acfPosts : [ $acfPosts ];
				}
			}

			if( $settings['buQueryTpes'] == 'acfcb' ) {
				$post_id = isset( $settings['isOptionsPpage'] ) ? 'option' : ( ! empty( $preview_id ) ? $preview_id : $post_id );

				$acfcbs = get_field( $settings['acfField'], $post_id );

				if( ! empty( $acfcbs ) ) {
					return is_array( $acfcbs ) ? $acfcbs : [ $acfcbs ];
				}
			}
		}

		if( $settings['buQueryTpes'] == 'mbImgAdv' && class_exists( 'RWMB_Loader' ) ) {
			$post_id = ( ! empty( $preview_id ) ? $preview_id : $post_id );
			$image_ids = get_post_meta( $post_id, $settings['mbImgAdvField'], true );

			if( ! empty( $image_ids ) && isset( $settings['mbgLimit'] ) ) {
				$slice = array_slice((array)$image_ids[0], 0, $settings['mbgLimit'] );
				unset( $image_ids );
				return is_array( $slice ) ? $slice : $results;
			}
 
			return is_array( $image_ids ) ? $image_ids[0] : $results;
		}

		if( $settings['buQueryTpes'] == 'mbcbl' && function_exists( 'rwmb_meta' ) && isset( $settings['mbFieldId'] )) {
			$post_id = ( ! empty( $preview_id ) ? $preview_id : $post_id );
			$cbList = rwmb_meta( $settings['mbFieldId'], '', $post_id );
 
			return is_array( $cbList ) ? $cbList : $results;
		}

		if( $settings['buQueryTpes'] == 'wpmenu' && isset( $settings['menuSlug'] ) ) {
			$menu = wp_get_nav_menu_object( $settings['menuSlug'] );

			if ( ! $menu ) {
				return $results;
			}

			if ( ! taxonomy_exists( 'nav_menu' ) ) {
				return $results;
			}

			$defaults = array(
				'order'                  => 'ASC',
				'orderby'                => 'menu_order',
				'post_type'              => 'nav_menu_item',
				'post_status'            => 'publish',
				'output'                 => ARRAY_A,
				'output_key'             => 'menu_order',
				'nopaging'               => true,
				'update_menu_item_cache' => true,
				'tax_query'              => array(
					array(
						'taxonomy' => 'nav_menu',
						'field'    => 'term_taxonomy_id',
						'terms'    => $menu->term_taxonomy_id,
					),
				),
			);

			$args = [];
			if( isset( $settings['itemInclude'] ) ) {
				$args['include'] = explode(',', bricks_render_dynamic_data( $settings['itemInclude'] ));
			}

			if( isset( $settings['itemExclude'] ) ) {
				$args['exclude'] = explode(',', bricks_render_dynamic_data( $settings['itemExclude'] ));
			}

			if( isset( $settings['itemParent'] ) ) {
				$args['meta_query'] = [
					[
						'key' 		=> '_menu_item_menu_item_parent',
						'value' 	=> bricks_render_dynamic_data( $settings['itemParent'] ),
						'compare' 	=> "=",
						'type' 		=> 'NUMERIC'
					]
				];
			}

			if( $menu->count > 0 ) {
				$args = apply_filters( 'bu_menu_loop_args', $args, $query );
				$args = wp_parse_args( $args, $defaults );
				$menu_items = array_map( 'wp_setup_nav_menu_item', get_posts( $args ) );
				
				_wp_menu_item_classes_by_context( $menu_items );

				$sorted_menu_items        = array();
				$menu_items_with_children = array();
				foreach ( (array) $menu_items as $menu_item ) {
					if ( (string) $menu_item->ID === (string) $menu_item->menu_item_parent ) {
						$menu_item->menu_item_parent = 0;
					}

					$sorted_menu_items[ $menu_item->menu_order ] = $menu_item;
					if ( $menu_item->menu_item_parent ) {
						$menu_items_with_children[ $menu_item->menu_item_parent ] = true;
					}
				}

				// Add the menu-item-has-children class where applicable.
				if ( $menu_items_with_children ) {
					foreach ( $sorted_menu_items as &$menu_item ) {
						if ( isset( $menu_items_with_children[ $menu_item->ID ] ) ) {
							$menu_item->classes[] = 'menu-item-has-children';
						}
					}
				}
				unset( $menu_items, $menu_item, $menu_items_with_children );
				return array_values($sorted_menu_items);
			}
		}

		return $results;
	}

	/**
	 * Run WooCommerce query builder loop
	 * 
	 * Handles various WooCommerce query types including mini cart contents, product galleries,
	 * and custom WooCommerce queries like bank accounts, customer orders, order items, coupons,
	 * related products, recently viewed products, top-rated products, best-selling products,
	 * featured products, on-sale products, upsells, and cross-sells.
	 * 
	 * @param array $results The default query results
	 * @param object $query The query object
	 * @return array The modified query results
	 */
	public function run_woo_query( $results, $query ) {
		if( $query->object_type == 'buwooMiniCart' && method_exists('WC_Cart', 'get_cart') ) {
			return ! is_null( WC()->cart ) ? WC()->cart->get_cart() : [];
		}

		if( $query->object_type == 'buprdgallery' ) {
			$post_id = isset( $_POST['postId'] ) ? $_POST['postId'] : ( isset( $_POST['data'] ) && is_numeric( $_POST['data'] ) ? $_POST['data'] : get_the_ID() );
			$preview_id = \Bricks\Helpers::get_template_setting( 'templatePreviewPostId', $post_id );
			$post_id = ( ! empty( $preview_id ) ? $preview_id : $post_id );

			$product = wc_get_product( $post_id );

			if( $product === false ) {
				return $results;
			}

			$gallery_image_ids = $product->get_gallery_image_ids();
			$get_variations = count( $product->get_children() ) <= apply_filters( 'woocommerce_ajax_variation_threshold', 30, $product );
			if( $get_variations && $product->get_type() == 'variable') {
				$available_variations = $product->get_available_variations();
				foreach ($available_variations as $key => $variation) { 
					if( ! in_array( $variation['image_id'], $gallery_image_ids ) )
						$gallery_image_ids[] = $variation['image_id'];
				}
			}
			
			if( $product->get_image_id() ) {
				array_unshift( $gallery_image_ids, $product->get_image_id() );
			}

			$image_ids = array_unique( $gallery_image_ids );

			return is_array( $image_ids ) ? $image_ids : $results;
		}

		if( $query->object_type != 'wooQuery' ) {
			return $results;
		}

		$settings = $query->settings;

		if( empty( $settings['wooQueryTpes'] ) || $settings['wooQueryTpes'] == 'none' ){
			return $results;
		}

		if( $settings['wooQueryTpes'] == 'bacs' ) {
			return get_option( 'woocommerce_bacs_accounts' );
		}

		if( $settings['wooQueryTpes'] == 'corders' ) {
			$order_args = [];
			if( isset( $settings['ordersCustomer'] ) && $settings['ordersCustomer'] == 'all' ) {
				$order_args['type'] = 'shop_order';
				$order_args['limit'] = 20;
				$order_args['orderby'] = 'date';
			} elseif( isset( $settings['ordersCustomer'] ) && $settings['ordersCustomer'] == 'custom' && 
				! empty( $settings['customCustomer'] ) ) {
				$order_args['customer'] = bricks_render_dynamic_data( $settings['customCustomer'] );
			} elseif( is_user_logged_in () && isset( $settings['ordersCustomer'] ) && $settings['ordersCustomer'] == 'logged_in' ) {
				$order_args['customer'] = get_current_user_id();
			} else {
				return $results;
			}

			global $wp;

			$post_type = ['shop_order', 'shop_order_refund'];

			if( isset( $settings['ordersType'] ) && 'all' != $settings['ordersType'] )
				$post_type = $settings['ordersType'];

			$order_args['type'] = $post_type;
			$order_args['status'] = isset( $settings['ordersStatus'] ) ? $settings['ordersStatus'] : array_keys( wc_get_order_statuses() );

			$current_page = isset( $wp->query_vars['orders'] ) && ! empty( $wp->query_vars['orders'] ) ? absint( $wp->query_vars['orders'] ) : ( $query->query_vars['paged'] ?? 1 );
			$order_args['limit'] = ! empty( $settings['ordersLimit'] ) ? $settings['ordersLimit'] : 10;
			$order_args['paginate'] = true;
			$order_args['page'] = $current_page;

			$order_args['order'] = ! empty( $settings['order'] ) ? $settings['order'] : 'DESC';

			$customer_orders = wc_get_orders(
				apply_filters(
					'woocommerce_my_account_my_orders_query',
					$order_args
				)
			);

			if( $customer_orders->total > 0 ) {
				$results = $customer_orders->orders;

				// STEP: Populate the total count
				$query->count = $customer_orders->total;

				// STEP : Populate the max number of pages
				$query->max_num_pages = empty( $order_args['limit'] ) ? 1 : ceil( $query->count / $order_args['limit'] );

				add_filter( 'bricks/query/result_max_num_pages', [ $this, 'set_max_num_pages' ], 10, 2 );

				$GLOBALS['totalOrdersPages'] = $query->max_num_pages;
			}

			return ! empty( $results ) ? $results : [];
		}

		if( $settings['wooQueryTpes'] == 'corditem' ) {
			$orderId = ( ! empty( $GLOBALS['orderID'] ) ? $GLOBALS['orderID'] : ( ! empty( $settings['orderId'] ) ? bricks_render_dynamic_data( $settings['orderId'] ) : false ) );
			
			if( ! $orderId )
				return $results;
			
			$order = wc_get_order( $orderId );
			
			if ( ! $order ) {
				return $results;
			}
			
			$items = $order->get_items( apply_filters( 'woocommerce_purchase_order_item_types', 'line_item' ) );
			
			return $items;
		}

		if( $settings['wooQueryTpes'] == 'coupons' ) {
			$args = [
				//'fields' 			=> 'ids',
				'post_type' 		=> 'shop_coupon',
				'post_status' 		=> 'publish',
				'posts_per_page' 	=> ! empty( $settings['productsNumber'] ) ? $settings['productsNumber'] : 3,
				'order' 			=> ! empty( $settings['order'] ) ? $settings['order'] : 'DESC',
				'meta_query' 		=> array(),
				'tax_query' 		=> array(
					'relation' => 'AND',
				),
				'paged' 			=> ( get_query_var('paged') ? get_query_var('paged') : ( $query->query_vars['paged'] ?? 1 ) )
			];

			if( isset( $settings['expiry_coupons'] ) || isset( $settings['exclude_expiry_coupons'] ) ) {
				$args['meta_query'] = [
					'relation' => 'OR',
					[
						'key' 	=> 'expiry_date',
						'value' => time(),
						'compare' => '<',
						'type' 	=> 'numeric'
					],
					[
						'key' 	=> 'date_expires',
						'value' => time(),
						'compare' => '<',
						'type' 	=> 'numeric'
					]
				];
			}

			if( isset( $settings['exclude_expiry_coupons'] ) ) {
				$args['fields'] = 'ids';
				$args['nopaging'] = true;
				$args['posts_per_page'] = -1;
				$expired_ids = [];
				$expireCoupons = new \WP_Query( $args );
				if( $expireCoupons->have_posts() ) {
					$expired_ids = $expireCoupons->posts;
					\wp_reset_query();
				}

				unset( $expireCoupons );
				unset( $args['fields'] );
				unset( $args['nopaging'] );
				$args['meta_query'] = [];
				$args['posts_per_page'] = ! empty( $settings['productsNumber'] ) ? $settings['productsNumber'] : 3;
				$args['post__not_in'] = $expired_ids;
			}

			$coupons = new \WP_Query( $args );
			if( $coupons->have_posts() ) {
				$results = $coupons->posts;

				// STEP: Populate the total count
				$query->count = is_array( $coupons->posts ) ? $coupons->found_posts : 0;

				// STEP : Populate the max number of pages
				$query->max_num_pages = empty( $args['posts_per_page'] ) ? 1 : ceil( $query->count / $args['posts_per_page'] );

				add_filter( 'bricks/query/result_max_num_pages', [ $this, 'set_max_num_pages' ], 10, 2 );

				\wp_reset_query();
			}

			return ! empty( $results ) ? $results : [];
		}

		$query_args = array(
			'no_found_rows' 	=> 1,
			'posts_per_page' 	=>  ! empty( $settings['productsNumber'] ) ? $settings['productsNumber'] : 3,
			'post_status' 		=> 'publish',
			'post_type' 		=> [ 'product', 'product_variation' ],
			'order' 			=> ! empty( $settings['order'] ) ? $settings['order'] : 'DESC',
			'meta_query' 		=> array(),
			'tax_query' 		=> array(
				'relation' => 'AND',
			),
		); // WPCS: slow query ok.

		if( $settings['wooQueryTpes'] == 'product' ) {
			$query_args['post_status'] = $settings['productStatus'] ?? ['publish'];

			//* Settings: WP Grid Builder
			$query_args['wp_grid_builder'] = 'wpgb-content';
			
			//* Settings: pagination
			$query_args['paged'] = ( get_query_var('paged') ? get_query_var('paged') : ( $query->query_vars['paged'] ?? 1 ) );
			unset( $query_args['no_found_rows'] );

			$type = [];

			if( empty( $settings['productType'] ) )
				$settings['productType'] = ['all'];

			if( in_array( 'variation', $settings['productType'] ) ) {
				$vkey = array_search( 'variation', $settings['productType'] );
				unset( $settings['productType'][ $vkey ] );
				$type[] = 'product_variation';
				$settings['show_hidden'] = 'yes';
			}

			if( ! in_array( 'all', $settings['productType'] ) && count( $settings['productType'] ) > 0) {
				$prdtypes = array_diff( array_keys( \wc_get_product_types() ), $settings['productType'] );
				$query_args['tax_query'][] = array(
					'taxonomy' => 'product_type',
					'field'    => 'slug',
					'terms'    => $prdtypes,
					'operator' => 'NOT IN',
				);
				unset( $prdtypes);
			}

			if( ! empty( $settings['productType'] ) && !in_array( 'variation', $settings['productType'] ) ) {
				$type[] = 'product';
			}

			$query_args['post_type'] = $type;
			unset( $type );

			//* Settings: Include products
			if ( ! empty( $settings['postParent'] ) ) {
				$post_parent = bricks_render_dynamic_data( $settings['postParent'] );

				if ( strpos( $post_parent, ',' ) !== false ) {
					$post_parent = explode( ',', $post_parent );

					$query_args['post_parent__in'] = (array) $post_parent;
					unset( $query_vars['post_parent'] );
				} else {
					$query_args['post_parent'] = (int) $post_parent;
				}
			}

			//* Settings: Include products
			if ( ! empty( $settings['include'] ) ) {
				$query_args['post__in'] = $settings['include'];
			}

			//* Settings: Exclude products
			if ( ! empty( $settings['exclude'] ) ) {
				$query_args['post__not_in'] = $settings['exclude'];
			}

			if( isset( $settings['productMeta'] ) ) {
				$query_args['meta_query']['relation'] = 'AND';
				foreach( $settings['productMeta'] as $meta ) {
					$value = bricks_render_dynamic_data( $meta['metaValue'] );
					
					if( strpos( $value, ',' ) != false ) {
						$value = explode( ",", $value );
					}

					$query_args['meta_query'][] = [
						'key' 		=> $meta['metaKey'],
						'value' 	=> $value,
						'compare' 	=> $meta['compare'] ?? '=',
						'type' 		=> $meta['type'] ?? 'CHAR',
					];
				}
			}
		}

		if( $settings['wooQueryTpes'] == 'related' || $settings['wooQueryTpes'] == 'upsells' || $settings['wooQueryTpes'] == 'csells' ) {
			$post_id = isset( $_POST['postId'] ) ? $_POST['postId'] : ( isset( $_POST['data'] ) && is_numeric( $_POST['data'] ) ? $_POST['data'] : get_the_ID() );

			$product = false;
			$preview_id = isset( \Bricks\Database::$page_data['preview_or_post_id'] ) ? \Bricks\Database::$page_data['preview_or_post_id'] : $post_id;
			$product = wc_get_product( $preview_id );
		}

		if( $settings['wooQueryTpes'] == 'related' ) {
			if( $product ) {
				$query_args['post__in']	= \wc_get_related_products( $product->get_id(), $query_args['posts_per_page'], $product->get_upsell_ids() );
				$settings['order_by'] = 'post_in';
			}
		}

		if( $settings['wooQueryTpes'] == 'recently' ) {
			$viewed_products = ! empty( $_COOKIE['woocommerce_recently_viewed'] ) ? (array) explode( '|', wp_unslash( $_COOKIE['woocommerce_recently_viewed'] ) ) : array(); // @codingStandardsIgnoreLine
			$viewed_products = array_reverse( array_filter( array_map( 'absint', $viewed_products ) ) );

			if ( empty( $viewed_products ) && ! Helpers::isBricksBuilderActive() ) {
				return [];
			}

			if ( ! empty( $viewed_products ) && ! Helpers::isBricksBuilderActive() ) {
				$query_args['post__in']	= $viewed_products;
				$settings['order_by'] = 'post_in';
				unset( $settings['order'] );
			}
		}

		if( $settings['wooQueryTpes'] == 'toprated' ) {
			unset( $query_args['meta_query'] );

			$query_args['tax_query'] = WC()->query->get_tax_query();

			$settings['order_by'] = 'toprated';
			$settings['order'] = 'DESC';
		}

		if( $settings['wooQueryTpes'] == 'bestsell' ) {
			$query_args['order'] = 'DESC';
			$query_args['meta_query'] = WC()->query->get_meta_query();
			$query_args['tax_query'] = WC()->query->get_tax_query();

			$settings['order_by'] = 'sales';
		}

		if( $settings['wooQueryTpes'] == 'featured' ) {
			$product_visibility_term_ids = wc_get_product_visibility_term_ids();
			$query_args['tax_query'][] = [
				'taxonomy' => 'product_visibility',
				'field'    => 'term_taxonomy_id',
				'terms'    => $product_visibility_term_ids['featured'],
			];
		}

		if( $settings['wooQueryTpes'] == 'onsale' ) {
			$product_ids_on_sale    = \wc_get_product_ids_on_sale();
			$product_ids_on_sale[]  = 0;
			$query_args['post__in'] = $product_ids_on_sale;
		}

		if( $settings['wooQueryTpes'] == 'upsells' ) {
			$upsells = ( $product !== false ) ? array_map( 'absint', $product->get_upsell_ids() ) : false;

			if( ! empty( $upsells ) ) {
				$query_args['post__in'] = $upsells;
			} elseif( Helpers::isBricksBuilderActive() && empty( $upsells ) ) {
				//* show latest products for preview
				$upsells = [];
				$products = wc_get_products( [ 'limit' => ( ! empty( $settings['productsNumber'] ) ? $settings['productsNumber'] : 3 ) ] );

				if ( $products ) {
					foreach ( $products as $product ) {
						$upsells[] = $product->get_id();
					}
					$query_args['post__in'] = $upsells;
				}
			} else {
				return [];
			}
		}

		if( $settings['wooQueryTpes'] == 'csells' ) {
			$csells = ( $product !== false ) ? $product->get_cross_sell_ids() : ( ! is_null( WC()->cart ) ? WC()->cart->get_cross_sells() : '' );

			if( ! empty( $csells ) ) {
				$query_args['post__in'] = array_map( 'absint', $csells );
			} elseif( Helpers::isBricksBuilderActive() && empty( $csells ) ) {
				//* show latest products for preview
				$csells = [];
				$products = wc_get_products( [ 'limit' => ( ! empty( $settings['productsNumber'] ) ? $settings['productsNumber'] : 3 ) ] );

				if ( $products ) {
					foreach ( $products as $product ) {
						$csells[] = $product->get_id();
					}
					$query_args['post__in'] = $csells;
				}
			} else {
				return [];
			}
		}

		$woo_products = new \WP_Query( self::generate_wc_query_args( $query_args, $settings ) );

		if( $woo_products->have_posts() ) {
			$results = $woo_products->posts;

			// STEP: Populate the total count
			$query->count = ! empty( $results ) ? $woo_products->found_posts : 0;

			// STEP : Populate the max number of pages
			$query->max_num_pages = empty( $settings['productsNumber'] ) ? 1 : ceil( $query->count / $settings['productsNumber'] );

			add_filter( 'bricks/query/result_max_num_pages', [ $this, 'set_max_num_pages' ], 10, 2 );

			\wp_reset_query();
		}

		return ! empty( $results ) ? $results : [];
	}

	/**
	 * Modifies the loop object for different query types
	 * 
	 * Processes the loop object based on the query type, setting up post data
	 * for various query types including ACF galleries, MetaBox image advanced fields,
	 * ACF post objects, wishlist items, compare items, MetaBox relationships,
	 * custom WP queries, ACF checkboxes, and MetaBox checkbox lists.
	 * 
	 * @param mixed $loop_object The current loop object
	 * @param int $loop_key The current loop key/index
	 * @param object $query The query object
	 * @return mixed The modified loop object
	 */
	public function bu_loop_object( $loop_object, $loop_key, $query ) {
		if( isset( $query->settings['buQueryTpes'] ) && ( $query->settings['buQueryTpes'] == 'acfgallery' || $query->settings['buQueryTpes'] == 'mbImgAdv' || $query->settings['buQueryTpes'] == 'acfpobj' || $query->settings['buQueryTpes'] == 'wishlist' || $query->settings['buQueryTpes'] == 'compare' || $query->settings['buQueryTpes'] == 'mbrel' || $query->settings['buQueryTpes'] == 'customwp' ) ) {

			global $post;

			$post = get_post( $loop_object );
			setup_postdata( $post );

			return $post;
		}

		if( isset( $query->settings['buQueryTpes'] ) && $query->settings['buQueryTpes'] == 'acfcb' ) {
			return $loop_object;
		}

		if( isset( $query->settings['buQueryTpes'] ) && $query->settings['buQueryTpes'] == 'mbcbl' ) {
			return $loop_object . '||' . $query->settings['mbFieldId'];
		}

		if( $query->object_type == 'wooQuery' && isset( $query->settings['wooQueryTpes'] ) 
			&& ( $query->settings['wooQueryTpes'] == 'corditem' || $query->settings['wooQueryTpes'] == 'bacs' ) ) {
			return $loop_object;
		}

		if( $query->object_type == 'wooQuery' ) {
			global $post;

			$post = get_post( $loop_object->ID );
			setup_postdata( $post );
		}

		if( $query->object_type == 'buprdgallery' || $query->object_type == 'fbtQuery' ) {
			global $post;

			$post = get_post( $loop_object );
			setup_postdata( $post );

			return $post;
		}

		if ( $query->object_type == 'buwooMiniCart' ) {
			$_product = is_array( $loop_object ) ? $loop_object['data'] : $loop_object;
			$product_id = apply_filters( 'woocommerce_cart_item_product_id', $_product->get_id(), $loop_object, $loop_key );

			if ( $_product && $_product->exists() /*&& $cart_item['quantity'] > 0
				&& apply_filters( 'woocommerce_widget_cart_item_visible', true, $cart_item, $loop_key ) */
			) {
				global $post;
				$post = get_post( $product_id );
				setup_postdata( $post );
				
				return $post;
			}
		}

		return $loop_object;
	}

	/**
	 * Generates WooCommerce query arguments
	 * 
	 * Creates a WP_Query arguments array for WooCommerce products based on provided settings.
	 * Handles various product filtering options including categories, visibility, stock status,
	 * free products, and sorting options.
	 * 
	 * @param array $query_args Base query arguments
	 * @param array $config Configuration options for the query
	 * @return array The modified query arguments
	 */
	public static function generate_wc_query_args( $query_args, $config ) {
		$product_visibility_term_ids = wc_get_product_visibility_term_ids();

		$outofstock 	= ! empty( $config['outofstock'] ) ? $config['outofstock'] : 'yes';
		$show_hidden 	= ! empty( $config['show_hidden'] ) ? $config['show_hidden'] : 'no';
		$hide_free 		= ! empty( $config['hide_free'] ) ? $config['hide_free'] : 'yes';
		$orderby 		= ! empty( $config['order_by'] ) ? $config['order_by'] : 'date';

		if( ! empty( $config['category_ids'] ) ) {
			$categories 	= explode( ',', $config['category_ids'] );
			$categories 	= array_map( 'absint', $categories );
			$cat_operator 	= ! empty( $config['cat_operator'] ) ? $config['cat_operator'] : 'IN';
			$query_args['tax_query'][] = [
				'taxonomy'         => 'product_cat',
				'terms'            => $categories,
				'field'            => 'term_taxonomy_id',
				'operator'         => $cat_operator,
				'include_children' => 'AND' === $cat_operator ? false : true,
			];
		}

		if ( $show_hidden == 'no' ) {
			$query_args['tax_query'][] = array(
				'taxonomy' => 'product_visibility',
				'field'    => 'term_taxonomy_id',
				'terms'    => is_search() ? $product_visibility_term_ids['exclude-from-search'] : $product_visibility_term_ids['exclude-from-catalog'],
				'operator' => 'NOT IN',
			);
			$query_args['post_parent'] = 0;
		}

		if ( $hide_free == 'yes' ) {
			$query_args['meta_query'][] = array(
				'key'     => '_price',
				'value'   => 0,
				'compare' => '>',
				'type'    => 'DECIMAL',
			);
		}

		if ( 'yes' === $outofstock ) {
			$query_args['tax_query'][] = array(
				array(
					'taxonomy' => 'product_visibility',
					'field'    => 'term_taxonomy_id',
					'terms'    => $product_visibility_term_ids['outofstock'],
					'operator' => 'NOT IN',
				),
			); // WPCS: slow query ok.
		}

		switch ( $orderby ) {
			case 'price':
				$query_args['meta_key'] = '_price'; // WPCS: slow query ok.
				$query_args['orderby']  = 'meta_value_num';
				break;
			case 'post_in':
				$query_args['orderby'] = 'post__in';
				break;
			case 'rand':
				$query_args['orderby'] = 'rand';
				break;
			case 'title':
				$query_args['orderby'] = 'title';
				break;
			case 'name':
				$query_args['orderby'] = 'name';
				break;
			case 'ID':
				$query_args['orderby'] = 'ID';
				break;
			case 'sales':
				$query_args['meta_key'] = 'total_sales'; // WPCS: slow query ok.
				$query_args['orderby']  = 'meta_value_num';
				break;
			case 'toprated':
				$query_args['meta_key'] = '_wc_average_rating'; // WPCS: slow query ok.
				$query_args['orderby']  = 'meta_value_num';
				break;
			default:
				$query_args['orderby'] = 'date';
		}

		return $query_args;
	}

	/**
	 * Retrieves Meta Box relationship IDs for dropdown options
	 * 
	 * Gets all available Meta Box relationships and formats them for use in a dropdown
	 * selection in the Bricks Builder interface.
	 * 
	 * @return array An array of relationship IDs with their titles as options
	 */
	public static function getMBRelstionshipIds() {
		$relIds = ['none' => esc_html__('Select relationship ID')];

		if( Helpers::isBricksBuilderActive() ) {
			$relationships = get_posts([
				'post_type' 		=> 'mb-relationship', 
				'posts_per_page' 	=> -1, 
				'post_status' 		=> 'publish'
			]);
			
			if( $relationships ) {
				foreach ( $relationships as $relpost ):
					$relid = get_post_meta( $relpost->ID, 'relationship', true );
					if( $relid ) {
						$relIds[ $relid['id'] ] = esc_attr( $relid['menu_title'] );
					}
				endforeach;
			}
		}

		return $relIds;
	}
}