<?php
/**
 * The Cart rule class
 *
 * @package YITH\DynamicPricingAndDiscounts\Classes
 */

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

/**
 * The cart rule class
 */
class YWDPD_Cart_Rule extends YWDPD_Rule {
	use YWDPD_Advanced_Conditions_Trait;

	/**
	 * 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(
		'apply_discount'      => 'no',
		'discount_combined'   => 'no',
		'allow_free_shipping' => 'no',
		'cart_discount_rules' => array(),
		'discount_rule'       => array(),
		'show_notice_cart'    => 'no',
		'cart_notice'         => '',
		'cart_notice_bg'      => '',
		'apply_discount_mode' => 'always',
	);

	/**
	 * Get the rule if the ID is passed, otherwise the rule is new and empty.
	 *
	 * @param int|YWDPD_Cart_Rule|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();
		$this->set_internal_meta();
	}

	/**
	 * Set special information about cart rule,useful to show the notices
	 *
	 * @author YITH <plugins@yithemes.com>
	 * @since  3.0.0
	 */
	protected function set_internal_meta() {
		if ( 'conditions' === $this->get_apply_discount_mode() ) {
			$conditions                                     = $this->get_conditions();
			$this->meta_data['has_subtotal_condition']      = false;
			$this->meta_data['invalid_subtotal_conditions'] = array();
			foreach ( $conditions as $condition ) {

				$type = $this->get_condition_type( $condition );
				if ( 'cart_subtotal' === $type ) {
					if ( ! $this->meta_data['has_subtotal_condition'] ) {
						$this->meta_data['has_subtotal_condition'] = true;
					}
					if ( ! $this->is_valid_cart_subtotal_condition( $condition ) ) {
						$this->meta_data['invalid_subtotal_conditions'][] = $condition;
					}
				}
			}
		}
	}

	/**
	 * Set if this rule can be used with other  coupons
	 *
	 * @param string $discount_combined The value ( yes or not ).
	 *
	 * @since  3.0.0
	 */
	public function set_discount_combined( $discount_combined ) {
		$this->set_prop( 'discount_combined', $discount_combined );
	}

	/**
	 * Set if this rule can be allow free shipping
	 *
	 * @param string $allow_free_shipping The value ( yes or not ).
	 *
	 * @since  3.0.0
	 */
	public function set_allow_free_shipping( $allow_free_shipping ) {
		$this->set_prop( 'allow_free_shipping', $allow_free_shipping );
	}

	/**
	 * Set  the discount conditions on this rule
	 *
	 * @param string $cart_discount_rules The conditions.
	 *
	 * @since  3.0.0
	 */
	public function set_cart_discount_rules( $cart_discount_rules ) {
		$this->set_prop( 'cart_discount_rules', $cart_discount_rules );
	}

	/**
	 * Set  the discount mode on this rule
	 *
	 * @param array $discount_rule The discount mode.
	 *
	 * @since  3.0.0
	 */
	public function set_discount_rule( $discount_rule ) {
		$this->set_prop( 'discount_rule', $discount_rule );
	}

	/**
	 * Set if show or not the notice in the cart
	 *
	 * @param string $show_notice Yes or no.
	 *
	 * @since  3.0.0
	 */
	public function set_show_notice_cart( $show_notice ) {
		$this->set_prop( 'show_notice_cart', $show_notice );
	}

	/**
	 * Set the notice to show
	 *
	 * @param string $cart_notice The notice.
	 *
	 * @since  3.0.0
	 */
	public function set_cart_notice( $cart_notice ) {
		$this->set_prop( 'cart_notice', $cart_notice );
	}

	/**
	 * Set the background color for the notice.
	 *
	 * @param string $bg_color An exadecimal color.
	 *
	 * @since  3.0.0
	 */
	public function set_cart_notice_bg( $bg_color ) {
		$this->set_prop( 'cart_notice_bg', $bg_color );
	}

	/**
	 * Set how apply the cart discount
	 *
	 * @param string $discount_mode The discount mode ( always or under conditions ).
	 *
	 * @return void
	 * @since  4.0.0
	 */
	public function set_apply_discount_mode( $discount_mode ) {
		$this->set_prop( 'apply_discount_mode', $discount_mode );
	}

	/**
	 * Set if is possible add a coupon in the cart
	 *
	 * @param string $apply_discount The option value.
	 *
	 * @return void
	 * @since 4.0.0
	 */
	public function set_apply_discount( $apply_discount ) {
		$this->set_prop( 'apply_discount', $apply_discount );
	}

	/**
	 * Get if this rule can be used with other  coupons
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 *
	 * @since  3.0.0
	 */
	public function get_discount_combined( $context = 'view' ) {
		return $this->get_prop( 'discount_combined', $context );
	}

	/**
	 * Get if this rule can be allow free shipping
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 *
	 * @since  3.0.0
	 */
	public function get_allow_free_shipping( $context = 'view' ) {
		return $this->get_prop( 'allow_free_shipping', $context );
	}

	/**
	 * Get the discount conditions on this rule
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 * @since  3.0.0
	 */
	public function get_cart_discount_rules( $context = 'view' ) {
		return $this->get_prop( 'cart_discount_rules', $context );
	}

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

	/**
	 * Get if show or not the notice in the cart
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 * @since  3.0.0
	 */
	public function get_show_notice_cart( $context ) {
		return $this->get_prop( 'show_notice_cart', $context );
	}

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

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

	/**
	 * Check if the rule can be used also with other coupons
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return bool
	 * @since  3.0.0
	 */
	public function can_be_used_with_other_coupons( $context = 'view' ) {
		$disable = $this->get_discount_combined( $context );

		return ! yith_plugin_fw_is_true( $disable );
	}

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

	/**
	 * Get if is possible add a coupon in the cart
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 * @since 4.0.0
	 */
	public function get_apply_discount( $context = 'view' ) {
		return $this->get_prop( 'apply_discount', $context );
	}

	/**
	 * Check if the rule allow the free shipping or not.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return bool
	 * @since  3.0.0
	 */
	public function allow_free_shipping( $context = 'view' ) {

		$allow = $this->get_allow_free_shipping( $context );

		return yith_plugin_fw_is_true( $allow );
	}

	/**
	 * Get all rule conditions
	 *
	 * @return array
	 * @since  3.0.0
	 */
	public function get_conditions() {
		$discount_rules = $this->get_cart_discount_rules();

		return maybe_unserialize( $discount_rules );
	}

	/**
	 * Get the discount type of this rule
	 *
	 * @return string
	 * @since  3.0.0
	 */
	public function get_discount_type() {
		$discounts = $this->get_discount_rule();
		$type      = isset( $discounts['discount_type'] ) ? $discounts['discount_type'] : 'percentage';

		return $type;
	}

	/**
	 * Get the discount amount of this rule
	 *
	 * @return float
	 * @since  3.0.0
	 */
	public function get_discount_amount() {
		$discounts = $this->get_discount_rule();
		$amount    = isset( $discounts['discount_amount'] ) ? wc_format_decimal( $discounts['discount_amount'], wc_get_price_decimals() ) : 10;

		return $amount;
	}

	/**
	 * Can apply a coupon
	 *
	 * @return bool
	 * @since 4.0.0
	 */
	public function can_add_coupon() {
		return yith_plugin_fw_is_true( $this->get_apply_discount() );
	}

	/**
	 * Return the rule type ( Price or Cart )
	 *
	 * @return string
	 * @since 3.0.0
	 */
	public function get_type() {
		return 'cart';
	}

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

		if ( $this->is_enabled( $context ) && $this->is_scheduled( $context ) ) {

			if ( 'always' === $this->get_apply_discount_mode( $context ) ) {
				return true;
			}
			$conditions = $this->get_conditions();

			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 notice are enable or not
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return bool
	 * @since  3.0.0
	 */
	public function is_notice_enabled( $context = 'view' ) {

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

		return $value && $this->get_meta( 'has_subtotal_condition' );
	}

	/**
	 * Get all invalid subtotal conditions
	 *
	 * @return array
	 * @since  3.0.0
	 */
	public function get_invalid_subtotal_conditions() {
		return $this->get_meta( 'invalid_subtotal_conditions' );
	}

	/**
	 * Return the discount mode
	 *
	 * @return string
	 */
	public function get_discount_mode() {
		return 'cart';
	}

	/**
	 * Check if is possible show the cart notice in the cart page
	 *
	 * @return bool
	 */
	public function can_show_notice() {
		if ( $this->is_enabled() && $this->is_scheduled() ) {
			if ( 'always' === $this->get_apply_discount_mode() ) {
				$can_show = true;
			} else {
				$conditions        = $this->get_conditions();
				$can_show = true;

				foreach ( $conditions as $condition ){
					$type = $this->get_condition_type( $condition );
					if( 'customers' === $type || 'product' === $type){
						$function_to_call = 'is_valid_'.$type. '_condition';
						$can_show = $this->$function_to_call($condition);
						if( !$can_show ){
							break;
						}
					}
				}
		}
			return $can_show;
		}
		return false;
	}

	/**
	 * Return an array of formatted notices
	 *
	 * @return array
	 */
	public function get_cart_notices() {
		$notices                 = array();
		$invalid_conditions      = $this->get_invalid_subtotal_conditions();
		$allow_with_other_coupon = ! ( ywdpd_dynamic_pricing_discounts()->get_cart_rules_manager()->cart_has_coupons() && ! $this->can_be_used_with_other_coupons() );
		$notice                  = ywdpd_get_note( $this->get_cart_notice() );

		/**
		 * APPLY_FILTERS: ywdpd_is_cart_notice_enabled
		 *
		 * Show the cart notice in the cart.
		 *
		 * @param bool $show Show or not.
		 * @param YWDPD_Cart_Rule $cart_rule The cart rule.
		 *
		 * @return bool
		 */
		$cart_notice_enabled = apply_filters( 'ywdpd_is_cart_notice_enabled', $allow_with_other_coupon && $this->is_notice_enabled() && count( $invalid_conditions ) > 0, $this );
		if ( $cart_notice_enabled ) {

			$cart_subtotal = $this->get_cart_subtotal();

			foreach ( $invalid_conditions as $invalid_condition ) {
				$min_sub       = $this->get_condition_minimum_subtotal( $invalid_condition );
				$remain_amount = $min_sub - $cart_subtotal;

				if ( $remain_amount > 0 ) {
					$remain_amount = wc_price( $remain_amount );
					if ( 'percentage' === $this->get_discount_type() ) {
						$discount_amount = ( $this->get_discount_amount() ) . '%';
						/**
						 * APPLY_FILTERS: ywdpd_discount_amount_percentage_cart_notice
						 *
						 * Filter the discount amount percentage string.
						 *
						 * @param float $discount_amount the discount amount.
						 *
						 * @return float
						 */
						$discount_amount = apply_filters( 'ywdpd_discount_amount_percentage_cart_notice', $discount_amount );
					} else {
						/**
						 * 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 = wc_price( apply_filters( 'ywdpd_maybe_should_be_converted', $this->get_discount_amount() ) );
					}

					$notice    = str_replace( '%remaining_amount%', $remain_amount, $notice );
					$notice    = str_replace( '%amount_discount%', $discount_amount, $notice );
					$notices[] = array( 'notice' => $notice, 'background' => $this->get_cart_notice_bg() );
				}
			}
		}

		return $notices;
	}

}
