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

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

/**
 * The Gift Products Class.
 */
class YWDPD_Gift_Products 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(
		'gift_product_selection'        => array(),
		'amount_gift_product_allowed'   => 1,
		'add_gift_automatically'        => 'no',
		'gift_mode'                     => 'cart_item',
		'n_items_in_cart'               => array(
			'condition' => '>',
			'n_items'   => 1,
		),
		'gift_subtotal'                 => 100,
		'text_in_modal_gift'            => '',
		'show_gift_adjustment_note' => 'no',
		'table_note_gift_adjustment_to' => '',
	);


	/**
	 * Get the rule if the ID is passed, otherwise the rule is new and empty.
	 *
	 * @param int|YWDPD_Gift_Products|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 product to offer as gift
	 *
	 * @param array $product_selection the products.
	 *
	 * @since 3.0.0
	 */
	public function set_gift_product_selection( $product_selection ) {
		$this->set_prop( 'gift_product_selection', $product_selection );
	}

	/**
	 * Set the amount of product offer as gift
	 *
	 * @param array $total_gift_allowed Amount of products.
	 *
	 * @since 3.0.0
	 */
	public function set_amount_gift_product_allowed( $total_gift_allowed ) {
		$this->set_prop( 'amount_gift_product_allowed', $total_gift_allowed );
	}

	/**
	 * Set amount of item need to show the popup
	 *
	 * @param array $n_items_in_cart The amount of product in cart.
	 *
	 * @since
	 * @author YITH <plugins@yithemes.com>
	 */
	public function set_n_items_in_cart( $n_items_in_cart ) {
		$this->set_prop( 'n_items_in_cart', $n_items_in_cart );
	}

	/**
	 * Set the gift mode
	 *
	 * @param string $gift_mode The gift offer mode.
	 *
	 * @since  3.0.0
	 */
	public function set_gift_mode( $gift_mode ) {
		$this->set_prop( 'gift_mode', $gift_mode );
	}

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

	/**
	 * Set the text that will be added in the product page
	 *
	 * @param string $table_note_gift_adjustment_to The notice.
	 *
	 * @since  3.0.0
	 */
	public function set_table_note_gift_adjustment_to( $table_note_gift_adjustment_to ) {
		$this->set_prop( 'table_note_gift_adjustment_to', $table_note_gift_adjustment_to );
	}

	/**
	 * Set the minimum subtotal to show the popup
	 *
	 * @param float $gift_subtotal The cart subtotal.
	 *
	 * @since  3.0.0
	 */
	public function set_gift_subtotal( $gift_subtotal ) {
		$this->set_prop( 'gift_subtotal', $gift_subtotal );
	}

	/**
	 * Set the add the gift product in cart automatically
	 *
	 * @param string $add_gift_automatically The opiton.
	 *
	 * @since  4.0.0
	 */
	public function set_add_gift_automatically( $add_gift_automatically ) {
		$this->set_prop( 'add_gift_automatically', $add_gift_automatically );
	}
	/**
	 * Set if show the notice in the product
	 *
	 * @param string $show_gift_adjustment_note The opiton.
	 *
	 * @since  4.0.0
	 */
	public function set_show_gift_adjustment_note( $show_gift_adjustment_note){
		$this->set_prop('show_gift_adjustment_note', $show_gift_adjustment_note );
	}
	/**
	 * Get the product to offer as gift
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return  array
	 *
	 * @since 3.0.0
	 */
	public function get_gift_product_selection( $context = 'view' ) {
		return $this->get_prop( 'gift_product_selection', $context );
	}

	/**
	 * Get the amount of product offer as gift
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return int
	 *
	 * @since 3.0.0
	 */
	public function get_amount_gift_product_allowed( $context = 'view' ) {

		return $this->get_prop( 'amount_gift_product_allowed', $context );
	}

	/**
	 * Get amount of item need to show the popup
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return array
	 *
	 * @since
	 */
	public function get_n_items_in_cart( $context = 'view' ) {
		return $this->get_prop( 'n_items_in_cart', $context );
	}

	/**
	 * Get the gift mode
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 *
	 * @since  3.0.0
	 */
	public function get_gift_mode( $context = 'view' ) {
		return $this->get_prop( 'gift_mode', $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_gift( $context = 'view' ) {
		return $this->get_prop( 'text_in_modal_gift', $context );
	}

	/**
	 * Get if possible show the adjustment notice.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 * @since 4.0.0
	 */
	public function get_show_note_adjustment_to( $context = 'view' ) {
		return $this->get_prop( 'show_gift_adjustment_note', $context );
	}
	/**
	 * Get the text that will be added in the product page
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @since  3.0.0
	 */
	public function get_table_note_gift_adjustment_to( $context = 'view' ) {
		return $this->get_prop( 'table_note_gift_adjustment_to', $context );
	}

	/**
	 * Get the minimum subtotal to show the popup
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return float
	 *
	 * @since  3.0.0
	 */
	public function get_gift_subtotal( $context = 'view' ) {
		$min_subtotal = $this->get_prop( 'gift_subtotal', $context );

		if ( 'view' === $context ) {
			/**
			 * APPLY_FILTERS: ywdpd_gift_min_subtotal
			 *
			 * Set minimum subtotal.
			 *
			 * @param float               $min_subtotal The subtotal.
			 * @param YWDPD_Gift_Products $rule         The  rule.
			 *
			 * @return float
			 */
			$min_subtotal = apply_filters( 'ywdpd_gift_min_subtotal', $min_subtotal, $this );
		}

		return $min_subtotal;
	}

	/**
	 * The message for adjustment products
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 * @since 3.0.0
	 */
	public function get_table_note_adjustment_to( $context = 'view' ) {
		$note = $this->get_table_note_gift_adjustment_to( $context );

		if ( 'cart_subtotal' === $this->get_gift_mode() ) {

			$subtotal = $this->get_gift_subtotal( $context );

			$note = str_replace( '%subtotal%', wc_price( $subtotal ), $note );
		}

		return $note;
	}

	/**
	 * Get where apply the Rule.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 * @since 3.0.0
	 */
	public function get_rule_apply_adjustment_discount_for( $context = 'view' ) {
		return 'specific_products';
	}

	/**
	 * Get the product ids where apply the rule
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return array
	 * @since 3.0.0
	 */
	public function get_apply_adjustment_products_list( $context = 'view' ) {
		return $this->get_gift_product_selection( $context );
	}

	/**
	 * Get the add the gift product in cart automatically
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 *
	 * @since  4.0.0
	 */
	public function get_add_gift_automatically( $context = 'view' ) {
		return $this->get_prop( 'add_gift_automatically', $context );
	}

	/**
	 * 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 3.0.0
	 */
	public function is_valid_to_apply( $product, $check_also_variation = false ) {
		$type     = $this->get_rule_for();
		$is_valid = false;
		$list_ids = array();

		if ( ! $this->is_product_excluded_from_apply( $product, $check_also_variation ) ) {

			if ( 'all_products' === $type ) {
				$is_valid = true;
			} elseif ( 'specific_products' === $type ) {
				$list_ids = $this->get_rule_for_product_list();

				$is_valid = $this->is_product_in_list( $product, $list_ids, 'product', $check_also_variation );

			} elseif ( 'specific_categories' === $type ) {
				$list_ids = $this->get_rule_for_categories_list();
				$is_valid = $this->is_product_in_list( $product, $list_ids, 'product_cat' );
			} elseif ( 'specific_tag' === $type ) {
				$list_ids = $this->get_rule_for_tags_list();
				$is_valid = $this->is_product_in_list( $product, $list_ids, 'product_tag' );
			}
			/**
			 * APPLY_FILTERS: ywdpd_is_valid_to_apply
			 *
			 * Set if rule is valid.
			 *
			 * @param bool                $value   True or false.
			 * @param string              $type    The type.
			 * @param WC_Product          $product the product.
			 * @param YWDPD_Gift_Products $rule    The  rule.
			 *
			 * @return bool
			 */
			$is_valid = apply_filters( 'ywdpd_is_valid_to_apply', $is_valid, $type, $product, $this );
		}

		return $is_valid;
	}

	/**
	 * Check if the rule is valid for the cart
	 *
	 * @param WC_Product $product The product.
	 *
	 * @return bool
	 * @since  3.5.0
	 */
	public function is_valid_for_cart( $product ) {
		$is_valid = $this->is_valid_to_apply( $product );
		if ( $is_valid ) {
			if ( 'cart_item' === $this->get_gift_mode() ) {
				$need_items_in_cart = $this->get_n_items_in_cart();
				$need_items         = intval( $need_items_in_cart['n_items'] );
				$criteria           = $need_items_in_cart['condition'];
				$items_in_cart      = $this->get_comulative_quantity();

				switch ( $criteria ) {
					case '>':
						$is_valid = $items_in_cart > $need_items;
						break;
					case '<':
						$is_valid = $items_in_cart < $need_items;
						break;
					case '==':
						$is_valid = $items_in_cart === $need_items;
						break;
					default:
						$is_valid = $items_in_cart !== $need_items;
						break;

				}
			} else {
				$min_subtotal = floatval( $this->get_gift_subtotal() );


				$subtotal = ! is_null( WC()->cart ) ? WC()->cart->get_subtotal() + WC()->cart->get_subtotal_tax() : 0;


				/**
				 * APPLY_FILTERS: ywdpd_gift_subtotal
				 *
				 * Set cart subtotal.
				 *
				 * @param float               $subtotal The subtotal.
				 * @param YWDPD_Gift_Products $rule     The  rule.
				 *
				 * @return float
				 */
				$subtotal = apply_filters( 'ywdpd_gift_subtotal', floatval( $subtotal ), $this );

				if ( $min_subtotal > $subtotal ) {
					$is_valid = false;
				}
			}
		}

		/**
		 * APPLY_FILTERS: yith_gift_rule_is_valid_on_cart
		 *
		 * Rule is valid on cart.
		 *
		 * @param bool                $is_valid Is valid or not.
		 * @param YWDPD_Gift_Products $rule     The  rule.
		 *
		 * @return bool
		 */
		return apply_filters( 'yith_gift_rule_is_valid_on_cart', $is_valid, $this );
	}

	/**
	 * Return the amount of gift product in the cart
	 *
	 * @return int
	 * @since  3.0.0
	 */
	public function get_total_gift_product_in_cart() {
		$total = 0;

		if ( ! is_null( WC()->cart ) && ! WC()->cart->is_empty() ) {

			foreach ( WC()->cart->get_cart_contents() as $cart_item ) {
				if ( isset( $cart_item['ywdpd_is_gift_product'] ) ) {
					if ( isset( $cart_item['ywdpd_rule_id'] ) && $this->get_id() === intval( $cart_item['ywdpd_rule_id'] ) ) {

						$total += $cart_item['quantity'];
					}
				}
			}
		}

		return $total;
	}

	/**
	 * 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 3.0.0
	 */
	public function get_active_apply_discount_to( $context = 'view' ) {
		return false;
	}

	/**
	 * 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 3.0.0
	 */
	public function is_valid_to_adjust( $product, $check_also_variation = false ) {
		$product_selected = $this->get_gift_product_selection();

		$valid = is_array( $product_selected ) && in_array( $product->get_id(), $product_selected ); //phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
		if ( 'variation' === $product->get_type() && ! $valid ) {

			$valid = in_array( $product->get_parent_id(), $product_selected ); // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
		}

		return $valid;
	}

	/**
	 * 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;
	}

	/**
	 * Check if this rule is enabled the adjustment to mode
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return bool
	 * @since 3.0.0
	 */
	public function is_enabled_apply_adjustment_to( $context = 'view' ) {
		$value = true;
		if ( 'view' === $context ) {
			/**
			 * APPLY_FILTERS: ywdpd_is_enabled_apply_adjustment_to
			 *
			 * Rule is enabled for adjustment.
			 *
			 * @param bool                $value Is valid or not.
			 * @param YWDPD_Gift_Products $rule  The  rule.
			 *
			 * @return bool
			 */
			$value = apply_filters( 'ywdpd_is_enabled_apply_adjustment_to', $value, $this );
		}

		return $value;
	}


	/**
	 * Return the quantity to check
	 *
	 * @param array $cart_item The cart item.
	 *
	 * @return int;
	 * @since 3.0.0
	 */
	public function get_quantity( $cart_item ) {
		$q = intval( $cart_item['quantity'] );

		return $q;
	}

	/**
	 * 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 3.0.0
	 */
	public function get_discounted_price( $price_to_discount, $quantity, $product ) {
		return 0;
	}


	/**
	 * 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_adj = isset( WC()->cart->cart_contents[ $cart_item_key_to_adjust ] ) ? WC()->cart->cart_contents[ $cart_item_key_to_adjust ] : false;
		$result        = false;
		if ( isset( $cart_item_adj['ywdpd_is_gift_product'] ) ) {
			$rule_id = $cart_item_adj['ywdpd_rule_id'];
			if ( $this->get_id() == $rule_id && ! isset( $cart_item['has_gift_applied'] ) ) {
				$price_to_discount = $this->get_price_to_discount( $cart_item_adj, $cart_item_key_to_adjust );
				$discounted_price  = 0;
				$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_gift_applied'] = true;
				}
			}
		}

		return $result;
	}

	/**
	 * Check if is possible add the gift product automatically
	 *
	 * @return bool
	 * @since 4.0
	 */
	public function can_add_to_cart_automatically() {
		$allow_add_to_cart = $this->get_add_gift_automatically();
		$can_add           = false;

		if ( yith_plugin_fw_is_true( $allow_add_to_cart ) ) {
			$product_selected = $this->get_gift_product_selection();
			$can_add          = count( $product_selected ) === 1;
			if ( $can_add ) {
				$product_id = current( $product_selected );
				$product    = wc_get_product( $product_id );
				$can_add    = $product instanceof WC_Product && 'simple' === $product->get_type();
				/**
				 * APPLY_FILTERS: ywdpd_gift_can_add_to_cart_automatically
				 * 
				 * can the product be added to cart automatically.
				 * 
				 * @param boolean    $can_add .
				 * @param string     $product_type type of product.
				 * @param WC_Product $_product wc product.
				 */
				$can_add    = apply_filters( 'ywdpd_gift_can_add_to_cart_automatically', $can_add, $product->get_type(), $product );
			}
		}

		return $can_add;
	}

	/**
	 * Add to cart the main product automatically
	 *
	 * @return void
	 * @throws Exception The exception.
	 */
	public function add_to_cart() {
		$product_config = $this->get_product_configuration_to_add_automatically();
		if ( ! empty( $product_config['product_id'] ) ) {

			$cart_item_data = array(
				'ywdpd_is_gift_product' => true,
				'ywdpd_rule_id'         => $this->get_id(),
				'ywdpd_time'            => time(),
			);
			$quantity       = $this->get_amount_gift_product_allowed();
			$cart_key       = WC()->cart->add_to_cart( $product_config['product_id'], $quantity, $product_config['variation_id'], $product_config['variation'], $cart_item_data );
			if ( $cart_key ) {
				wc_add_to_cart_message( array( $product_config['product_id'] => $quantity ), false );
			}
		}
	}

	/**
	 * Get the product configuration
	 *
	 * @return array
	 * @throws Exception The exception.
	 */
	protected function get_product_configuration_to_add_automatically() {
		$product          = array(
			'product_id'   => '',
			'variation_id' => 0,
			'variation'    => array(),
		);
		$product_selected = $this->get_gift_product_selection();
		$num_of_product   = count( $product_selected );

		if ( 1 === $num_of_product ) {
			$product_id  = current( $product_selected );
			$product_obj = wc_get_product( $product_id );
			if ( $product_obj->is_purchasable() && $product_obj->is_in_stock() ) {
				$product['product_id'] = $product_id;
			}
		}

		return $product;
	}


	/**
	 * Return the discounted price html
	 *
	 * @param WC_Product $product The product.
	 *
	 * @return string
	 * @since 4.0.0
	 */
	public function get_discounted_price_html( $product ) {
		if ( 'variable' === $product->get_type() ) {
			$old_price_html = $product->get_price_html();
		} else {
			$old_price      = $product->is_on_sale() ? $product->get_sale_price() : $product->get_regular_price();
			$old_price_html = wc_get_price_to_display( $product, array( 'price' => $old_price ) );

		}
		/**
		 * APPLY_FILTERS: ywdpd_gift_discounted_price_html
		 *
		 * Can change the formatted price 0$ to another string example Free!
		 *
		 * @param string              $gift_price_html The price.
		 * @param WC_Product          $product         The product.
		 * @param YWDPD_Gift_Products $rule            The  rule.
		 *
		 * @return bool
		 */
		$new_price_html = apply_filters( 'ywdpd_gift_discounted_price_html', wc_price( 0 ), $product, $this );

		return wc_format_sale_price( $old_price_html, $new_price_html );
	}
}

