<?php // phpcs:ignore WordPress.Files.FileName
/**
 * The class that manage the cart rules
 *
 * @package  YITH\DynamicPricing\And\Discounts\Classes
 */

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

/**
 * The class that manage the cart rules
 */
class YWDPD_Cart_Rules_Manager {

	use YWDPD_Singleton_Trait;

	/**
	 * The array that contain all cart rules object.
	 *
	 * @var array
	 */
	protected $cart_rules;

	/**
	 * YWDPD_Cart_Rules_Manager constructor.
	 */
	private function __construct() {
		add_action( 'woocommerce_cart_updated', array( $this, 'apply_coupon_cart_discount' ), 99 );
		add_action( 'woocommerce_before_calculate_totals', array( $this, 'apply_coupon_cart_discount' ), 99 );
		add_action( 'woocommerce_cart_loaded_from_session', array( $this, 'apply_coupon_cart_discount' ), 101 );
		add_filter( 'woocommerce_cart_totals_coupon_label', array( $this, 'dynamic_label_coupon' ), 10, 2 );
		add_filter( 'woocommerce_cart_totals_coupon_html', array( $this, 'coupon_cart_html' ), 10, 2 );
		add_filter( 'woocommerce_coupon_error', array( $this, 'remove_coupon_cart_message' ), 10, 3 );
		add_filter( 'woocommerce_coupon_message', array( $this, 'remove_coupon_cart_message' ), 10, 3 );
		add_action( 'woocommerce_shipping_init', array( $this, 'load_shipping_method' ), 10 );
		add_filter( 'woocommerce_shipping_methods', array( $this, 'register_shipping_method' ) );
		add_filter( 'ywdpd_product_can_be_counted_in_condition', array( $this, 'exclude_special_items' ), 20, 2 );


	}

	/**
	 * Load the cart rules
	 *
	 * @author YITH <plugins@yithemes.com>
	 * @since  3.0.0
	 */
	public function load_cart_rules() {
		$this->cart_rules = $this->get_valid_rules( ywdpd_get_cart_rules() );
	}

	/**
	 * Return all valid cart rule with coupon
	 *
	 * @since 4.0.0
	 */
	public function load_cart_rules_for_coupon() {
		$cart_rules             = $this->get_valid_rules( ywdpd_get_cart_rules() );
		$cart_rules_with_coupon = array();
		foreach ( $cart_rules as $cart_coupon ) {
			if ( $cart_coupon->can_add_coupon() ) {
				$cart_rules_with_coupon[] = $cart_coupon;
			}
		}
		$this->cart_rules = $cart_rules_with_coupon;
	}

	/**
	 * Check if is possible apply the coupon
	 *
	 * @return bool
	 * @since 4.3.0
	 */
	public function can_add_coupon() {
		$this->load_cart_rules_for_coupon();

		return count( $this->cart_rules ) > 0;
	}

	/**
	 * Get the valid cart rules
	 *
	 * @param array $rules The enabled cart rules.
	 *
	 * @return YWDPD_Cart_Rule[]
	 * @since  3.0.0
	 */
	public function get_valid_rules( $rules ) {
		$valid_rules = array();

		if ( ! is_null( WC()->cart ) && ! WC()->cart->is_empty() && count( $rules ) > 0 ) {
			foreach ( $rules as $cart_rule ) {
				/**
				 * The single rule
				 *
				 * @var YWDPD_Cart_Rule $cart_rule
				 */
				$allow_with_other_coupon = true;
				if ( $this->cart_has_coupons() ) {
					if ( ! $cart_rule->can_be_used_with_other_coupons() ) {
						$allow_with_other_coupon = false;
					}
				}
				if ( $cart_rule->is_valid() && $allow_with_other_coupon ) {
					$valid_rules[] = $cart_rule;
					/**
					 * APPLY_FILTERS: ywdpd_not_allow_multiple_cart_rules
					 *
					 * Avoid to apply different carts rules at the same time.
					 *
					 * @param bool
					 */
					if ( apply_filters( 'ywdpd_not_allow_multiple_cart_rules', false ) ) {
						break;
					}
				}
			}
		}

		return $valid_rules;
	}

	/**
	 * Check if in the cart there are other coupon applied
	 *
	 * @return bool
	 * @since  3.0.0
	 */
	public function cart_has_coupons() {

		if ( ! is_null( WC()->cart ) ) {
			$coupons_applied = count( WC()->cart->applied_coupons );

			if ( 1 === $coupons_applied && WC()->cart->has_discount( $this->get_coupon_code() ) ) {
				return false;
			}
		}

		return $coupons_applied > 0;
	}

	/**
	 * Check if the coupon should allow the free shipping
	 *
	 * @param array $valid_rules Valid Cart rules.
	 *
	 * @return bool
	 * @since  3.0.0
	 */
	public function allow_free_shipping( $valid_rules ) {

		$allow_shipping = false;
		foreach ( $valid_rules as $cart_rule ) {

			if ( $cart_rule->allow_free_shipping() ) {
				$allow_shipping = true;
				break;
			}
		}

		return $allow_shipping;
	}

	/**
	 * Check if is possible add the free shipping
	 *
	 * @return bool
	 * @since  4.0
	 */
	public function can_add_free_shipping() {
		$this->load_cart_rules();

		return $this->allow_free_shipping( $this->cart_rules );
	}

	/**
	 * Apply the coupon in the cart
	 *
	 * @throws Exception The exception.
	 * @since  3.0.0
	 */
	public function apply_coupon_cart_discount() {
		if ( ! is_null( WC()->cart ) && ! WC()->cart->is_empty() ) {

			$this->load_cart_rules_for_coupon();
			$coupon_code = $this->get_coupon_code();

			if ( count( $this->cart_rules ) > 0 ) {

				$wc_discount = new WC_Discounts( WC()->cart );
				$coupon      = new WC_Coupon( $coupon_code );
				$valid       = $wc_discount->is_coupon_valid( $coupon );
				$valid       = is_wp_error( $valid ) ? false : $valid;
				$coupon_data = $this->get_shop_coupon_data( array(), $coupon_code );
				if ( $valid ) {
					foreach ( $coupon_data as $data_key => $data_value ) {
						if ( is_callable( array( $coupon, 'set_' . $data_key ) ) ) {
							$method = 'set_' . $data_key;
							$coupon->$method( $data_value );
						}
					}
				} else {
					$coupon->add_meta_data( 'ywdpd_coupon', 1 );
					$coupon->add_meta_data( 'ywdpd_coupon_version', '3.0.0' );
					$coupon->read_manual_coupon( $coupon_code, $coupon_data );
				}

				if ( ! $valid || $coupon->get_changes() ) {
					$coupon->save();
				}
				if ( ! WC()->cart->has_discount( $coupon_code ) ) {
					WC()->cart->add_discount( $coupon_code );
				}
			} else {
				$request_uri    = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
				$is_request_uri = strpos( $request_uri, '/wp-json/wc/store/v1/checkout?_locale=user' ) !== false || strpos( $request_uri, '/wp-json/wc/store/v1/batch?_locale=user' ) !== false;
				if ( ! $is_request_uri ) {
					$this->remove_all_dynamic_coupons();
				}
			}
		}
	}

	/**
	 * Remove the dynamic coupon
	 *
	 * @since  3.0.0
	 */
	public function remove_all_dynamic_coupons() {
		$applied_coupons = WC()->cart->get_applied_coupons();

		foreach ( $applied_coupons as $applied_coupon ) {
			$coupon   = new WC_Coupon( $applied_coupon );
			$is_ywdpd = $coupon->get_meta( 'ywdpd_coupon', true );

			if ( $is_ywdpd ) {
				remove_action( 'woocommerce_cart_updated', array( $this, 'apply_coupon_cart_discount' ), 99 );
				remove_action(
					'woocommerce_cart_loaded_from_session',
					array(
						$this,
						'apply_coupon_cart_discount',
					),
					101
				);
				WC()->cart->remove_coupon( $applied_coupon );
				add_action( 'woocommerce_cart_updated', array( $this, 'apply_coupon_cart_discount' ), 99 );
				add_action( 'woocommerce_cart_loaded_from_session', array( $this, 'apply_coupon_cart_discount' ), 101 );
			}
		}
	}

	/**
	 * Get the total of discount
	 *
	 * @param YWDPD_Cart_Rule[] $rules All cart rules.
	 *
	 * @return float
	 * @since  3.0.0
	 */
	public function get_total_discount( $rules ) {
		$discount = 0;

		foreach ( $rules as $cart_rule ) {

			$single_discount = 0;
			$single_subtotal = $cart_rule->get_cart_subtotal();
			$discount_type   = $cart_rule->get_discount_type();
			$discount_amount = $cart_rule->get_discount_amount();
			if ( 'percentage' === $discount_type ) {
				$single_discount = ( $single_subtotal * $discount_amount ) / 100;
			} elseif ( 'price' === $discount_type ) {
				$single_discount = $discount_amount;
			} elseif ( 'fixed-price' === $discount_type ) {
				/**
				 * APPLY_FILTERS: ywdpd_maybe_should_be_converted
				 *
				 * Is possible change the discount amount. Useful for multicurrency plugins.
				 *
				 * @param float $discount_amount the discount amount.
				 *
				 * @return float
				 */
				$discount_amount = apply_filters( 'ywdpd_maybe_should_be_converted', $discount_amount );
				$single_discount = ( $single_subtotal - $discount_amount ) > 0 ? ( $single_subtotal - $discount_amount ) : 0;
			}

			$discount += $single_discount;
		}

		return $discount;
	}

	/**
	 * Get all excluded product by cart rule
	 *
	 * @param YWDPD_Cart_Rule $rule The rule.
	 *
	 * @return array
	 * @since  3.0.0
	 */
	public function get_excluded_product_ids( $rule ) {
		$product_ids = array();

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

				$product_id = $product->get_id();
				if ( ! in_array( $product_id, $product_ids ) ) { // phpcs:ignore
					$product_ids[] = $product_id;
				}
			}
		}

		return $product_ids;
	}

	/**
	 * Get the coupon code
	 *
	 * @return string
	 * @since  3.0.0
	 */
	public function get_coupon_code() {
		$label_coupon = 'ywdpd_discount';
		if ( is_user_logged_in() ) {
			/**
			 * APPLY_FILTERS: ywdpd_coupon_code
			 *
			 * Is possible change the coupon code.
			 *
			 * @param string $coupon_code the coupon code.
			 *
			 * @return string
			 */
			$current_coupon_code = apply_filters( 'ywdpd_coupon_code', $label_coupon . '_' . get_current_user_id(), $label_coupon );
			$session             = WC()->session->get( 'ywdpd_coupon_code', '' );

			if ( ! empty( $session ) ) {

				$coupon = new WC_Coupon( $session );
				$coupon->delete( true );
				WC()->session->set( 'ywdpd_coupon_code', '' );
				WC()->session->save_data();
				WC()->cart->remove_coupon( $session );
			}
		} else {
			$session = WC()->session->get( 'ywdpd_coupon_code', '' );
			if ( empty( $session ) ) {
				/**
				 * APPLY_FILTERS: ywdpd_coupon_code
				 *
				 * Is possible change the coupon code.
				 *
				 * @param string $coupon_code the coupon code.
				 *
				 * @return string
				 */
				$current_coupon_code = apply_filters( 'ywdpd_coupon_code', uniqid( $label_coupon . '_' ), $label_coupon );
				WC()->session->set( 'ywdpd_coupon_code', $current_coupon_code );
				WC()->session->save_data();
			} else {
				$current_coupon_code = $session;
			}
		}

		return $current_coupon_code;
	}

	/**
	 * Change the dynamic coupon label
	 *
	 * @param string $string Coupon code.
	 * @param WC_Coupon $coupon The coupon.
	 *
	 * @return string
	 * @since  3.0.0
	 *
	 */
	public function dynamic_label_coupon( $string, $coupon ) {

		if ( is_null( $coupon ) || ! is_object( $coupon ) ) {
			return $string;
		}

		$is_ywdpd = $coupon->get_meta( 'ywdpd_coupon', true );

		$coupon_label = $this->get_coupon_code_details( true );
		/**
		 * APPLY_FILTERS: ywdpd_dynamic_label_coupon
		 *
		 * Is possible change the coupon label.
		 *
		 * @param string $coupon_label the coupon label.
		 *
		 * @return string
		 */
		$coupon_label = apply_filters( 'ywdpd_dynamic_label_coupon', $coupon_label, $coupon );

		return $is_ywdpd ? $coupon_label : $string;
	}

	/**
	 * Delete the remove link in the coupon
	 *
	 * @param string $value The old html value.
	 * @param WC_Coupon $coupon The coupon.
	 *
	 * @return string
	 * @since  3.0.0
	 *
	 */
	public function coupon_cart_html( $value, $coupon ) {
		$is_ywdpd = $coupon->get_meta( 'ywdpd_coupon', true );
		if ( $is_ywdpd ) {

			$amount = WC()->cart->get_coupon_discount_amount( $coupon->get_code(), WC()->cart->display_cart_ex_tax );
			$value  = '-' . wc_price( $amount );

		}

		return $value;
	}

	/**
	 * Remove coupon cart message.
	 *
	 * @param string $error Error Message.
	 * @param string $msg_code Code message.
	 * @param WC_Coupon $coupon Coupon.
	 *
	 * @return bool
	 */
	public function remove_coupon_cart_message( $error, $msg_code, $coupon ) {
		if ( $coupon instanceof WC_Coupon ) {
			$is_ywdpd = $coupon->get_meta( 'ywdpd_coupon', true );
			if ( $is_ywdpd ) {
				return false;
			}
		}

		return $error;
	}

	/**
	 * Remove from the quantity, the product with a gift or bogo rule
	 *
	 * @auhtor YITh
	 *
	 * @param int $num_items The items.
	 *
	 * @return int
	 * @since  3.0.0
	 */
	public function remove_items_from_quantities( $num_items ) {
		$items_to_remove = 0;

		foreach ( WC()->cart->get_cart_contents() as $cart_item_key => $cart_item ) {

			if ( isset( $cart_item['ywdpd_is_gift_product'] ) || isset( $cart_item['has_bogo_applied'] ) ) {
				$items_to_remove ++;
			}
		}

		if ( is_array( $num_items ) ) {
			$num_items = array_sum( $num_items );
		}

		return $num_items - $items_to_remove;
	}

	/**
	 * Exclude special items from cart rule condition
	 *
	 * @param bool $is_valid is valid.
	 * @param array $cart_item The cart item.
	 *
	 * @since  3.0.0
	 */
	public function exclude_special_items( $is_valid, $cart_item ) {

		if ( $is_valid ) {
			$is_valid = ! ( isset( $cart_item['ywdpd_is_gift_product'] ) || isset( $cart_item['has_bogo_applied'] ) );
		}

		return $is_valid;
	}

	/**
	 * Build the right coupon code
	 *
	 * @param bool $formatted Return the coupon code formatted or simple.
	 *
	 * @return string
	 * @since  3.0.0
	 */
	public function get_coupon_code_details( $formatted = false ) {
		$label = '';
		if ( 'default_name' === YITH_WC_Dynamic_Options::get_coupon_label_mode() ) {
			$label = YITH_WC_Dynamic_Options::get_coupon_label();
			$label = $formatted ? $label . ':' : str_replace( ' ', '-', strtolower( $label ) );
		} else {
			$is_mobile = wp_is_mobile();
			if ( ! empty( $_REQUEST['wc-ajax'] ) && 'update_order_review' === sanitize_text_field( wp_unslash( $_REQUEST['wc-ajax'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
				$this->load_cart_rules();
			}

			if ( is_array( $this->cart_rules ) && count( $this->cart_rules ) > 0 ) {
				$names = array();
				foreach ( $this->cart_rules as $cart_rule ) {
					if ( $cart_rule->can_add_coupon() ) {
						$names[] = $cart_rule->get_name();
					}
				}
				if ( $is_mobile ) {

					$label = implode(
						', ',
						array_map(
							function ( $value ) {
								return str_replace( ' ', '-', strtolower( $value ) );
							},
							$names
						)
					);
				} else {
					$names = $formatted ? implode( '<li></li>', $names ) : implode(
						', ',
						array_map(
							function ( $value ) {
								return str_replace( ' ', '-', strtolower( $value ) );
							},
							$names
						)
					);
					$label .= $names;
					if ( $formatted ) {
						$label = '<ul class="ywdpd_list_cart_rules_applied">' . $label . '</ul>';
					}
				}
			}
		}

		return $label;
	}

	/**
	 * Load the class that manage the shipping method
	 *
	 * @return void
	 * @since 4.0
	 */
	public function load_shipping_method() {
		require_once YITH_YWDPD_INC . 'class-ywdpd-wc-shipping-free-shipping.php';
	}

	/**
	 * Register the shipping method
	 *
	 * @param array $methods The registered shipping methods.
	 *
	 * @return array
	 * @since 4.0
	 */
	public function register_shipping_method( $methods ) {
		$methods['yith_ywdpd_free_shipping'] = 'YWDPD_WC_Shipping_Free_Shipping';

		return $methods;
	}


	/**
	 * Get the coupon data
	 *
	 * @param array $data The coupon data.
	 * @param string $coupon_code The coupon code
	 *
	 * @return array
	 */
	public function get_shop_coupon_data( $data, $coupon_code ) {
		if ( strtolower( $coupon_code ) === $this->get_coupon_code() ) {
			$data = array(
				'discount_type'        => 'fixed_cart',
				'individual_use'       => false,
				'usage_limit'          => 0,
				'excluded_product_ids' => array(),
			);
			if ( 1 === count( $this->cart_rules ) ) {

				$valid_rule = reset( $this->cart_rules );
				/**
				 * The unique valid rule
				 *
				 * @var YWDPD_Cart_Rule $valid_rule
				 */

				if ( 'percentage' === $valid_rule->get_discount_type() ) {
					$data['amount']               = $valid_rule->get_discount_amount();
					$data['excluded_product_ids'] = $this->get_excluded_product_ids( $valid_rule );
					$data['discount_type']        = 'percent';
				} else {
					$data['amount'] = $this->get_total_discount( $this->cart_rules );
				}
			} else {
				$data ['amount'] = $this->get_total_discount( $this->cart_rules );
			}
		}

		return $data;
	}

	public function get_free_shipping_notice() {
		$notices    = array();
		$cart_rules = ywdpd_get_cart_rules();

		foreach ( $cart_rules as $cart_rule ) {
			if ( $cart_rule->can_show_notice() ) {
				$cart_notices = $cart_rule->get_cart_notices();

				if ( count( $cart_notices ) > 0 ) {
					$notices = array_merge( $notices, $cart_notices );
				}
			}
		}

		$args = array(
			'notices' => $notices,
		);

		ob_start();
		wc_get_template( 'woocommerce/cart/ywdpd-cart-notices.php', $args, '', YITH_YWDPD_TEMPLATE_PATH );

		return ob_get_clean();
	}
}
