<?php
/**
 * This class manage all frontend features
 *
 * @package YITH\DynamicPricingAndDiscounts\Classes
 */

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

/**
 * Class YWDPD_Frontend
 */
class YWDPD_Frontend {
	use YWDPD_Singleton_Trait;

	/**
	 * This array will contain all dynamic prices ( the key will be the product id )
	 *
	 * @var array
	 */
	protected $has_get_price_filter;

	/**
	 * This array will contain all dynamic formatted prices ( the key will be the product id )
	 *
	 * @var array
	 */
	protected $has_get_price_html_filter;

	/**
	 * This array will contain for each product the bulk rule applied, each product can have only one bulk rule applied
	 * ( Quantity, Whole discount and Category discount are bulk rule )
	 *
	 * @var array
	 */
	protected $valid_bulk_rule;
	/**
	 * This array will contain all bulk rules
	 *
	 * @var array
	 */
	protected $all_bulk_rules;

	/**
	 * YWDPD_Frontend constructor.
	 *
	 * @author YITH <plugins@yithemes.com>
	 * @sice   3.0.0
	 */
	private function __construct() {

		$this->has_get_price_filter      = array();
		$this->has_get_price_html_filter = array();
		$this->valid_bulk_rule           = array();
		$this->all_bulk_rules            = array();

		add_action( 'init', array( $this, 'initialize_bulk_rules' ), 10 );
        add_filter( 'woocommerce_get_price_html', array( $this, 'get_product_price_html' ), 10, 2 );
		add_filter( 'woocommerce_variable_price_html', array( $this, 'get_product_variable_price_html' ), 10, 2 );
		add_filter(
			'woocommerce_available_variation',
			array(
				$this,
				'add_quantity_table_to_available_variation',
			),
			10,
			3
		);
		add_filter( 'woocommerce_show_variation_price', array( $this, 'show_variation_price' ), 99, 3 );
		add_action( 'ywdpd_before_calculate_discounts', array( $this, 'remove_price_filters' ), 99 );
		add_action( 'ywdpd_after_calculate_discounts', array( $this, 'add_price_filters' ), 99 );
		add_action( 'ywdpd_before_replace_cart_item_price', array( $this, 'remove_price_filters' ), 99 );
		add_action( 'ywdpd_after_replace_cart_item_price', array( $this, 'add_price_filters' ), 99 );
		if ( yith_plugin_fw_wc_is_using_block_template_in_single_product() ) {
			add_action( 'template_redirect', array( $this, 'init_tables_and_notices_block' ), 20 );
			add_filter( 'ywdpd_column_product_info_class', array( $this, 'add_custom_block_class' ), 10 );
		} else {
			add_action( 'init', array( $this, 'init_tables_and_notices' ), 20 );
		}

		add_filter( 'woocommerce_cart_item_price', array( $this, 'replace_cart_item_price' ), 100, 3 );
		add_filter( 'woocommerce_cart_item_subtotal', array( $this, 'replace_cart_item_subtotal' ), 100, 3 );

		add_action( 'woocommerce_before_cart_totals', array( $this, 'show_discount_info_in_cart_check' ) );
		add_action( 'render_block_woocommerce/cart', array( $this, 'display_free_shipping_banner_block' ), 10, 2 );

		// Store the rules in the order item.
		add_action(
			'woocommerce_checkout_create_order_line_item',
			array(
				$this,
				'save_dynamic_rules_in_order_item',
			),
			20,
			3
		);
		add_action(
			'woocommerce_checkout_create_order_coupon_item',
			array(
				$this,
				'save_dynamic_coupon_name',
			),
			20,
			2
		);

		add_action( 'woocommerce_order_item_meta_end', array( $this, 'format_custom_meta_data' ), 20, 2 );

		add_action( 'woocommerce_before_cart_table', array( $this, 'show_cart_notices' ) );

		add_filter( 'woocommerce_quantity_input_args', array( $this, 'manage_qty_input_args' ), 100, 2 );
		add_filter(
			'woocommerce_store_api_product_quantity_editable',
			array(
				$this,
				'manage_qty_input_args_wc_blocks',
			),
			20,
			3
		);
		add_filter( 'woocommerce_add_cart_item_data', array( $this, 'manage_cart_item_data' ), 100, 4 );

	}


	/**
	 * Initialize the bulk rules
	 *
	 * @return void
	 * @since 4.8
	 */
	public function initialize_bulk_rules() {
		$this->all_bulk_rules = ywdpd_get_price_rules_by_type(
			array(
				'bulk',
				'discount_whole',
				'category_discount',
			)
		);
	}

	/**
	 * Check if there are bulk rules
	 *
	 * @return bool
	 * @since 4.8
	 */
	public function has_bulk_rules() {
		return count( $this->all_bulk_rules ) > 0;
	}

	/**
	 * Manage Cart Rule notice in cart page.
	 *
	 * @return void
	 * @since  3.12.1
	 */
	public function show_discount_info_in_cart_check() {
		if ( YITH_WC_Dynamic_Options::show_discount_info_in_cart() ) {
			if ( ! is_null( WC()->cart ) && WC()->cart->needs_shipping() && WC()->cart->show_shipping() ) {
				add_action(
					'woocommerce_cart_totals_before_shipping',
					array(
						$this,
						'show_total_discount_message_on_cart',
					),
					99
				);
			} else {
				add_action(
					'woocommerce_cart_totals_before_order_total',
					array(
						$this,
						'show_total_discount_message_on_cart',
					),
					99
				);
			}
		}
	}

	public function display_free_shipping_banner_block( $content, $parsed_block ) {
		ob_start();
		$this->show_cart_notices();
		$banner = ob_get_clean();

		return $banner . $content;
	}


	/**
	 * Check if is possible calculate the dynamic price.
	 *
	 * @param float      $price The product price.
	 * @param WC_Product $product The product.
	 *
	 * @return bool
	 * @since  3.0.0
	 */
	public function can_calculate_dynamic_price( $price, $product ) {

		$can_calculate = ! ( is_cart() || is_checkout() || empty( $price ) );

		/**
		 * APPLY_FILTERS: ywdpd_can_calculate_dynamic_price
		 *
		 * Can calculate the dynamic price for the product.
		 *
		 * @param bool       $can_calculate True or false.
		 * @param WC_Product $product The product.
		 *
		 * @return bool
		 */
		return apply_filters( 'ywdpd_can_calculate_dynamic_price', $can_calculate, $product );
	}

	/**
	 * Return the right price html with the dynamic price format.
	 *
	 * @param string     $price_html The price html.
	 * @param WC_Product $product The product.
	 *
	 * @return string
	 * @since  3.0.0
	 */
	public function get_product_price_html( $price_html, $product ) {
		if ( $this->has_bulk_rules() ) {
			$this->remove_price_filters();
			if ( ( 'grouped' !== $product->get_type() && 'variable' !== $product->get_type() ) && $this->can_calculate_dynamic_price( $price_html, $product ) ) {
				if ( isset( $this->has_get_price_html_filter[ $product->get_id() ] ) ) {
					$price_html = $this->has_get_price_html_filter[ $product->get_id() ];
				} else {
					/**
					 * APPLY_FILTERS: ywdpd_change_base_price
					 *
					 * Change the base product price.
					 *
					 * @param float      $price The price.
					 * @param WC_Product $product The product.
					 *
					 * @return float
					 */
					$old_price = apply_filters( 'ywdpd_change_base_price', floatval( $product->get_price() ), $product );
					$new_price = $this->get_quantity_price( $old_price, $product );
					if ( $old_price !== $new_price && isset( $this->valid_bulk_rule[ $product->get_id() ] ) ) {
						$price_html = YWDPD_Utils::get_product_new_price_html( $old_price, $new_price, $product );
						/**
						 * APPLY_FILTERS: ywdpd_get_product_price_html
						 *
						 * Return the price html.
						 *
						 * @param string     $price_html price html.
						 * @param float      $old_price The old price.
						 * @param float      $new_price The new price.
						 * @param WC_Product $product The product.
						 *
						 * @return string
						 */
						$price_html = apply_filters( 'ywdpd_get_product_price_html', $price_html, $old_price, $new_price, $product );
					}
					$this->has_get_price_html_filter[ $product->get_id() ] = $price_html;

				}
			}
			$this->add_price_filters();
		}

		return $price_html;
	}

	/**
	 * Return the right price html with the Dynamic price format
	 *
	 * @param string              $price_html The price html.
	 * @param WC_Product_Variable $product The variable product.
	 *
	 * @return string
	 * @since  3.0.0
	 */
	public function get_product_variable_price_html( $price_html, $product ) {

		if ( $this->has_bulk_rules() && $this->can_calculate_dynamic_price( $price_html, $product ) ) {

			if ( isset( $this->has_get_price_html_filter[ $product->get_id() ] ) ) {
				$price_html = $this->has_get_price_html_filter[ $product->get_id() ];
			} else {

				$this->remove_price_filters();
				$min_price = $product->get_variation_price( 'min' );
				$max_price = $product->get_variation_price( 'max' );
				$min_dynamic_price = $this->get_variation_dynamic_price( $product );
				$max_dynamic_price = $this->get_variation_dynamic_price( $product, 'max' );
				$has_dynamic_price = ( $min_price !== $min_dynamic_price || $max_price !== $max_dynamic_price );

				if ( ! empty( $min_dynamic_price ) && ! empty( $max_dynamic_price ) && $has_dynamic_price ) {
					$min_dynamic_price = wc_get_price_to_display(
						$product,
						array(
							'qty'   => 1,
							'price' => $min_dynamic_price,
						)
					);
					$max_dynamic_price = wc_get_price_to_display(
						$product,
						array(
							'qty'   => 1,
							'price' => $max_dynamic_price,
						)
					);
					$min_price         = wc_get_price_to_display(
						$product,
						array(
							'qty'   => 1,
							'price' => $min_price,
						)
					);
					$max_price         = wc_get_price_to_display(
						$product,
						array(
							'qty'   => 1,
							'price' => $max_price,
						)
					);

					if ( $min_price !== $max_price ) {
						/**
						 * APPLY_FILTERS: ywdpd_change_variable_products_html_regular_price
						 *
						 * Return the variable regular price html.
						 *
						 * @param string     $price_html price html.
						 * @param float      $min The min price.
						 * @param float      $max The max price.
						 * @param WC_Product $product The product.
						 *
						 * @return string
						 */
						$original_price_html = apply_filters( 'ywdpd_change_variable_products_html_regular_price', wc_format_price_range( $min_price, $max_price ), $min_price, $max_price, $product );
					} else {
						$original_price_html = wc_price( $min_price );
					}

					if ( $min_dynamic_price !== $max_dynamic_price ) {
						/**
						 * APPLY_FILTERS: ywdpd_change_variable_products_html_discount_price
						 *
						 * Return the variable dynamic price html.
						 *
						 * @param string     $price_html price html.
						 * @param float      $min The min price.
						 * @param float      $max The max price.
						 * @param WC_Product $product The product.
						 *
						 * @return string
						 */
						$dynamic_price_html = apply_filters( 'ywdpd_change_variable_products_html_discount_price', wc_format_price_range( $min_dynamic_price, $max_dynamic_price ), $min_dynamic_price, $max_dynamic_price, $product );
					} else {
						$dynamic_price_html = wc_price( $min_dynamic_price );
					}

					$min_percentage_discount = YWDPD_Utils::get_discount_percentage( $min_dynamic_price, $min_price );
					$max_percentage_discount = YWDPD_Utils::get_discount_percentage( $max_dynamic_price, $max_price );
					if ( $min_percentage_discount > 0 ) {
						if ( $min_percentage_discount !== $max_percentage_discount ) {
							$percentage_discount = YWDPD_Utils::get_discount_percentage_from_to_html( $min_percentage_discount, $max_percentage_discount );
						} else {
							$percentage_discount = YWDPD_Utils::get_discount_percentage_html( $min_percentage_discount );
						}
					} else {
						$percentage_discount = '';
					}

					$price_html = YWDPD_Utils::get_formatted_price_html( $original_price_html, $dynamic_price_html, $percentage_discount, $product ) . $product->get_price_suffix();

					/**
					 * APPLY_FILTERS: ywdpd_get_variable_price_html
					 *
					 * Return the variable price html.
					 *
					 * @param string     $price_html price html.
					 * @param float      $min_price The min price.
					 * @param float      $max_price The max price.
					 * @param float      $min_dynamic_price The min dynamic price.
					 * @param float      $max_dynamic_price The max dynamic price.
					 * @param WC_Product $product The product.
					 *
					 * @return string
					 */
					$price_html = apply_filters( 'ywdpd_get_variable_price_html', $price_html, $min_price, $max_price, $min_dynamic_price, $max_dynamic_price, $product );

					$this->has_get_price_html_filter[ $product->get_id() ] = $price_html;
				}
			}
			$this->add_price_filters();

		}

		return $price_html;
	}

	/**
	 * Return the dynamic price
	 *
	 * @param float      $price The base price.
	 * @param WC_Product $product The product.
	 * @param int        $qt The quantity to check.
	 * @param bool       $check_loop Force to check if is rule is enabled on loop.
	 *
	 * @return float
	 * @since  3.0.0
	 */
	public function get_dynamic_price( $price, $product, $qt = 1, $check_loop = false ) {

		$price_rules = ywdpd_get_price_rules_by_type( array( 'bulk', 'discount_whole', 'category_discount' ) );

		if ( is_array( $price_rules ) && count( $price_rules ) > 0 ) {

			foreach ( $price_rules as $price_rule ) {
				/**
				 * Current price rule
				 *
				 * @var YWDPD_Price_Rule $price_rule
				 */

				if ( $price_rule->is_valid() ) {

					$is_valid_to_apply = $this->rule_is_valid_for_product( $product, $price_rule );
					if ( $is_valid_to_apply ) {
						$price = $price_rule->get_discounted_price( $price, $qt, $product );
						break;
					}
				}
			}
		}

		return $price;
	}

	/**
	 * Check if the rule is valid for product.
	 *
	 * @param WC_Product       $product The product.
	 * @param YWDPD_Price_Rule $price_rule The rule.
	 *
	 * @since  3.0.0
	 */
	public function rule_is_valid_for_product( $product, $price_rule ) {
		if ( ! $product ) {
			return false;
		}

		$is_enabled_for_on_sale_products = ! ( $product->is_on_sale() && $price_rule->is_disabled_on_sale() );

		$show_in_loop = is_single() || ( method_exists( $price_rule, 'can_show_discount_in_loop' ) && $price_rule->can_show_discount_in_loop() );
		$is_valid     = false;

		if ( apply_filters( 'ywdpd_rule_is_valid_for_product', true, $product, $price_rule ) ) {
			if ( $is_enabled_for_on_sale_products && $show_in_loop && $price_rule->is_valid() ) {
				$rule_adjustment_to_active = $price_rule->is_enabled_apply_adjustment_to();

				if ( $rule_adjustment_to_active ) {
					$is_valid = $this->is_price_rule_valid_in_cart( $price_rule ) && $price_rule->is_valid_to_adjust( $product, 'variable' === $product->get_type() );
				} else {
					$is_valid = $price_rule->is_valid_to_apply( $product, 'variable' === $product->get_type() );

				}
			}
		}

		/**
		 * APPLY_FILTERS: ywdpd_is_rule_valid_in_cart
		 *
		 * The price rule is valid in cart or not.
		 *
		 * @param bool                                                              $is_valid True or false.
		 * @param YWDPD_Quantity_Table|YWDPD_Discount_Whole|YWDPD_Category_Discount $price_rule The rule.
		 * @param WC_Product                                                        $product The product.
		 *
		 * @return bool
		 */
		return apply_filters( 'ywpdpd_is_valid_for_product', $is_valid, $price_rule, $product );
	}

	/**
	 * Check if a rule has already been applied in the cart
	 *
	 * @param YWDPD_Price_Rule $price_rule The price rule.
	 *
	 * @return  bool
	 * @since  3.0.0
	 */
	public function is_price_rule_valid_in_cart( $price_rule ) {

		$is_valid = false;

		if ( ! is_null( WC()->cart ) && ! WC()->cart->is_empty() ) {

			foreach ( WC()->cart->get_cart_contents() as $cart_item_key => $cart_item ) {
				$product = $cart_item['data'];

				if ( $price_rule->is_valid_to_apply( $product ) && ! isset( $cart_item['has_bulk_applied'] ) ) {
					$is_valid = true;
					break;
				}
			}
		}

		return $is_valid;
	}

	/**
	 * Get the min or max dynamic price for the variable product
	 *
	 * @param WC_Product_Variable $product The variable product.
	 * @param string              $min_max Min or max value.
	 * @param bool                $for_display Return price for display or not.
	 *
	 * @since  3.0.0
	 */
	public function get_variation_dynamic_price( $product, $min_max = 'min', $for_display = false ) {

		$variable_rule          = $this->get_valid_bulk_rule_for_product( $product );
		$prices        = $product->get_variation_prices();
		$prices        = isset( $prices['price'] ) ? $prices['price'] : array();
		$min_price     = current( $prices );
		$max_price     = end( $prices );
		$current_price        = 'min' === $min_max ? $min_price : $max_price;
		$current_variation_id = array_search( $current_price, $prices ); //phpcs:ignore  WordPress.PHP.StrictInArray.MissingTrueStrict
		$variation = wc_get_product( $current_variation_id );
        $variation_rule = $this->get_valid_bulk_rule_for_product( $variation );
        $rule = $variation_rule !== false ? $variation_rule : $variable_rule;
        if ( false !== $rule ) {
			if ( $min_price === $max_price ) {
				foreach ( $prices as $variation_id => $price ) {
					$variation = wc_get_product( $variation_id );
					/**
					 * APPLY_FILTERS: ywdpd_change_base_price
					 *
					 * Change the base product price.
					 *
					 * @param float      $price The price.
					 * @param WC_Product $product The product.
					 *
					 * @return float
					 */
					$price = apply_filters( 'ywdpd_change_base_price', $price, $variation );
					if ( $this->rule_is_valid_for_product( $variation, $rule ) ) {
						$this->valid_bulk_rule[ $variation_id ] = $rule;
						$new_price                              = $this->get_quantity_price( $price, $variation, $rule );
                    } else {
						$new_price = $price;
					}
					if ( $new_price < $current_price ) {
						$current_price = $new_price;
					}
				}
			} else {

				$variation = wc_get_product( $current_variation_id );
				/**
				 * APPLY_FILTERS: ywdpd_change_base_price
				 *
				 * Change the base product price.
				 *
				 * @param float      $price The price.
				 * @param WC_Product $product The product.
				 *
				 * @return float
				 */
				$current_price = apply_filters( 'ywdpd_change_base_price', $current_price, $variation );
                if ( $this->rule_is_valid_for_product( $variation, $rule ) ) {

					$this->valid_bulk_rule[ $current_variation_id ] = $rule;
					$new_price                                      = $this->get_quantity_price( $current_price, $variation, $variation_rule );
                } else {
					$new_price = $current_price;
				}
				if ( $new_price < $current_price ) {
					$current_price = $new_price;
				}
			}
		}

		return $current_price;
	}


	/**
	 * Return the right price for a "Bulk" rule ( quantity table,whole discount and category discount )
	 *
	 * @param float           $price The product price.
	 * @param WC_Product      $product The product.
	 * @param bool|YWDPD_Rule $rule The rule.
	 *
	 * @since  3.0.0
	 */
	public function get_quantity_price( $price, $product, $rule = false ) {

		if ( false === $rule ) {
			$rule = $this->get_valid_bulk_rule_for_product( $product );
		}
		if ( false !== $rule ) {
			$show_minimum_price = YITH_WC_Dynamic_Options::get_default_price();
			if ( 'bulk' === $rule->get_discount_mode() && 'max' === $show_minimum_price ) {
				$qty_type          = $rule->get_qty_type();
				$table_price_rules = 'range' === $qty_type ? $rule->get_rules() : $rule->get_fixed_rules();
				$max_rule_discount = end( $table_price_rules );
				$price             = $rule->get_discount_amount( $price, $max_rule_discount );
			} else {

				$price = $rule->get_discounted_price( $price, 1, $product );

			}
		}

		return $price;
	}

	/**
	 * Return a valid bulk rule if exist, otherwise false
	 *
	 * @param WC_Product $product The product.
	 *
	 * @return YWDPD_Quantity_Table|YWDPD_Discount_Whole|YWDPD_Category_Discount|bool
	 * @since  3.0.0
	 */
	public function get_valid_bulk_rule_for_product( $product ) {
		if ( ! $product instanceof WC_Product ) {
			return false;
		}
		$product_id = $product->get_id();
		$rule       = false;
		$this->remove_price_filters();

		if ( isset( $this->valid_bulk_rule[ $product_id ] ) ) {
			$rule = $this->valid_bulk_rule[ $product_id ];
		} else {

			if ( is_array( $this->all_bulk_rules ) && count( $this->all_bulk_rules ) > 0 ) {

				$found = false;

				foreach ( $this->all_bulk_rules as $price_rule ) {
					/**
					 * Current price rule
					 *
					 * @var YWDPD_Price_Rule $price_rule
					 */

					$is_valid = $this->rule_is_valid_for_product( $product, $price_rule );

					if ( $is_valid ) {
						$this->valid_bulk_rule[ $product_id ] = $price_rule;
						$rule                                 = $this->valid_bulk_rule[ $product_id ];
						$found                                = true;
					}


					if ( $found ) {
						break;
					}
				}
			}
		}
		$this->add_price_filters();

		return $rule;
	}


	/**
	 * Init the hook to show quantity table and notices in the product page
	 *
	 * @since  3.0.0
	 */
	public function init_tables_and_notices() {
		$show_tables = YITH_WC_Dynamic_Options::show_quantity_table();

		if ( yith_plugin_fw_is_true( $show_tables ) ) {
			$this->init_quantity_tables();
		}

		$show_notice = YITH_WC_Dynamic_Options::show_note_on_products();

		if ( yith_plugin_fw_is_true( $show_notice ) ) {
			$this->init_product_notices();
		}
	}

	/**
	 * Show tables and notes in product block
	 *
	 * @return void
	 */
	public function init_tables_and_notices_block() {

		$show_tables    = YITH_WC_Dynamic_Options::show_quantity_table();
		$table_position = YITH_WC_Dynamic_Options::get_quantity_table_position();

		if ( $show_tables && 'shortcode' !== $table_position ) {
			switch ( $table_position ) {
				case 'before_excerpt':
				case 'after_excerpt':
					$hook = 'render_block_core/post-excerpt';
					break;
				case 'before_add_to_cart':
				case 'after_add_to_cart':
					$hook = 'render_block_woocommerce/add-to-cart-form';
					break;
				default:
					$hook = 'render_block_woocommerce/product-meta';
					break;
			}
			$method = 'show_table_' . $table_position . '_block';
			add_filter( $hook, array( $this, $method ), 10, 3 );
		}

		$show_notice     = YITH_WC_Dynamic_Options::show_note_on_products();
		$notice_position = YITH_WC_Dynamic_Options::get_show_note_on_products_place();

		if ( $show_notice && 'shortcode' !== $notice_position ) {
			switch ( $notice_position ) {
				case 'before_excerpt':
				case 'after_excerpt':
					$hook = 'render_block_core/post-excerpt';
					break;
				case 'before_add_to_cart':
				case 'after_add_to_cart':
					$hook = 'render_block_woocommerce/add-to-cart-form';
					break;
				default:
					$hook = 'render_block_woocommerce/product-meta';
					break;
			}
			$method = 'show_notice_' . $notice_position . '_block';
			add_filter( $hook, array( $this, $method ), 11, 3 );
		}

	}

	/**
	 * Show the table in the right position
	 *
	 * @since  3.0.0
	 */
	public function init_quantity_tables() {
		$table_position = YITH_WC_Dynamic_Options::get_quantity_table_position();

		if ( 'shortcode' !== $table_position ) {
			$priority_single_add_to_cart = has_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_add_to_cart' );
			$priority_single_excerpt     = has_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_excerpt' );
			/**
			 * APPLY_FILTERS: ywdpd_table_custom_hook
			 *
			 * Return a custom hook to show the table.
			 *
			 * @param array $custom_hook The custom hook.
			 *
			 * @return array
			 */
			$custom_hook = apply_filters( 'ywdpd_table_custom_hook', array() );
			if ( ! empty( $custom_hook ) && isset( $custom_hook['hook'] ) ) {
				$hook     = $custom_hook['hook'];
				$priority = isset( $custom_hook['priority'] ) ? $custom_hook['priority'] : 10;
				add_action( $hook, array( $this, 'show_table_quantity' ), $priority );
			} else {
				switch ( $table_position ) {
					case 'after_add_to_cart':
						if ( $priority_single_add_to_cart ) {
							$priority_single_add_to_cart ++;
						} else {
							$priority_single_add_to_cart = 32;
						}

						add_action(
							'woocommerce_single_product_summary',
							array(
								$this,
								'show_table_quantity',
							),
							$priority_single_add_to_cart
						);
						break;
					case 'before_excerpt':
						if ( $priority_single_excerpt ) {
							$priority_single_excerpt --;
						} else {
							$priority_single_excerpt = 18;
						}
						add_action(
							'woocommerce_single_product_summary',
							array(
								$this,
								'show_table_quantity',
							),
							$priority_single_excerpt
						);
						break;
					case 'after_excerpt':
						if ( $priority_single_excerpt ) {
							$priority_single_excerpt ++;
						} else {
							$priority_single_excerpt = 22;
						}
						add_action(
							'woocommerce_single_product_summary',
							array(
								$this,
								'show_table_quantity',
							),
							$priority_single_excerpt
						);

						break;
					case 'after_meta':
						$priority_after_meta = has_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_meta' );
						if ( $priority_after_meta ) {
							$priority_after_meta ++;

						} else {
							$priority_after_meta = 42;
						}
						add_action(
							'woocommerce_single_product_summary',
							array(
								$this,
								'show_table_quantity',
							),
							$priority_after_meta
						);
						break;
					default:
						if ( $priority_single_add_to_cart ) {

							$priority_single_add_to_cart --;

						} else {
							$priority_single_add_to_cart = 28;
						}
						add_action(
							'woocommerce_single_product_summary',
							array(
								$this,
								'show_table_quantity',
							),
							$priority_single_add_to_cart
						);
						break;
				}
			}
		}
	}

	/**
	 * Show table before excerpt
	 *
	 * @param string   $content The content.
	 * @param array    $parsed_block The parsed block
	 * @param WP_Block $block The block.
	 *
	 * @return string
	 */
	public function show_table_before_excerpt_block( $content, $parsed_block, $block ) {
		return $this->show_table_in_excerpt_block( $content, false );
	}

	/**
	 * Show table after excerpt
	 *
	 * @param string   $content The content.
	 * @param array    $parsed_block The parsed block
	 * @param WP_Block $block The block.
	 *
	 * @return string
	 */
	public function show_table_after_excerpt_block( $content, $parsed_block, $block ) {
		return $this->show_table_in_excerpt_block( $content );
	}

	/**
	 * Print the table in excerpt
	 *
	 * @param string $content The content.
	 * @param bool   $after Add the table after the content.
	 *
	 * @return string
	 */
	public function show_table_in_excerpt_block( $content, $after = true ) {
		global $post;
		if ( $post && 'product' === get_post_type( $post ) ) {
			$product = wc_get_product( $post->ID );
			ob_start();
			$this->show_table_quantity( $product );
			$table = ob_get_clean();
			if ( $after ) {
				$content = $content . $table;
			} else {
				$content = $table . $content;
			}
		}

		return $content;
	}

	/**
	 * Show table before add to cart form
	 *
	 * @param string   $content The content.
	 * @param array    $parsed_block The parsed block
	 * @param WP_Block $block The block.
	 *
	 * @return string
	 */
	public function show_table_before_add_to_cart_block( $content, $parsed_block, $block ) {
		return $this->show_table_in_add_to_cart_block( $content, $block, false );
	}

	/**
	 * Show table after add to cart form
	 *
	 * @param string   $content The content.
	 * @param array    $parsed_block The parsed block
	 * @param WP_Block $block The block.
	 *
	 * @return string
	 */
	public function show_table_after_add_to_cart_block( $content, $parsed_block, $block ) {
		return $this->show_table_in_add_to_cart_block( $content, $block );
	}

	/**
	 * Print the table in add to cart form
	 *
	 * @param string $content The content.
	 * @param bool   $after Add the table after the content.
	 *
	 * @return string
	 */
	public function show_table_in_add_to_cart_block( $content, $block, $after = true ) {
		$post_id = $block->context['postId'];
		$product = wc_get_product( $post_id );
		ob_start();
		$this->show_table_quantity( $product );
		$table = ob_get_clean();
		if ( $after ) {
			$content = $content . $table;
		} else {
			$content = $table . $content;
		}

		return $content;
	}

	/**
	 * Show table after product meta
	 *
	 * @param string   $content The content.
	 * @param array    $parsed_block The parsed block
	 * @param WP_Block $block The block.
	 *
	 * @return string
	 */
	public function show_table_after_meta_block( $content, $parsed_block, $block ) {
		global $post;

		if ( $post && 'product' == get_post_type( $post ) ) {
			$product = wc_get_product( $post->ID );
			ob_start();
			$this->show_table_quantity( $product );
			$table = ob_get_clean();

			$content = $content . $table;
		}

		return $content;
	}


	/**
	 * Show notice before excerpt
	 *
	 * @param string   $content The content.
	 * @param array    $parsed_block The parsed block
	 * @param WP_Block $block The block.
	 *
	 * @return string
	 */
	public function show_notice_before_excerpt_block( $content, $parsed_block, $block ) {
		return $this->show_notice_in_excerpt_block( $content, false );
	}

	/**
	 * Show notice after excerpt
	 *
	 * @param string   $content The content.
	 * @param array    $parsed_block The parsed block
	 * @param WP_Block $block The block.
	 *
	 * @return string
	 */
	public function show_notice_after_excerpt_block( $content, $parsed_block, $block ) {
		return $this->show_notice_in_excerpt_block( $content );
	}

	/**
	 * Print the notice in excerpt
	 *
	 * @param string $content The content.
	 * @param bool   $after Add the notice after the content.
	 *
	 * @return string
	 */
	public function show_notice_in_excerpt_block( $content, $after = true ) {
		global $post;
		if ( $post && 'product' === get_post_type( $post ) ) {
			$product = wc_get_product( $post->ID );
			ob_start();
			$this->show_note_on_products( $product );
			$table = ob_get_clean();
			if ( $after ) {
				$content = $content . $table;
			} else {
				$content = $table . $content;
			}
		}

		return $content;
	}

	/**
	 * Show notice before add to cart form
	 *
	 * @param string   $content The content.
	 * @param array    $parsed_block The parsed block
	 * @param WP_Block $block The block.
	 *
	 * @return string
	 */
	public function show_notice_before_add_to_cart_block( $content, $parsed_block, $block ) {
		return $this->show_notice_in_add_to_cart_block( $content, $block, false );
	}

	/**
	 * Show notice after add to cart form
	 *
	 * @param string   $content The content.
	 * @param array    $parsed_block The parsed block
	 * @param WP_Block $block The block.
	 *
	 * @return string
	 */
	public function show_notice_after_add_to_cart_block( $content, $parsed_block, $block ) {
		return $this->show_notice_in_add_to_cart_block( $content, $block );
	}

	/**
	 * Print the notice in add to cart form
	 *
	 * @param string $content The content.
	 * @param bool   $after Add the notice after the content.
	 *
	 * @return string
	 */
	public function show_notice_in_add_to_cart_block( $content, $block, $after = true ) {
		$post_id = $block->context['postId'];
		$product = wc_get_product( $post_id );
		ob_start();
		$this->show_note_on_products( $product );
		$table = ob_get_clean();
		if ( $after ) {
			$content = $content . $table;
		} else {
			$content = $table . $content;
		}

		return $content;
	}

	/**
	 * Show notice after product meta
	 *
	 * @param string   $content The content.
	 * @param array    $parsed_block The parsed block
	 * @param WP_Block $block The block.
	 *
	 * @return string
	 */
	public function show_notice_after_meta_block( $content, $parsed_block, $block ) {
		global $post;

		if ( $post && 'product' == get_post_type( $post ) ) {
			$product = wc_get_product( $post->ID );
			ob_start();
			$this->show_note_on_products( $product );
			$table = ob_get_clean();

			$content = $content . $table;
		}

		return $content;
	}

	/**
	 * Show the rule notices in the right position
	 *
	 * @since  3.0.0
	 */
	public function init_product_notices() {
		$position                    = YITH_WC_Dynamic_Options::get_show_note_on_products_place();
		$priority_single_add_to_cart = has_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_add_to_cart' );
		$priority_single_excerpt     = has_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_excerpt' );

		if ( 'shortcode' !== $position ) {
			$custom_hook = apply_filters( 'ywdpd_note_custom_hook', array() );

			if ( ! empty( $custom_hook ) && isset( $custom_hook['hook'] ) ) {
				$hook     = $custom_hook['hook'];
				$priority = isset( $custom_hook['priority'] ) ? $custom_hook['priority'] : 10;
				add_action( $hook, array( $this, 'show_note_on_products' ), $priority );

				return;
			}

			switch ( $position ) {
				case 'after_add_to_cart':
					if ( $priority_single_add_to_cart ) {
						$priority_single_add_to_cart ++;
					} else {
						$priority_single_add_to_cart = 32;
					}
					add_action(
						'woocommerce_single_product_summary',
						array(
							$this,
							'show_note_on_products',
						),
						$priority_single_add_to_cart
					);
					break;
				case 'before_excerpt':
					if ( $priority_single_excerpt ) {
						$priority_single_excerpt --;
					} else {
						$priority_single_excerpt = 18;
					}
					add_action(
						'woocommerce_single_product_summary',
						array(
							$this,
							'show_note_on_products',
						),
						$priority_single_excerpt
					);
					break;
				case 'after_excerpt':
					if ( $priority_single_excerpt ) {
						$priority_single_excerpt ++;
					} else {
						$priority_single_excerpt = 22;
					}
					add_action(
						'woocommerce_single_product_summary',
						array(
							$this,
							'show_note_on_products',
						),
						$priority_single_excerpt
					);

					break;
				case 'after_meta':
					$priority_after_meta = has_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_meta' );
					if ( $priority_after_meta ) {
						$priority_after_meta ++;
					} else {
						$priority_after_meta = 42;
					}
					add_action(
						'woocommerce_single_product_summary',
						array(
							$this,
							'show_note_on_products',
						),
						$priority_after_meta
					);
					break;
				default:
					if ( $priority_single_add_to_cart ) {
						$priority_single_add_to_cart --;
					} else {
						$priority_single_add_to_cart = 28;
					}
					add_action(
						'woocommerce_single_product_summary',
						array(
							$this,
							'show_note_on_products',
						),
						$priority_single_add_to_cart
					);
					break;
			}
		}
	}

	/**
	 * Show the quantity table
	 *
	 * @param false|WC_Product $product The product object.
	 *
	 * @since  3.0.0
	 */
	public function show_table_quantity( $product = false ) {

		if ( ! $product || is_array( $product ) ) {
			global $product;
		}

		// @codingStandardsIgnoreStart
		if ( function_exists( 'apply_shortcodes' ) ) {
			echo apply_shortcodes( '[yith_ywdpd_quantity_table product=' . $product->get_id() . ']' );
		} else {
			echo do_shortcode( '[yith_ywdpd_quantity_table product=' . $product->get_id() . ']' );
		}
		// @codingStandardsIgnoreEnd
	}

	/**
	 * Show the notices
	 *
	 * @param false|WC_Product $product The product.
	 *
	 * @since  3.0.0
	 */
	public function show_note_on_products( $product = false ) {

		if ( ! $product || is_array( $product ) ) {
			global $product;
		}
		// @codingStandardsIgnoreStart
		if ( function_exists( 'apply_shortcodes' ) ) {
			echo apply_shortcodes( '[yith_ywdpd_product_note product=' . $product->get_id() . ']' );
		} else {
			echo do_shortcode( '[yith_ywdpd_product_note product=' . $product->get_id() . ']' );
		}
		// @codingStandardsIgnoreEnd
	}

	/**
	 * Add if exist the quantity table in the variation data
	 *
	 * @param array                $variation_data The variation data.
	 * @param WC_Product_Variable  $variable The variable product.
	 * @param WC_Product_Variation $variation The variation product.
	 *
	 * @return array
	 * @since  3.0.0
	 */
	public function add_quantity_table_to_available_variation( $variation_data, $variable, $variation ) {

		if ( $this->has_bulk_rules() ) {
			ob_start();
			$this->show_table_quantity( $variation );
			$variation_data['table_price'] = ob_get_clean();

			ob_start();
			$this->show_note_on_products( $variation );
			$variation_data['ywdpd_notices'] = ob_get_clean();
		}

		return $variation_data;
	}

	/**
	 * Force if show the variation price or not.
	 *
	 * @param bool                 $show Show or not the variation price.
	 * @param WC_Product_Variable  $variable The variable product.
	 * @param WC_Product_Variation $variation The variation product.
	 *
	 * @return bool
	 * @since  3.0.0
	 */
	public function show_variation_price( $show, $variable, $variation ) {

		if ( ! $show ) {
			$show = isset( $this->valid_bulk_rule[ $variation->get_id() ] );
		}

		return $show;
	}

	/**
	 * Remove the price filters
	 *
	 * @since  3.0.0
	 */
	public function remove_price_filters() {
		$priority = apply_filters( 'ywdpd_remove_filters_woocommerce_get_price_html_priority', 10 );
		remove_filter( 'woocommerce_get_price_html', array( $this, 'get_product_price_html' ), $priority );
		remove_filter( 'woocommerce_variable_price_html', array(
			$this,
			'get_product_variable_price_html'
		), $priority );
	}

	/**
	 * Add the price filters
	 *
	 * @since  3.0.0
	 */
	public function add_price_filters() {
		$priority = apply_filters( 'ywdpd_add_filters_woocommerce_get_price_html_priority', 10 );
		add_filter( 'woocommerce_get_price_html', array( $this, 'get_product_price_html' ), $priority, 2 );
		add_filter( 'woocommerce_variable_price_html', array(
			$this,
			'get_product_variable_price_html'
		), $priority, 2 );

	}

	/**
	 * Show the new price if the cart item has a dynamic rule
	 *
	 * @param string $price_html The old cart item price.
	 * @param array  $cart_item The cart item object.
	 * @param string $cart_item_key The cart item key.
	 *
	 * @return string
	 * @since  3.0.0
	 */
	public function replace_cart_item_price( $price_html, $cart_item, $cart_item_key ) {
		/**
		 * DO_ACTION: ywdpd_before_replace_cart_item_price
		 *
		 * Triggered before replace the cart item price.
		 *
		 * @param string $price_html the price html.
		 * @param array  $cart_item the cart item.
		 * @param string $cart_item_key the cart item key.
		 */
		do_action( 'ywdpd_before_replace_cart_item_price', $price_html, $cart_item, $cart_item_key );

		if ( isset( $cart_item['ywdpd_discounts'] ) ) {

			/**
			 * APPLY_FILTERS: ywdpd_cart_item_display_price
			 *
			 * Change the original price in cart.
			 *
			 * @param float $price the price.
			 * @param array $cart_item The cart item.
			 *
			 * @return float
			 */

			$original_price = apply_filters( 'ywdpd_cart_item_display_price', floatval( $cart_item['ywdpd_discounts']['display_price'] ), $cart_item );

			/**
			 * APPLY_FILTERS: ywdpd_cart_item_adjusted_price
			 *
			 * Change the dynamic price.
			 *
			 * @param float $price the price.
			 * @param array $cart_item The cart item.
			 *
			 * @return float
			 */
			$discounted_price = apply_filters( 'ywdpd_cart_item_adjusted_price', floatval( $cart_item['ywdpd_discounts']['price_adjusted'] ), $cart_item );
			$tax_mode         = is_callable(
				array(
					WC()->cart,
					'get_tax_price_display_mode',
				)
			) ? WC()->cart->get_tax_price_display_mode() : WC()->cart->tax_display_cart;

			if ( 'excl' === $tax_mode ) {
				$new_price = floatval( wc_get_price_excluding_tax( $cart_item['data'], array( 'price' => $discounted_price ) ) );
			} else {
				$new_price = floatval( wc_get_price_including_tax( $cart_item['data'], array( 'price' => $discounted_price ) ) );
			}

			$how_show = YITH_WC_Dynamic_Options::how_show_special_offer_subtotal();
			$can_show = ! isset( $cart_item['has_special_offer'] ) || ( isset( $cart_item['has_special_offer'] ) && 'unit_price' === $how_show ) || defined( 'DOING_AJAX' );
			/**
			 * APPLY_FILTERS: ywdpd_show_discount_cart_item_price
			 *
			 * Show the sale price format html.
			 *
			 * @param bool  $show Show or not.
			 * @param float $original_price The original price.
			 * @param float $new_price The new price.
			 * @param float $discounted_price The discounted price.
			 * @param array $cart_item The cart item.
			 *
			 * @return float
			 */
			$show_discounted_price = apply_filters( 'ywdpd_show_discount_cart_item_price', $original_price !== $new_price, $original_price, $discounted_price, $cart_item );
			if ( $can_show && $show_discounted_price ) {
				$price_html = wc_format_sale_price( wc_price( $original_price ), wc_price( $new_price ) );
			} else {
				$price_html = wc_price( $new_price );
			}
			/**
			 * APPLY_FILTERS: ywdpd_replace_cart_item_price
			 *
			 * Replace the cart item price.
			 *
			 * @param string $price_html The price html.
			 * @param float  $new_price The new price.
			 * @param array  $cart_item The cart item.
			 * @param string $cart_item_key The cart item key.
			 *
			 * @return string
			 */
			$price_html = apply_filters( 'ywdpd_replace_cart_item_price', $price_html, $new_price, $cart_item, $cart_item_key );
		}
		/**
		 * DO_ACTION: ywdpd_after_replace_cart_item_price
		 *
		 * Triggered after replace the cart item price.
		 *
		 * @param string $price_html the price html.
		 * @param array  $cart_item the cart item.
		 * @param string $cart_item_key the cart item key.
		 */
		do_action( 'ywdpd_after_replace_cart_item_price', $price_html, $cart_item, $cart_item_key );

		return $price_html;
	}

	/**
	 * Replace cart item subtotal if a special offer is applied
	 *
	 * @param string $subtotal The old subtotal html.
	 * @param array  $cart_item Tha cart item array.
	 * @param string $cart_item_key The cart item key.
	 *
	 * @return string;
	 * @since  3.0
	 */
	public function replace_cart_item_subtotal( $subtotal, $cart_item, $cart_item_key ) {

		/**
		 * DO_ACTION: ywdpd_before_replace_cart_item_subtotal
		 *
		 * Triggered before replace the cart item subtotal.
		 *
		 * @param string $subtotal the price html.
		 * @param array  $cart_item the cart item.
		 * @param string $cart_item_key the cart item key.
		 */
		do_action( 'ywdpd_before_replace_cart_item_subtotal', $subtotal, $cart_item, $cart_item_key );

		if ( isset( $cart_item['ywdpd_discounts'] ) ) {
			$how_show = YITH_WC_Dynamic_Options::how_show_special_offer_subtotal();
			/**
			 * APPLY_FILTERS: ywdpd_cart_item_display_price
			 *
			 * Change the original price in cart.
			 *
			 * @param float $price the price.
			 * @param array $cart_item The cart item.
			 *
			 * @return float
			 */
			$original_price = apply_filters( 'ywdpd_cart_item_display_price', floatval( $cart_item['ywdpd_discounts']['display_price'] ), $cart_item );
			$new_price      = floatval( $cart_item['data']->get_price( 'edit' ) );

			if ( 'subtotal' === $how_show && isset( $cart_item['ywdpd_discounts'] ) && isset( $cart_item['has_special_offer'] ) ) {
				if ( $new_price !== $original_price ) {
					$old_product = wc_get_product( $cart_item['data'] );
					$old_product->set_price( $original_price );
					$old_product_subtotal = wc_price( $original_price * $cart_item['quantity'] );
					$subtotal             = wc_format_sale_price( $old_product_subtotal, $subtotal );
					$special_offer_name   = '';
					if ( isset( $cart_item['ywdpd_discounts']['applied_discounts'] ) && count( $cart_item['ywdpd_discounts']['applied_discounts'] ) > 0 ) {
						$rules_to_check = array( 'bogo', 'special_offer', 'gift_products' );
						foreach ( $cart_item['ywdpd_discounts']['applied_discounts'] as $applied_discount ) {
							if ( in_array( $applied_discount['discount_mode'], $rules_to_check, true ) ) {

								$rule_id   = $applied_discount['set_id'];
								$rule      = ywdpd_get_rule( $rule_id );
								$rule_name = $rule instanceof YWDPD_Price_Rule ? $rule->get_name() : '';
								/**
								 * APPLY_FILTERS: ywdpd_special_offer_name_subtotal
								 *
								 * Change the Special offer name.
								 *
								 * @param string $rule_name the price.
								 *
								 * @return string
								 */
								$special_offer_name = apply_filters( 'ywdpd_special_offer_name_subtotal', $rule_name );
								break;
							}
						}
					}
					$subtotal = sprintf( "<div class='ywdpd_subtotal_row'>%s<p><small><strong>%s</strong></small></p></div>", $subtotal, $special_offer_name );
				}
			} elseif ( isset( $cart_item['has_bogo_applied'] ) && floatval( 0 ) === $new_price ) {

				$old_product = wc_get_product( $cart_item['data'] );
				$old_product->set_price( $original_price );
				$old_product_subtotal = wc_price( $original_price * $cart_item['quantity'] );
				$subtotal             = wc_format_sale_price( $old_product_subtotal, $subtotal );
				$rule                 = ywdpd_get_rule( $cart_item['rule_id'] );
				$rule_name            = $rule instanceof YWDPD_Price_Rule ? $rule->get_name() : '';
				$subtotal             = sprintf( "<div class='ywdpd_subtotal_row'>%s<p><small><strong>%s</strong></small></p></div>", $subtotal, $rule_name );

			} elseif ( isset( $cart_item['ywdpd_is_gift_product'] ) || isset( $cart_item['ywdpd_is_deals_product'] ) ) {

				$rule      = ywdpd_get_rule( $cart_item['ywdpd_rule_id'] );
				$rule_name = $rule instanceof YWDPD_Price_Rule ? $rule->get_name() : '';
				$subtotal  = sprintf( "<div class='ywdpd_subtotal_row'>%s<p><small><strong>%s</strong></small></p></div>", $subtotal, $rule_name );
			}
		}
		/**
		 * DO_ACTION: ywdpd_after_replace_cart_item_subtotal
		 *
		 * Triggered after replace the cart item subtotal.
		 *
		 * @param string $subtotal the price html.
		 * @param array  $cart_item the cart item.
		 * @param string $cart_item_key the cart item key.
		 */
		do_action( 'ywdpd_after_replace_cart_item_subtotal', $subtotal, $cart_item, $cart_item_key );

		return $subtotal;
	}

	/**
	 * Store in the order item the dynamic rules applied
	 *
	 * @param WC_Order_Item_Product $item_product The  product item object.
	 * @param string                $cart_key The cart item key.
	 * @param array                 $cart_item The cart item.
	 *
	 * @since  3.0.0
	 */
	public function save_dynamic_rules_in_order_item( $item_product, $cart_key, $cart_item ) {

		if ( isset( $cart_item['ywdpd_discounts'] ) ) {
			$applied_discount = $cart_item['ywdpd_discounts'];
			$item_product->add_meta_data( '_ywdpd_discounts', $applied_discount );
			$item_product->save();
		}
	}

	/**
	 * Save dynamic coupon information
	 *
	 * @param WC_Order_Item_Coupon $item_coupon The coupon item object.
	 * @param string               $code The coupon code.
	 *
	 * @since  3.4.0
	 */
	public function save_dynamic_coupon_name( $item_coupon, $code ) {
		$dynamic_code = ywdpd_dynamic_pricing_discounts()->get_cart_rules_manager()->get_coupon_code();

		if ( $dynamic_code === $code ) {
			$dynamic_coupon_name = ywdpd_dynamic_pricing_discounts()->get_cart_rules_manager()->get_coupon_code_details();
			$item_coupon->add_meta_data( '_ywdpd_coupon_info', $dynamic_coupon_name );
			$item_coupon->save();
		}

	}

	/**
	 * Show the custom meta for the item.
	 *
	 * @param int           $item_id The item id.
	 * @param WC_Order_Item $item The order item object.
	 *
	 * @since  3.0.0
	 */
	public function format_custom_meta_data( $item_id, $item ) {
		if ( YITH_WC_Dynamic_Options::can_show_rule_details() ) {
			$dynamic_rules = $item->get_meta( '_ywdpd_discounts' );
			if ( ! empty( $dynamic_rules ) ) {
				$custom_meta = '';

				foreach ( $dynamic_rules['applied_discounts'] as $applied_discount ) {

					if ( isset( $applied_discount['set_id'] ) ) {
						$rule_id = $applied_discount['set_id'];
						$rule    = ywdpd_get_rule( $rule_id );
					} else {
						$rule = $applied_discount['by'];
					}
					if ( $rule instanceof YWDPD_Price_Rule && ! empty( $rule->get_name() ) ) {
						/**
						 * APPLY_FILTERS: ywdpd_rule_name
						 *
						 * Change the rule name.
						 *
						 * @param string $rule_name the price.
						 *
						 * @return string
						 */
						$custom_meta .= '<li>' . apply_filters( 'ywdpd_rule_name', $rule->get_name(), $rule, $applied_discount ) . '</li>';
					}
				}
				if ( ! empty( $custom_meta ) ) { ?>
                    <ul class="wc-item-meta ywdpd-applied-discounts-item-meta">
						<span class="ywdpd-applied-discounts-label"
                              style="font-weight: bold;"><?php esc_html_e( 'Offer applied:', 'ywdpd' ); ?></span>
						<?php echo wp_kses_post( $custom_meta ); ?>
                    </ul>
					<?php
				}
			}
		}
	}

	/**
	 * Show the discount amount info in cart
	 *
	 * @since  2.0.0
	 */
	public function show_total_discount_message_on_cart() {
		$message = yith_ywdpd_get_total_discount_message();
		if ( ! empty( $message ) ) {
			?>
            <tr class="dynamic-discount">
                <td colspan="2"
                    data-title="<?php esc_attr_e( 'Discount', 'ywdpd' ); ?>">
                    <div class="ywdpd_single_cart_notice">
						<?php echo wp_kses_post( $message ); ?>
                    </div>
                </td>
            </tr>
			<?php
		}
	}

	/**
	 * Show the cart notices
	 *
	 * @since  3.0.0
	 */
	public function show_cart_notices() {
		// @codingStandardsIgnoreStart
		if ( function_exists( 'apply_shortcodes' ) ) {
			echo apply_shortcodes( '[yith_ywdpd_cart_notice]' );
		} else {
			echo do_shortcode( '[yith_ywdpd_cart_notice]' );
		}
		// @codingStandardsIgnoreEnd
	}

	/**
	 * Change the args in the qty field
	 *
	 * @param array      $args the qty args.
	 * @param WC_Product $product The product.
	 *
	 * @return array
	 */
	public function manage_qty_input_args( $args, $product ) {
		if ( ! is_cart() ) {
			$qty_rule = $this->get_valid_bulk_rule_for_product( $product );
			if ( $qty_rule && 'bulk' === $qty_rule->get_discount_mode() && 'fixed' === $qty_rule->get_qty_type() ) {
				$qtys              = wp_list_pluck( $qty_rule->get_fixed_rules(), 'units' );
				$args['readonly']  = true;
				$args['min_value'] = min( $qtys );

				if ( - 1 === $args['max_value'] ) {
					$args['max_value'] = max( $qtys );
				} else {
					$args['max_value'] = min( $args['max_value'], max( $qtys ) );
				}
			}
		}

		return $args;
	}

	/**
	 * Avoid to update cart quantity in wc blocks
	 *
	 * @param bool       $value The value.
	 * @param WC_Product $product The product.
	 * @param array      $cart_item The item.
	 *
	 * @return false|mixed
	 */
	public function manage_qty_input_args_wc_blocks( $value, $product, $cart_item ) {
		if ( ! is_null( $cart_item ) ) {
			if ( isset( $cart_item['has_bulk_applied'] ) ) {
				$applied_discounts = $cart_item['ywdpd_discounts']['applied_discounts'];
				$qty_rule          = false;
				foreach ( $applied_discounts as $discount ) {

					if ( 'bulk' === $discount['discount_mode'] ) {
						$qty_rule = $discount['set_id'];
						break;
					}
				}

				$qty_rule = ywdpd_get_rule( $qty_rule );
				if ( $qty_rule && 'fixed' === $qty_rule->get_qty_type() ) {
					$value = false;
				}
			}
		}

		return $value;
	}

	/**
	 * Add custom data in cart item
	 *
	 * @param array $cart_item_data The data.
	 * @param int   $product_id The product id.
	 * @param int   $variation_id The variation id.
	 * @param int   $quantity The quantity.
	 *
	 * @return array
	 */
	public function manage_cart_item_data( $cart_item_data, $product_id, $variation_id, $quantity ) {
		if ( $variation_id ) {
			$product = wc_get_product( $variation_id );
		} else {
			$product = wc_get_product( $product_id );
		}
		$qty_rule = $this->get_valid_bulk_rule_for_product( $product );
		if ( $qty_rule && 'bulk' === $qty_rule->get_discount_mode() && 'fixed' === $qty_rule->get_qty_type() ) {
			$cart_item_data['ywdpd_bulk_fixed'] = $quantity;
		}

		return $cart_item_data;
	}

	public function add_custom_block_class( $class ) {
		return str_replace( '.single-product .summary', 'div.woocommerce.product', $class );
	}

}
