<?php
/**
 * The Special Offer class
 *
 * @package YITH WooCommerce Dynamic Pricing & Discounts\Classes\Price Rules
 */

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

/**
 * The Special offer class.
 */
class YWDPD_Special_Offer extends YWDPD_Price_Rule {

	/**
	 * Extra data for this object. Name value pairs (name + default value).
	 * Used as a standard way for sub classes (like product types) to add
	 * additional information to an inherited class.
	 *
	 * @since 3.0.0
	 * @var array
	 */
	protected $extra_data = array(
		'so-rule'                     => array(),
		'so-repeat'                   => 'no',
		'can_special_offer_in_popup'  => 'no',
		'text_in_modal_special_offer' => '',
	);

	/**
	 * This array will contain the special meta key , the key without _ prefix.
	 *
	 * @since 3.0.0
	 * @var array
	 */
	protected $special_meta = array(
		'so-rule',
	);

	/**
	 * Get the rule if the ID is passed, otherwise the rule is new and empty.
	 *
	 * @param int|YWDPD_Special_Offer|object $obj ID to load from the DB (optional) or already queried data.
	 *
	 * @throws Exception The exception.
	 */
	public function __construct( $obj = 0 ) {

		parent::__construct( $obj );

		$this->read();
	}

	/**
	 * Set the rule for the special offer.
	 *
	 * @param array $rule The rule to set.
	 *
	 * @since 3.0.0
	 */
	public function set_so_rule( $rule ) {
		$this->set_prop( 'so-rule', $rule );
	}

	/**
	 * Set the repeat field.
	 *
	 * @param string $repeat The repeat value ( Yes or not ).
	 *
	 * @since 3.0.0
	 */
	public function set_so_repeat( $repeat ) {
		$this->set_prop( 'so-repeat', $repeat );
	}

	/**
	 * Set if a special offer can be promote in modal.
	 *
	 * @param string $allow_popup Yes or not.
	 *
	 * @since 3.0.0
	 */
	public function set_can_special_offer_in_popup( $allow_popup ) {
		$this->set_prop( 'can_special_offer_in_popup', $allow_popup );
	}

	/**
	 * Set the text to show in modal
	 *
	 * @param string $text_in_modal_special_offer The text.
	 *
	 * @since 3.0.0
	 */
	public function set_text_in_modal_special_offer( $text_in_modal_special_offer ) {
		$this->set_prop( 'text_in_modal_special_offer', $text_in_modal_special_offer );
	}


	/**
	 * Get the rule for the special offer.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return array
	 * @since 3.0.0
	 */
	public function get_so_rule( $context = 'view' ) {
		return $this->get_prop( 'so-rule', $context );
	}

	/**
	 * Get the repeat field.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 * @since 3.0.0
	 */
	public function get_so_repeat( $context = 'view' ) {
		return $this->get_prop( 'so-repeat', $context );
	}

	/**
	 * Get if a special offer can be promote in popup.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 * @since 3.0.0
	 */
	public function get_can_special_offer_in_popup( $context = 'view' ) {
		return $this->get_prop( 'can_special_offer_in_popup', $context );
	}

	/**
	 * Get the text to show in popup
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 * @since 3.0.0
	 */
	public function get_text_in_modal_special_offer( $context = 'view' ) {
		return $this->get_prop( 'text_in_modal_special_offer', $context );
	}


	/**
	 * Check if the rule can be promote in popup
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return bool
	 * @since 3.0.0
	 */
	public function is_promoted_in_popup( $context = 'view' ) {

		$value            = yith_plugin_fw_is_true( $this->get_can_special_offer_in_popup( $context ) );
		$is_active_adjust = yith_plugin_fw_is_true( $this->get_active_apply_discount_to( $context ) );
		$value            = $is_active_adjust && $value;
		if ( 'view' === $context ) {
			/**
			 * APPLY_FILTERS: ywdpd_is_promoted_in_popup
			 *
			 * Set if possible show the discount in popup.
			 *
			 * @param bool                $value Is valid or not.
			 * @param YWDPD_Special_Offer $rule  The  rule.
			 *
			 * @return bool
			 */
			$value = apply_filters( 'ywdpd_is_promoted_in_popup', $value, $this );
		}

		return $value;
	}

	/**
	 * Check if the rule can repeat.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return bool
	 * @since 3.0.0
	 */
	public function can_repeat_rule( $context = 'view' ) {

		$value = yith_plugin_fw_is_true( $this->get_so_repeat( $context ) );

		return $value;
	}

	/**
	 * Check if the rule is offer in popup
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return bool
	 * @since  3.0.0
	 * @author YITH <plugins@yithemes.com>
	 */
	public function can_show_in_popup( $context = 'view' ) {

		return yith_plugin_fw_is_true( $this->get_can_special_offer_in_popup( $context ) );

	}

	/**
	 * Return the new price for the specific quantity
	 *
	 * @param float      $default_price The original price.
	 * @param int        $quantity      The quantity to check.
	 * @param WC_Product $product       The product object.
	 *
	 * @return float
	 * @since 3.0.0
	 */
	public function get_discounted_price( $default_price, $quantity, $product ) {

		if ( $product instanceof WC_Product_Gift_Card ) {
			return $default_price;
		}

		$discounted_price = $default_price;
		$rule             = $this->get_so_rule();
		$discount_type    = $rule['type_discount'];
		$discount_amount  = floatval( str_replace( ',', '.', $rule['discount_amount'] ) );

		switch ( $discount_type ) {
			case 'percentage':
				$percent = floatval( $discount_amount / 100 );

				$discounted_price = $discounted_price - ( $discounted_price * $percent );
				break;
			case 'price':
				/**
				 * 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 );
				$discounted_price = $discounted_price - $discount_amount;
				break;
			default:
				/**
				 * 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
				 */
				$discounted_price = apply_filters( 'ywdpd_maybe_should_be_converted', $discount_amount );
				break;

		}

		return $discounted_price > 0 ? $discounted_price : 0;
	}

	/**
	 * Return the total target
	 *
	 * @param string $cart_item_key The cart item key.
	 *
	 * @return int
	 * @since  3.0.0
	 */
	public function get_total_target( $cart_item_key ) {
		$tt = 0;

		if ( ! is_null( WC()->cart ) && ! isset( WC()->cart->cart_contents[ $cart_item_key ]['ywdpd_is_gift_product'] ) ) {
			$tt = isset( WC()->cart->cart_contents[ $cart_item_key ][ $this->get_id() ]['total_target'] ) ? WC()->cart->cart_contents[ $cart_item_key ][ $this->get_id() ]['total_target'] : 0;
		}

		return $tt;
	}

	/**
	 * Get the amount of repetitions
	 *
	 * @param int $rcq      The remain clear quantity.
	 * @param int $rmq      The mix quantity.
	 * @param int $purchase Total product to purchase.
	 *
	 * @return int
	 * @since  3.0.0
	 */
	public function get_total_repetitions( $rcq, $rmq, $purchase ) {
		$repetitions = 1;
		if ( $this->can_repeat_rule() ) {
			$repetitions = floor( ( $rcq + $rmq ) / $purchase );
		}

		return $repetitions;
	}

	/**
	 * Add the rule in the cart
	 *
	 * @param string $cart_item_key_to_apply  The item key that allow the apply.
	 * @param string $cart_item_key_to_adjust the cart The item key where add the rule.
	 *
	 * @return bool
	 * @since 3.0.0
	 */
	public function apply_rule_in_cart( $cart_item_key_to_apply, $cart_item_key_to_adjust ) {

		$cart_item         = WC()->cart->cart_contents[ $cart_item_key_to_apply ];
		$cart_item_adj     = WC()->cart->cart_contents[ $cart_item_key_to_adjust ];

		$has_special_offer = isset( WC()->cart->cart_contents[ $cart_item_key_to_adjust ]['has_special_offer'] );
		$tt                = $this->get_total_target( $cart_item_key_to_adjust );

		if ( ! $has_special_offer && $tt > 0 ) {

			$same_product      = $cart_item_key_to_apply === $cart_item_key_to_adjust;
			$quantity          = $cart_item_adj['quantity'];
			$product           = $cart_item_adj['data'];
			$price_to_discount = $this->get_price_to_discount( $cart_item_adj, $cart_item_key_to_adjust );
			$quantity_mode     = $this->get_quantity_based();
			$rule              = $this->get_so_rule();
			$type_discount     = $rule['type_discount'];

			if ( $same_product && in_array( $quantity_mode, array( 'cart_line', 'single_variation_product' ), true ) ) {
				YWDPD_Counter::reset_special_offer_counter( $this->get_id() );
				$adj_counter = YWDPD_Counter::get_special_offer_counter( $this->get_id(), $tt );
			} elseif ( $same_product && 'single_product' === $quantity_mode && 'variation' === $product->get_type() ) {
				$adj_counter = YWDPD_Counter::get_special_offer_counter( $this->get_id() . '_' . $product->get_parent_id(), $tt );
			} else {
				$adj_counter = YWDPD_Counter::get_special_offer_counter( $this->get_id(), $tt );
			}
			$a = ( $adj_counter > $quantity ) ? $quantity : $adj_counter;
			if ( $a > 0 ) {
				$full_price_quantity = intval( $cart_item_adj['available_quantity'] - $a );
				$discount_quantity   = intval( $a );
				$difference_s        = $this->get_discounted_price( $price_to_discount, $tt, $product );

				$normal_line_total = $quantity * $price_to_discount;

				switch ( $type_discount ) {
					case 'percentage':
						$line_total         = ( $discount_quantity * $difference_s ) + ( $full_price_quantity * $price_to_discount );
						$current_difference = ( $normal_line_total - $line_total ) / $quantity;
						$price              = $current_difference > 0 ? $price_to_discount - $current_difference : $price_to_discount;
						break;
					case 'price':
						$full_price_quantity = $quantity - $a;
						$line_total          = ( $discount_quantity * $difference_s ) + ( $full_price_quantity * $price_to_discount );
						$current_difference  = ( $normal_line_total - $line_total ) / $quantity;
						$price               = $current_difference > 0 ? $price_to_discount - $current_difference : $price_to_discount;
						break;
					case 'fixed-price':
						$full_price_quantity = $quantity - $a;
						$line_total          = ( $discount_quantity * $difference_s ) + ( $full_price_quantity * $price_to_discount );
						$current_difference  = ( $normal_line_total - $line_total ) / $quantity;
						$price               = $current_difference > 0 ? $price_to_discount - $current_difference : $price_to_discount;
						break;
				}

				$price = $price >= 0 ? $price : 0;
				/**
				 * APPLY_FILTERS: ywdpd_round_total_price
				 *
				 * Allow round totals.
				 *
				 * @param bool $allow_round True or false.
				 *
				 * @return bool
				 */
				if ( apply_filters( 'ywdpd_round_total_price', false ) ) {
					$price = round( $price, wc_get_price_decimals() );
				}

				if ( $same_product && 'single_product' === $quantity_mode && 'variation' === $product->get_type() ) {

					if ( $tt >= $cart_item_adj['quantity'] ) {

						YWDPD_Counter::update_special_offer_counter( $this->get_id() . '_' . $product->get_parent_id(), $cart_item_adj['quantity'] );
						WC()->cart->cart_contents[ $cart_item_key_to_adjust ]['available_quantity'] = 0;

					} else {

						WC()->cart->cart_contents[ $cart_item_key_to_adjust ]['available_quantity'] = $cart_item_adj['quantity'] - $adj_counter;
						YWDPD_Counter::reset_special_offer_counter( $this->get_id() . '_' . $product->get_parent_id(), false );

					}
				} else {

					if ( $tt > $cart_item_adj['quantity'] ) {

						YWDPD_Counter::update_special_offer_counter( $this->get_id(), $cart_item_adj['quantity'] );
						WC()->cart->cart_contents[ $cart_item_key_to_adjust ]['available_quantity'] = 0;

					} else {
						$new_av = $cart_item_adj['quantity'] - $adj_counter;
						$new_av = max( $new_av, 0 );

						WC()->cart->cart_contents[ $cart_item_key_to_adjust ]['available_quantity'] = $new_av;
						YWDPD_Counter::reset_special_offer_counter( $this->get_id(), false );
					}
				}
				$result = $this->save_discount_in_cart( $cart_item_key_to_adjust, $price_to_discount, $price );
				YWDPD_Counter::update_product_with_dynamic_rule_counter( 'special_offer', $this->get_id(), $cart_item_adj['quantity'] );
				if ( $result ) {
					WC()->cart->cart_contents[ $cart_item_key_to_adjust ]['has_special_offer'] = true;
				}

				return $result;
			}
		}

		return false;
	}

	/**
	 * Calculate the amout of product for special offer.
	 *
	 * @param array $cart_item The item to check.
	 * @param bool  $clean     The clean args.
	 *
	 * @return int
	 * @since 3.0.0
	 */
	public function num_valid_product_to_apply_in_cart( $cart_item, $clean = false ) {
		$num           = 0;
		$quantity_mode = $this->get_quantity_based();
		$product       = $cart_item['data'];
		$product_id    = 'variation' === $product->get_type() ? $product->get_parent_id() : $product->get_id();

		if ( in_array( $quantity_mode, array(
				'cart_line',
				'single_variation_product'
			), true ) || 'single_product' === $quantity_mode && 'variation' !== $product->get_type() ) {
			if ( $this->is_valid_to_apply( $product, true ) ) {

				$is_valid_to_adjust = $this->is_valid_to_adjust( $product );
				if ( $clean ) {
					if ( ! $is_valid_to_adjust ) {
						$num = $cart_item['available_quantity'];
					}
				} else {
					if ( $is_valid_to_adjust ) {
						$num = isset( $cart_item['available_quantity'] ) ? $cart_item['available_quantity'] : 1;
					}
				}
			}
		} elseif ( 'single_product' === $quantity_mode && 'variation' === $product->get_type() ) {
			if ( $this->is_valid_to_apply( $product ) ) {

				$is_valid_to_adjust = $this->is_valid_to_adjust( $product );
				if ( $clean ) {
					if ( ! $is_valid_to_adjust ) {
						$num = YWDPD_Counter::get_product_count( $product_id );
					}
				} else {
					if ( $is_valid_to_adjust ) {
						$num = YWDPD_Counter::get_product_count( $product_id );
					}
				}
			}
		} else {
			if ( ! is_null( WC()->cart ) && ! WC()->cart->is_empty() ) {
				foreach ( WC()->cart->get_cart_contents() as $cart_item_key => $cart_it ) {
					if ( $clean ) {
						if ( $this->is_valid_to_apply( $cart_it['data'] ) && ! $this->is_valid_to_adjust( $cart_it['data'] ) ) {
							$num += $cart_it['available_quantity'];
						}
					} else {
						if ( $this->is_valid_to_apply( $cart_it['data'] ) && $this->is_valid_to_adjust( $cart_it['data'] ) ) {
							$num += $cart_it['available_quantity'];
						}
					}
				}
			}
		}

		return $num;
	}

	/**
	 * Return the discounted price html
	 *
	 * @param WC_Product $product The product.
	 *
	 * @return string
	 * @since 4.0.0
	 */
	public function get_discounted_price_html( $product ) {
		$price_html = $product->get_price_html();

		if ( 'variable' === $product->get_type() ) {
			if ( $product->is_on_sale() ) {
				$min_price = $product->get_variation_sale_price( 'min', false );
				$max_price = $product->get_variation_sale_price( 'max', false );
			} else {
				$min_price = $product->get_variation_regular_price( 'min', false );
				$max_price = $product->get_variation_regular_price( 'max', false );
			}
			$min_price = wc_get_price_to_display( $product, array( 'price' => $min_price ) );
			$max_price = wc_get_price_to_display( $product, array( 'price' => $max_price ) );
			if ( $min_price !== $max_price ) {
				$old_price_html = wc_format_price_range( $min_price, $max_price );
			} else {
				$old_price_html = wc_price( $min_price );
			}

			$min_discounted_price = $this->get_discounted_price( $min_price, 1, $product );
			$max_discounted_price = $this->get_discounted_price( $max_price, 1, $product );
			$min_discounted_price = floatval( wc_get_price_to_display( $product, array( 'price' => $min_discounted_price ) ) );
			$max_discounted_price = floatval( wc_get_price_to_display( $product, array( 'price' => $max_discounted_price ) ) );

			if ( $min_discounted_price !== $max_discounted_price ) {
				$new_price_html = wc_format_price_range( $min_discounted_price, $max_discounted_price );
			} else {
				$new_price_html = wc_price( $min_discounted_price );
			}

			$price_html = wc_format_sale_price( $old_price_html, $new_price_html );

		} else {

			$old_price        = $product->is_on_sale() ? $product->get_sale_price() : $product->get_regular_price();
			$discounted_price = $this->get_discounted_price( $old_price, 1, $product );
			if ( $old_price > $discounted_price ) {
				$old_price_html = wc_get_price_to_display( $product, array( 'price' => $old_price ) );
				$new_price_html = wc_get_price_to_display( $product, array( 'price' => $discounted_price ) );
				$price_html     = wc_format_sale_price( $old_price_html, $new_price_html );
			}
		}

		return $price_html;
	}
}
