<?php
/**
 * The Last Deals rule class
 *
 * @package YITH\Dynamic\PricingAndDiscounts\Classes\Price Rules
 */

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

/**
 * The Last Deals Class
 */
class YWDPD_Last_Deals extends YWDPD_Price_Rule {

	use YWDPD_Advanced_Conditions_Trait {
		is_product_excluded_from_conditions as trait_product_excluded;
	}

	/**
	 * 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(
		'show_deal'                    => 'always',
		'deal_conditions'              => array(),
		'deal_product_selection'       => array(),
		'deal_set_limits'              => 'no',
		'deal_total_product_to_add'    => array(),
		'deal_max_unit_product_to_add' => array(),
		'apply_deal_discount'          => 'no',
		'deal_discount'                => array(
			'discount_type'   => 'percentage',
			'discount_amount' => 10,
		),
		'deal_layout'                  => 'modal',
		'deal_note'                    => '',
		'deal_enable_countdown'        => 'no',
		'deal_countdown'               => array(
			'countdown_value' => 60,
			'countdown_type'  => 'seconds',
		),
	);

	/**
	 * Get the rule if the ID is passed, otherwise the rule is new and empty.
	 *
	 * @param int|YWDPD_Last_Deals|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 show deal field
	 *
	 * @param string $show_del The field value.
	 *
	 * @return void
	 * @since 4.0.0
	 */
	public function set_show_deal( $show_del ) {
		$this->set_prop( 'show_deal', $show_del );
	}

	/**
	 * Set the deal conditions field
	 *
	 * @param array $deal_conditions The field value.
	 *
	 * @return void
	 * @since 4.0.0
	 */
	public function set_deal_conditions( $deal_conditions ) {
		$this->set_prop( 'deal_conditions', $deal_conditions );
	}

	/**
	 * Set the deal product field
	 *
	 * @param array $deal_product_selection The field value.
	 *
	 * @return void
	 * @since 4.0.0
	 */
	public function set_deal_product_selection( $deal_product_selection ) {
		$this->set_prop( 'deal_product_selection', $deal_product_selection );
	}

	/**
	 * Set if limit the selection or not
	 *
	 * @param string $deal_set_limits The field value.
	 *
	 * @return void
	 * @since 4.0.0
	 */
	public function set_deal_set_limits( $deal_set_limits ) {
		$this->set_prop( 'deal_set_limits', $deal_set_limits );
	}

	/**
	 * Set if limit the selection or not
	 *
	 * @param array $deal_total_product_to_add The field value.
	 *
	 * @return void
	 * @since 4.0.0
	 */
	public function set_deal_total_product_to_add( $deal_total_product_to_add ) {
		$this->set_prop( 'deal_total_product_to_add', $deal_total_product_to_add );
	}

	/**
	 * Set the total deal product field
	 *
	 * @param array $deal_max_unit_product_to_add The field value.
	 *
	 * @return void
	 * @since 4.0.0
	 */
	public function set_deal_max_unit_product_to_add( $deal_max_unit_product_to_add ) {
		$this->set_prop( 'deal_max_unit_product_to_add', $deal_max_unit_product_to_add );
	}

	/**
	 * Set the apply_deal_discount field
	 *
	 * @param string $apply_deal_discount The field value.
	 *
	 * @return void
	 * @since 4.0.0
	 */
	public function set_apply_deal_discount( $apply_deal_discount ) {
		$this->set_prop( 'apply_deal_discount', $apply_deal_discount );
	}

	/**
	 * Set the deal discount field
	 *
	 * @param array $deal_discount The field value.
	 *
	 * @return void
	 * @since 4.0.0
	 */
	public function set_deal_discount( $deal_discount ) {
		$this->set_prop( 'deal_discount', $deal_discount );
	}

	/**
	 * Set the deal layout field
	 *
	 * @param string $deal_layout The field value.
	 *
	 * @return void
	 * @since 4.0.0
	 */
	public function set_deal_layout( $deal_layout ) {
		$this->set_prop( 'deal_layout', $deal_layout );
	}

	/**
	 * Set the deal note field
	 *
	 * @param string $deal_note The field value.
	 *
	 * @return void
	 * @since 4.0.0
	 */
	public function set_deal_note( $deal_note ) {
		$this->set_prop( 'deal_note', $deal_note );
	}

	/**
	 * Set the enable countdown field
	 *
	 * @param string $deal_enable_countdown The field value.
	 *
	 * @return void
	 * @since 4.0.0
	 */
	public function set_deal_enable_countdown( $deal_enable_countdown ) {
		$this->set_prop( 'deal_enable_countdown', $deal_enable_countdown );
	}

	/**
	 * Set the deal countdown field
	 *
	 * @param array $deal_countdown The field value.
	 *
	 * @return void
	 * @since 4.0.0
	 */
	public function set_deal_countdown( $deal_countdown ) {
		$this->set_prop( 'deal_countdown', $deal_countdown );
	}

	/** ========================================================== */

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

	/**
	 * Get the deal conditions field
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return array|string
	 * @since 4.0.0
	 */
	public function get_deal_conditions( $context = 'view' ) {
		return $this->get_prop( 'deal_conditions', $context );
	}

	/**
	 * Get the deal product field
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return array
	 * @since 4.0.0
	 */
	public function get_deal_product_selection( $context = 'view' ) {
		return $this->get_prop( 'deal_product_selection', $context );
	}

	/**
	 * Get if limit the selection or not
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 * @since 4.0.0
	 */
	public function get_deal_set_limits( $context = 'view' ) {
		return $this->get_prop( 'deal_set_limits', $context );
	}

	/**
	 * Get how many different product can add as last deal
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return int
	 * @since 4.0.0
	 */
	public function get_deal_total_product_to_add( $context = 'view' ) {
		$value      = $this->get_prop( 'deal_total_product_to_add', $context );
		$products   = $this->get_deal_product_selection();
		$deal_total = $value['deal_total_product_to_add'] ?? 1;

		return count( $products ) > 1 ? $deal_total : 1;
	}

	/**
	 * Get how units of same product can add.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return int
	 * @since 4.0.0
	 */
	public function get_deal_max_unit_product_to_add( $context = 'view' ) {
		$value = $this->get_prop( 'deal_max_unit_product_to_add', $context );

		return $value['deal_max_unit_product_to_add'] ?? 1;
	}


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

	/**
	 * Get the deal discount field
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return array
	 * @since 4.0.0
	 */
	public function get_deal_discount( $context = 'view' ) {
		return $this->get_prop( 'deal_discount', $context );
	}

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

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

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

	/**
	 * Get the deal countdown field
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return array
	 * @since 4.0.0
	 */
	public function get_deal_countdown( $context = 'view' ) {
		return $this->get_prop( 'deal_countdown', $context );
	}

	/**
	 * Check if is possible show the countdown or not
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return bool
	 * @since 4.0.0
	 */
	public function can_show_countdown( $context = 'view' ) {
		return yith_plugin_fw_is_true( $this->get_deal_enable_countdown( $context ) );
	}

	/**
	 * Check if is possible apply the discount
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return bool
	 * @since 4.0.0
	 */
	public function can_add_discount( $context = 'view' ) {
		return yith_plugin_fw_is_true( $this->get_apply_deal_discount( $context ) );
	}

	/**
	 * Check if this rule is Valid.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return bool
	 * @since 4.0.0
	 */
	public function is_valid( $context = 'view' ) {
		$is_valid = false;
		if ( $this->is_enabled( $context ) && $this->is_scheduled( $context ) ) {
			$when_show_deal = $this->get_show_deal( $context );
			if ( 'always' === $when_show_deal ) {
				$is_valid = true;
			} else {
				$conditions = $this->get_deal_conditions( $context );
				foreach ( $conditions as $condition ) {

					$function_to_call = 'is_valid_' . $this->get_condition_type( $condition ) . '_condition';
					if ( is_callable( array( $this, $function_to_call ) ) ) {

						$is_valid = $this->$function_to_call( $condition );
					}

					if ( ! $is_valid ) {
						break;
					}
				}
			}
		}

		return $is_valid;
	}

	/**
	 * Check if the rule is valid for apply discount.
	 *
	 * @param WC_Product $product The product object.
	 * @param bool $check_also_variation Check also the variation or not.
	 *
	 * @return bool
	 * @since 4.0.0
	 */
	public function is_valid_to_apply( $product, $check_also_variation = false ) {
		return true;
	}

	/**
	 * Get if apply discount to a different products.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return boolean
	 * @since 4.0.0
	 */
	public function get_active_apply_discount_to( $context = 'view' ) {
		return true;
	}

	/**
	 * Check if the rule is valid for adjustment discount.
	 *
	 * @param WC_Product $product The product object.
	 * @param bool $check_also_variation Check also the variation or not.
	 *
	 * @return bool
	 * @since 4.0.0
	 */
	public function is_valid_to_adjust( $product, $check_also_variation = false ) {
		$product_to_offer = $this->get_deal_product_selection();

		return $this->is_product_in_list( $product, $product_to_offer, 'product', true );

	}

	/**
	 * Check if the rule has limit or no
	 *
	 * @return bool
	 * @since 4.0.0
	 */
	public function has_limits() {
		return yith_plugin_fw_is_true( $this->get_deal_set_limits() );
	}


	/**
	 * Get the deals conditions
	 *
	 * @return array
	 * @since 4.0.0
	 */
	public function get_conditions() {
		return maybe_unserialize( $this->get_deal_conditions() );
	}


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

		if ( $this->can_add_discount() ) {
			$discount_conf    = $this->get_deal_discount();
			$discounted_price = floatval( $price_to_discount );
			switch ( $discount_conf['discount_type'] ) {

				case 'percentage':
					$percent          = $discount_conf['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_conf['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_conf['discount_amount'] );
					break;
			}

			return $discounted_price;
		}

		return $price_to_discount;
	}

	/**
	 * 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 4.0.0
	 */
	public function apply_rule_in_cart( $cart_item_key_to_apply, $cart_item_key_to_adjust ) {
		$cart_item = WC()->cart->get_cart_item( $cart_item_key_to_adjust );
		$result    = false;

		if ( isset( $cart_item['ywdpd_is_deals_product'] ) ) {
			$rule_id = $cart_item['ywdpd_rule_id'];
			if ( $this->get_id() == $rule_id && ! isset( $cart_item['has_last_deals_applied'] ) ) {

				$price_to_discount = $this->get_price_to_discount( $cart_item, $cart_item_key_to_adjust );
				$discounted_price  = $this->get_discounted_price( $price_to_discount, $cart_item['quantity'], $cart_item['data'] );
				$result            = $this->save_discount_in_cart( $cart_item_key_to_adjust, $price_to_discount, $discounted_price );
				if ( $result ) {
					WC()->cart->cart_contents[ $cart_item_key_to_adjust ]['has_last_deals_applied'] = true;
				}
			}
		}

		return $result;
	}

	/**
	 * Check if this rule is disabled if there are coupons in cart.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return bool
	 * @since 3.0.0
	 */
	public function is_disabled_with_other_coupon( $context = 'view' ) {
		return false;
	}

	/**
	 * Check after this rule is applied, if is possible apply other rules with lower priority.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return bool
	 * @since 3.0.0
	 */
	public function is_possible_apply_other_rules( $context = 'view' ) {
		return true;
	}
	/**
	 * Check if this rule is disabled for on sale product.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return bool
	 * @since 3.0.0
	 */
	public function is_disabled_on_sale( $context = 'view' ) {
		return false;
	}

	/**
	 * 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 ( $this->can_add_discount() ) {
			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;
	}


	/**
	 * Check if the product is in a exclusion condition
	 *
	 * @param array $cart_item The cart item.
	 *
	 * @return bool
	 * @since  4.0.0
	 * @author YITH <plugins@yithemes.com>
	 */
	public function is_product_excluded_from_conditions( $cart_item ) {
		return isset( $cart_item['ywdpd_is_deals_product'] ) || $this->trait_product_excluded( $cart_item );
	}

	public function has_last_deals_in_cart() {
		$result = false;
		foreach ( WC()->cart->cart_contents as $cart_key => $cart_item ) {
			if ( isset( $cart_item['has_last_deals_applied'] ) ) {
				$result = true;
				break;
			}
		}

		return $result;
	}
}