<?php
/**
 * The Quantity table class
 *
 * @package YITH WooCommerce Dynamic Pricing & Discounts\Classes\PriceRules
 */

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

/**
 * The Quantity Table Class.
 */
class YWDPD_Quantity_Table 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(
		'qty_type'         => 'range',
		'fixed_rules'      => array(),
		'rules'            => array(),
		'show_table_price' => 'no',
		'table_style'      => 'classic',
		'show_in_loop'     => 'yes',
		'show_extra_note'  => 'no',
		'table_note'       => '',
	);

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

	/**
	 * Get the rule if the ID is passed, otherwise the rule is new and empty.
	 *
	 * @param int|YWDPD_Quantity_Table|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 quantity rule field.
	 *
	 * @param array $rules the rules.
	 *
	 * @since 3.0.0
	 */
	public function set_rules( $rules ) {
		$this->set_prop( 'rules', $rules );
	}

	/**
	 * Set if show or not the quantity table.
	 *
	 * @param string $show_table Yes or no.
	 *
	 * @since 3.0.0
	 */
	public function set_show_table_price( $show_table ) {
		$this->set_prop( 'show_table_price', $show_table );
	}

	/**
	 * Set the style of table
	 *
	 * @param string $table_style The style.
	 *
	 * @return void
	 * @since 5.0.0
	 */
	public function set_table_style( $table_style ) {
		$this->set_prop( 'table_style', $table_style );
	}

	/**
	 * Set if show the discount on loop.
	 *
	 * @param string $show_in_loop Yes or not.
	 *
	 * @since 3.0.0
	 */
	public function set_show_in_loop( $show_in_loop ) {
		$this->set_prop( 'show_in_loop', $show_in_loop );
	}

	/**
	 * Set if show the extra table notice.
	 *
	 * @param string $show_extra_note Yes or not.
	 *
	 * @since 4.0.0
	 */
	public function set_show_extra_note( $show_extra_note ) {
		$this->set_prop( 'show_extra_note', $show_extra_note );
	}

	/**
	 * Set the table notes
	 *
	 * @param string $note The table notes.
	 *
	 * @since  3.0.0
	 * @author YITH <plugins@yithemes.com>
	 */
	public function set_table_note( $note ) {
		$this->set_prop( 'table_note', $note );
	}

	/**
	 * Set how apply the quantity rule
	 *
	 * @param string $qty_type The type.
	 *
	 * @return void
	 * @since 4.0.0
	 */
	public function set_qty_type( $qty_type ) {
		$this->set_prop( 'qty_type', $qty_type );
	}

	/**
	 * Set the fixed rules.
	 *
	 * @param array $fixed_rules The rules.
	 *
	 * @return void
	 * @since 4.0.0
	 */
	public function set_fixed_rules( $fixed_rules ) {
		$this->set_prop( 'fixed_rules', $fixed_rules );
	}

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

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

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

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


	/**
	 * Get the style of table
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 * @since 5.0.0
	 */
	public function get_table_style( $context = 'view' ) {
		return $this->get_prop( 'table_style', $context );
	}

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

	/**
	 * Get if show the extra table 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_extra_note( $context = 'view' ) {
		return $this->get_prop( 'show_extra_note', $context );
	}

	/**
	 * Get the table notes
	 *
	 * @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( $context = 'view' ) {
		return $this->get_prop( 'table_note', $context );
	}

	/**
	 * Check if the table can be show on product.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return bool
	 * @since 3.0.0
	 */
	public function can_show_table_price( $context = 'view' ) {

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

		if ( 'view' === $context ) {
			/**
			 * APPLY_FILTERS: ywdpd_can_show_table_price
			 *
			 * Set if possible show the quantity table.
			 *
			 * @param bool                 $value Is valid or not.
			 * @param YWDPD_Quantity_Table $rule The  rule.
			 *
			 * @return bool
			 */
			$value = apply_filters( 'ywdpd_can_show_table_price', $value, $this );
		}

		return $value;
	}

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

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

		if ( 'view' === $context ) {
			/**
			 * APPLY_FILTERS: ywdpd_can_show_discount_in_loop
			 *
			 * Set if possible show the discount in loop.
			 *
			 * @param bool                 $value Is valid or not.
			 * @param YWDPD_Quantity_Table $rule The  rule.
			 *
			 * @return bool
			 */
			$value = apply_filters( 'ywdpd_can_show_discount_in_loop', $value, $this );
		}

		return $value;
	}

	/**
	 * CHeck if is possible show the extra note
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return bool
	 * @since 4.0.0
	 */
	public function can_show_extra_notice( $context = 'view' ) {
		return yith_plugin_fw_is_true( $this->get_show_extra_note( $context ) );
	}

	/**
	 * Return the discount amount
	 *
	 * @param float $price_to_discount The original price.
	 * @param array $discount_type_rule The discount info.
	 *
	 * @return float
	 * @since  3.0.0
	 */
	public function get_discount_amount( $price_to_discount, $discount_type_rule ) {

		/**
		 * If catalog is enabled it's possible that the value is empty so it's necessary to convert the value to a valid number
		 */
		if ( empty( $price_to_discount ) ) {
			$price_to_discount = 0;
		}

		$discount_type   = $discount_type_rule['type_discount'];
		$discount_amount = floatval( str_replace( ',', '.', $discount_type_rule['discount_amount'] ) );

		switch ( $discount_type ) {
			case 'percentage':
				/**
				 * APPLY_FILTERS: ywdpd_percent_discount_amount
				 *
				 * Manage the discount amount.
				 *
				 * @param float $percent_discount_amount discount amount percentage.
				 * @param float $discount_amout discount amount
				 */
				$percent          = floatval( apply_filters( 'ywdpd_percent_discount_amount', round( $discount_amount / 100, 2 ), $discount_amount ) );
				$discounted_price = $price_to_discount - ( $price_to_discount * $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 = $price_to_discount - $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;
		}
		/**
		 * 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', true ) ) {
			$discounted_price = round( $discounted_price, wc_get_price_decimals() );
		}

		return $discounted_price;
	}

	/**
	 * 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 ) {

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

		$discounted_price = $price_to_discount;
		$qty_type         = $this->get_qty_type();

		$price_rules = 'range' === $qty_type ? $this->get_rules() : $this->get_fixed_rules();
		if ( $price_to_discount ) {

			foreach ( $price_rules as $price_rule ) {
				if ( 'range' === $qty_type ) {
					$min = ! empty( $price_rule['min_quantity'] ) ? $price_rule['min_quantity'] : 1;
					$max = ! empty( $price_rule['max_quantity'] ) ? $price_rule['max_quantity'] : '*';
					if ( ( $quantity >= $min && '*' === $max ) || ( $quantity <= $price_rule['max_quantity'] && $quantity >= $price_rule['min_quantity'] ) ) {

						$discounted_price = $this->get_discount_amount( $price_to_discount, $price_rule );
						break;
					}
				} else {
					$min = ! empty( $price_rule['units'] ) ? $price_rule['units'] : 1;
					if ( $quantity >= $min ) {
						$discounted_price = $this->get_discount_amount( $price_to_discount, $price_rule );
					}
				}
			}
		}

		return max( $discounted_price, 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;

		if ( $cart_item_adj && $this->can_apply_rule( $cart_item_adj ) ) {
			$has_bulk_rule = isset( WC()->cart->cart_contents[ $cart_item_key_to_adjust ]['has_bulk_applied'] );
			if ( ! $has_bulk_rule ) {
				$price_to_discount = $this->get_price_to_discount( $cart_item_adj, $cart_item_key_to_adjust );
				$cart_item_apply   = WC()->cart->cart_contents[ $cart_item_key_to_apply ];
				$quantity          = $this->get_quantity( $cart_item_apply );

				$discounted_price = $this->get_discounted_price( $price_to_discount, $quantity, $cart_item_apply['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_bulk_applied'] = true;
				}

				return $result;
			}
		}

		return false;
	}

}

