<?php
/**
 * This file belongs to the YIT Framework.
 *
 * This source file is subject to the GNU GENERAL PUBLIC LICENSE (GPL 3.0)
 * that is bundled with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.gnu.org/licenses/gpl-3.0.txt
 *
 * @package YITH\PreOrder\Includes
 * @author YITH <plugins@yithemes.com>
 */

if ( ! defined( 'YITH_WCPO_VERSION' ) ) {
	exit( 'Direct access forbidden.' );
}

if ( ! class_exists( 'YITH_Pre_Order_Cart_Messages' ) ) {
	/**
	 * Class YITH_Pre_Order_Cart_Messages
	 */
	class YITH_Pre_Order_Cart_Messages {

		/**
		 * Main Instance
		 *
		 * @var YITH_Pre_Order_Cart_Messages
		 * @access public
		 */
		protected static $instance;

		/**
		 * Returns single instance of the class
		 *
		 * @return YITH_Pre_Order_Cart_Messages
		 */
		public static function get_instance() {
			if ( is_null( self::$instance ) ) {
				self::$instance = new self();
			}

			return self::$instance;
		}

		/**
		 * Construct
		 *
		 */
		public function __construct() {
			if ( 'classic' === get_option( 'ywpo_cart_options_mode', 'new' ) ) {
				add_filter( 'woocommerce_add_to_cart_validation', array( $this, 'check_cart_errors_when_adding_to_cart' ), 10, 4 );
				add_action( 'woocommerce_cart_item_restored', array( $this, 'prevent_cart_mixing_on_restore_item' ), 10, 2 );
			} else {
				add_filter( 'woocommerce_add_to_cart_validation', array( $this, 'check_max_qty_when_adding_to_cart' ), 100, 4 );
				add_filter( 'woocommerce_update_cart_validation', array( $this, 'check_max_qty_when_updating_cart' ), 100, 4 );
				add_action( 'woocommerce_check_cart_items', array( $this, 'check_cart_errors_on_cart' ) );
				add_filter( 'woocommerce_notice_types', array( $this, 'add_notice_type' ) );
				add_filter( 'wc_get_template', array( $this, 'override_pre_order_error_notice_wc_template' ), 10, 2 );
			}
		}

		public function check_cart_errors_when_adding_to_cart( $validation, $product_id, $quantity, $variation = 0 ) {
			global $sitepress;

			if ( $variation ) {
				$id = $sitepress ? yit_wpml_object_id( $variation, 'product', true, $sitepress->get_default_language() ) : $variation;
			} else {
				$id = $sitepress ? yit_wpml_object_id( $product_id, 'product', true, $sitepress->get_default_language() ) : $product_id;
			}

			$cart_errors = $this->check_cart_errors( wc_get_product( $id ) );

			if ( ! empty( $cart_errors ) && is_array( $cart_errors ) ) {
				$this->print_errors( $cart_errors, 'no-cart' );
				return false;
			}

			return $validation;
		}

		/**
		 * Check the items in the cart for errors.
		 */
		public function check_cart_errors_on_cart() {
			$cart_errors = $this->check_cart_errors();
			if ( ! empty( $cart_errors ) && is_array( $cart_errors ) ) {
				$this->print_errors( $cart_errors, 'cart' );
			}
		}

		public function print_errors( $cart_errors, $context ) {
			$charge_type_mixed_added = false;
			foreach ( $cart_errors as $error ) {
				if ( 'charge_type_mixed' === $error ) {
					$charge_type_mixed_msg = __( 'It is not possible to buy pre-order products with different payment options (upfront payment/payment upon release) in the same order. Remove a pre-order product to proceed to checkout.', 'yith-pre-order-for-woocommerce' );
					$charge_type_mixed_msg = apply_filters( 'ywpo_cart_upfront_with_upon_release_mixing_message', $charge_type_mixed_msg );
					if ( 'no-cart' === $context ) {
						wc_add_notice( $charge_type_mixed_msg, 'error' );
					} else {
						wc_add_notice( $charge_type_mixed_msg, 'pre-order-error' );
					}
					$charge_type_mixed_added = true;
				}
				if ( 'multiple_upon_release' === $error ) {
					$multiple_upon_release_msg = __( 'It is not possible to buy more than one pre-order product to be charged upon release. Remove a pre-order product to proceed to checkout.', 'yith-pre-order-for-woocommerce' );
					$multiple_upon_release_msg = apply_filters( 'ywpo_cart_multiple_upon_release_message', $multiple_upon_release_msg );
					if ( 'no-cart' === $context ) {
						wc_add_notice( $multiple_upon_release_msg, 'error' );
					} else {
						wc_add_notice( $multiple_upon_release_msg, 'pre-order-error' );
					}
				}
				if ( 'pre_order_mixed' === $error && ! $charge_type_mixed_added ) {
					$pre_order_mixed_msg = esc_html__( 'It is not possible to buy pre-order products and standard products in the same order.', 'yith-pre-order-for-woocommerce' );
					$pre_order_mixed_msg = apply_filters( 'ywpo_cart_mixing_message', $pre_order_mixed_msg );
					if ( 'no-cart' === $context ) {
						wc_add_notice( $pre_order_mixed_msg, 'error' );
					} else {
						wc_add_notice( $pre_order_mixed_msg, 'pre-order-error' );

					}
				}
				if ( 'more_than_pre_order' === $error && ! $charge_type_mixed_added ) {
					$more_than_pre_order_msg = esc_html__( 'It is not possible to buy more than one pre-order product. Remove a pre-order product to proceed to checkout.', 'yith-pre-order-for-woocommerce' );
					$more_than_pre_order_msg = apply_filters( 'ywpo_more_than_pre_order_msg_message', $more_than_pre_order_msg );
					if ( 'no-cart' === $context ) {
						wc_add_notice( $more_than_pre_order_msg, 'error' );
					} else {
						wc_add_notice( $more_than_pre_order_msg, 'pre-order-error' );
					}
				}
			}
		}

		public function check_cart_errors( $product = false ) {
			$global_charge_type                            = get_option( 'ywpo_charge_type', 'upfront' );
			$prevent_mixing_regular_and_pre_order_products = 'yes' === get_option( 'yith_wcpo_mixing', 'no' );
			$one_pre_order_in_cart                         = 'yes' === get_option( 'yith_wcpo_one_pre_order_in_cart', 'no' );

			$errors = array();

			if ( ! WC()->cart ) {
				return false;
			}

			$type_to_check = $global_charge_type;

			$pre_order_count       = 0;
			$regular_product_count = 0;
			$upon_release_count    = 0;

			if ( $product instanceof WC_Product && apply_filters( 'ywpo_check_cart_errors_product_is_valid', true, $product ) ) {
				if ( YITH_Pre_Order_Utils::is_pre_order_active( $product ) ) {
					if (
						(
							'yes' === YITH_Pre_Order_Utils::get_override_charge_type( $product ) &&
							(
								'upon_release' === YITH_Pre_Order_Utils::get_charge_type( $product ) ||
								'pay_later' === YITH_Pre_Order_Utils::get_charge_type( $product )
							)
						) ||
						(
							'yes' !== YITH_Pre_Order_Utils::get_override_charge_type( $product ) &&
							(
								'upon_release' === $global_charge_type ||
								'pay_later' === $global_charge_type
							)
						)
					) {
						$upon_release_count++;
					}
					$pre_order_count++;
				} else {
					$regular_product_count++;
				}
			}

			$i = 0;

			foreach ( WC()->cart->get_cart() as $cart_item ) {
				$_product = isset( $cart_item['data'] ) && $cart_item['data'] instanceof WC_Product ? $cart_item['data'] : false;
				$type    = '';

				if ( ! apply_filters( 'ywpo_check_cart_errors_product_is_valid', true, $_product ) ) {
					continue;
				}
				if ( $_product && YITH_Pre_Order_Utils::is_pre_order_active( $_product ) ) {
					$pre_order_count++;
					if ( 'yes' === YITH_Pre_Order_Utils::get_override_charge_type( $_product ) ) {
						$override_charge_type = ! empty( YITH_Pre_Order_Utils::get_charge_type( $_product ) ) ? YITH_Pre_Order_Utils::get_charge_type( $_product ) : '';
						if ( 0 === $i ) {
							$type_to_check = $override_charge_type;
						}
						$type = $override_charge_type;
					} else {
						if ( 0 === $i ) {
							$type_to_check = $global_charge_type;
						}
						$type = $global_charge_type;
					}
					if ( 'upon_release' === $type || 'pay_later' === $type ) {
						$upon_release_count++;
					}
				} else {
					if ( 0 === $i ) {
						$type_to_check = 'upfront';
					}
					$type = 'upfront';
					$regular_product_count++;
				}

				if ( $product ) {
					$i++;
				}

				if ( $i > 0 ) {
					if ( $type && $type !== $type_to_check ) {
						$errors[] = 'charge_type_mixed';
					}
					if ( $upon_release_count > 1 || $upon_release_count > 0 && $pre_order_count > 1  ) {
						$errors[] = 'multiple_upon_release';
					}
					if ( $prevent_mixing_regular_and_pre_order_products && ! in_array( 'pre_order_mixed', $errors, true ) ) {
						if ( $pre_order_count > 0 && $regular_product_count > 0 ) {
							$errors[] = 'pre_order_mixed';
						}
					}
					if ( $one_pre_order_in_cart && ! in_array( 'more_than_pre_order', $errors, true ) ) {
						if ( $pre_order_count > 1 ) {
							$errors[] = 'more_than_pre_order';
						}
					}
				}
				$i++;
			}
			if ( ! empty( $errors ) ) {
				return $errors;
			}
			return false;
		}

		public function prevent_cart_mixing_on_restore_item( $cart_item_key, $cart ) {
			$cart_errors = $this->check_cart_errors( $cart->get_cart_item( $cart_item_key )['data'] );

			if ( ! empty( $cart_errors ) && is_array( $cart_errors ) ) {
				$this->print_errors( $cart_errors, 'no-cart' );
				$cart->remove_cart_item( $cart_item_key );
				return false;
			}
		}

		/**
		 * Check if the maximum quantity for the product is not exceeded when adding it to the cart.
		 *
		 * @param bool $bool         Boolean value for determine the add to cart validation.
		 * @param int  $product_id   The product ID.
		 * @param int  $quantity     The quantity added to the cart.
		 * @param int  $variation_id The variation ID if exists.
		 *
		 * @return bool
		 */
		public function check_max_qty_when_adding_to_cart( $bool, $product_id, $quantity, $variation_id = 0 ) {
			return $this->max_qty( $bool, $product_id, $quantity, $variation_id );
		}

		/**
		 * Check if the maximum quantity for the product is not exceeded when updating the cart.
		 *
		 * @param bool  $bool          Boolean value for determine the update cart validation.
		 * @param int   $cart_item_key The cart item key (not used).
		 * @param array $values        The cart item array.
		 * @param int   $quantity      The quantity to be updated.
		 *
		 * @return bool
		 */
		public function check_max_qty_when_updating_cart( $bool, $cart_item_key, $values, $quantity ) {
			return $this->max_qty( $bool, $values['data']->get_id(), $quantity, 0, 'updating' );
		}

		/**
		 * Check if the maximum quantity for the product is not exceeded.
		 *
		 * @param bool   $bool         Boolean value to determine if the validation is passed.
		 * @param int    $product_id   The product ID.
		 * @param int    $quantity     The quantity that is being validated.
		 * @param int    $variation_id The variation ID if any.
		 * @param string $mode         The validation mode. If 'updating', it's considered as 'update cart validation'.
		 *                             Otherwise, 'add to cart validation'.
		 *
		 * @return bool
		 */
		public function max_qty( $bool, $product_id, $quantity, $variation_id = 0, $mode = '' ) {
			$product_id = ! empty( $variation_id ) ? $variation_id : $product_id;
			$product = wc_get_product( $product_id );

			if ( 'yes' === YITH_Pre_Order_Utils::get_max_qty_enabled( $product ) ) {
				$max_qty = YITH_Pre_Order_Utils::get_max_qty( $product );
				if ( is_numeric( $max_qty ) ) {
					$customer_email  = wp_get_current_user()->user_email;
					$qty_purchased   = yith_ywpo_times_product_has_been_bought_by_customer( $customer_email, $product_id );
					$product_in_cart = WC()->cart->get_cart_item( WC()->cart->find_product_in_cart( WC()->cart->generate_cart_id( $product_id ) ) );
					$qty_in_cart     = is_array( $product_in_cart ) && isset( $product_in_cart['quantity'] ) ? $product_in_cart['quantity'] : 0;

					$total_qty = (int) $qty_purchased + (int) $quantity + (int) $qty_in_cart;
					if ( 'updating' === $mode ) {
						$total_qty = (int) $qty_purchased + (int) $quantity;
					}

					if ( $total_qty > (int) $max_qty ) {
						$message = sprintf(
						// translators: %d: maximum units that can be purchased.
							_n(
								"It's not possible to pre-order more than %d unit of this product.",
								"It's not possible to pre-order more than %d units of this product.",
								$max_qty,
								'yith-pre-order-for-woocommerce'
							),
							$max_qty
						);

						if ( $qty_purchased > 0 ) {
							$message .= '<br>' . sprintf(
								// translators: %d: units that have been purchased.
								_n(
									'You have already pre-ordered %d unit of this product in previous orders.',
									'You have already pre-ordered %d units of this product in previous orders.',
									$qty_purchased,
									'yith-pre-order-for-woocommerce'
								),
								$qty_purchased
							);
						}

						wc_add_notice(
							$message,
							'error',
							array(
								'product_id'    => $product_id,
								'quantity'      => $quantity,
								'qty_purchased' => $qty_purchased,
								'max_qty'       => $max_qty,
							)
						);
						return false;
					}
				}
			}
			return $bool;
		}

		/**
		 * Add the custom WooCommerce notice type for Pre-order errors.
		 *
		 * @param array $notice_types Array of WooCommerce notice types.
		 *
		 * @return array
		 */
		public function add_notice_type( $notice_types ) {
			$notice_types[] = 'pre-order-error';
			return $notice_types;
		}

		/**
		 * Find the template notices/pre-order-error.php in the correct place.
		 * Also replace the checkout/form-checkout.php template in case there are pre-order errors in order to cancel
		 * the Checkout page.
		 *
		 * @param string $template      The current template path.
		 * @param string $template_name The current template name.
		 *
		 * @return string
		 */
		public function override_pre_order_error_notice_wc_template( $template, $template_name ) {
			if ( ! function_exists( 'wc_notice_count' ) ) {
				return $template;
			}

			/*
			 * If $template_name is 'notices/pre-order-error.php', define $template as
			 * 'yith-woocommerce-pre-order-premium/templates/notices/pre-order-error.php'.
			 */
			if ( 'notices/pre-order-error.php' === $template_name ) {
				$template = YITH_WCPO_TEMPLATE_PATH . 'notices/pre-order-error.php';
			}

			/*
			 * If there are any 'pre-order-error' notice, replace the default 'checkout/form-checkout.php' template
			 * with the 'checkout/cart-errors.php' template to avoid placing orders.
			 */
			if ( 'checkout/form-checkout.php' === $template_name && isset( WC()->session ) && wc_notice_count( 'pre-order-error' ) > 0 ) {
				$template = wc_locate_template( 'checkout/cart-errors.php' );
				wc_clear_notices();
			}

			return $template;
		}
	}
}

/**
 * Unique access to instance of YITH_Pre_Order_Cart_Messages class
 *
 * @return YITH_Pre_Order_Cart_Messages
 */
function YITH_Pre_Order_Cart_Messages() { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid
	return YITH_Pre_Order_Cart_Messages::get_instance();
}
