<?php
/**
 * Class to handle feature Coupons By Product Quantity
 *
 * @category    Admin
 * @author      StoreApps
 * @package     woocommerce-smart-coupons/includes
 * @since       5.0.0
 * @version     2.7.0
 */

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

if ( ! class_exists( 'WC_SC_Coupons_By_Product_Quantity' ) ) {

	/**
	 * Class WC_SC_Coupons_By_Product_Quantity
	 */
	class WC_SC_Coupons_By_Product_Quantity {

		/**
		 * Variable to hold instance of this class
		 *
		 * @var $instance
		 */
		private static $instance = null;

		/**
		 * Constructor
		 */
		private function __construct() {
			add_action( 'woocommerce_coupon_options_usage_restriction', array( $this, 'usage_restriction' ), 99, 2 );
			add_action( 'admin_footer', array( $this, 'styles_and_scripts' ) );
			add_action( 'woocommerce_coupon_options_save', array( $this, 'process_meta' ), 10, 2 );
			add_filter( 'woocommerce_coupon_is_valid', array( $this, 'validate' ), 11, 3 );

			add_filter( 'is_protected_meta', array( $this, 'make_sc_meta_protected' ), 10, 3 );
			add_filter( 'sc_generate_coupon_meta', array( $this, 'generate_coupon_meta' ), 10, 2 );
			add_filter( 'smart_coupons_parser_postmeta_defaults', array( $this, 'postmeta_defaults' ) );
			add_filter( 'wc_sc_export_coupon_meta', array( $this, 'export_coupon_meta' ), 10, 2 );
			add_filter( 'wc_sc_process_coupon_meta_value_for_import', array( $this, 'process_coupon_meta_value_for_import' ), 10, 2 );
			add_filter( 'wc_smart_coupons_export_headers', array( $this, 'export_headers' ) );
			add_action( 'wc_sc_new_coupon_generated', array( $this, 'copy_meta' ) );
		}

		/**
		 * Get single instance of this class
		 *
		 * @return this class Singleton object of this class
		 */
		public static function get_instance() {
			// Check if instance is already exists.
			if ( is_null( self::$instance ) ) {
				self::$instance = new self();
			}

			return self::$instance;
		}

		/**
		 * Handle call to functions which is not available in this class
		 *
		 * @param string $function_name The function name.
		 * @param array  $arguments Array of arguments passed while calling $function_name.
		 * @return result of function call
		 */
		public function __call( $function_name = '', $arguments = array() ) {

			global $woocommerce_smart_coupon;

			if ( ! is_callable( array( $woocommerce_smart_coupon, $function_name ) ) ) {
				return;
			}

			if ( ! empty( $arguments ) ) {
				return call_user_func_array( array( $woocommerce_smart_coupon, $function_name ), $arguments );
			} else {
				return call_user_func( array( $woocommerce_smart_coupon, $function_name ) );
			}
		}

		/**
		 * Display field for coupon by product quantity
		 *
		 * @param integer   $coupon_id The coupon id.
		 * @param WC_Coupon $coupon The coupon object.
		 */
		public function usage_restriction( $coupon_id = 0, $coupon = null ) {
			try {
				if ( ! is_a( $coupon, 'WC_Coupon' ) || empty( $coupon_id ) ) {
					return;
				}

				if ( ! is_a( $coupon, 'WC_Coupon' ) ) {
					$coupon = ( ! empty( $coupon_id ) ) ? new WC_Coupon( $coupon_id ) : null;
				}

				if ( $this->is_callable( $coupon, 'get_meta' ) ) {
					$product_quantity_restrictions = $coupon->get_meta( 'wc_sc_product_quantity_restrictions' );
				} else {
					$product_quantity_restrictions = $this->get_post_meta( $coupon_id, 'wc_sc_product_quantity_restrictions', true );
				}

				if ( ! is_array( $product_quantity_restrictions ) ) {
					$product_quantity_restrictions = array();
				}

				$cart_min_quantity                  = ! empty( $product_quantity_restrictions['values']['cart']['min'] ) ? intval( $product_quantity_restrictions['values']['cart']['min'] ) : '';
				$cart_max_quantity                  = ! empty( $product_quantity_restrictions['values']['cart']['max'] ) ? intval( $product_quantity_restrictions['values']['cart']['max'] ) : '';
				$product_quantity_restrictions_type = ! empty( $product_quantity_restrictions['type'] ) ? $product_quantity_restrictions['type'] : 'cart';
				?>
				<div class="options_group smart-coupons-field">
					<h4 style="padding-left: 10px;"><?php echo esc_html__( 'Product quantity based restrictions', 'woocommerce-smart-coupons' ); ?></h4>
					<p class="form-field">
						<span class='left_column'><label for="wc_sc_product_quantity_type_cart"><?php echo esc_html__( 'Validate quantity of', 'woocommerce-smart-coupons' ); ?></label></span>
						<label for="wc_sc_product_quantity_type_cart" class="cart_quantity">
							<input type="radio" name="wc_sc_product_quantity_restrictions[type]" id="wc_sc_product_quantity_type_cart" class="wc_sc_product_quantity_type wc_sc_product_quantity_type_cart" value="cart" <?php checked( 'cart', $product_quantity_restrictions_type, true ); ?> />
							<?php echo esc_html__( 'Cart', 'woocommerce-smart-coupons' ); ?>
						</label>
						<label for="wc_sc_product_quantity_type_product" class="product_quantity">
							<input type="radio" name="wc_sc_product_quantity_restrictions[type]" id="wc_sc_product_quantity_type_product" class="wc_sc_product_quantity_type wc_sc_product_quantity_type_product" value="product" <?php checked( 'product', $product_quantity_restrictions_type, true ); ?>>
							<?php echo esc_html__( 'Product', 'woocommerce-smart-coupons' ); ?>
						</label>
						<?php
						$tooltip_text = esc_html__( 'Choose whether to validate the quantity, cart-wise or product-wise', 'woocommerce-smart-coupons' );
						echo wc_help_tip($tooltip_text); // phpcs:ignore ?>
					</p>
					<p class="form-field wc_sc_cart_quantity" style="<?php echo ( 'cart' === $product_quantity_restrictions_type ) ? '' : 'display: none;'; ?>">
						<label for="wc_sc_min_product_quantity"><?php echo esc_html__( 'Minimum quantity', 'woocommerce-smart-coupons' ); ?></label>
						<input type="number" name="wc_sc_product_quantity_restrictions[values][cart][min]" id="wc_sc_min_product_quantity" class="wc_sc_min_product_quantity" placeholder="<?php echo esc_attr__( 'No minimum', 'woocommerce-smart-coupons' ); ?>" value="<?php echo esc_attr( $cart_min_quantity ); ?>" min="0">
					</p>
					<p class="form-field wc_sc_cart_quantity" style="<?php echo ( 'cart' === $product_quantity_restrictions_type ) ? '' : 'display: none;'; ?>">
						<label for="wc_sc_max_product_quantity"><?php echo esc_html__( 'Maximum quantity', 'woocommerce-smart-coupons' ); ?></label>
						<input type="number" name="wc_sc_product_quantity_restrictions[values][cart][max]" id="wc_sc_max_product_quantity" class="wc_sc_max_product_quantity" placeholder="<?php echo esc_attr__( 'No maximum', 'woocommerce-smart-coupons' ); ?>" value="<?php echo esc_attr( $cart_max_quantity ); ?>" min="0">
					</p>
					<div class=" wc_sc_product_quantity" style="<?php echo isset( $product_quantity_restrictions['type'] ) && ( 'product' === $product_quantity_restrictions['type'] ) ? '' : 'display: none;'; ?>">
						<?php
						$product_quantities = ! empty( $product_quantity_restrictions['values']['product'] ) ? $product_quantity_restrictions['values']['product'] : array();
						if ( ! empty( $product_quantities ) ) {
							$display_label = true;
							foreach ( $product_quantities as $product_id => $value ) {
								if ( 0 !== $product_id ) {
									$product = wc_get_product( $product_id );
									if ( ! empty( $product ) && is_object( $product ) ) {
										$product_name         = is_callable( array( $product, 'get_name' ) ) ? $product->get_name() : '';
										$product_max_quantity = ! empty( $value['max'] ) ? intval( $value['max'] ) : '';
										$product_min_quantity = ! empty( $value['min'] ) ? intval( $value['min'] ) : '';
										?>
										<p class="form-field" data-index="<?php echo esc_attr( $product_id ); ?>">
											<?php if ( true === $display_label ) { ?>
												<label><?php echo esc_html__( 'Products', 'woocommerce-smart-coupons' ); ?></label>
											<?php } ?>
											<span>
												<input type="text" name="wc_sc_product_quantity_restrictions[values][product][<?php echo esc_attr( $product_id ); ?>][product_id]" placeholder="<?php echo esc_attr( $product_id ); ?>" value="<?php echo esc_attr( $product_name ); ?>" disabled>
												<input type="number" name="wc_sc_product_quantity_restrictions[values][product][<?php echo esc_attr( $product_id ); ?>][min]" class="product_min_quantity_field" placeholder="<?php echo esc_attr__( 'No minimum', 'woocommerce-smart-coupons' ); ?>" value="<?php echo esc_attr( $product_min_quantity ); ?>" min="0">
												<input type="number" name="wc_sc_product_quantity_restrictions[values][product][<?php echo esc_attr( $product_id ); ?>][max]" class="product_max_quantity_field" placeholder="<?php echo esc_attr__( 'No maximum', 'woocommerce-smart-coupons' ); ?>" value="<?php echo esc_attr( $product_max_quantity ); ?>" min="0">
											</span>
										</p>
										<?php
									}
								}
								$display_label = false;
							}
						} else {
							?>
							<p class="form-field">
								<span class='left_column'><label><?php echo esc_html__( 'Products', 'woocommerce-smart-coupons' ); ?></label></span>
								<label class="hypertext_css wc_sc_click_select_product">
									<?php echo esc_html__( 'Please select some products in "Products" restrictions above...', 'woocommerce-smart-coupons' ); ?>
								</label>
							</p>
							<?php
						}
						?>
					</div>
					<div class=" wc_sc_category_quantity" style="<?php echo isset( $product_quantity_restrictions['type'] ) && ( 'product' === $product_quantity_restrictions['type'] ) ? '' : 'display: none;'; ?>">
						<?php
						$product_category_quantities = ! empty( $product_quantity_restrictions['values']['product_category'] ) ? $product_quantity_restrictions['values']['product_category'] : array();
						if ( ! empty( $product_category_quantities ) ) {
							$i = 0;
							foreach ( $product_category_quantities as $category_id => $value ) {
								if ( 0 !== $category_id ) {
									$term = get_term_by( 'id', $category_id, 'product_cat', ARRAY_A );
									if ( ! empty( $term ) && is_array( $term ) ) {
										$category_name = ! empty( $term['name'] ) ? $term['name'] : '';
										?>
										<p class="form-field" data-index="<?php echo esc_attr( $category_id ); ?>">
											<?php if ( 0 === $i ) { ?>
												<label><?php echo esc_html__( 'Categories', 'woocommerce-smart-coupons' ); ?></label>
											<?php } ?>
											<span>
												<input type="text" name="wc_sc_product_quantity_restrictions[values][product_category][<?php echo esc_attr( $category_id ); ?>][category_id]" placeholder="<?php echo esc_attr( $category_id ); ?>" value="<?php echo esc_attr( $category_name ); ?>" disabled>
												<input type="number" name="wc_sc_product_quantity_restrictions[values][product_category][<?php echo esc_attr( $category_id ); ?>][min]" class="product_min_quantity_field" placeholder="<?php echo esc_attr__( 'No minimum', 'woocommerce-smart-coupons' ); ?>" value="<?php echo esc_attr( ! empty( $value['min'] ) ? intval( $value['min'] ) : '' ); ?>" min="0">
												<input type="number" name="wc_sc_product_quantity_restrictions[values][product_category][<?php echo esc_attr( $category_id ); ?>][max]" class="product_max_quantity_field" placeholder="<?php echo esc_attr__( 'No maximum', 'woocommerce-smart-coupons' ); ?>" value="<?php echo esc_attr( ! empty( $value['max'] ) ? intval( $value['max'] ) : '' ); ?>" min="0">
											</span>
										</p>
										<?php

									}
								}
								++$i;
							}
						} else {
							?>
							<p class="form-field">
								<span class='left_column'><label><?php echo esc_html__( 'Categories', 'woocommerce-smart-coupons' ); ?></label></span>
								<label for="product_categories" class="hypertext_css">
									<?php echo esc_html__( 'Please select some categories in "Product categories" restrictions above...', 'woocommerce-smart-coupons' ); ?>
								</label>
							</p>
							<?php
						}
						?>
					</div>
				</div>


				<div class="wc_sc_auto_generate_product_quantity" style="display: none;">
					<p class="form-field" data-index="{i}">
						<label>{p}</label>
						<span>
							<input type="text" name="wc_sc_product_quantity_restrictions[values][product][{i}][product_id]" placeholder="{i}" value="{n}" disabled>
							<input type="number" name="wc_sc_product_quantity_restrictions[values][product][{i}][min]" class="product_min_quantity_field" placeholder="<?php echo esc_attr__( 'No minimum', 'woocommerce-smart-coupons' ); ?>" min="0">
							<input type="number" name="wc_sc_product_quantity_restrictions[values][product][{i}][max]" class="product_max_quantity_field" placeholder="<?php echo esc_attr__( 'No maximum', 'woocommerce-smart-coupons' ); ?>" min="0">
						</span>
					</p>
				</div>


				<div class="wc_sc_auto_generate_category_quantity" style="display: none;">
					<p class="form-field" data-index="{i}">
						<label>{c}</label>
						<span>
							<input type="text" name="wc_sc_product_quantity_restrictions[values][product_category][{i}][category_id]" placeholder="{i}" value="{n}" disabled>
							<input type="number" name="wc_sc_product_quantity_restrictions[values][product_category][{i}][min]" class="product_min_quantity_field" placeholder="<?php echo esc_attr__( 'No minimum', 'woocommerce-smart-coupons' ); ?>" min="0">
							<input type="number" name="wc_sc_product_quantity_restrictions[values][product_category][{i}][max]" class="product_max_quantity_field" placeholder="<?php echo esc_attr__( 'No maximum', 'woocommerce-smart-coupons' ); ?>" min="0">
						</span>
					</p>
				</div>

				<div class="wc_sc_empty_products" style="display: none;">
					<p class="form-field">
						<span class='left_column'><label><?php echo esc_html__( 'Products', 'woocommerce-smart-coupons' ); ?></label></span>
						<label class="hypertext_css wc_sc_click_select_product">
							<?php echo esc_html__( 'Please select some products', 'woocommerce-smart-coupons' ); ?>
						</label>
					</p>
				</div>
				<div class="wc_sc_empty_categories" style="display: none;">
					<p class="form-field">
						<span class='left_column'><label><?php echo esc_html__( 'Categories', 'woocommerce-smart-coupons' ); ?></label></span>
						<label for="product_categories" class="hypertext_css">
							<?php echo esc_html__( 'Please select some categories', 'woocommerce-smart-coupons' ); ?>
						</label>
					</p>
				</div>
				<div class="wc_sc_product_section_title" style="display: none;">
					<?php echo esc_html__( 'Products', 'woocommerce-smart-coupons' ); ?>
				</div>
				<div class="wc_sc_category_section_title" style="display: none;">
					<?php echo esc_html__( 'Categories', 'woocommerce-smart-coupons' ); ?>
				</div>
				<?php
			} catch ( \Throwable $e ) {
				$this->sc_block_catch_error( $e );
			}
		}


		/**
		 * Styles and scripts
		 */
		public function styles_and_scripts() {
			if ( ! wp_script_is( 'jquery' ) ) {
				wp_enqueue_script( 'jquery' );
			}
			?>
			<style type="text/css">
				.wc_sc_product_quantity_type_product {
					margin-left: 10px !important;
				}

				.product_min_quantity_field, .product_max_quantity_field {
					width: 8.5em !important;
					margin-left: 10px !important;
				}
				.hypertext_css {
					width: 50% !important;
					color: #1870f0 !important;
					text-decoration: underline !important;
					margin-left: 1px !important;
				}
				.width_32{
					width: 32% !important;
				}
				.padding_left_3{
					padding-left: 3px;
				}
				label.cart_quantity{
					width: 75px !important;
					margin-left: 1px !important;
				}

				label.product_quantity {
					margin-left: 0px !important;
				}
			</style>
			<script type="text/javascript">
				jQuery(document).ready(function ($) {
					/**
					*Change quantity type cart or product.
					*/
					$(document).on('change', '.wc_sc_product_quantity_type', function () {
						let value = $('input[name="wc_sc_product_quantity_restrictions[type]"]:checked').val();
						if ("product" === value) {
							$('.wc_sc_cart_quantity').hide();
							$('.wc_sc_product_quantity, .wc_sc_category_quantity').show();
						} else {
							$('.wc_sc_cart_quantity').show();
							$('.wc_sc_product_quantity, .wc_sc_category_quantity').hide();
						}
					});
					/**
					*Auto add product field.
					*/
					$(document).on('change', 'select[name="product_ids[]"]', function () {
						var product_element_count = 0;
						$('.wc_sc_product_quantity').empty();
						let options =  $("select[name='product_ids[]'] option:selected").val();
						if( options === undefined ){
							let product_empty = $('.wc_sc_empty_products').html();
							$('.wc_sc_product_quantity').append(product_empty);
						}

						$("select[name='product_ids[]'] option:selected").each(function () {
							var $this = $(this);
							if ($this.length) {
								product_element_count = parseInt(product_element_count) + parseInt(1);
								let product_name = $this.text();
								let product_id = $this.val();
								let section_title = $('.wc_sc_product_section_title').text();
								let wc_sc_product_quantity = $('.wc_sc_auto_generate_product_quantity').html();
								wc_sc_product_quantity = wc_sc_product_quantity.replace(new RegExp('{i}', 'g'), product_id);
								wc_sc_product_quantity = wc_sc_product_quantity.replace(new RegExp('{n}', 'g'), product_name);

								if (product_element_count >= 2) {
									wc_sc_product_quantity = wc_sc_product_quantity.replace(new RegExp('{p}', 'g'), '');
								} else {
									wc_sc_product_quantity = wc_sc_product_quantity.replace(new RegExp('{p}', 'g'), section_title);
								}
								$('.wc_sc_product_quantity').append(wc_sc_product_quantity);
							}
						});
					});

					/**
					*Auto add category field.
					*/
					$(document).on('change', 'select[name="product_categories[]"]', function () {
						var category_element_count = 0;
						$('.wc_sc_category_quantity').empty();
						let options =  $("#product_categories option:selected").val();
						if( options === undefined ){
							let category_empty = $('.wc_sc_empty_categories').html();
							$('.wc_sc_category_quantity').append(category_empty);
						}
						$("#product_categories option:selected").each(function () {
							var $this = $(this);
							if ($this.length) {
								category_element_count = parseInt(category_element_count) + parseInt(1);
								let category_name = $this.text();
								let category_id = $this.val();
								let section_title = $('.wc_sc_category_section_title').text();
								let wc_sc_category_quantity = $('.wc_sc_auto_generate_category_quantity').html();
								wc_sc_category_quantity = wc_sc_category_quantity.replace(new RegExp('{i}', 'g'), category_id);
								wc_sc_category_quantity = wc_sc_category_quantity.replace(new RegExp('{n}', 'g'), category_name);

								if (category_element_count >= 2) {
									wc_sc_category_quantity = wc_sc_category_quantity.replace(new RegExp('{c}', 'g'), '');
								} else {
									wc_sc_category_quantity = wc_sc_category_quantity.replace(new RegExp('{c}', 'g'), section_title);
								}
								$('.wc_sc_category_quantity').append(wc_sc_category_quantity);
							}
						});
					});

					$("body").on('click', '.wc_sc_click_select_product', function() {
						$(".wc-product-search").parent('p:nth-child(1)').css('background-color', '#ffffcc').animate({
							backgroundColor: 'transparent'
						}, 2000);
						$('html, body').animate({
							'scrollTop' : $(".wc-product-search").parent('p:nth-child(1)').position().top,
						});
					});
				});
			</script>
			<?php
		}

		/**
		 * Save coupon by product quantity data in meta
		 *
		 * @param Integer   $post_id The coupon post ID.
		 * @param WC_Coupon $coupon The coupon object.
		 */
		public function process_meta( $post_id = 0, $coupon = null ) {
			try {
				if ( empty( $post_id ) ) {
					return;
				}

				$coupon = new WC_Coupon( $coupon );

				$product_quantity_restrictions = (isset($_POST['wc_sc_product_quantity_restrictions'])) ? wc_clean(wp_unslash($_POST['wc_sc_product_quantity_restrictions'])) : array(); // phpcs:ignore

				if ( ! isset( $product_quantity_restrictions['condition'] ) ) {
					$product_quantity_restrictions['condition'] = 'any'; // Values: any, all.
				}

				if ( isset( $product_quantity_restrictions['values']['product']['{i}'] ) ) {
					unset( $product_quantity_restrictions['values']['product']['{i}'] );
				}

				if ( isset( $product_quantity_restrictions['values']['product_category']['{i}'] ) ) {
					unset( $product_quantity_restrictions['values']['product_category']['{i}'] );
				}

				if ( ! empty( $product_quantity_restrictions ) ) {
					foreach ( $product_quantity_restrictions as $restriction_key => $restrictions ) {
						if ( 'values' === $restriction_key ) {
							// Max quantity feature not included for product quantity.
							foreach ( $restrictions['product'] as $id => $restriction ) {
								$id = absint( $id );
								if ( 0 !== $id && isset( $product_quantity_restrictions['values']['product'][ $id ]['max'] ) ) {
									$product_quantity_restrictions['values']['product'][ $id ]['max'] = ! empty( $restriction['max'] ) ? intval( $restriction['max'] ) : '';
								}
							}

							// Max quantity feature not included for product category quantity.
							foreach ( $restrictions['product_category'] as $id => $restriction ) {
								$id = absint( $id );
								if ( 0 !== $id && isset( $product_quantity_restrictions['values']['product_category'][ $id ]['max'] ) ) {
									$product_quantity_restrictions['values']['product_category'][ $id ]['max'] = ! empty( $restriction['max'] ) ? intval( $restriction['max'] ) : '';
								}
							}
						}
					}
				}

				if ( $this->is_callable( $coupon, 'update_meta_data' ) && $this->is_callable( $coupon, 'save' ) ) {
					$coupon->update_meta_data( 'wc_sc_product_quantity_restrictions', $product_quantity_restrictions );
					$coupon->save();
				} else {
					$this->update_post_meta( $post_id, 'wc_sc_product_quantity_restrictions', $product_quantity_restrictions );
				}
			} catch ( \Throwable $e ) {
				$this->sc_block_catch_error( $e );
			}
		}

		/**
		 * Validate the coupon based on product quantity
		 *
		 * @param boolean      $valid Is valid or not.
		 * @param WC_Coupon    $coupon The coupon object.
		 * @param WC_Discounts $wc_discounts The discounts object.
		 *
		 * @return boolean           Is valid or not
		 * @throws Exception If the coupon is invalid.
		 */
		public function validate( $valid = false, $coupon = null, $wc_discounts = null ) {

			// If coupon is invalid already, no need for further checks.
			if ( false === $valid ) {
				return $valid;
			}

			if ( ! is_a( $coupon, 'WC_Coupon' ) ) {
				return $valid;
			}
			if ( ! is_a( $wc_discounts, 'WC_Discounts' ) ) {
				return $valid;
			}

			$items_to_validate = array();
			$cart_quantity     = 0;

			if ( is_callable( array( $wc_discounts, 'get_items_to_validate' ) ) ) {
				$items_to_validate = $wc_discounts->get_items_to_validate();
			} else {
				return $valid;
			}

			if ( ! empty( $items_to_validate ) ) {
				foreach ( $items_to_validate as $key => $cart_content ) {
					$cart_item      = ! empty( $cart_content->object ) ? $cart_content->object : array();
					$cart_quantity += ! empty( $cart_item['quantity'] ) ? intval( $cart_item['quantity'] ) : 0;
				}
			} else {
				return $valid;
			}

			// If the cart quantity is empty the rule will not work.
			if ( $cart_quantity <= 0 ) {
				return $valid;
			}

			$coupon_id = is_callable( array( $coupon, 'get_id' ) ) ? $coupon->get_id() : 0;

			if ( $this->is_callable( $coupon, 'get_meta' ) ) {
				$product_quantity_restrictions = $coupon->get_meta( 'wc_sc_product_quantity_restrictions' );
			} else {
				$product_quantity_restrictions = $this->get_post_meta( $coupon_id, 'wc_sc_product_quantity_restrictions', true );
			}
			if ( is_array( $product_quantity_restrictions ) && ! empty( $product_quantity_restrictions ) ) {
				$type      = ! empty( $product_quantity_restrictions['type'] ) ? $product_quantity_restrictions['type'] : '';
				$values    = ! empty( $product_quantity_restrictions['values'] ) ? $product_quantity_restrictions['values'] : '';
				$condition = ! empty( $product_quantity_restrictions['condition'] ) ? $product_quantity_restrictions['condition'] : 'any';

				/**
				 * Filter the condition used to validate product quantity restrictions.
				 *
				 * Allows developers to modify the condition (e.g., 'any', 'all') used for product quantity validation while applying a coupon.
				 *
				 * @since 9.35.0
				 *
				 * @param string $condition The condition type, e.g., 'any' or 'all'.
				 * @param array  $args {
				 *     Additional context about the validation.
				 *
				 *     @type object      $source            Source object (usually the current class instance).
				 *     @type WC_Coupon   $coupon_obj         The coupon object being validated.
				 *     @type WC_Discounts $wc_discounts_obj  WooCommerce discounts object.
				 *     @type bool        $original_validity  The original validation result before applying quantity restrictions.
				 * }
				 */
				$condition = apply_filters(
					'wc_sc_product_quantity_condition',
					$condition,
					array(
						'source'            => $this,
						'coupon_obj'        => $coupon,
						'wc_discounts_obj'  => $wc_discounts,
						'original_validity' => $valid,
					)
				);

				switch ( $type ) {
					case 'cart':
						$min      = ! empty( $product_quantity_restrictions['values']['cart']['min'] ) ? intval( $product_quantity_restrictions['values']['cart']['min'] ) : 0;
						$max      = ! empty( $product_quantity_restrictions['values']['cart']['max'] ) ? intval( $product_quantity_restrictions['values']['cart']['max'] ) : 0;
						$messages = array(
							__( 'Your cart does not meet the quantity requirement.', 'woocommerce-smart-coupons' ),
						);

						if ( empty( $min ) && empty( $max ) ) {
							return $valid;
						} elseif ( empty( $min ) && ! empty( $max ) && $cart_quantity <= $max ) {
							return $valid;
						} elseif ( empty( $max ) && ! empty( $min ) && $cart_quantity >= $min ) {
							return $valid;
						} elseif ( ! empty( $min ) && ! empty( $max ) && $cart_quantity >= $min && $cart_quantity <= $max ) {
							return $valid;
						} else {
							if ( $cart_quantity > $max ) {
								/* translators: 1. Number of quantity 2. Singular or plural text based on number of quantities */
								$messages[] = sprintf( __( 'Your cart should have a maximum of %1$d %2$s in total.', 'woocommerce-smart-coupons' ), $max, _n( 'quantity', 'quantities', $max ) );
							}
							if ( $cart_quantity < $min ) {
								/* translators: 1. Number of quantity 2. Singular or plural text based on number of quantities */
								$messages[] = sprintf( __( 'Your cart should have a minimum of %1$d %2$s in total.', 'woocommerce-smart-coupons' ), $min, _n( 'quantity', 'quantities', $min ) );
							}
							throw new Exception( implode( ' ', $messages ) );
						}
						break;
					default:
					case 'product':
						$product_quantity_restrictions          = ! empty( $values['product'] ) ? $values['product'] : array();
						$product_category_quantity_restrictions = ! empty( $values['product_category'] ) ? $values['product_category'] : array();

						$params                     = array(
							'condition'         => $condition,
							'items_to_validate' => $items_to_validate,
						);
						$product_condition          = $this->process_product_quantities( $product_quantity_restrictions, $params );
						$product_category_condition = $this->process_category_quantities( $product_category_quantity_restrictions, $params );

						if ( false === $product_condition && false === $product_category_condition ) {
							throw new Exception( __( 'Your cart does not meet the product quantity requirement.', 'woocommerce-smart-coupons' ) );
						} elseif ( 'empty' === $product_condition && false === $product_category_condition ) {
							throw new Exception( __( 'Your cart does not meet the product quantity requirement.', 'woocommerce-smart-coupons' ) );
						} elseif ( false === $product_condition && 'empty' === $product_category_condition ) {
							throw new Exception( __( 'Your cart does not meet the product quantity requirement.', 'woocommerce-smart-coupons' ) );
						}
						break;
				}
			}

			return $valid;
		}

		/**
		 * Make meta data of this plugin, protected
		 *
		 * @param bool   $protected Is protected.
		 * @param string $meta_key the meta key.
		 * @param string $meta_type The meta type.
		 * @return bool $protected
		 */
		public function make_sc_meta_protected( $protected, $meta_key, $meta_type ) {
			if ( 'wc_sc_product_quantity_restrictions' === $meta_key ) {
				return true;
			}
			return $protected;
		}

		/**
		 * Add product quantity restriction meta with value in coupon meta
		 *
		 * @param  array $data The row data.
		 * @param  array $post The POST values.
		 * @return array Modified data
		 */
		public function generate_coupon_meta( $data = array(), $post = array() ) {

			$data['wc_sc_product_quantity_restrictions'] = array();
			if ( isset( $post['wc_sc_product_quantity_restrictions'] ) && is_array( $post['wc_sc_product_quantity_restrictions'] ) ) {
				$data['wc_sc_product_quantity_restrictions'] = $this->product_quantity_restrictions_encode( $post['wc_sc_product_quantity_restrictions'] );
			}

			return $data;
		}

		/**
		 * Post meta defaults for product quantity restriction meta
		 *
		 * @param  array $defaults Existing postmeta defaults.
		 * @return array
		 */
		public function postmeta_defaults( $defaults = array() ) {
			return array_merge( $defaults, array( 'wc_sc_product_quantity_restrictions' => '' ) );
		}

		/**
		 * Function to handle coupon meta data during export of existing coupons
		 *
		 * @param  mixed $meta_value The meta value.
		 * @param  array $args       Additional arguments.
		 * @return string Processed meta value
		 */
		public function export_coupon_meta( $meta_value = '', $args = array() ) {
			try {
				$index       = ( ! empty( $args['index'] ) ) ? $args['index'] : -1;
				$meta_keys   = ( ! empty( $args['meta_keys'] ) ) ? $args['meta_keys'] : array();
				$meta_values = ( ! empty( $args['meta_values'] ) ) ? $args['meta_values'] : array();

				if ( $index >= 0 && ! empty( $meta_keys[ $index ] ) && 'wc_sc_product_quantity_restrictions' === $meta_keys[ $index ] && ! empty( $meta_values[ $index ] ) ) {
					$meta_value = maybe_unserialize( $meta_values[ $index ] ); // phpcs:ignore
					$meta_value = $this->product_quantity_restrictions_encode( $meta_value );
				}
			} catch ( \Throwable $e ) {
				$this->sc_block_catch_error( $e );
			}

			return $meta_value;
		}

		/**
		 * Process coupon meta value for import
		 *
		 * @param  mixed $meta_value The meta value.
		 * @param  array $args       Additional Arguments.
		 * @return mixed $meta_value
		 */
		public function process_coupon_meta_value_for_import( $meta_value = null, $args = array() ) {

			if ( ! empty( $args['meta_key'] ) && 'wc_sc_product_quantity_restrictions' === $args['meta_key'] && ! empty( $args['postmeta']['wc_sc_product_quantity_restrictions'] ) ) {
				$meta_value = $this->product_quantity_restrictions_decode( $args['postmeta']['wc_sc_product_quantity_restrictions'] );
			}

			return $meta_value;
		}

		/**
		 * Add product quantity restriction meta in export headers
		 *
		 * @param  array $headers Existing headers.
		 * @return array
		 */
		public function export_headers( $headers = array() ) {

			$product_quantity_restriction_headers = array(
				'wc_sc_product_quantity_restrictions' => __( 'Product quantity based restrictions', 'woocommerce-smart-coupons' ),
			);

			return array_merge( $headers, $product_quantity_restriction_headers );
		}

		/**
		 * Function to copy coupon product quantity restriction meta in newly generated coupon
		 *
		 * @param  array $args The arguments.
		 */
		public function copy_meta( $args = array() ) {
			try {
				$new_coupon_id = ( ! empty( $args['new_coupon_id'] ) ) ? absint( $args['new_coupon_id'] ) : 0;
				$coupon        = ( ! empty( $args['ref_coupon'] ) ) ? $args['ref_coupon'] : false;

				if ( empty( $new_coupon_id ) || empty( $coupon ) ) {
					return;
				}

				$add_product_details = array();
				if ( $this->is_wc_gte_30() && $this->is_callable( $coupon, 'get_meta' ) && $this->is_callable( $coupon, 'update_meta_data' ) && $this->is_callable( $coupon, 'save' ) ) {
					$add_product_details = $coupon->get_meta( 'wc_sc_product_quantity_restrictions' );
					$coupon->update_meta_data( 'wc_sc_product_quantity_restrictions', $add_product_details );
					$coupon->save();
				} else {
					$old_coupon_id       = ( ! empty( $coupon->id ) ) ? $coupon->id : 0;
					$add_product_details = $this->get_post_meta( $old_coupon_id, 'wc_sc_product_quantity_restrictions', true );
					$this->update_post_meta( $new_coupon_id, 'wc_sc_product_quantity_restrictions', $add_product_details );
				}

				$new_coupon = new WC_Coupon( $new_coupon_id );

				if ( $this->is_callable( $new_coupon, 'update_meta_data' ) && $this->is_callable( $new_coupon, 'save' ) ) {
					$new_coupon->update_meta_data( 'wc_sc_product_quantity_restrictions', $add_product_details );
					$new_coupon->save();
				} else {
					update_post_meta( $new_coupon_id, 'wc_sc_product_quantity_restrictions', $add_product_details );
				}
			} catch ( \Throwable $e ) {
				$this->sc_block_catch_error( $e );
			}
		}

		/**
		 * Process cart product quantities
		 *
		 * @param array $product_quantity_restrictions values.
		 * @param array $params condition and cart contents.
		 * @return bool|string
		 * @throws Exception If empty product quantities.
		 */
		public function process_product_quantities( $product_quantity_restrictions = array(), $params = array() ) {
			try {
				if ( ! empty( $product_quantity_restrictions ) ) {
					$status            = array();
					$condition         = ! empty( $params['condition'] ) ? $params['condition'] : 'any';
					$items_to_validate = ! empty( $params['items_to_validate'] ) ? $params['items_to_validate'] : array();
					foreach ( $product_quantity_restrictions as $id => $restriction ) {
						if ( 0 === $id ) {
							continue;
						}
						$min_quantity            = ! empty( $restriction['min'] ) ? intval( $restriction['min'] ) : 0;
						$max_quantity            = ! empty( $restriction['max'] ) ? intval( $restriction['max'] ) : 0;
						$cart_product_quantities = $this->cart_product_quantities( $items_to_validate );
						$product_quantity        = ! empty( $cart_product_quantities[ $id ] ) ? intval( $cart_product_quantities[ $id ] ) : 0;

						if ( empty( $min_quantity ) && empty( $max_quantity ) ) {
							$status[] = 'empty';
						} elseif ( empty( $min_quantity ) && ! empty( $max_quantity ) && $product_quantity <= $max_quantity ) {
							$status[] = 'true';
						} elseif ( empty( $max_quantity ) && ! empty( $min_quantity ) && $product_quantity >= $min_quantity ) {
							$status[] = 'true';
						} elseif ( ! empty( $min_quantity ) && ! empty( $max_quantity ) && $product_quantity >= $min_quantity && $product_quantity <= $max_quantity ) {
							$status[] = 'true';
						} else {
							$status[] = 'false';
						}
					}

					switch ( $condition ) {
						case 'all':
							if ( in_array( 'false', $status, true ) ) {
								return false;
							} elseif ( in_array( 'true', $status, true ) ) {
								return true;
							} else {
								return 'empty';
							}
						default:
						case 'any':
							if ( in_array( 'true', $status, true ) ) {
								return true;
							} elseif ( in_array( 'false', $status, true ) ) {
								return false;
							} else {
								return 'empty';
							}
					}
				}
			} catch ( \Throwable $e ) {
				$this->sc_block_catch_error( $e );
			}

			return 'empty';
		}

		/**
		 * Process cart category quantities
		 *
		 * @param array $product_category_quantity_restrictions values.
		 * @param array $params condition and cart contents.
		 * @return bool|string
		 * @throws Exception If empty cart category quantities.
		 */
		public function process_category_quantities( $product_category_quantity_restrictions = array(), $params = array() ) {
			try {
				if ( ! empty( $product_category_quantity_restrictions ) ) {
					$status                             = array();
					$condition                          = ! empty( $params['condition'] ) ? $params['condition'] : 'any';
					$items_to_validate                  = ! empty( $params['items_to_validate'] ) ? $params['items_to_validate'] : array();
					$cart_product_categories_quantities = $this->cart_product_categories_quantities( $items_to_validate );
					foreach ( $product_category_quantity_restrictions as $id => $restriction ) {
						$min_quantity = ! empty( $restriction['min'] ) ? intval( $restriction['min'] ) : '';
						$max_quantity = ! empty( $restriction['max'] ) ? intval( $restriction['max'] ) : '';
						if ( 0 === $id ) {
							continue;
						}
						$category_quantity = ! empty( $cart_product_categories_quantities[ $id ] ) ? intval( $cart_product_categories_quantities[ $id ] ) : 0;

						if ( empty( $min_quantity ) && empty( $max_quantity ) ) {
							$status[] = 'empty';
						} elseif ( empty( $min_quantity ) && ! empty( $max_quantity ) && $category_quantity <= $max_quantity ) {
							$status[] = 'true';
						} elseif ( empty( $max_quantity ) && ! empty( $min_quantity ) && $category_quantity >= $min_quantity ) {
							$status[] = 'true';
						} elseif ( ! empty( $min_quantity ) && ! empty( $max_quantity ) && $category_quantity >= $min_quantity && $category_quantity <= $max_quantity ) {
							$status[] = 'true';
						} else {
							$status[] = 'false';
						}
					}

					switch ( $condition ) {
						case 'all':
							if ( in_array( 'false', $status, true ) ) {
								return false;
							} elseif ( in_array( 'true', $status, true ) ) {
								return true;
							} else {
								return 'empty';
							}
						default:
						case 'any':
							if ( in_array( 'true', $status, true ) ) {
								return true;
							} elseif ( in_array( 'false', $status, true ) ) {
								return false;
							} else {
								return 'empty';
							}
					}
				}
			} catch ( \Throwable $e ) {
				$this->sc_block_catch_error( $e );
			}
			return 'empty';
		}

		/**
		 * Calculate product quantities
		 *
		 * @param array $cart_contents cart contents.
		 * @return array
		 */
		public function cart_product_quantities( $cart_contents = array() ) {
			if ( empty( $cart_contents ) ) {
				return $cart_contents;
			}
			$cart_product_quantities = array();
			foreach ( $cart_contents as $cart_content ) {
				$cart_item    = ! empty( $cart_content->object ) ? $cart_content->object : array();
				$quantity     = ! empty( $cart_item['quantity'] ) ? intval( $cart_item['quantity'] ) : 1;
				$product_id   = ! empty( $cart_item['product_id'] ) ? intval( $cart_item['product_id'] ) : 0;
				$variation_id = ! empty( $cart_item['variation_id'] ) ? intval( $cart_item['variation_id'] ) : 0;
				if ( empty( $cart_content->object['wc_sc_product_source'] ) ) {
					if ( ! empty( $variation_id ) ) {
						if ( isset( $cart_product_quantities[ $variation_id ] ) ) {
							$cart_product_quantities[ $variation_id ] = $cart_product_quantities[ $variation_id ] + $quantity;
						} else {
							$cart_product_quantities[ $variation_id ] = $quantity;
						}
					}

					if ( isset( $cart_product_quantities[ $product_id ] ) ) {
						$cart_product_quantities[ $product_id ] = $cart_product_quantities[ $product_id ] + $quantity;
					} else {
						$cart_product_quantities[ $product_id ] = $quantity;
					}
				}
			}

			return $cart_product_quantities;
		}

		/**
		 * Calculate category quantities
		 *
		 * @param array $cart_contents cart contents.
		 * @return array
		 */
		public function cart_product_categories_quantities( $cart_contents = array() ) {
			if ( empty( $cart_contents ) ) {
				return $cart_contents;
			}
			$categories_quantities = array();
			foreach ( $cart_contents as $key => $cart_content ) {
				$cart_item = ! empty( $cart_content->object ) ? $cart_content->object : array();
				$product   = ! empty( $cart_item['data'] ) ? $cart_item['data'] : array();
				$quantity  = ! empty( $cart_item['quantity'] ) ? intval( $cart_item['quantity'] ) : 1;

				if ( is_object( $product ) && is_callable( array( $product, 'get_category_ids' ) ) ) {
					$product_variation = ( is_callable( array( $product, 'is_type' ) ) ) ? $product->is_type( 'variation' ) : false;
					if ( $product_variation ) {
						$parent_id = ( is_callable( array( $product, 'get_parent_id' ) ) ) ? $product->get_parent_id() : 0;
						if ( ! empty( $parent_id ) ) {
							$product = wc_get_product( $parent_id );
						}
					}
					$categories = $product->get_category_ids();
					if ( ! empty( $categories ) ) {
						foreach ( $categories as $category ) {
							if ( isset( $categories_quantities[ $category ] ) ) {
								$categories_quantities[ $category ] = $categories_quantities[ $category ] + $quantity;
							} else {
								$categories_quantities[ $category ] = $quantity;
							}
						}
					}
				}
			}

			return $categories_quantities;
		}

		/**
		 * Calculate category quantities
		 *
		 * @param array $input_array .
		 * @return string
		 */
		public function product_quantity_restrictions_encode( $input_array = array() ) {
			try {
				if ( ! is_array( $input_array ) || empty( $input_array ) || ! isset( $input_array['type'] ) ) {
					return '';
				}

				$encode_string = '';

				switch ( $input_array['type'] ) {
					case 'cart':
						if ( isset( $input_array['values'] ) && isset( $input_array['values']['cart'] ) && ! empty( $input_array['values']['cart'] ) ) {
							$cart_data     = $input_array['values']['cart'];
							$min           = isset( $cart_data['min'] ) && ! empty( $cart_data['min'] ) && is_scalar( $cart_data['min'] ) ? (int) $cart_data['min'] : 0;
							$max           = isset( $cart_data['max'] ) && ! empty( $cart_data['max'] ) && is_scalar( $cart_data['max'] ) ? (int) $cart_data['max'] : 0;
							$encode_string = 'cart@' . $min . '_' . $max;
						}
						break;
					case 'product':
						$segment = array();
						if ( isset( $input_array['values'] ) && isset( $input_array['values']['product'] ) && ! empty( $input_array['values']['product'] ) ) {
							$item_product = array();
							foreach ( $input_array['values']['product'] as $product_id => $product_data ) {
								$product_id = (int) $product_id;
								$min        = isset( $product_data['min'] ) && ! empty( $product_data['min'] ) && is_scalar( $product_data['min'] ) ? (int) $product_data['min'] : 0;
								$max        = isset( $product_data['max'] ) && ! empty( $product_data['max'] ) && is_scalar( $product_data['max'] ) ? (int) $product_data['max'] : 0;
								if ( ! empty( $product_id ) ) {
									$item_product[] = $product_id . '-' . $min . '_' . $max;
								}
							}
							if ( ! empty( $item_product ) ) {
								$segment[] = 'product@' . implode( '#', $item_product );
							}
						}

						if ( isset( $input_array['values'] ) && isset( $input_array['values']['product_category'] ) && ! empty( $input_array['values']['product_category'] ) ) {
							$item_category = array();
							foreach ( $input_array['values']['product_category'] as $category_id => $category_data ) {
								$category_id = (int) $category_id;
								$min         = isset( $category_data['min'] ) && ! empty( $category_data['min'] ) && is_scalar( $category_data['min'] ) ? (int) $category_data['min'] : 0;
								$max         = isset( $category_data['max'] ) && ! empty( $category_data['max'] ) && is_scalar( $category_data['max'] ) ? (int) $category_data['max'] : 0;
								if ( ! empty( $category_id ) ) {
									$item_category[] = $category_id . '-' . $min . '_' . $max;
								}
							}
							if ( ! empty( $item_category ) ) {
								$segment[] = 'category@' . implode( '#', $item_category );
							}
						}

						if ( ! empty( $segment ) ) {
							$encode_string = implode( '|', $segment );
						}
						break;
				}
			} catch ( \Throwable $e ) {
				$this->sc_block_catch_error( $e );
				$encode_string = '';
			}

			return $encode_string;
		}

		/**
		 * Calculate category quantities
		 *
		 * @param string $decode_string .
		 * @return array
		 */
		public function product_quantity_restrictions_decode( $decode_string = '' ) {
			try {
				if ( empty( $decode_string ) || ! is_string( $decode_string ) ) {
					return array();
				}
				$input_array = array(
					'type'      => '',
					'values'    => array(),
					'condition' => 'any',
				);

				$segments = explode( '|', $decode_string );
				if ( is_array( $segments ) && ! empty( $segments ) ) {
					foreach ( $segments as $segment ) {
						$parts = explode( '@', $segment );

						switch ( $parts[0] ) {
							case 'cart':
								$cart_values                   = explode( '_', $parts[1] );
								$input_array['type']           = 'cart';
								$input_array['values']['cart'] = array(
									'min' => (int) ( $cart_values[0] ?? 0 ),
									'max' => (int) ( $cart_values[1] ?? 0 ),
								);
								break;
							case 'product':
								$product_values = explode( '#', $parts[1] );
								foreach ( $product_values as $product_item ) {
									$product_data        = explode( '-', $product_item );
									$input_array['type'] = 'product';
									$min_max             = isset( $product_data[1] ) && ! empty( $product_data[1] ) ? explode( '_', $product_data[1] ) : array();
									$input_array['values']['product'][ $product_data[0] ] = array(
										'min' => (int) ( $min_max[0] ?? 0 ),
										'max' => (int) ( $min_max[1] ?? 0 ),
									);
								}
								break;
							case 'category':
								$category_values = explode( '#', $parts[1] );
								foreach ( $category_values as $category_item ) {
									$category_data       = explode( '-', $category_item );
									$input_array['type'] = 'product';
									$min_max             = isset( $category_data[1] ) && ! empty( $category_data[1] ) ? explode( '_', $category_data[1] ) : array();
									$input_array['values']['product_category'][ $category_data[0] ] = array(
										'min' => (int) ( $min_max[0] ?? 0 ),
										'max' => (int) ( $min_max[1] ?? 0 ),
									);
								}
								break;
						}
					}
				}
			} catch ( \Throwable $e ) {
				$this->sc_block_catch_error( $e );
				$input_array = array();
			}

			return $input_array;
		}
	}
}

WC_SC_Coupons_By_Product_Quantity::get_instance();
