<?php

defined( 'ABSPATH' ) || exit;

/**
 * Get cart table columns
 *
 * @return Array
 *
 */
function dswcp_get_cart_columns(){

	return apply_filters( 'dswcp_cart_columns', array(
		'remove' 	=> '',
		'thumbnail' => '',
		'name' 		=> esc_html__( 'Product', 'woocommerce' ),
		'price' 	=> esc_html__( 'Price', 'woocommerce' ),
		'quantity' 	=> esc_html__( 'Quantity', 'woocommerce' ),
		'subtotal' 	=> esc_html__( 'Subtotal', 'woocommerce' ),
	) );

}

/**
 * Get decoded icon character
 *
 * @return String
 *
 */
function dswcp_decoded_et_icon( $icon ){
	//return '\\'.str_replace( ';', '', str_replace( '&#x', '', html_entity_decode( et_pb_process_font_icon( $icon ) ) ) );
	return str_replace( ';', '', str_replace( '&#x', '', html_entity_decode( et_pb_process_font_icon( $icon ) ) ) );
}


/**
 * Get if the endpoint is valid accoount type
 *
 */
function dswcp_is_account_endpoint( $type = '' ){
	return ( ( !empty( $type ) && is_wc_endpoint_url( $type ) ) || ( $type === '' && is_account_page() ) ) && $type === WC()->query->get_current_endpoint();
}

// phpcs:disable WordPress.Security.NonceVerification -- read-only operation
if ( isset($_POST['ags_wc_filters_product_counts']) ) {
	
	add_action('wp', function() {
		class AGS_WC_Shortcode_Products_Count_Simulator extends WC_Shortcode_Products {
			protected $wpq;

			function __construct($attributes=[], $type='products') {
				parent::__construct(
					array_merge(
						$attributes,
						[
							'cache' => false,
							'paginate' => false
						]
					)
				);
				$this->wpq = new WP_Query();
			}

			function getSimulatedCount($filter=null, $value=null) {
				
				if ($filter) {
					if (isset($_GET[$filter])) {
						// phpcs:ignore ET.Sniffs.ValidatedSanitizedInput.InputNotSanitized -- value is not used, only saved for later restoration
						$originalValue = $_GET[$filter];
					}

					$_GET[$filter] = $value;
				}

				dscwp_ags_filters_hooks();
				add_filter('posts_fields', [$this, 'filterQueryFields'], 9999);
				add_filter('posts_groupby', '__return_empty_string', 9999);
				$result = current($this->wpq->query(
					array_merge(
						apply_filters( 'woocommerce_shortcode_products_query', $this->query_args, $this->attributes, $this->type ),
						['fields' => 'ids']
					)
				));

				dscwp_ags_filters_hooks_remove();
				remove_filter('posts_fields', [$this, 'filterQueryFields'], 9999);
				remove_filter('posts_groupby', '__return_empty_string', 9999);

				if (isset($originalValue)) {
					$_GET[$filter] = $originalValue;
				} else if ($filter) {
					unset($_GET[$filter]);
				}

				return (int) $result;
			}

			function filterQueryFields($fields) {
				global $wpdb;
				return 'COUNT(DISTINCT '.$wpdb->posts.'.ID)';
			}
		}
		
		// phpcs:ignore ET.Sniffs.ValidatedSanitizedInput.InputNotValidated -- false positive, isset() check wraps this line
		$request = json_decode( sanitize_text_field( wp_unslash($_POST['ags_wc_filters_product_counts']) ) );

		$result = [];
		$simulator = new AGS_WC_Shortcode_Products_Count_Simulator();

		foreach ($request as $countRequest) {
			if ( $countRequest->value != 'all' ) {
				$count = $simulator->getSimulatedCount( $countRequest->filter, $countRequest->value );
			} else {
				if (!isset($allCount)) {
					$allCount = $simulator->getSimulatedCount();
				}
				$count = $allCount;
			}

			$result[] = [
				'parent' => $countRequest->parent,
				'count' => $count
			];
		}

		wp_send_json($result);
	});
	
}
// phpcs:enable WordPress.Security.NonceVerification

add_filter( 'ags_woo_shop_plus_before_print_shop', 'dscwp_ags_filters_before_shop', 10, 3 );
add_filter( 'ags_woo_shop_html', 'dscwp_ags_filters_shop_output', 10, 2 );
	
// phpcs:ignore WordPress.Security.NonceVerification.Missing -- just a read-only flag
if ( isset($_POST['ags_wc_filters_ajax_shop']) ) {
	ob_start();
	add_filter('wp_redirect', 'dswcp_handle_axax_shop_redirect', 999);
}
if ( !empty($_POST['ags_wc_filters_ajax_notices']) ) {
	add_filter('ags_woo_notices_html', 'dswcp_ags_filters_notices_output', 10, 2);
}

add_filter( 'save_post', 'dswcp_ags_filters_whitelist_filters', 10, 2 );
add_filter( 'wp_ajax_ags_wc_filters_search_suggestions', 'dswcp_ags_filters_search_suggestions' );
add_filter( 'wp_ajax_nopriv_ags_wc_filters_search_suggestions', 'dswcp_ags_filters_search_suggestions' );
add_filter( 'et_global_assets_list', 'dswcp_ags_filters_maybe_add_full_icons' );

function dswcp_ags_filters_notices_output($noticesHtml, $orderClass) {
	if (in_array($orderClass, $_POST['ags_wc_filters_ajax_notices'])) {
		global $dswcp_ajax_notices_html, $dswcp_ajax_shop_html;
		
		if (!isset($dswcp_ajax_notices_html)) {
			$dswcp_ajax_notices_html = [];
		}
		
		$dswcp_ajax_notices_html[$orderClass] = $noticesHtml;
		
		if ( !empty($dswcp_ajax_shop_html) && $orderClass == end($_POST['ags_wc_filters_ajax_notices']) ) {
			dswcp_ags_filters_ajax_output();
		}
	}
}

function dswcp_ags_filters_ajax_output() {
	if (isset($_REQUEST['add-to-cart'])) {
		add_filter('woocommerce_add_to_cart_fragments', '_dswcp_ags_filters_ajax_output', 999999);
		WC_Ajax::get_refreshed_fragments();
	} else {
		_dswcp_ags_filters_ajax_output();
	}
}

function _dswcp_ags_filters_ajax_output($wcFragments=null) {
	ob_end_clean();
	
	global $dswcp_ajax_notices_html, $dswcp_ajax_shop_html;
	
	$response = [
		'shop' => isset($dswcp_ajax_shop_html) ? $dswcp_ajax_shop_html : [],
		'notices' => isset($dswcp_ajax_notices_html) ? $dswcp_ajax_notices_html : []
	];
	
	if ($wcFragments) {
		$response['wc-fragments'] = $wcFragments;
		$response['wc-cart-hash'] = WC()->cart->get_cart_hash();
	}
	
	wp_send_json($response);
}

function dswcp_handle_axax_shop_redirect($url) {
	if ($url) {
		ob_end_clean();
		wp_send_json(['dswcpRedirect' => $url]);
	}
}

function dswcp_ags_filters_whitelist_filters($postId, $post) {
	$allowedFilters = [];
	
	if (strpos($post->post_content, '[ags_woo_products_filters_child') !== false) {
		
		preg_match_all(
			'/'.get_shortcode_regex(['ags_woo_products_filters_child']).'/',
			$post->post_content,
			$shortcodeMatches
		);
		
		foreach ($shortcodeMatches[0] as $shortcode) {
			
			if (preg_match('/\\schoose\\_filter="([[:alpha:]\\_]+)"/', $shortcode, $filterTypeMatch)) {
				
				switch ( $filterTypeMatch[1] ) {
					case 'category':
						$allowedFilters[] = 'shopCategory';
						break;
					case 'tag':
						$allowedFilters[] = 'shopTag';
						break;
					case 'attribute':
						if (preg_match('/\\sattribute="(.+)"/U', $shortcode, $attributeMatch)) {
							$allowedFilters[] = 'shopAttribute_'.$attributeMatch[1];
						}
						break;
					case 'search':
						$allowedFilters[] = 'shopSearch';
						break;
					case 'rating':
						$allowedFilters[] = 'shopRating';
						break;
					case 'price':
						$allowedFilters[] = 'shopPrice';
						break;
					case 'stock_status':
						$allowedFilters[] = 'shopStockStatus';
						break;
					case 'sale':
						$allowedFilters[] = 'shopSale';
						break;
				}
			}
		}
		
	}
	
	if ($allowedFilters) {
		update_post_meta($post->ID, '_ags_wc_filters_allowed', $allowedFilters);
	} else {
		delete_post_meta($post->ID, '_ags_wc_filters_allowed');
	}
}
	
function dscwp_ags_filters_before_shop($shouldRender, $props, $orderClass) {
	// phpcs:ignore WordPress.Security.NonceVerification.Missing -- just a flag to enable some hooks
	if ( isset($_POST['ags_wc_filters_ajax_shop']) ) {
		if ( $orderClass != $_POST['ags_wc_filters_ajax_shop'] ) {
			return false;
		}
		add_filter( 'woocommerce_pagination_args', 'dswcp_ags_filters_strip_unwanted_pagination_url_params', 999 );
		if ($props['filter_target'] == 'on') {
			dscwp_ags_filters_hooks();
		}
	}
	
	if ($props['filter_target'] == 'on') {
		add_filter( 'term_link', 'dswcp_ags_filters_transform_category_urls', 10, 2 );
		add_filter( 'term_links-product_cat', 'dswcp_ags_filters_transform_category_links' );
	}
	
	return $shouldRender;
}

function dscwp_ags_filters_hooks() {
	// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only operation
	if ( !empty($_GET['shopCategory']) && dscwp_ags_filters_is_filter_allowed('shopCategory') ) {
		add_filter( 'woocommerce_shortcode_products_query', 'dswcp_ags_filters_categories' );
	}
	
	// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only operation
	if ( !empty($_GET['shopTag']) && dscwp_ags_filters_is_filter_allowed('shopTag') ) {
		add_filter( 'woocommerce_shortcode_products_query', 'dswcp_ags_filters_tags' );
	}
	
	// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only operation
	if ( !empty($_GET['shopSearch']) && dscwp_ags_filters_is_filter_allowed('shopSearch') ) {
		add_filter( 'woocommerce_shortcode_products_query', 'dswcp_ags_filters_search' );
	}
	
	// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only operation
	if ( !empty($_GET['shopRating']) && dscwp_ags_filters_is_filter_allowed('shopRating') ) {
		add_filter( 'woocommerce_shortcode_products_query', 'dswcp_ags_filters_rating' );
	}
	
	// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only operation
	if ( !empty($_GET['shopPrice']) && dscwp_ags_filters_is_filter_allowed('shopPrice') ) {
		add_filter( 'woocommerce_shortcode_products_query', 'dswcp_ags_filters_price' );
	}
	
	// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only operation
	if ( !empty($_GET['shopStockStatus']) && dscwp_ags_filters_is_filter_allowed('shopStockStatus') ) {
		add_filter( 'woocommerce_shortcode_products_query', 'dswcp_ags_filters_stock_status' );
	}
	
	// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only operation
	if ( isset($_GET['shopSale']) && $_GET['shopSale'] == 'onsale' && dscwp_ags_filters_is_filter_allowed('shopSale') ) {
		$GLOBALS['ags_wc_filters_sale_filter'] = new AGS_WC_Filters_Sale_Filter();
	}
	
	add_filter( 'woocommerce_shortcode_products_query', 'dswcp_ags_filters_attributes' );
}


function dscwp_ags_filters_hooks_remove() {
	remove_filter( 'woocommerce_shortcode_products_query', 'dswcp_ags_filters_categories' );
	remove_filter( 'woocommerce_shortcode_products_query', 'dswcp_ags_filters_tags' );
	remove_filter( 'woocommerce_shortcode_products_query', 'dswcp_ags_filters_search' );
	remove_filter( 'woocommerce_shortcode_products_query', 'dswcp_ags_filters_rating' );
	remove_filter( 'woocommerce_shortcode_products_query', 'dswcp_ags_filters_price' );
	remove_filter( 'woocommerce_shortcode_products_query', 'dswcp_ags_filters_stock_status' );
	if (isset($GLOBALS['ags_wc_filters_sale_filter'])) {
		$GLOBALS['ags_wc_filters_sale_filter']->unhook();
		unset($GLOBALS['ags_wc_filters_sale_filter']);
	}
	remove_filter( 'woocommerce_shortcode_products_query', 'dswcp_ags_filters_attributes' );
}

function dscwp_ags_filters_is_filter_allowed($filter) {
	global $post;
	
	$postIdToCheck = ET_Builder_Element::get_theme_builder_layout_id();
	if ( !$postIdToCheck && isset($post) ) {
		$postIdToCheck = $post->ID;
	}
	
	if ($postIdToCheck) {
		$allowed = get_post_meta($postIdToCheck, '_ags_wc_filters_allowed', true);
		return is_array($allowed) && in_array($filter, $allowed);
	}
	
	return false;
}

function dswcp_ags_filters_maybe_add_full_icons($assets) {
	if ( ( dscwp_ags_filters_is_filter_allowed('shopRating') || et_core_is_fb_enabled() ) && isset($assets['et_icons_base']['css']) ) {
		$assets['et_icons_all'] = $assets['et_icons_base'];
		unset($assets['et_icons_base']);
		$assets['et_icons_all']['css'] = substr( $assets['et_icons_all']['css'], 0, strrpos($assets['et_icons_all']['css'], '/') ).'/icons_all.css';
	}
	return $assets;
}


function dscwp_ags_filters_shop_output($shopHtml, $orderClass) {
	remove_filter( 'term_link', 'dswcp_ags_filters_transform_category_urls', 10, 2 );
	remove_filter( 'term_links-product_cat', 'dswcp_ags_filters_transform_category_links' );
	
	// phpcs:ignore WordPress.Security.NonceVerification.Missing -- just a flag to enable some hooks
	if ( isset($_POST['ags_wc_filters_ajax_shop']) ) {
	
		if ( $orderClass == $_POST['ags_wc_filters_ajax_shop'] ) {
			global $dswcp_ajax_notices_html, $dswcp_ajax_shop_html;
			
			if (!isset($dswcp_ajax_shop_html)) {
				$dswcp_ajax_shop_html = [];
			}
			
			$dswcp_ajax_shop_html[$orderClass] = $shopHtml;
			
			if ( empty($_POST['ags_wc_filters_ajax_notices']) || ( isset($dswcp_ajax_notices_html) && count($dswcp_ajax_notices_html) == count($_POST['ags_wc_filters_ajax_notices']) ) ) {
				dswcp_ags_filters_ajax_output();
			}
		}
		return '';
	}
	
	return $shopHtml;
}

function dswcp_ags_filters_strip_unwanted_pagination_url_params($paginationArgs) {
	$baseUrl = $paginationArgs['base'];
	$queryStart = strrpos($baseUrl, '?');
	parse_str( substr($baseUrl, $queryStart + 1), $query );
	
	unset($query['add-to-cart']);
	
	$paginationArgs['base'] = substr($baseUrl, 0, $queryStart + 1).preg_replace( '/(shopPage[\\d]*\\=)%25%23%25/', '$1%#%', http_build_query($query) );
	
	return $paginationArgs;
}

function dswcp_ags_filters_transform_category_urls( $url, $categoryTerm ) {
	if ( $categoryTerm->taxonomy == 'product_cat' ) {
		return $url.'#agsWcCategoryFilter='.urlencode(add_query_arg('shopCategory', $categoryTerm->term_id));
	}
	return $url;
}

function dswcp_ags_filters_transform_category_links( $links ) {
	foreach ($links as &$link) {
		if (strpos($link, '#agsWcCategoryFilter=') !== false) {
			$link = str_replace('#agsWcCategoryFilter=', '" data-filter-url="', $link);
		}
	}
	
	return $links;
}

function dswcp_ags_filters_categories($query) {
	
	if (empty($query['tax_query'])) {
		$query['tax_query'] = [];
	} else {
		$query['tax_query']['relation'] = 'AND';
	}
	
	$query['tax_query'][] = [
		'taxonomy' => 'product_cat',
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended, ET.Sniffs.ValidatedSanitizedInput.InputNotValidated, ET.Sniffs.ValidatedSanitizedInput.InputNotSanitized -- read-only operation; GET field existence is checked before this function is hooked; sanitized with absint function
		'terms' => array_map( 'absint', explode(',', $_GET['shopCategory']) )
	];
	
	return $query;
}

function dswcp_ags_filters_tags($query) {
	
	if (empty($query['tax_query'])) {
		$query['tax_query'] = [];
	} else {
		$query['tax_query']['relation'] = 'AND';
	}
	
	$query['tax_query'][] = [
		'taxonomy' => 'product_tag',
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended, ET.Sniffs.ValidatedSanitizedInput.InputNotValidated, ET.Sniffs.ValidatedSanitizedInput.InputNotSanitized -- read-only operation; GET field existence is checked before this function is hooked; sanitized with absint function
		'terms' => array_map( 'absint', explode(',', $_GET['shopTag']) )
	];
	
	return $query;
}

function dswcp_ags_filters_search($query) {
	// phpcs:ignore WordPress.Security.NonceVerification.Recommended, ET.Sniffs.ValidatedSanitizedInput.InputNotValidated -- read-only operation; GET field existence is checked before this function is hooked
	$query['s'] = sanitize_text_field($_GET['shopSearch']);
	return $query;
}

function dswcp_ags_filters_rating($query) {
	// phpcs:disable WordPress.Security.NonceVerification.Recommended -- read-only operation
	
	if (empty($query['meta_query'])) {
		$query['meta_query'] = [];
	} else {
		$query['meta_query']['relation'] = 'AND';
	}
	
	// phpcs:ignore ET.Sniffs.ValidatedSanitizedInput.InputNotValidated, ET.Sniffs.ValidatedSanitizedInput.InputNotSanitized -- GET field existence is checked before this function is hooked; checking input right here
	if ( strlen($_GET['shopRating']) == 2 && (int) $_GET['shopRating'][0] >= 1 && (int) $_GET['shopRating'][0] <= 5 && $_GET['shopRating'][1] == '+' ) {
		$compare = '>=';
		// phpcs:ignore ET.Sniffs.ValidatedSanitizedInput.InputNotValidated -- GET field existence is checked before this function is hooked
		$value = (int) $_GET['shopRating'][0];
	} else {
		$compare = 'IN';
		$value = array_filter(
					// phpcs:ignore ET.Sniffs.ValidatedSanitizedInput.InputNotValidated, ET.Sniffs.ValidatedSanitizedInput.InputNotSanitized -- GET field existence is checked before this function is hooked; sanitized by absint()
					array_map('absint', explode(',', $_GET['shopRating'])),
					function($value) {
						return $value >= 1 && $value <= 5;
					}
		);
	}
	
	if (!empty($value)) {
		$query['meta_query'][] = [
			'key' => '_wc_average_rating',
			'value' => $value,
			'compare' => $compare,
			'type' => 'NUMERIC'
		];
	}
	
	return $query;
	// phpcs:enable WordPress.Security.NonceVerification.Recommended
}

function dswcp_ags_filters_price($query) {
	
	if (empty($query['meta_query'])) {
		$query['meta_query'] = [];
	} else {
		$query['meta_query']['relation'] = 'AND';
	}
	
	// phpcs:ignore WordPress.Security.NonceVerification.Recommended, ET.Sniffs.ValidatedSanitizedInput.InputNotValidated, ET.Sniffs.ValidatedSanitizedInput.InputNotSanitized -- read-only operation; GET field existence is checked before this function is hooked; input parts will be sanitized via (int) casts below
	$values = explode('-', $_GET['shopPrice']);
	
	if (strlen($values[0])) {
		$query['meta_query'][] = [
			'key' => '_price',
			'value' => (int) $values[0],
			'compare' => '>=',
			'type' => 'NUMERIC'
		];
	}
	
	if (strlen($values[1])) {
		$query['meta_query'][] = [
			'key' => '_price',
			'value' => (int) $values[1],
			'compare' => '<=',
			'type' => 'NUMERIC'
		];
	}
	
	return $query;
}

function dswcp_ags_filters_stock_status($query) {
	
	if (empty($query['meta_query'])) {
		$query['meta_query'] = [];
	} else {
		$query['meta_query']['relation'] = 'AND';
	}
	
	// phpcs:ignore WordPress.Security.NonceVerification.Recommended, ET.Sniffs.ValidatedSanitizedInput.InputNotValidated, ET.Sniffs.ValidatedSanitizedInput.InputNotSanitized -- read-only operation; GET field existence is checked before this function is hooked; array is sanitized
	$values = array_map('sanitize_text_field', explode(',', $_GET['shopStockStatus']));
	
	if ($values) {
		$query['meta_query'][] = [
			'key' => '_stock_status',
			'value' => $values
		];
	}
	
	return $query;
}

function dswcp_ags_filters_attributes($query) {
	
	// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only operation
	foreach ($_GET as $field => $value) {
		if ( strlen($field) > 14 && substr($field, 0, 14) == 'shopAttribute_' && dscwp_ags_filters_is_filter_allowed($field) ) {
	
			if (empty($query['tax_query'])) {
				$query['tax_query'] = [];
			} else {
				$query['tax_query']['relation'] = 'AND';
			}
			
			$query['tax_query'][] = [
				'taxonomy' => substr($field, 14),
				'terms' => array_map( 'absint', explode(',', $value) )
			];
		}
	}
	
	return $query;
}

function dswcp_ags_filters_search_suggestions() {
	// phpcs:ignore WordPress.Security.NonceVerification.Missing -- read-only operation
	if (!empty($_POST['query'])) {
		$products = get_posts([
			'post_type' => 'product',
			// phpcs:ignore WordPress.Security.NonceVerification.Missing -- read-only operation
			's' => sanitize_text_field($_POST['query']),
			'posts_per_page' => 5,
		]);
		
		$result = [];
		foreach ($products as $product) {
			$result[] = [
				'label' => $product->post_title,
				'link' => get_permalink($product->ID)
			];
		}
		
		wp_send_json_success($result);
	}
	
	wp_send_json_error();
}

class AGS_WC_Filters_Sale_Filter {
	
	private $page, $pageLength, $result = [], $query;
	
	function __construct() {
		add_filter( 'woocommerce_shortcode_products_query', [$this, 'filterQuery'], 99 );
		add_filter( 'woocommerce_shortcode_products_query_results', [$this, 'filterResult'] );
	}

	function unhook() {
		remove_filter( 'woocommerce_shortcode_products_query', [$this, 'filterQuery'], 99 );
		remove_filter( 'woocommerce_shortcode_products_query_results', [$this, 'filterResult'] );
	}
	
	function filterQuery($query) {
		
		$this->page = isset($query['paged']) ? $query['paged'] : 1;
		$this->pageLength = $query['posts_per_page'];
		
		$query['paged'] = 1;
		$query['fields'] = 'ids';
		$this->query = $query;
		
		return $query;
	}
	
	function filterResult($result) {
		$ids = $result->ids;
		
		while ( $this->processResult($ids) ) {
			++$this->query['paged'];
			$ids = get_posts($this->query);
		}
		
		$result->ids = array_slice($this->result, ($this->page - 1) * $this->pageLength, $this->pageLength);
		$result->total = count($this->result);
		$result->total_pages = ceil( $result->total / $this->pageLength );
		
		return $result;
	}
	
	// true = need more results; false = done
	function processResult($result) {
		if (!$result) {
			return false;
		}
		
		foreach ($result as $productId) {
			$product = wc_get_product($productId);
			if ($product && $product->is_on_sale()) {
				$this->result[] = $productId;
			}
		}
		
		if (count($result) < $this->pageLength) {
			return false;
		}
		
		return true;
	}
	
}