<?php // phpcs:ignore WordPress.Files.FileName
/**
 * The Class that manage the how many product there are in the cart.
 *
 * @package YITH WooCommerce Dynamic Pricing & Discounts\Classes\
 */

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

/**
 * The Class that manage the how many product there are in the cart.
 *
 * @package YITH WooCommerce Dynamic Pricing & Discounts\Classes\
 */
class YWDPD_Counter {
	use YWDPD_Singleton_Trait;

	/**
	 * Store for each product id how the quantity in the cart.
	 *
	 * @var array
	 */
	protected $products_counter = array();

	/**
	 * Store for each product variation id how the quantity in the cart.
	 *
	 * @var array
	 */
	protected $variations_counter = array();

	/**
	 * Store for each category id how many products there are in the cart.
	 *
	 * @var array
	 */
	protected $categories_counter = array();

	/**
	 * Store for each tag id how many products there are in the cart.
	 *
	 * @var array
	 */
	protected $tag_counter = array();

	/**
	 * Store for each taxonomy name how many products there are in the cart.
	 *
	 * @var array
	 */
	protected $taxonomy_counter = array();

	/**
	 * Store if a cart item has been counted.
	 *
	 * @var array
	 */
	protected $cart_item_counted = array();

	/**
	 * Store the counter for the special offers.
	 *
	 * @var array
	 */
	protected $special_offer_counter = array();

	/**
	 * Store how many products use a particular rule.
	 *
	 * @var array
	 */
	protected $product_with_dynamic_rule_counter = array();

	/**
	 * Store for each bogo rule, the cart item key of the product with the bogo applied
	 *
	 * @var array
	 */
	protected $cart_item_key_with_bogo = array();

	/**
	 * The construct of the class.
	 */
	public function __construct() {
		add_action( 'woocommerce_before_calculate_totals', array( $this, 'init_counters' ), 101, 1 );
		add_action( 'woocommerce_cart_loaded_from_session', array( $this, 'init_counters' ), 101, 1 );
		add_action( 'woocommerce_cart_emptied', array( $this, 'clear_counters' ), 0 );

	}

	/**
	 * Reset all counters
	 *
	 * @author YITH <plugins@yithemes.com>
	 * @since  3.0.0
	 */
	public function clear_counters() {
		$this->products_counter                  = array();
		$this->variations_counter                = array();
		$this->categories_counter                = array();
		$this->tag_counter                       = array();
		$this->taxonomy_counter                  = array();
		$this->cart_item_counted                 = array();
		$this->special_offer_counter             = array();
		$this->product_with_dynamic_rule_counter = array();
		$this->cart_item_key_with_bogo           = array();

	}

	/**
	 * Initialize all counters.
	 *
	 * @param WC_Cart $cart The current cart object.
	 *
	 * @since  3.0.0
	 */
	public function init_counters( $cart ) {

		$this->clear_counters();
		$cart = WC()->cart;
		/**
		 * APPLY_FILTERS: ywdpd_skip_cart_check
		 *
		 * Skip the cart check.
		 *
		 * @param bool $skip Skip or not.
		 *
		 * @return bool
		 */
		if ( ! is_null( $cart ) && ! $cart->is_empty() && ! apply_filters( 'ywdpd_skip_cart_check', false, $cart ) ) {

			foreach ( $cart->cart_contents as $cart_item_key => $cart_item ) {
				if ( isset( $cart_item['data'] ) ) {
					/**
					 * APPLY_FILTERS: ywdpd_init_cart_item_counter
					 *
					 * Process the cart item check.
					 *
					 * @param bool  $process Process item or not.
					 * @param array $cart_item The cart item.
					 *
					 * @return bool
					 */
					if ( apply_filters( 'ywdpd_init_cart_item_counter', true, $cart_item ) ) {
						WC()->cart->cart_contents[ $cart_item_key ]['available_quantity'] =  $cart_item['quantity'];

						if ( isset( WC()->cart->cart_contents[ $cart_item_key ]['ywdpd_discounts'] ) ) {
							if ( isset( WC()->cart->cart_contents[ $cart_item_key ]['ywdpd_discounts']['price_base'] ) ) {
								$price_to_restore = WC()->cart->cart_contents[ $cart_item_key ]['ywdpd_discounts']['price_base'];
							} else {
								$cart_product     = WC()->cart->cart_contents[ $cart_item_key ]['data'];
								$price_to_restore = wc_prices_include_tax() ? wc_get_price_including_tax( $cart_product ) : wc_get_price_excluding_tax( $cart_product );
							}
							/**
							 * APPLY_FILTERS: ywdpd_set_price_base
							 *
							 * Set the original product price.
							 *
							 * @param float  $price_to_restore The price.
							 * @param array  $cart_item The cart item.
							 * @param string $cart_item_key The cart item key.
							 *
							 * @return float
							 */
							$price_to_restore = apply_filters( 'ywdpd_set_price_base', $price_to_restore, WC()->cart->cart_contents[ $cart_item_key ], $cart_item_key );


							WC()->cart->cart_contents[ $cart_item_key ]['data']->set_price( $price_to_restore );
							unset( WC()->cart->cart_contents[ $cart_item_key ]['ywdpd_discounts'] );
						}
						if ( isset( WC()->cart->cart_contents[ $cart_item_key ]['has_bulk_applied'] ) ) {
							unset( WC()->cart->cart_contents[ $cart_item_key ]['has_bulk_applied'] );
						}

						if ( isset( WC()->cart->cart_contents[ $cart_item_key ]['has_special_offer'] ) ) {
							unset( WC()->cart->cart_contents[ $cart_item_key ]['has_special_offer'] );
						}
						if ( isset( WC()->cart->cart_contents[ $cart_item_key ]['has_last_deals_applied'] ) ) {
							unset( WC()->cart->cart_contents[ $cart_item_key ]['has_last_deals_applied'] );
						}
						if ( isset( WC()->cart->cart_contents[ $cart_item_key ]['has_gift_applied'] ) ) {
							unset( WC()->cart->cart_contents[ $cart_item_key ]['has_gift_applied'] );
						}
						$this->initialize_cart_item( $cart_item, $cart_item_key );
					}
				}
			}
		}
	}

	/**
	 * Initialize the single cart item.
	 *
	 * @param array  $cart_item The cart item object.
	 * @param string $cart_item_key The cart item key.
	 *
	 * @since  3.0.0
	 */
	public function initialize_cart_item( $cart_item, $cart_item_key ) {
		if ( ! isset( $this->cart_item_counted[ $cart_item_key ] ) ) {
			$this->cart_item_counted[ $cart_item_key ] = true;

			// Store the current quantity, useful for the special offers.

			$product = isset( $cart_item['data'] ) ? $cart_item['data'] : false;

			if ( $product instanceof WC_Product ) {

				$product_id   = 'variation' === $product->get_type() ? $product->get_parent_id() : $product->get_id();
				$variation_id = 'variation' === $product->get_type() ? $product->get_id() : false;
				$quantity     = isset( $cart_item['quantity'] ) ? $cart_item['quantity'] : 0;

				if ( isset( $this->products_counter[ $product_id ] ) ) {
					$this->products_counter[ $product_id ] += $quantity;
				} else {
					$this->products_counter[ $product_id ] = $quantity;
				}

				if ( $variation_id ) {

					if ( isset( $this->variations_counter[ $variation_id ] ) ) {
						$this->variations_counter[ $variation_id ] += $quantity;
					} else {
						$this->variations_counter[ $variation_id ] = $quantity;
					}
				}

				/**
				 * APPLY_FILTERS: ywdpd_cart_item_valid_for_count
				 *
				 * Allow to count the item.
				 *
				 * @param bool  $can_count If the item can count.
				 * @param array $cart_item If the item .
				 * @param bool  $cart_item_key If the item key.
				 *
				 * @return bool
				 */
				if ( apply_filters( 'ywdpd_cart_item_valid_for_count', true, $cart_item, $cart_item_key ) ) {
					$this->init_categories_counter( $product, $quantity );
					$this->init_tag_counter( $product, $quantity );

					if ( isset( $cart_item['has_bogo_applied'] ) && isset( $cart_item['original_cart_key'] ) ) {
						self::save_bogo_product_in_list( $cart_item['original_cart_key'], $cart_item_key );
					}
					/**
					 * APPLY_FILTERS: ywdpd_custom_taxonomy
					 *
					 * Add custom taxonomy.
					 *
					 * @param array $custom_taxonomies The custom taxonomies.
					 *
					 * @return array
					 */
					$custom_taxonomies = apply_filters( 'ywdpd_custom_taxonomy', array() );

					if ( count( $custom_taxonomies ) > 0 ) {
						foreach ( $custom_taxonomies as $taxonomy_name ) {
							$this->init_custom_taxonomy_counter( $product, $taxonomy_name, $quantity );
						}
					}
				}
			}
		}
	}


	/**
	 * Init the categories counter
	 *
	 * @param WC_Product $product the product.
	 * @param int        $quantity The quantity in cart line.
	 *
	 * @since  3.0.0
	 */
	public function init_categories_counter( $product, $quantity ) {

		$product_categories = YWDPD_Taxonomy::get_product_category_ids( $product );
		if ( count( $product_categories ) > 0 ) {

			foreach ( $product_categories as $category_id ) {

				if ( isset( $this->categories_counter[ $category_id ] ) ) {
					$this->categories_counter[ $category_id ] += $quantity;
				} else {
					$this->categories_counter[ $category_id ] = $quantity;
				}
			}
		}
	}

	/**
	 * Init the tag counter
	 *
	 * @param WC_Product $product the product.
	 * @param int        $quantity The quantity in cart line.
	 *
	 * @since  3.0.0
	 */
	public function init_tag_counter( $product, $quantity ) {

		$product_tags = YWDPD_Taxonomy::get_product_tag_ids( $product );

		if ( count( $product_tags ) > 0 ) {

			foreach ( $product_tags as $tag_id ) {

				if ( isset( $this->tag_counter[ $tag_id ] ) ) {
					$this->tag_counter[ $tag_id ] += $quantity;
				} else {
					$this->tag_counter[ $tag_id ] = $quantity;
				}
			}
		}
	}

	/**
	 * Init custom taxonomy counters
	 *
	 * @param WC_Product $product the product.
	 * @param string     $taxonomy_name The taxonomy name.
	 * @param int        $quantity the quantity.
	 *
	 * @since  3.0.0
	 */
	public function init_custom_taxonomy_counter( $product, $taxonomy_name, $quantity ) {

		$taxonomy_ids = YWDPD_Taxonomy::get_product_term_ids( $product, $taxonomy_name );

		if ( ! isset( $this->taxonomy_counter[ $taxonomy_name ] ) ) {
			$this->taxonomy_counter[ $taxonomy_name ] = array();
		}

		foreach ( $taxonomy_ids as $taxonomy_id ) {

			if ( isset( $this->taxonomy_counter[ $taxonomy_name ][ $taxonomy_id ] ) ) {
				$this->taxonomy_counter[ $taxonomy_name ][ $taxonomy_id ] += $quantity;
			} else {
				$this->taxonomy_counter[ $taxonomy_name ][ $taxonomy_id ] = $quantity;
			}
		}
	}

	/**
	 * Return how many product there are in the cart.
	 *
	 * @param int $product_id The product id.
	 *
	 * @return int
	 * @since  3.0.0
	 */
	public static function get_product_count( $product_id ) {
		return isset( self::$instance->products_counter[ $product_id ] ) ? self::$instance->products_counter[ $product_id ] : 0;
	}

	/**
	 * Return how many variation there are in the cart.
	 *
	 * @param int $variation_id The variation id.
	 *
	 * @return int
	 * @since  3.0.0
	 */
	public static function get_variation_count( $variation_id ) {
		return isset( self::$instance->variations_counter[ $variation_id ] ) ? self::$instance->variations_counter[ $variation_id ] : 0;
	}

	/**
	 * Return how many product category there are in the cart.
	 *
	 * @param int $category_id The category id.
	 *
	 * @return int
	 * @since  3.0.0
	 */
	public static function get_category_count( $category_id ) {
		return isset( self::$instance->categories_counter[ $category_id ] ) ? self::$instance->categories_counter[ $category_id ] : 0;
	}

	/**
	 * Return how many product tag there are in the cart.
	 *
	 * @param int $tag_id The tag id.
	 *
	 * @return int
	 * @since  3.0.0
	 */
	public static function get_tag_count( $tag_id ) {
		return isset( self::$instance->tag_counter[ $tag_id ] ) ? self::$instance->tag_counter[ $tag_id ] : 0;
	}

	/**
	 * Return how many product with specific taxonomy there are in the cart.
	 *
	 * @param int    $taxonomy_id The taxonomy id.
	 * @param string $taxonomy_name the taxonomy name.
	 *
	 * @return int
	 * @since  3.0.0
	 */
	public static function get_taxonomy_count( $taxonomy_id, $taxonomy_name ) {

		return isset( self::$instance->taxonomy_counter[ $taxonomy_name ] [ $taxonomy_id ] ) ? self::$instance->taxonomy_counter[ $taxonomy_name ] [ $taxonomy_id ] : 0;
	}

	/**
	 * Return the amount of products in the cart.
	 *
	 * @param array $product_ids The array with product id.
	 *
	 * @since  3.0.0
	 */
	public static function get_comulative_product_count( $product_ids = array() ) {

		$sum = 0;
		if ( count( $product_ids ) > 0 ) {

			foreach ( $product_ids as $product_id ) {
				$sum += self::$instance->get_product_count( $product_id ) + self::$instance->get_variation_count( $product_id );
			}
		} else {
			$sum = array_sum( self::$instance->products_counter );
		}

		return $sum;
	}


	/**
	 * Return the amount of tag in the cart.
	 *
	 * @param array $tag_ids The array with tag id.
	 *
	 * @since  3.0.0
	 */
	public static function get_comulative_tag_count( $tag_ids = array() ) {

		$sum = 0;
		if ( count( $tag_ids ) > 0 ) {

			foreach ( $tag_ids as $tag_id ) {
				$sum += self::$instance->get_tag_count( $tag_id );
			}
		} else {
			$sum = array_sum( self::$instance->tag_counter );
		}

		return $sum;
	}

	/**
	 * Return the amount of categories in the cart.
	 *
	 * @param array $category_ids The array with category id.
	 *
	 * @since  3.0.0
	 */
	public static function get_comulative_category_count( $category_ids = array() ) {

		$sum = 0;
		if ( count( $category_ids ) > 0 ) {

			foreach ( $category_ids as $category_id ) {
				$sum += self::$instance->get_category_count( $category_id );
			}
		} else {
			$sum = array_sum( self::$instance->categories_counter );
		}

		return $sum;
	}

	/**
	 * Return the amount of taxonomy in the cart.
	 *
	 * @param array  $taxonomy_ids The array with taxonomy id.
	 * @param string $taxonomy_name The taxonomy name.
	 *
	 * @since  3.0.0
	 */
	public static function get_comulative_taxonomy_count( $taxonomy_ids, $taxonomy_name ) {

		$sum = 0;
		if ( count( $taxonomy_ids ) > 0 ) {

			foreach ( $taxonomy_ids as $taxonomy_id ) {
				$sum += self::$instance->get_taxonomy_count( $taxonomy_id, $taxonomy_name );
			}
		}

		return $sum;
	}

	/**
	 * Return the amount of variations in the cart.
	 *
	 * @param array $variation_ids The array with product id.
	 *
	 * @since  3.0.0
	 */
	public static function get_comulative_variation_count( $variation_ids ) {

		$sum = 0;
		if ( count( $variation_ids ) > 0 ) {

			foreach ( $variation_ids as $variation_id ) {
				$sum += self::$instance->get_variation_count( $variation_id );
			}
		}

		return $sum;
	}


	/**
	 * Update the special offer counter
	 *
	 * @param string $key the array key.
	 * @param int    $amount the value.
	 *
	 * @since  3.0.0
	 */
	public static function update_special_offer_counter( $key, $amount ) {
		if ( isset( self::$instance->special_offer_counter[ $key ] ) ) {
			$old_amount                                    = self::$instance->special_offer_counter[ $key ];
			self::$instance->special_offer_counter[ $key ] = $old_amount - $amount;
		}
	}

	/**
	 * Reset the special offer counter
	 *
	 * @param string $key the array key.
	 * @param bool   $remove Check if remove or not the entry.
	 *
	 * @since  3.0.0
	 */
	public static function reset_special_offer_counter( $key, $remove = true ) {
		if ( isset( self::$instance->special_offer_counter[ $key ] ) ) {
			if ( $remove ) {
				unset( self::$instance->special_offer_counter[ $key ] );
			} else {
				self::$instance->special_offer_counter[ $key ] = 0;
			}
		}
	}

	/**
	 * Get the special offer counter
	 *
	 * @param string $key the array key.
	 * @param int    $default the default value.
	 *
	 * @return int
	 * @since  3.0.0
	 */
	public static function get_special_offer_counter( $key, $default = 0 ) {
		if ( isset( self::$instance->special_offer_counter[ $key ] ) ) {
			return self::$instance->special_offer_counter[ $key ];
		} else {
			self::$instance->special_offer_counter[ $key ] = $default;

			return $default;
		}
	}

	/**
	 * Update the counter
	 *
	 * @param string $rule_type The rule type, can be bulk, special_offer etc.
	 * @param int    $key The rule id.
	 * @param int    $amount The amount product with this rule.
	 *
	 * @since  3.0.0
	 */
	public static function update_product_with_dynamic_rule_counter( $rule_type, $key, $amount ) {

		if ( isset( self::$instance->product_with_dynamic_rule_counter[ $rule_type ][ $key ] ) ) {
			self::$instance->product_with_dynamic_rule_counter[ $rule_type ][ $key ] += $amount;
		} else {
			self::$instance->product_with_dynamic_rule_counter[ $rule_type ]         = array();
			self::$instance->product_with_dynamic_rule_counter[ $rule_type ][ $key ] = $amount;
		}
	}

	/**
	 * Get how many product use a particular rule
	 *
	 * @param string $rule_type The rule type, can be bulk , special_offer , etc.
	 * @param int    $key The rule id.
	 *
	 * @return int
	 *
	 * @since  3.0.0
	 */
	public static function get_product_with_dynamic_rule_counter( $rule_type, $key ) {

		if ( ! isset( self::$instance->product_with_dynamic_rule_counter[ $rule_type ][ $key ] ) ) {
			return 0;
		} else {
			return self::$instance->product_with_dynamic_rule_counter[ $rule_type ][ $key ];
		}
	}

	/**
	 * Store in the array a new product bogo
	 *
	 * @param string $original_cart_item_key The key of original item.
	 * @param string $cart_item_key_with_bogo The key of bogo item.
	 *
	 * @since  3.0.0
	 */
	public static function save_bogo_product_in_list( $original_cart_item_key, $cart_item_key_with_bogo ) {

		if ( ! isset( self::$instance->cart_item_key_with_bogo[ $original_cart_item_key ] ) ) {
			self::$instance->cart_item_key_with_bogo[ $original_cart_item_key ] = $cart_item_key_with_bogo;
		}
	}

	/**
	 * Check if a bogo product is in array.
	 *
	 * @param string $original_cart_item_key The key.
	 *
	 * @return bool|string
	 * @since  3.0.0
	 */
	public static function is_bogo_product_in_list( $original_cart_item_key ) {
		$found = false;
		if ( isset( self::$instance->cart_item_key_with_bogo[ $original_cart_item_key ] ) ) {
			$found = self::$instance->cart_item_key_with_bogo[ $original_cart_item_key ];
		}

		return $found;
	}

	/**
	 * Remove a bogo product from the array.
	 *
	 * @param string $original_cart_item_key The key.
	 *
	 * @since  3.0.0
	 */
	public static function remove_bogo_product_in_list( $original_cart_item_key ) {
		if ( isset( self::$instance->cart_item_key_with_bogo[ $original_cart_item_key ] ) ) {

			unset( self::$instance->cart_item_key_with_bogo[ $original_cart_item_key ] );

		}
	}

	/**
	 * Check if the ids in the list are in cart
	 *
	 * @param array  $list The ids list to check.
	 * @param string $taxonomy The taxonomy name.
	 * @param string $mode The mode to check.
	 *
	 * @since  3.0.0
	 */
	public static function check_taxonomy_in_cart( $list, $taxonomy, $mode ) {
		$check = false;

		foreach ( $list as $id ) {
			if ( 'product' === $taxonomy ) {

				if ( 'product_variation' === get_post_type( $id ) ) {
					$check = self::$instance::get_variation_count( $id ) > 0;
				} else {
					$check = self::$instance::get_product_count( $id ) > 0;
				}
			} else {
				if ( 'product_cat' === $taxonomy ) {
					$check = self::$instance::get_category_count( $id ) > 0;
				} elseif ( 'product_tag' === $taxonomy ) {
					$check = self::$instance::get_tag_count( $id ) > 0;
				} else {
					$check = self::$instance::get_taxonomy_count( $id, $taxonomy ) > 0;
				}
			}
			if ( 'at_least' === $mode && $check ) {
				break;
			} elseif ( 'all_product' === $mode && ! $check ) {
				break;
			}
		}

		return $check;
	}
}

YWDPD_Counter::get_instance();
