<?php
/**
 * Handles adding products to wishlists, including hooks and shortcodes.
 *
 * @since 1.0.0
 * @package TInvWishlist\Public
 */

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

use DiDom\Document;

/**
 * Manages the addition of products to wishlists.
 */
class TInvWL_Public_AddToWishlist
{
	/**
	 * The plugin name.
	 *
	 * @var string
	 */
	private $_name;

	/**
	 * The global product.
	 *
	 * @var object
	 */
	private $product;

	/**
	 * User's wishlists.
	 *
	 * @var array
	 */
	private $user_wishlist;

	/**
	 * Wishlist and product association.
	 *
	 * @var array
	 */
	private $wishlist;

	/**
	 * All products in the wishlist.
	 *
	 * @var array
	 */
	private $all_products;

	/**
	 * Indicates if the button is in a loop.
	 *
	 * @var bool
	 */
	private $is_loop;

	/**
	 * Singleton instance of the class.
	 *
	 * @var TInvWL_Public_AddToWishlist
	 */
	protected static $_instance = null;

	/**
	 * Variation ID for variable products.
	 *
	 * @var mixed
	 */
	private $variation_id;

	/**
	 * Variation IDs for variable products in loops.
	 *
	 * @var array
	 */
	private $variation_ids;

	/**
	 * Returns the singleton instance.
	 *
	 * @param string $plugin_name Plugin name.
	 *
	 * @return TInvWL_Public_AddToWishlist
	 */
	public static function instance($plugin_name = TINVWL_PREFIX)
	{
		if (is_null(self::$_instance)) {
			self::$_instance = new self($plugin_name);
		}

		return self::$_instance;
	}

	/**
	 * Constructor
	 *
	 * @param string $plugin_name Plugin name.
	 */
	function __construct($plugin_name)
	{
		$this->_name = $plugin_name;
		$this->is_loop = false;
		$this->define_hooks();
	}

	/**
	 * Defined shortcode and hooks
	 */
	function define_hooks()
	{
		switch (tinv_get_option('add_to_wishlist', 'position')) {
			case 'before':
				add_action('tinvwl_before_add_to_cart_button', 'tinvwl_view_addto_html');
				add_action('tinvwl_single_product_summary', 'tinvwl_view_addto_htmlout');
				add_action('woocommerce_before_add_to_cart_button', 'tinvwl_view_addto_html', 9);
				add_action('woocommerce_single_product_summary', 'tinvwl_view_addto_htmlout', 29);
				add_action('catalog_visibility_before_alternate_add_to_cart_button', 'tinvwl_view_addto_html');
				break;
			case 'after':
				add_action('tinvwl_after_add_to_cart_button', 'tinvwl_view_addto_html');
				add_action('tinvwl_single_product_summary', 'tinvwl_view_addto_htmlout');
				add_action('woocommerce_after_add_to_cart_button', 'tinvwl_view_addto_html', 20);
				add_action('woocommerce_single_product_summary', 'tinvwl_view_addto_htmlout', 31);
				add_action('catalog_visibility_after_alternate_add_to_cart_button', 'tinvwl_view_addto_html');
				break;
			case 'thumbnails':
				add_action('tinvwl_after_thumbnails', 'tinvwl_view_addto_html');
				add_action('woocommerce_product_thumbnails', 'tinvwl_view_addto_html', 21);
				break;
			case 'summary':
				add_action('tinvwl_after_summary', 'tinvwl_view_addto_html');
				add_action('woocommerce_after_single_product_summary', 'tinvwl_view_addto_html', 11);
				break;
		}
		if (tinv_get_option('add_to_wishlist_catalog', 'show_in_loop')) {
			switch (tinv_get_option('add_to_wishlist_catalog', 'position')) {
				case 'before':
					add_action('tinvwl_after_shop_loop_item', 'tinvwl_view_addto_htmlloop');
					add_action('woocommerce_after_shop_loop_item', 'tinvwl_view_addto_htmlloop', 8);
					add_action('uael_woo_products_add_to_cart_before', 'tinvwl_view_addto_htmlloop');
					break;
				case 'above_thumb':
					add_action('tinvwl_above_thumb_loop_item', 'tinvwl_view_addto_htmlloop');
					add_action('woocommerce_before_shop_loop_item', 'tinvwl_view_addto_htmlloop', 9);
					add_action('uael_woo_products_before_summary_wrap', 'tinvwl_view_addto_htmlloop');
					break;
				case 'shortcode':
					break;
				case 'after':
				default: // Compatibility with previous versions.
					add_action('tinvwl_after_shop_loop_item', 'tinvwl_view_addto_htmlloop');
					add_action('woocommerce_after_shop_loop_item', 'tinvwl_view_addto_htmlloop', 20);
					add_action('uael_woo_products_add_to_cart_after', 'tinvwl_view_addto_htmlloop');
					break;
			}


		}

		// WooCommerce Blocks
		add_filter('woocommerce_blocks_product_grid_item_html', array($this, 'htmloutput_block'), 9, 3);
		add_filter('woocommerce_product_get_description', array($this, 'woocommerce_blocks_all_products'), 10, 2);
		add_action('init', array($this, 'woocommerce_blocks'));

		add_action('wp_loaded', array($this, 'add_to_wishlist'), 0);
		if (is_user_logged_in() && apply_filters('tinvwl_allow_data_cookies', true)) {
			add_action('init', array($this, 'set_wishlists_data_cookies'));
		}
	}

	/**
	 * Set cookies to sync session data across devices
	 *
	 * @return void
	 */
	function set_wishlists_data_cookies()
	{
		$class = TInvWL_Public_WishlistCounter::instance();
		$counter = $class->get_counter();
		wc_setcookie('tinvwl_wishlists_data_counter', $counter);


		if (tinv_get_option('general', 'product_stats')) {
			global $wpdb;
			$table = sprintf('%s%s', $wpdb->prefix, 'tinvwl_items');
			$table_lists = sprintf('%s%s', $wpdb->prefix, 'tinvwl_lists');
			$table_stats = sprintf('%s%s', $wpdb->prefix, 'tinvwl_analytics');
			$stats_count = 0;

			$stats_sql = "SELECT SUM(`count`) as `stats_count` FROM (SELECT COUNT(`B`.`ID`) AS `count` FROM `{$table_stats}` AS `A` LEFT JOIN `{$table}` AS `C` ON `C`.`wishlist_id` = `A`.`wishlist_id` AND `C`.`product_id` = `A`.`product_id` AND `C`.`variation_id` = `A`.`variation_id` LEFT JOIN `{$table_lists}` AS `B` ON `C`.`wishlist_id` = `B`.`ID` LEFT JOIN `{$table_lists}` AS `G` ON `C`.`wishlist_id` = `G`.`ID` AND `G`.`author` = 0 WHERE `A`.`product_id` > 0 GROUP BY `A`.`product_id`, `A`.`variation_id` HAVING `count` > 0 LIMIT 0, 9999999) AS `A`";

			$stats_results = $wpdb->get_results($stats_sql, ARRAY_A);

			if (!empty($stats_results)) {
				foreach ($stats_results as $product_stats) {
					$stats_count = $product_stats['stats_count'];
				}
			}
			if ($stats_count) {
				wc_setcookie('tinvwl_wishlists_data_stats', $stats_count);
			}
		}
	}

	/**
	 * Action add product to wishlist
	 *
	 * @return boolean
	 */
	function add_to_wishlist()
	{
		if (is_null(filter_input(INPUT_POST, 'tinv_wishlist_id'))) {
			return false;
		} else {
			remove_action('init', 'woocommerce_add_to_cart_action');
			remove_action('wp_loaded', 'WC_Form_Handler::add_to_cart_action', 20);
		}
		ob_start();
		$post = filter_input_array(INPUT_POST, array(
			'tinv_wishlist_id' => FILTER_VALIDATE_INT,
			'tinv_wishlist_name' => FILTER_SANITIZE_FULL_SPECIAL_CHARS,
			'product_id' => FILTER_VALIDATE_INT,
			'product_variation' => FILTER_VALIDATE_INT,
			'product_type' => FILTER_SANITIZE_FULL_SPECIAL_CHARS,
			'product_action' => FILTER_SANITIZE_FULL_SPECIAL_CHARS,
			'redirect' => FILTER_SANITIZE_URL,
		));

		$post['original_product_id'] = $post['product_id'];

		$wlp = null;
		$wishlist = null;
		$data = array('msg' => array());
		if (is_user_logged_in()) {
			$wl = new TInvWL_Wishlist($this->_name);
			$wishlist = $wl->add_user_default();
			$wishlist = apply_filters('tinvwl_addtowishlist_wishlist', $wishlist);
			if (empty($wishlist)) {
				$data['status'] = false;
				$data = apply_filters('tinvwl_addtowishlist_return_ajax', $data, $post);
				ob_clean();
				wp_send_json($data);
			}
			$wlp = new TInvWL_Product($wishlist, $this->_name);
		} elseif (tinv_get_option('general', 'require_login')) {
			$data['status'] = false;
			$data['icon'] = 'icon_big_times';
			if (tinv_get_option('general', 'redirect_require_login')) {
				$data['msg'] = array();
				$data['force_redirect'] = apply_filters('tinvwl_addtowishlist_login_page', add_query_arg('tinvwl_redirect', rawurlencode($post['redirect']), wc_get_page_permalink('myaccount')), $post);
			} else {
				$data['msg'][] = __('Please, login to add products to Wishlist', 'ti-woocommerce-wishlist');
				$data['dialog_custom_url'] = apply_filters('tinvwl_addtowishlist_login_page', add_query_arg('tinvwl_redirect', rawurlencode($post['redirect']), wc_get_page_permalink('myaccount')), $post);
				$data['dialog_custom_html'] = '<i class="ftinvwl ftinvwl-key"></i>' . esc_html(__('Login', 'ti-woocommerce-wishlist'));
			}
			$data['msg'] = array_unique($data['msg']);
			$data['msg'] = implode('<br>', $data['msg']);
			if (!empty($data['msg'])) {
				$data['msg'] = tinv_wishlist_template_html('ti-addedtowishlist-dialogbox.php', apply_filters('tinvwl_addtowishlist_dialog_box', $data, $post));
			}
			$data = apply_filters('tinvwl_addtowishlist_return_ajax', $data, $post);
			ob_clean();
			wp_send_json($data);
		} else {
			$wl = new TInvWL_Wishlist($this->_name);
			$wishlist = $wl->add_sharekey_default();
			$wlp = new TInvWL_Product($wishlist);
		}

		$status = true;
		if (empty($post['product_id']) || apply_filters('tinvwl_addtowishlist_not_allowed', false, $post)) {
			$status = false;
			$data['msg'][] = __('Something went wrong', 'ti-woocommerce-wishlist');
		} else {
			$post['product_type'] = apply_filters('tinvwl_addtowishlist_modify_type', $post['product_type'], $post);
			$post = apply_filters('tinvwl_addtowishlist_prepare', $post);
			$form = apply_filters('tinvwl_addtowishlist_prepare_form', filter_input(INPUT_POST, 'form', FILTER_DEFAULT, FILTER_FORCE_ARRAY), $_POST, $_FILES);
			if (empty($form)) {
				$form = array();
			}
			if (!empty($form)) {
				array_walk_recursive($form, function (&$value) {
					if (is_string($value)) {
						$value = wp_strip_all_tags($value, true);
					}
				});
			}
			wp_recursive_ksort($form);
			switch ($post['product_type']) {
				case 'group':
				case 'grouped' :
					$product = $wlp->product_data($post['product_id']);
					if (empty($product)) {
						$status = false;
					} else {
						$variations = $product->get_children();

						foreach ($variations as $variation_id) {
							$quantity = 1;
							$allowed_adding = !count($wlp->get_wishlist(array(
								'product_id' => $post['product_id'],
								'variation_id' => $variation_id,
								'external' => false,
							)));
							if (tinv_get_option('general', 'simple_flow') && 'remove' === $post['product_action']) {
								if ($wlp->remove_product_from_wl(0, $post['product_id'], $variation_id, apply_filters('tinvwl_addtowishlist_add_form', $form))) {
									$data['msg'][] = apply_filters('tinvwl_removed_from_wishlist_text', tinv_get_option('general', 'text_removed_from'));
									$data['removed'] = true;
									$status = false;
								}
							} elseif (!$allowed_adding) {
								$data['msg'][] = apply_filters('tinvwl_already_in_wishlist_text', tinv_get_option('general', 'text_already_in'));
								$status = false;
							} elseif ($wlp->add_product(apply_filters('tinvwl_addtowishlist_add', array(
								'product_id' => $post['product_id'],
								'variation_id' => $variation_id,
								'quantity' => $quantity,
							)))
							) {
								$data['msg'][] = apply_filters('tinvwl_added_to_wishlist_text', tinv_get_option('general', 'text_added_to'));
							} else {
								$status = false;
							}
						}
					}
					break;
				case 'variable' :
				case 'variation' :
				case 'variable-subscription' :

					if ($post['product_variation']) {
						$variation_id = $post['product_variation'];
					} else {
						$variation_id = absint(array_key_exists('variation_id', $form) ? filter_var($form['variation_id'], FILTER_VALIDATE_INT) : 0);
					}

					$post['original_product_id'] = $variation_id;

					$quantity = 1;
					$allowed_adding = !count($wlp->get_wishlist(array(
						'product_id' => $post['product_id'],
						'variation_id' => $variation_id,
						'meta' => apply_filters('tinvwl_addtowishlist_add_form', $form),
						'external' => false,
					)));
					if (tinv_get_option('general', 'simple_flow') && 'remove' === $post['product_action']) {
						if ($wlp->remove_product_from_wl(0, $post['product_id'], $variation_id, apply_filters('tinvwl_addtowishlist_add_form', $form))) {
							$data['msg'][] = apply_filters('tinvwl_removed_from_wishlist_text', tinv_get_option('general', 'text_removed_from'));
							$data['removed'] = true;
							$status = false;
						}
					} elseif (!$allowed_adding) {
						$data['msg'][] = apply_filters('tinvwl_already_in_wishlist_text', tinv_get_option('general', 'text_already_in'));
						$status = false;
					} elseif ($wlp->add_product(apply_filters('tinvwl_addtowishlist_add', array(
						'product_id' => $post['product_id'],
						'quantity' => $quantity,
						'variation_id' => $variation_id,
					)), apply_filters('tinvwl_addtowishlist_add_form', $form))) {
						$data['msg'][] = apply_filters('tinvwl_added_to_wishlist_text', tinv_get_option('general', 'text_added_to'));
					} else {
						$status = false;
					}
					break;
				case 'simple' :
				default:
					$quantity = 1;
					$allowed_adding = !count($wlp->get_wishlist(array(
						'product_id' => $post['product_id'],
						'meta' => apply_filters('tinvwl_addtowishlist_add_form', $form),
						'external' => false,
					)));
					if (tinv_get_option('general', 'simple_flow') && 'remove' === $post['product_action']) {
						if ($wlp->remove_product_from_wl(0, $post['product_id'], 0, apply_filters('tinvwl_addtowishlist_add_form', $form))) {
							$data['msg'][] = apply_filters('tinvwl_removed_from_wishlist_text', tinv_get_option('general', 'text_removed_from'));
							$data['removed'] = true;
							$status = false;
						}
					} elseif (!$allowed_adding) {
						$data['msg'][] = apply_filters('tinvwl_already_in_wishlist_text', tinv_get_option('general', 'text_already_in'));
						$status = false;
					} elseif ($wlp->add_product(apply_filters('tinvwl_addtowishlist_add', array(
						'product_id' => $post['product_id'],
						'quantity' => $quantity,
					)), apply_filters('tinvwl_addtowishlist_add_form', $form))) {
						$data['msg'][] = apply_filters('tinvwl_added_to_wishlist_text', tinv_get_option('general', 'text_added_to'));
					} else {
						$status = false;
					}
					break;
			} // End switch().
		} // End if().
		$data['status'] = $status;
		$data['wishlist_url'] = tinv_url_wishlist_default();

		if (!empty($wishlist)) {
			$data['wishlist_url'] = tinv_url_wishlist($wishlist['ID']);
		}

		if ($status && tinv_get_option('general', 'redirect') && tinv_get_option('page', 'wishlist') && tinv_get_option('general', 'show_notice')) {
			$data['redirect'] = apply_filters('tinvwl_addtowishlist_redirect', $data['wishlist_url']);
		}

		if (empty($form)) {
			$form = array();
		}

		$data['icon'] = $data['status'] ? 'icon_big_heart_check' : 'icon_big_times';
		$data['msg'] = array_unique($data['msg']);
		$data['msg'] = implode('<br>', $data['msg']);

		$product = $original_product = wc_get_product($post['product_id']);

		if ($post['original_product_id'] && $post['product_id'] !== $post['original_product_id']) {
			$original_product = wc_get_product($post['original_product_id']);
		}

		if (!empty($data['msg'])) {
			if ($original_product) {
				$data['msg'] = tinvwl_message_placeholders($data['msg'], $original_product, $wishlist);
			}
			$data['msg'] = apply_filters('tinvwl_addtowishlist_message_after', $data['msg'], $data, $post, $form, $product);
			$data['wishlist'] = $wishlist;
			$data['msg'] = tinv_wishlist_template_html('ti-addedtowishlist-dialogbox.php', apply_filters('tinvwl_addtowishlist_dialog_box', $data, $post));
		}
		if (!tinv_get_option('general', 'show_notice') && array_key_exists('msg', $data)) {
			unset($data['msg']);
			if (!$status && !tinv_get_option('general', 'simple_flow')) {
				$data['force_redirect'] = $data['wishlist_url'];
			}
		}
		if (tinv_get_option('general', 'simple_flow')) {
			$data['make_remove'] = $data['status'];
		}
		$share_key = false;

		if (!is_user_logged_in()) {
			$share_key = $wishlist['share_key'];
		}
		$data['action'] = 'add_to_wishlist';
		$data['wishlists_data'] = $this->get_wishlists_data($share_key);
		$data = apply_filters('tinvwl_addtowishlist_return_ajax', $data, $post, $form, $product);
		ob_clean();
		wp_send_json($data);
	}

	/**
	 * @param string $share_key
	 *
	 * @return array
	 */
	function get_wishlists_data($share_key)
	{
		global $wpdb;

		$table = $wpdb->prefix . 'tinvwl_items';
		$table_lists = $wpdb->prefix . 'tinvwl_lists';
		$table_stats = $wpdb->prefix . 'tinvwl_analytics';
		$table_translations = $wpdb->prefix . 'icl_translations';
		$table_languages = $wpdb->prefix . 'icl_languages';

		// Sanitize inputs
		$lang = sanitize_text_field(filter_input(INPUT_POST, 'lang', FILTER_SANITIZE_FULL_SPECIAL_CHARS));
		$lang_default = sanitize_text_field(filter_input(INPUT_POST, 'lang_default', FILTER_SANITIZE_FULL_SPECIAL_CHARS));
		$stats = sanitize_text_field(filter_input(INPUT_POST, 'stats', FILTER_SANITIZE_FULL_SPECIAL_CHARS));
		$share_key = sanitize_text_field($share_key);

		$data = $products = $wishlists = $results = $analytics = array();

		if (is_user_logged_in()) {
			$data['author'] = get_current_user_id();
		}

		if ((isset($data['author']) && $data['author']) || $share_key) {
			$default = array(
				'count' => 99999,
				'field' => null,
				'offset' => 0,
				'order' => 'DESC',
				'order_by' => 'date',
				'external' => true,
				'sql' => '',
			);

			// Validate order and order_by parameters
			$allowed_orders = array('ASC', 'DESC');
			$allowed_order_by_fields = array('date', 'title', 'ID'); // Add other allowed fields

			foreach ($default as $_k => $_v) {
				if (array_key_exists($_k, $data)) {
					$default[$_k] = $data[$_k];
					unset($data[$_k]);
				}
			}

			// Sanitize order parameters
			$default['order'] = in_array(strtoupper($default['order']), $allowed_orders) ? strtoupper($default['order']) : 'DESC';
			$default['order_by'] = in_array($default['order_by'], $allowed_order_by_fields) ? $default['order_by'] : 'date';

			$default['offset'] = absint($default['offset']);
			$default['count'] = absint($default['count']);

			// Build the base SELECT clause
			if ($lang) {
				$select_fields = "{$table}.ID, t.element_id AS product_id, t2.element_id AS variation_id,
                            {$table}.formdata, {$table}.author, {$table}.date, {$table}.quantity,
                            {$table}.price, {$table}.in_stock";
			} else {
				$select_fields = "{$table}.*";
			}

			// Add the wishlist fields to the SELECT clause
			$select_fields .= ", {$table_lists}.ID as wishlist_id, {$table_lists}.status as wishlist_status,
                           {$table_lists}.title as wishlist_title, {$table_lists}.share_key as wishlist_share_key";

			// Build base query
			$sql = "SELECT {$select_fields}
                FROM {$table}
                INNER JOIN {$table_lists} ON {$table}.wishlist_id = {$table_lists}.ID
                AND {$table_lists}.type = 'default'";

			// Add share key condition if present
			if ($share_key) {
				$sql .= $wpdb->prepare(" AND {$table_lists}.share_key = %s", $share_key);
			}

			// Add language joins if needed
			if ($lang) {
				$language_conditions = array($lang);
				if ($lang_default) {
					$language_conditions[] = $lang_default;
				}

				$languages_in = "'" . implode("','", array_map('esc_sql', $language_conditions)) . "'";

				$sql .= " LEFT JOIN {$table_translations} tr ON
                        {$table}.product_id = tr.element_id AND tr.element_type = 'post_product'
                    LEFT JOIN {$table_translations} tr2 ON
                        {$table}.variation_id != 0 AND {$table}.variation_id = tr2.element_id
                        AND tr2.element_type = 'post_product_variation'
                    LEFT JOIN {$table_translations} t ON
                        tr.trid = t.trid AND t.element_type = 'post_product'
                        AND t.language_code IN ({$languages_in})
                    LEFT JOIN {$table_translations} t2 ON
                        {$table}.variation_id != 0 AND tr2.trid = t2.trid
                        AND t2.element_type = 'post_product_variation'
                        AND t2.language_code IN ({$languages_in})
                    JOIN {$table_languages} l ON
                        (t.language_code = l.code OR t2.language_code = l.code) AND l.active = 1";
			}

			// Build WHERE clause
			$where_conditions = array('1=1');
			if (!empty($data) && is_array($data)) {
				foreach ($data as $field => $value) {
					if (is_array($value)) {
						$placeholders = array_fill(0, count($value), '%s');
						$where_conditions[] = $wpdb->prepare(
							"{$table}.{$field} IN (" . implode(',', $placeholders) . ")",
							$value
						);
					} else {
						$where_conditions[] = $wpdb->prepare(
							"{$table}.{$field} = %s",
							$value
						);
					}
				}
			}

			$sql .= " WHERE " . implode(' AND ', $where_conditions);

			// Add GROUP BY, ORDER BY, and LIMIT
			$sql .= " GROUP BY {$table}.ID";
			$sql .= " ORDER BY {$default['order_by']} {$default['order']}";
			$sql .= $wpdb->prepare(" LIMIT %d, %d", $default['offset'], $default['count']);

			// Execute query
			$results = $wpdb->get_results($sql, ARRAY_A);

			// Process results
			if (!empty($results)) {
				foreach ($results as $product) {
					$wishlists[$product['wishlist_id']] = array(
						'ID' => (int)$product['wishlist_id'],
						'title' => sanitize_text_field($product['wishlist_title']),
						'status' => sanitize_text_field($product['wishlist_status']),
						'share_key' => sanitize_text_field($product['wishlist_share_key']),
					);
				}

				foreach ($wishlists as $wishlist) {
					foreach ($results as $product) {
						if ((int)$wishlist['ID'] !== (int)$product['wishlist_id']) {
							continue;
						}

						if (array_key_exists($product['product_id'], $products)) {
							$products[$product['product_id']][$wishlist['ID']]['in'][] = (int)$product['variation_id'];
						} else {
							$products[$product['product_id']][$wishlist['ID']] = $wishlist;
							$products[$product['product_id']][$wishlist['ID']]['in'][] = (int)$product['variation_id'];
						}
					}
				}
			}
		}

		// Handle statistics query
		if ($stats) {
			$stats_count = 0;
			$analytics = array();

			$stats_sql = "SELECT A.product_id, A.variation_id, COUNT(B.ID) AS count
                      FROM {$table_stats} AS A
                      LEFT JOIN {$table} AS C ON C.wishlist_id = A.wishlist_id
                          AND C.product_id = A.product_id
                          AND C.variation_id = A.variation_id
                      LEFT JOIN {$table_lists} AS B ON C.wishlist_id = B.ID
                      LEFT JOIN {$table_lists} AS G ON C.wishlist_id = G.ID AND G.author = 0
                      WHERE A.product_id > 0
                      GROUP BY A.product_id, A.variation_id
                      HAVING count > 0
                      LIMIT 0, 9999999";

			$stats_results = $wpdb->get_results($stats_sql, ARRAY_A);

			if (!empty($stats_results)) {
				foreach ($stats_results as $product_stats) {
					$analytics[$product_stats['product_id']][$product_stats['variation_id']] = (int)$product_stats['count'];
					$stats_count += (int)$product_stats['count'];
				}
			}
		}

		$count = is_array($results) ? count($results) : 0;

		$response = array(
			'products' => $products,
			'counter' => $count,
		);

		if ($lang) {
			$response['lang'] = sanitize_text_field($lang);
		}

		if ($lang_default) {
			$response['lang_default'] = sanitize_text_field($lang_default);
		}

		if ($stats) {
			$response['stats'] = $analytics;
			$response['stats_count'] = (int)$stats_count;
		}

		return $response;
	}

	/**
	 * Get user wishlist
	 *
	 * @return array
	 */
	function user_wishlists()
	{
		if (!empty($this->user_wishlist)) {
			return $this->user_wishlist;
		}

		$wl = new TInvWL_Wishlist($this->_name);
		if (is_user_logged_in()) {
			$wishlists = $wl->get_by_user_default();
		} else {
			$wishlists = $wl->get_by_sharekey_default();
		}
		$wishlists = array_filter($wishlists);
		if (!empty($wishlists)) {
			$_wishlists = array();
			foreach ($wishlists as $key => $wishlist) {
				if (is_array($wishlist) && array_key_exists('ID', $wishlist)) {
					$_wishlists[$key] = array(
						'ID' => $wishlist['ID'],
						'title' => $wishlist['title'],
						'url' => tinv_url_wishlist_by_key($wishlist['share_key']),
					);
				}
			}
			$wishlists = $_wishlists;
		}
		$this->user_wishlist = $wishlists;

		return $wishlists;
	}

	/**
	 * Check exists product in user wishlists
	 *
	 * @param object $product Product object.
	 * @param object $wlp Product class, used for local products.
	 *
	 * @return array
	 */
	function user_wishlist($product, $wlp = null)
	{

		$product = apply_filters('tinvwl_addtowishlist_check_product', $product);

		$this->wishlist = array();
		$vproduct = in_array($product->get_type(), array(
			'variable',
			'variation',
			'variable-subscription',
		));
		$wlp = new TInvWL_Product();
		$wishlists = $this->user_wishlists();
		$ids = array();
		foreach ($wishlists as $key => $wishlist) {
			$ids[] = $wishlist['ID'];
		}
		$ids = array_filter($ids);

		if (empty($ids)) {
			return $wishlists;
		}

		if (!$this->all_products) {
			$this->all_products = $wlp->get(array(
				'wishlist_id' => $ids,
				'external' => false,
				'count' => 9999999,
			));
		}

		$products = array();
		foreach ($this->all_products as $_product) {
			if ($_product['product_id'] === $product->is_type('variation') ? $product->get_parent_id() : $product->get_id()) {
				$products[] = $_product;
			}
		}

		$in = array();
		if (!empty($products)) {
			foreach ($products as $product) {
				$in[$product['wishlist_id']][] = $product['variation_id'];
			}
			foreach ($in as $wishlist_id => $products) {
				sort($in[$wishlist_id], SORT_NUMERIC);
				if (empty($in[$wishlist_id]) && ($this->is_loop || !$vproduct)) {
					$in[$wishlist_id] = true;
				}
			}
		}
		foreach ($wishlists as $key => $wishlist) {
			$wishlists[$key]['in'] = array_key_exists($wishlist['ID'], $in) ? $in[$wishlist['ID']] : false;
		}
		$wishlists = apply_filters('tinvwl_addtowishlist_preparewishlists', $wishlists, $product);
		$this->wishlist = $wishlists;

		return $wishlists;
	}

	/**
	 * Create add button in loop
	 *
	 * @global object $product
	 */
	function htmloutput_loop()
	{
		global $product;

		if ($product) {
			if (apply_filters('tinvwl_allow_addtowishlist_shop_loop_item', true, $product)) { // @codingStandardsIgnoreLine WordPress.PHP.StrictInArray.MissingTrueStrict
				$this->is_loop = true;
				$this->htmloutput();
				$this->is_loop = false;
			}
		}
	}

	/**
	 * Create add button if simple product out stock
	 *
	 * @global object $product
	 */
	function htmloutput_out()
	{
		global $product;

		if ($product) {
			$allow = false;
			if ('simple' === $product->get_type()) {
				$allow = ((!$product->is_purchasable() && '' == $product->get_price()) || ($product->is_purchasable() && !$product->is_in_stock()));
			}

			if (in_array($product->get_type(), array(
				'variable',
				'variable-subscription'
			))) {
				$get_variations = count($product->get_children()) <= apply_filters('woocommerce_ajax_variation_threshold', 30, $product);
				$available_variations = $get_variations ? $product->get_available_variations() : false;
				$allow = (empty($available_variations) && false !== $available_variations);
			}

			if (apply_filters('tinvwl_allow_addtowishlist_single_product_summary', $allow, $product)) {
				$this->htmloutput();
			}
		}
	}

	/**
	 * Output page
	 *
	 * @param array $attr Array parameter for shortcode.
	 * @param boolean $is_shortcode Shortcode or action.
	 *
	 * @return boolean
	 * @global object $product
	 *
	 */
	function htmloutput($attr = array(), $is_shortcode = false)
	{
		$attr = apply_filters('tinvwl_addtowishlist_out_prepare_attr', $attr);
		//is shortcode
		$this->variation_id = null;
		if ($is_shortcode) {
			$position = 'shortcode';

			$product_id = !empty($attr['product_id']) ? absint($attr['product_id']) : (($this->product instanceof WC_Product) ? $this->product->get_id() : null);

			$variation_id = !empty($attr['variation_id']) ? absint($attr['variation_id']) : null;

			if ('product_variation' == get_post_type($product_id)) { // WPCS: loose comparison ok.
				$variation_id = $product_id;
				$product_id = wp_get_post_parent_id($variation_id);
			}

			$product_data = ($product_id !== (($this->product instanceof WC_Product) ? $this->product->get_id() : null)) ? wc_get_product($variation_id ? $variation_id : $product_id) : $this->product;

			if ($product_data instanceof WC_Product && 'trash' !== get_post($product_data->get_id())->post_status) {
				$this->product = apply_filters('tinvwl_addtowishlist_out_prepare_product', $product_data);
			} else {
				return false;
			}
		} else {

			global $product, $post;

			$_product = $product;
			if (empty($product) && !empty($post) && 'product' === $post->post_type) {
				$_product = wc_get_product($post->ID);
			}

			$this->product = apply_filters('tinvwl_addtowishlist_out_prepare_product', $_product);

			$position = $this->is_loop ? tinv_get_option('add_to_wishlist_catalog', 'position') : tinv_get_option('add_to_wishlist', 'position');

		}

		if (empty($this->product) || !($this->product instanceof WC_Product) || !apply_filters('tinvwl_allow_addtowishlist_single_product', true, $this->product)) {
			return false;
		}

		if (isset($variation_id)) {
			$this->variation_id = $variation_id;
		}

		if ($this->is_loop && in_array($this->product->get_type(), array(
				'variable',
				'variable-subscription',
			))) {

			$this->variation_ids = array();

			if (!tinv_get_option('general', 'simple_flow')) {
				foreach ($this->product->get_children() as $oid) {
					$this->variation_ids[] = apply_filters('wpml_object_id', $oid, 'product', true);
				}
			}

			$this->variation_ids[] = 0;

			$this->variation_ids = apply_filters('tinvwl_wishlist_addtowishlist_button_variation_ids', $this->variation_ids, $this);

			if (!isset($this->variation_id)) {
				$this->variation_id = 0;
				$match_attributes = array();

				foreach ($this->product->get_default_attributes() as $attribute_name => $value) {
					$match_attributes['attribute_' . sanitize_title($attribute_name)] = $value;
				}

				if ($match_attributes) {

					add_action('tinvwl_wishlist_addtowishlist_button', array(
						$this,
						'default_variation_loop'
					), 10, 2);

					$data_store = WC_Data_Store::load('product');
					$this->variation_id = $data_store->find_matching_product_variation($this->product, $match_attributes);
				}
			}
		}
		add_action('tinvwl_wishlist_addtowishlist_button', array($this, 'button'));

		$action_class = current_action() ? ' tinvwl-' . current_action() : ' tinvwl-no-action';

		$data = array(
			'class_postion' => sprintf('tinvwl-%s-add-to-cart', $position) . ($this->is_loop ? ' tinvwl-loop-button-wrapper' : '') . $action_class,
			'product' => $this->product,
			'variation_id' => ($this->is_loop && in_array(($this->product->get_type()), array(
					'variable',
					'variable-subscription',
				))) ? $this->variation_id : ($this->product->is_type('variation') ? $this->product->get_id() : 0),
			'button_icon' => tinv_get_option('add_to_wishlist' . ($this->is_loop ? '_catalog' : ''), 'icon'),
			'add_to_wishlist' => apply_filters('tinvwl_added_to_wishlist_text_loop', tinv_get_option('add_to_wishlist' . ($this->is_loop ? '_catalog' : ''), 'text')),
			'browse_in_wishlist' => apply_filters('tinvwl_view_wishlist_text', tinv_get_option('general', 'text_browse')),
			'product_in_wishlist' => apply_filters('tinvwl_already_in_wishlist_text', tinv_get_option('general', 'text_already_in')),
			'product_to_wishlist' => apply_filters('tinvwl_added_to_wishlist_text', tinv_get_option('general', 'text_added_to')),
			'loop' => $this->is_loop,
		);
		tinv_wishlist_template('ti-addtowishlist.php', $data);
	}

	/**
	 * Create button
	 *
	 * @param boolean $echo Return or output.
	 */
	function button($echo = true)
	{
		$content = apply_filters('tinvwl_wishlist_button_before', '');
		$button_text = apply_filters('tinvwl_added_to_wishlist_text_loop', tinv_get_option('add_to_wishlist' . ($this->is_loop ? '_catalog' : ''), 'text'));
		$text = (tinv_get_option('add_to_wishlist' . ($this->is_loop ? '_catalog' : ''), 'show_text')) ? $button_text : '';
		$icon = tinv_get_option('add_to_wishlist' . ($this->is_loop ? '_catalog' : ''), 'icon');
		$icon_color = tinv_get_option('add_to_wishlist' . ($this->is_loop ? '_catalog' : ''), 'icon_style');
		$icon_class = '';
		$action = 'addto';
		if (empty($text)) {
			$icon_class = ' no-txt';
		} else {
			$content .= '<div class="tinv-wishlist-clear"></div>';
			if (tinv_get_option('general', 'simple_flow')) {
				$text = sprintf('<span class="tinvwl_add_to_wishlist-text">%s</span><span class="tinvwl_remove_from_wishlist-text">%s</span>', $text, apply_filters('tinvwl_remove_from_wishlist_text' . ($this->is_loop ? '_loop' : ''), tinv_get_option('add_to_wishlist' . ($this->is_loop ? '_catalog' : ''), 'text_remove')));
			} else {

				$already_on = tinv_get_option('add_to_wishlist' . ($this->is_loop ? '_catalog' : ''), 'already_on') ? sprintf('<span class="tinvwl_already_on_wishlist-text">%s</span>', apply_filters('tinvwl_already_on_wishlist_text' . ($this->is_loop ? '_loop' : ''), tinv_get_option('add_to_wishlist' . ($this->is_loop ? '_catalog' : ''), 'text_already_on'))) : '';

				$text = sprintf('<span class="tinvwl_add_to_wishlist-text">%s</span>' . $already_on, $text);
			}
		}
		if (!empty($icon)) {
			$icon_upload = tinv_get_option('add_to_wishlist' . ($this->is_loop ? '_catalog' : ''), 'icon_upload');
			$icon_upload_added = tinv_get_option('add_to_wishlist' . ($this->is_loop ? '_catalog' : ''), 'icon_upload_added');
			if ('custom' === $icon && !empty($icon_upload)) {
				$text = sprintf('<img src="%s" alt="%s"' . (!empty($icon_upload_added) ? ' class="icon-add-on-wishlist"' : '') . '  /> %s', esc_url($icon_upload), esc_attr(apply_filters('tinvwl_add_to_wishlist_text_loop', tinv_get_option('add_to_wishlist' . ($this->is_loop ? '_catalog' : ''), 'text'))), $text);
				if (!empty($icon_upload_added)) {
					$text = sprintf('<img src="%s" alt="%s" class="icon-already-on-wishlist" /> %s', esc_url($icon_upload_added), esc_attr(apply_filters('tinvwl_added_to_wishlist_text_loop', tinv_get_option('add_to_wishlist' . ($this->is_loop ? '_catalog' : ''), 'text_already_on'))), $text);
				}
			}
			$icon = 'tinvwl-icon-' . $icon;
			if ('custom' !== $icon && $icon_color) {
				$icon .= ' icon-' . $icon_color;
			}
		}
		$icon .= $icon_class;

		$icon .= tinv_get_option('add_to_wishlist' . ($this->is_loop ? '_catalog' : ''), 'already_on') ? ' tinvwl-product-already-on-wishlist' : '';

		$icon .= ' ' . tinv_get_option('add_to_wishlist' . ($this->is_loop ? '_catalog' : ''), 'class');

		$icon .= ' tinvwl-position-' . tinv_get_option('add_to_wishlist' . ($this->is_loop ? '_catalog' : ''), 'position');

		$icon .= (tinv_get_option('add_to_wishlist' . ($this->is_loop ? '_catalog' : ''), 'show_preloader')) ? ' ftinvwl-animated' : '';

		$icon .= $this->is_loop ? ' tinvwl-loop' : '';

		$product_variation = apply_filters('wpml_object_id', (($this->is_loop && in_array($this->product->get_type(), array(
				'variable',
				'variable-subscription',
			))) ? $this->variation_id : ($this->product->is_type('variation') ? $this->product->get_id() : 0)), 'product', true);

		$content .= sprintf('<a role="button" tabindex="0" name="%s" aria-label="%s" class="tinvwl_add_to_wishlist_button %s" data-tinv-wl-list="[]" data-tinv-wl-product="%s" data-tinv-wl-productvariation="%s" data-tinv-wl-productvariations="%s" data-tinv-wl-producttype="%s" data-tinv-wl-action="add">%s</a>',
			esc_attr(sanitize_title($button_text)),
			$button_text,
			$icon,
			apply_filters('wpml_object_id', ($this->product->is_type('variation') ? $this->product->get_parent_id() : $this->product->get_id()), 'product', true),
			($product_variation) ?: 0,
			json_encode(($this->is_loop && in_array($this->product->get_type(), array(
					'variable',
					'variable-subscription',
				))) ? $this->variation_ids : ($this->product->is_type('variation') ? array($this->product->get_id()) : array())),
			$this->product->get_type(),
			$text);
		$content .= apply_filters('tinvwl_wishlist_button_after', '');

		if (!empty($text)) {
			$content .= '<div class="tinv-wishlist-clear"></div>';
		}

		echo apply_filters('tinvwl_wishlist_button', $content, $this->wishlist, $this->product, $this->is_loop, $icon, $action, $text); // WPCS: xss ok.
	}

	/**
	 * Shortcode basic function
	 *
	 * @param array $atts Array parameter from shortcode.
	 *
	 * @return string
	 * @global object $product
	 *
	 */
	function shortcode($atts = array())
	{
		global $product;

		$default = array(
			'product_id' => 0,
			'variation_id' => 0,
			'loop' => 'no',
		);
		if ($product && is_a($product, 'WC_Product')) {
			$default['product_id'] = $product->is_type('variation') ? $product->get_parent_id() : $product->get_id();
			$default['variation_id'] = $product->is_type('variation') ? $product->get_id() : 0;
		}
		$atts = shortcode_atts($default, $atts);

		ob_start();
		if ('yes' === $atts['loop']) {
			$this->is_loop = true;
			$this->htmloutput($atts, true);
			$this->is_loop = false;
		} else {
			$this->htmloutput($atts, true);
		}

		return ob_get_clean();
	}

	/**
	 * Registers the WooCommerce blocks.
	 */
	function woocommerce_blocks()
	{
		/**
		 * Registers the custom product label block.
		 */
		register_block_type(
			'tinvwl/add-to-wishlist',
			[
				'render_callback' => array($this, 'woocommerce_blocks_render'),
			]
		);
	}

	/**
	 * Renders the WooCommerce blocks.
	 *
	 * @param array $attributes The block attributes.
	 *
	 * @return string The rendered output.
	 */
	function woocommerce_blocks_render($attributes)
	{
		global $product;

		ob_start();
		echo do_shortcode('[ti_wishlists_addtowishlist loop=yes]');
		$output = ob_get_clean();

		return $output;
	}

	/**
	 * Add button to WC Blocks
	 *
	 * @filter woocommerce_blocks_product_grid_item_html
	 */
	function htmloutput_block($html, $data, $product_object)
	{

		global $product;

		$position = tinv_get_option('add_to_wishlist_catalog', 'position');

		if (!in_array($position, array('before', 'after', 'above_thumb'))) {
			return $html;
		}

		$product = $product_object;
		ob_start();
		tinvwl_view_addto_htmlloop();
		$add_to_wishlist = ob_get_clean();

		if (!$add_to_wishlist) {
			return $html;
		}

		$add_to_wishlist_document = new Document();
		$add_to_wishlist_document->load($add_to_wishlist,
			false, Document::TYPE_HTML, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
		$add_to_wishlist_element = $add_to_wishlist_document->toElement();

		$product = '';

		$document = new Document();
		$document->load($html,
			false, Document::TYPE_HTML, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);

		if ('above_thumb' === $position && $document->find('.wc-block-grid__product-link')) {
			$document->find('.wc-block-grid__product-link')[0]->insertSiblingBefore($add_to_wishlist_element->getNode());
		}

		if ('before' === $position && $document->find('.wc-block-grid__product-add-to-cart')) {
			$document->find('.wc-block-grid__product-add-to-cart')[0]->insertSiblingBefore($add_to_wishlist_element->getNode());
		}

		if ('after' === $position && $document->find('.wc-block-grid__product-add-to-cart')) {
			$document->find('.wc-block-grid__product-add-to-cart')[0]->insertSiblingAfter($add_to_wishlist_element->getNode());
		}

		return $document->html();
	}

	/**
	 * Add button to WC Block All Products
	 *
	 */
	function woocommerce_blocks_all_products($description, $product_object)
	{

		global $product;

		// This is basically the store_api init, but as that calls no action, we need to replicate the logic of its protected function
		// here for the time being. IOK 2020-09-02
		if (empty($_SERVER['REQUEST_URI'])) {
			return $description;
		}
		if (!did_action('rest_api_init')) {
			return $description;
		}
		$request_uri = esc_url_raw(wp_unslash($_SERVER['REQUEST_URI']));

		if (preg_match('/wc\/store(\/v\d)?\/products/', $request_uri) !== 1) {
			return $description;
		}

		$product = $product_object;
		ob_start();
		echo do_shortcode('[ti_wishlists_addtowishlist loop=yes]');
		$add_to_wishlist = ob_get_clean();

		$product = '';

		return $description . $add_to_wishlist;
	}

	/**
	 * Outputs hidden input fields for default variation attributes.
	 *
	 * @param WC_Product $product The product.
	 * @param bool $loop Optional. Whether to enable the loop. Default is false.
	 */
	function default_variation_loop($product, $loop)
	{

		if ($loop && in_array($product->get_type(), array(
				'variable',
				'variable-subscription',
			))) {
			$match_attributes = [];
			foreach ($product->get_default_attributes() as $attribute_name => $value) {
				$match_attributes['attribute_' . sanitize_title($attribute_name)] = $value;
			}

			foreach ($match_attributes as $name => $value) {
				?>
				<input name="<?php echo esc_attr($name); ?>" type="hidden" value="<?php echo esc_attr($value); ?>"/>
				<?php
			}
		}
	}
}
