<?php
/**
 * File: Ecre_Coupon_Tracking.php
 *
 * The Ecre_Coupon_Tracking class manages coupon tracking and validation.
 *
 * @package  ECRE\Frontend
 * @since 1.0.0
 */

namespace ECRE\Frontend;

if ( ! class_exists( 'Ecre_Coupon_Tracking' ) ) {
	/**
	 * The Ecre_Coupon_Tracking class manages coupon tracking and validation.
	 *
	 * @since 1.0.0
	 */
	class Ecre_Coupon_Tracking {
		/**
		 * Holds the settings configuration for the ECRE order tracking system.
		 *
		 * This property stores various settings retrieved from the database,
		 * such as email templates, discount configurations, and tracking options.
		 *
		 * @var array
		 */
		public $erce_settings = array();

		private $database;


		/**
		 * Initializes the Ecre Coupon Tracking class with the provided settings.
		 *
		 * This constructor sets up the coupon tracking functionality by assigning the configuration settings
		 * and registering the necessary WooCommerce filters and actions.
		 *
		 * @param array $settings The settings array for configuring Ecre Coupon Tracking.
		 *
		 * @since 1.0.0
		 */
		public function __construct( $settings ) {
			$this->erce_settings = $settings;

			if ( class_exists( '\ECRE\Ecre_Database' ) ) {
				$this->database = new \ECRE\Ecre_Database();
			}

			add_filter( 'woocommerce_cart_totals_coupon_html', array( $this, 'ecre_modify_percent_coupon_html_with_discount' ), 10, 3 );
			add_filter( 'woocommerce_coupon_is_valid', array( $this, 'ecre_prevent_coupon_reuse' ), 10, 2 );
			add_action( 'woocommerce_checkout_order_processed', array( $this, 'ecre_handle_checkout_order_processed' ), 10, 3 );
			add_action( 'woocommerce_store_api_checkout_order_processed', array( $this, 'ecre_handle_store_api_order_processed' ), 10, 1 );
		}

		/**
		 * Prevent coupon reuse and customize coupon validation messages.
		 *
		 * @param bool      $passed Whether the coupon validation has passed.
		 * @param WC_Coupon $coupon The coupon object.
		 *
		 * @return bool
		 *
		 * @since 1.0.0
		 */
		public function ecre_prevent_coupon_reuse( $passed, $coupon ) {
			if ( ! $passed ) {
				return $passed;
			}

			$coupon_id           = $coupon->get_id();
			$referral_user_id    = get_post_meta( $coupon->get_id(), 'referral_user_id', true );
			$reward_user_id      = get_post_meta( $coupon->get_id(), 'reward_user_id', true );
			$applied_coupon_code = get_post_meta( $coupon->get_id(), 'applied_coupon_code', true );
			$coupon_code         = $coupon->get_code();
			$user_id             = get_current_user_id();

			$user_id          = ! empty( $user_id ) ? (int) $user_id : $user_id;
			$referral_user_id = ! empty( $referral_user_id ) ? (int) $referral_user_id : $referral_user_id;
			$reward_user_id   = ! empty( $reward_user_id ) ? (int) $reward_user_id : $reward_user_id;

			$this->erce_settings = ecre_merge_with_custom_settings( $this->erce_settings, $referral_user_id );

			$referral_discount                = $this->erce_settings['referralDiscount'];
			$reward_discount                  = $this->erce_settings['rewardDiscount'];
			$referral_minimum_purchase_amount = $this->erce_settings['referralMinimumPurchaseAmount'];
			$reward_minimum_purchase_amount   = $this->erce_settings['rewardMinimumPurchaseAmount'];
			$referral_discount_type           = $this->erce_settings['referralCouponType']['value'];
			$reward_discount_type             = $this->erce_settings['rewardType']['value'];
			$referral_discount_capping        = $this->erce_settings['referralDiscountCapping'];
			$reward_discount_capping          = $this->erce_settings['rewardDiscountCapping'];
			$enable_monthly_limit             = $this->erce_settings['enableReferralLimit'];
			$monthly_referral_limit           = $this->erce_settings['referralMonthlyLimit'];
			$referral_usage_count             = get_post_meta( $coupon_id, 'referral_usage_count', true );
			$coupon_use_status                = $this->coupon_use_status( $user_id );
			$ecre_currency_sign               = ecre_custom_currency_symbol();

			// Get cart subtotal with tax for validation using our helper function.
			$cart_subtotal        = $this->get_cart_total_for_validation();
			$subtotal_without_tax = WC()->cart->get_subtotal();

			$cart_discount_type         = $coupon->get_discount_type();
			$calculated_discount_amount = '';

			if ( ! empty( $referral_user_id ) && $enable_monthly_limit ) {
				if ( ! empty( $monthly_referral_limit ) && 0 < $monthly_referral_limit ) {

					if ( (int) $referral_usage_count >= (int) $monthly_referral_limit ) {
						$reward_error_message = __( 'This coupon cannot be used at this moment!', 'echo-rewards' );

						add_filter(
							'woocommerce_coupon_error',
							function ( $error_message, $coupon ) use ( $reward_error_message ) {
								return $reward_error_message;
							},
							10,
							2
						);
						return false;
					}
				}
			}

			if ( 'percent' === $cart_discount_type ) {
				$calculated_discount_amount = ( $coupon->get_amount() / 100 ) * $subtotal_without_tax;
			} else {
				$calculated_discount_amount = $coupon->get_amount();
			}

			if ( ! empty( $reward_user_id ) && ! empty( $user_id ) ) {
				if ( $reward_user_id === $user_id ) {

					if ( ! empty( $reward_minimum_purchase_amount ) && 0 !== $reward_minimum_purchase_amount ) {
						if ( $cart_subtotal < $reward_minimum_purchase_amount ) {
							$reward_minimum_purchase_notice = sprintf(
								/* translators: %1$s: currency symbol, %2$s: minimum purchase amount */
								__( 'Total cart amount must be %1$s%2$s or more to apply the coupon code', 'echo-rewards' ),
								$ecre_currency_sign,
								$reward_minimum_purchase_amount
							);

							add_filter(
								'woocommerce_coupon_error',
								function ( $error_message, $coupon ) use ( $reward_minimum_purchase_notice ) {
									return $reward_minimum_purchase_notice;
								},
								10,
								2
							);
							return false;
						}
					}

					if ( ! empty( $reward_discount_capping ) && 0 !== $reward_discount_capping ) {
						if ( $calculated_discount_amount > $reward_discount_capping && 'percent' === $cart_discount_type ) {
							$percentage_discount = ( $reward_discount_capping / $subtotal_without_tax ) * 100;

							$coupon->set_amount( $percentage_discount );
							return $passed;
						}
					}

					return $passed;
				} else {
					$reward_error_message = __( 'This coupon is not valid for you.', 'echo-rewards' );
					add_filter(
						'woocommerce_coupon_error',
						function ( $error_message, $coupon ) use ( $reward_error_message ) {
							return $reward_error_message;
						},
						10,
						2
					);
					return false;
				}
			}

			// Prevent guest users from using reward coupons.
			if ( ! empty( $reward_user_id ) && empty( $user_id ) ) {
				$reward_guest_error_message = __( 'You are not eligible to apply this coupon. Please log in to use your reward coupon.', 'echo-rewards' );
				add_filter(
					'woocommerce_coupon_error',
					function ( $error_message, $coupon ) use ( $reward_guest_error_message ) {
						return $reward_guest_error_message;
					},
					10,
					2
				);
				return false;
			}

			if ( ! empty( $referral_user_id ) && ! empty( $user_id ) && $referral_user_id !== $user_id ) {

				if ( 'yes' === $coupon_use_status ) {

					if ( ! empty( $referral_minimum_purchase_amount ) && 0 !== $referral_minimum_purchase_amount ) {

						if ( $cart_subtotal < $referral_minimum_purchase_amount ) {
							$referral_minimum_purchase_notice = sprintf(
								/* translators: %1$s: currency symbol, %2$s: minimum purchase amount */
								__( 'Total cart amount must be %1$s%2$s or more to apply the coupon code', 'echo-rewards' ),
								$ecre_currency_sign,
								$referral_minimum_purchase_amount
							);

							add_filter(
								'woocommerce_coupon_error',
								function ( $error_message, $coupon ) use ( $referral_minimum_purchase_notice ) {
									return $referral_minimum_purchase_notice;
								},
								10,
								2
							);
							return false;
						}
					}

					if ( ! empty( $referral_discount_capping ) && 0 !== $referral_discount_capping ) {
						if ( $calculated_discount_amount > $referral_discount_capping && 'percent' === $cart_discount_type ) {

							$percentage_discount = ( $referral_discount_capping / $subtotal_without_tax ) * 100;

							$coupon->set_amount( $percentage_discount );
							return $passed;
						}
					}

					return $passed;
				} else {
					$custom_error_message = '';

					if ( 'no' === $coupon_use_status ) {
						$custom_error_message = __( 'This coupon is only for new customer first order.', 'echo-rewards' );
					} elseif ( 'matched' === $coupon_use_status ) {
						$custom_error_message = __( 'You already applied with this coupon.', 'echo-rewards' );
					}

					add_filter(
						'woocommerce_coupon_error',
						function ( $error_message, $coupon ) use ( $custom_error_message ) {
							return $custom_error_message;
						},
						10,
						2
					);

					return false;
				}
			} elseif ( ! empty( $referral_user_id ) && ! empty( $user_id ) && $referral_user_id === $user_id ) {
				$reffrer_error_message = __( 'You can not use your own referral coupon.', 'echo-rewards' );
				add_filter(
					'woocommerce_coupon_error',
					//phpcs:ignore
					function ( $error_message, $coupon ) use ( $reffrer_error_message ) {
						return $reffrer_error_message;
					},
					10,
					2
				);
				return false;
			}

			if ( ! is_user_logged_in() && ! empty( $referral_user_id ) ) {

				$billing_email = WC()->session->get( 'guest_billing_email' );

				if ( empty( $billing_email ) ) {
					$cart = WC()->cart->get_cart();
					foreach ( $cart as $cart_item ) {
						if ( isset( $cart_item['data'] ) && $cart_item['data'] instanceof WC_Product ) {
							$billing_email = WC()->checkout()->get_value( 'billing_email' );
							break;
						}
					}
				}

				if ( ! empty( $billing_email ) ) {
					$orders = wc_get_orders(
						array(
							'billing_email' => $billing_email,
							'limit'         => -1,
							'meta_key'      => '_referral_user_id',
							'meta_value'    => $referral_user_id,
						)
					);

					if ( ! empty( $orders ) ) {
						$already_used_message = __( 'A referral coupon has already been used with this email address.', 'echo-rewards' );
						add_filter(
							'woocommerce_coupon_error',
							function ( $error_message, $coupon ) use ( $already_used_message ) {
								return $already_used_message;
							},
							10,
							2
						);
						return false;
					}
				}

				if ( ! empty( $referral_minimum_purchase_amount ) && 0 !== $referral_minimum_purchase_amount ) {
					if ( $cart_subtotal < $referral_minimum_purchase_amount ) {

						$referral_minimum_purchase_notice = sprintf(
							/* translators: %1$s: currency symbol, %2$s: minimum purchase amount */
							__( 'Total cart amount must be %1$s%2$s or more to apply the coupon code', 'echo-rewards' ),
							$ecre_currency_sign,
							$referral_minimum_purchase_amount
						);

						add_filter(
							'woocommerce_coupon_error',
							function ( $error_message, $coupon ) use ( $referral_minimum_purchase_notice ) {
								return $referral_minimum_purchase_notice;
							},
							10,
							2
						);
						return false;
					}
				}

				if ( ! empty( $referral_discount_capping ) && 0 !== $referral_discount_capping ) {
					if ( $calculated_discount_amount > $referral_discount_capping && 'percent' === $cart_discount_type ) {

						$percentage_discount = ( $referral_discount_capping / $subtotal_without_tax ) * 100;

						$coupon->set_amount( $percentage_discount );
						return $passed;
					}

					return $passed;
				}
			}

			return $passed;
		}

		/**
		 * Determine the status of coupon usage based on orders and coupon code.
		 *
		 * This function checks the status of a coupon code, taking into account various conditions:
		 * - If the coupon is intended only for new customers.
		 * - Whether the coupon code matches any order.
		 * - How many times the coupon has been used in previous orders.
		 *
		 * @param int $user_id An array of WooCommerce orders.
		 *
		 * @return string The status of the coupon, which can be one of the following:
		 * - 'yes': The coupon is eligible for use.
		 * - 'no': The coupon is not eligible due to previous usage by referral users (only for new customers).
		 * - 'used': The coupon has been used.
		 * - 'matched': The coupon code has matched in at least one order.
		 */
		public function coupon_use_status( $user_id ) {
			// Initialize variables to track coupon usage and matching.
			$coupon_status = 'yes';

			$args = array(
				'customer' => $user_id,
				'status'   => array( 'wc-pending', 'wc-processing', 'wc-on-hold', 'wc-completed', 'wc-cancelled', 'wc-refunded', 'wc-failed', 'wc-draft' ),
			);

			$all_orders = wc_get_orders( $args );

			if ( ! empty( $all_orders ) ) {
				// Iterate through all orders to check coupon usage and matching.
				foreach ( $all_orders as $order ) {
					$order_id     = $order->get_id();
					$order_status = $order->get_status();

					// Get the referral_user_id from the order meta.
					$referral_user_id = get_post_meta( $order_id, '_referral_user_id', true );

					if ( $referral_user_id ) {
						$referral_user_id = (int) $referral_user_id;
					}

					// Check order status and referral_user_id.
					if ( in_array( $order_status, array( 'pending', 'processing', 'on-hold', 'completed' ), true ) ) {
						if ( ! is_null( $referral_user_id ) && ! empty( $referral_user_id ) && is_numeric( $referral_user_id ) ) {
							$coupon_status = 'no';
							break;
						}
					} elseif ( in_array( $order_status, array( 'cancelled', 'refunded', 'failed', 'draft' ), true ) ) {
						$coupon_status = 'yes';
					}
				}
			}

			return $coupon_status;
		}

		/**
		 * Wrapper function for woocommerce_checkout_order_processed hook.
		 *
		 * @param int   $order_id     The order ID.
		 * @param array $posted_data  Posted data.
		 * @param WC_Order $order     The order object.
		 */
		public function ecre_handle_checkout_order_processed( $order_id, $posted_data, $order ) {
			$this->ecre_add_referral_data_to_order( $order_id );
		}

		/**
		 * Wrapper function for woocommerce_store_api_checkout_order_processed hook.
		 *
		 * @param WC_Order $order The order object.
		 */
		public function ecre_handle_store_api_order_processed( $order ) {
			$this->ecre_add_referral_data_to_order( $order->get_id() );
		}

		/**
		 * Adds referral data to order when checkout is processed.
		 *
		 * @param int $order_id The order ID.
		 */
		public function ecre_add_referral_data_to_order( $order_id ) {
			$order = wc_get_order( $order_id );

			// Check if $order is a valid WC_Order object before proceeding.
			if ( ! $order || ! is_a( $order, 'WC_Order' ) ) {
				return; // Exit the function if $order is not valid.
			}

			$applied_coupons = $order->get_coupon_codes();

			foreach ( $applied_coupons as $coupon_code ) {
				// Get coupon ID.
				$coupon_id = wc_get_coupon_id_by_code( $coupon_code );

				// Check if the coupon has a referral_user_id.
				$referral_user_id = get_post_meta( $coupon_id, 'referral_user_id', true );
				$reward_user_id   = get_post_meta( $coupon_id, 'reward_user_id', true );

				if ( ! empty( $referral_user_id ) ) {
					// Update order meta for backward compatibility.
					update_post_meta( $order_id, '_referral_user_id', $referral_user_id );
					update_post_meta( $order_id, '_referral_coupon_code', $coupon_code );
					update_post_meta( $order_id, '_referral_coupon_id', $coupon_id );
					// Store referral data in ecre_referrals_orders table.
					$this->database->store_referral_order_data( $order, $coupon_code, $coupon_id, $referral_user_id );

					$this->database->update_referrer_stats_on_order( $order, $coupon_code, $coupon_id, $referral_user_id );
					$this->database->update_dashboard_summary_stats_on_order( $order, $coupon_code, $coupon_id, $referral_user_id );
				}

				if ( ! empty( $reward_user_id ) ) {

					$applied_referral_user_id     = get_post_meta( $coupon_id, 'ecre_applied_referral_user_id', true );
					$applied_referral_coupon_code = get_post_meta( $coupon_id, 'ecre_applied_referral_coupon_code', true );
					$applied_referral_coupon_id   = get_post_meta( $coupon_id, 'ecre_applied_referral_coupon_id', true );

					$this->database->store_reward_order_data( $order, $coupon_code, $coupon_id, $reward_user_id, $applied_referral_coupon_code, $applied_referral_coupon_id, $applied_referral_user_id );
					// Sync the usage count and limit from WooCommerce to our table.
					$this->database->sync_reward_coupon_usage( $coupon_id );
					// Coupon has a reward_user_id.
					update_post_meta( $order_id, '_reward_user_id', $reward_user_id );
					update_post_meta( $order_id, '_reward_coupon_code', $coupon_code );
					update_post_meta( $order_id, '_reward_coupon_id', $coupon_id );
				}
			}
		}

		/**
		 * Modifies the HTML representation of a WooCommerce percent-based coupon to include discount details.
		 *
		 * @param string    $coupon_html        The default HTML representation of the applied coupon.
		 * @param WC_Coupon $coupon          The WooCommerce coupon object.
		 * @param string    $discount_amount The HTML representation of the discount amount.
		 *
		 * @return string The modified HTML representation of the coupon with discount details.
		 */
		public function ecre_modify_percent_coupon_html_with_discount( $coupon_html, $coupon, $discount_amount ) {
			$default_values = array();

			$referral_user_id           = get_post_meta( $coupon->get_id(), 'referral_user_id', true );
			$reward_user_id             = get_post_meta( $coupon->get_id(), 'reward_user_id', true );
			$referral_discount_capping  = $this->erce_settings['referralDiscountCapping'];
			$reward_discount_capping    = $this->erce_settings['rewardDiscountCapping'];
			$cart_subtotal              = WC()->cart->get_subtotal();
			$discount_type              = $coupon->get_discount_type();
			$calculated_discount_amount = '';

			if ( 'percent' === $discount_type ) {
				$calculated_discount_amount = ( $coupon->get_amount() / 100 ) * $cart_subtotal;
			} else {
				// For fixed discounts, use the same logic as in ecre_convert_fixed_coupon_amount.
				$calculated_discount_amount = $coupon->get_amount();
			}

			if ( ! empty( $referral_discount_capping ) && 0 !== $referral_discount_capping ) {

				if ( $calculated_discount_amount >= $referral_discount_capping && ! empty( $referral_user_id ) ) {
					$coupon_html = $this->modify_percent_coupon_html( $coupon_html, $coupon, $discount_amount, $referral_user_id, $calculated_discount_amount, $referral_discount_capping );
				} else {
					$coupon_html = $this->modify_percent_coupon_html( $coupon_html, $coupon, $discount_amount, $referral_user_id, $calculated_discount_amount, $referral_discount_capping = 0 );
				}
			} elseif ( empty( $referral_discount_capping ) || 0 === $referral_discount_capping ) {
				if ( ! empty( $referral_user_id ) ) {
					$coupon_html = $this->modify_percent_coupon_html( $coupon_html, $coupon, $discount_amount, $referral_user_id, $calculated_discount_amount, $referral_discount_capping = 0 );
				}
			}

			if ( ! empty( $reward_discount_capping ) && 0 !== $reward_discount_capping ) {

				if ( $calculated_discount_amount >= $reward_discount_capping && ! empty( $reward_user_id ) ) {
					$coupon_html = $this->modify_percent_coupon_html( $coupon_html, $coupon, $discount_amount, $reward_user_id, $calculated_discount_amount, $reward_discount_capping );
				} else {
					$coupon_html = $this->modify_percent_coupon_html( $coupon_html, $coupon, $discount_amount, $reward_user_id, $calculated_discount_amount, $reward_discount_capping = 0 );
				}
			} elseif ( empty( $reward_discount_capping ) || 0 === $reward_discount_capping ) {
				$coupon_html = $this->modify_percent_coupon_html( $coupon_html, $coupon, $discount_amount, $reward_user_id, $calculated_discount_amount, $reward_discount_capping = 0 );
			}

			return $coupon_html;
		}

		/**
		 * Modifies the HTML representation of a WooCommerce percent-based coupon to include discount details.
		 *
		 * @param string    $coupon_html        The default HTML representation of the applied coupon.
		 * @param WC_Coupon $coupon          The WooCommerce coupon object.
		 * @param double    $discount_amount The HTML representation of the discount amount.
		 * @param int       $user_id          The user ID for referral or reward.
		 * @param double    $calculated_discount_amount Total discount.
		 * @param double    $discount_capping The discount capping for referral or reward.
		 *
		 * @return string The modified HTML representation of the coupon with discount details.
		 */
		private function modify_percent_coupon_html( $coupon_html, $coupon, $discount_amount, $user_id, $calculated_discount_amount, $discount_capping ) {
			$cart_subtotal = WC()->cart->get_subtotal();

			if ( 0 !== $discount_capping && ! empty( $discount_capping ) ) {
				if ( $calculated_discount_amount <= $discount_capping && ! empty( $user_id ) && 'percent' === $coupon->get_discount_type() ) {
					$percent = $coupon->get_amount();

					$discount_amount_html = $discount_amount . ' ( ' . $percent . '% ' . __( 'discount applied', 'echo-rewards' ) . ' )';

					$coupon_html = $discount_amount_html . ' <a href="' . esc_url(
						add_query_arg(
							'remove_coupon',
							rawurlencode( $coupon->get_code() ),
							defined( 'WOOCOMMERCE_CHECKOUT' ) ? wc_get_checkout_url() : wc_get_cart_url()
						)
					) . '" class="woocommerce-remove-coupon" data-coupon="' . esc_attr( $coupon->get_code() ) . '">' . __( '[Remove]', 'echo-rewards' ) . '</a>';
				}
			} elseif ( 0 === $discount_capping || empty( $discount_capping ) ) {
				if ( ! empty( $user_id ) && 'percent' === $coupon->get_discount_type() ) {
					$percent = $coupon->get_amount();

					$discount_amount_html = $discount_amount . ' ( ' . $percent . '% ' . __( 'discount applied', 'echo-rewards' ) . ' )';

					$coupon_html = $discount_amount_html . ' <a href="' . esc_url(
						add_query_arg(
							'remove_coupon',
							rawurlencode( $coupon->get_code() ),
							defined( 'WOOCOMMERCE_CHECKOUT' ) ? wc_get_checkout_url() : wc_get_cart_url()
						)
					) . '" class="woocommerce-remove-coupon" data-coupon="' . esc_attr( $coupon->get_code() ) . '">' . __( '[Remove]', 'echo-rewards' ) . '</a>';
				}
			}

			return $coupon_html;
		}
		/**
		 * Get cart subtotal with tax for validation purposes.
		 *
		 * This function returns the cart subtotal with tax included, regardless of
		 * how prices are entered or displayed in the store. This ensures coupon
		 * validation always checks against the final amount the customer will pay.
		 *
		 * @since 1.0.0
		 * @return float The cart subtotal with tax for validation purposes.
		 */
		public function get_cart_total_for_validation() {
			// Start with basic subtotal.
			$cart_subtotal = WC()->cart->get_subtotal();

			// Check if tax is enabled in WooCommerce.
			if ( wc_tax_enabled() ) {
				// Get tax settings.
				$round_at_subtotal = get_option( 'woocommerce_tax_round_at_subtotal' ) === 'yes';

				// Get raw cart values.
				$subtotal_excl_tax = WC()->cart->get_subtotal();
				$tax_amount        = WC()->cart->get_subtotal_tax();
				$total_incl_tax    = $subtotal_excl_tax + $tax_amount;

				// Always use total with tax for coupon validation.
				$cart_subtotal = $total_incl_tax;

				// Apply rounding if needed.
				if ( $round_at_subtotal ) {
					$cart_subtotal = round( $cart_subtotal, wc_get_price_decimals() );
				} else {
					$cart_subtotal = wc_format_decimal( $cart_subtotal, wc_get_price_decimals() );
				}
			}

			return $cart_subtotal;
		}
	}
}
