<?php

/**
 * Applies the computed discount prices to cart line items at cart-calculation time.
 *
 * @link       https://nudgesells.com
 * @since      1.0.0
 *
 * @package    NudgeSells
 * @subpackage NudgeSells/includes
 */

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

/**
 * NudgeSells_Cart_Pricing
 *
 * Hooks WooCommerce's cart calculation to apply add-on, upgrade, bundle, and tier discounts to the relevant line items.
 *
 * @since      1.0.0
 * @package    NudgeSells
 * @subpackage NudgeSells/includes
 * @author     NudgeSells <support@nudgesells.com>
 */
class NudgeSells_Cart_Pricing
{

	public static $suppress_cascade = false;

	/**
	 * Cascade remove orphans.
	 *
	 * @param mixed $cart_item_key
	 * @param mixed $cart
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function cascade_remove_orphans($cart_item_key, $cart)
	{
		if (self::$suppress_cascade) {
			return;
		}
		if ('yes' !== NudgeSells_Admin_Settings::get('cascade_remove_addons', 'yes')) {
			return;
		}
		if (did_action('woocommerce_cart_item_removed') > 20) {
			return;
		}

		$removed = $cart->removed_cart_contents[$cart_item_key] ?? null;
		if (! $removed || empty($removed['nudgesells_group'])) {
			return;
		}
		if (! in_array($removed['nudgesells_role'] ?? '', array('host', 'upgrade'), true)) {
			return;
		}

		$group_id = $removed['nudgesells_group'];
		foreach ($cart->get_cart() as $key => $item) {
			if (isset($item['nudgesells_group']) && $item['nudgesells_group'] === $group_id) {
				$cart->remove_cart_item($key);
			}
		}
	}

	/**
	 * Get fresh product.
	 *
	 * @param WC_Product $cart_item_product
	 *
	 * @return mixed
	 * @since 1.0.0
	 */
	private function get_fresh_product(WC_Product $cart_item_product)
	{
		$fresh = wc_get_product($cart_item_product->get_id());
		return $fresh ? $fresh : $cart_item_product;
	}

	/**
	 * Apply prices.
	 *
	 * @param mixed $cart
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function apply_prices($cart)
	{
		if (is_admin() && ! defined('DOING_AJAX')) {
			return;
		}
		if (did_action('woocommerce_before_calculate_totals') >= 2) {
		}

		$groups = array();
		foreach ($cart->get_cart() as $key => $item) {
			if (empty($item['nudgesells_group'])) {
				continue;
			}
			$groups[$item['nudgesells_group']][$key] = $item;
		}

		foreach ($groups as $group_key => $items) {
			$this->apply_group_prices($items);
		}
	}

	/**
	 * Apply group prices.
	 *
	 * @param array $items
	 *
	 * @return void
	 * @since 1.0.0
	 */
	private function apply_group_prices(array $items)
	{
		$host_id = null;
		$host_variation_id = 0;
		$addon_items = array();
		$upgrade_item_key = null;
		$parent_present = false;

		foreach ($items as $key => $item) {
			if ('addon' === $item['nudgesells_role']) {
				$addon_items[$key] = $item;
			}
			if ('upgrade' === $item['nudgesells_role']) {
				$upgrade_item_key = $key;
				$parent_present   = true;
			}
			if ('host' === $item['nudgesells_role']) {
				$parent_present    = true;
				$host_variation_id = (int) ($item['variation_id'] ?? 0);
			}
			if (! empty($item['nudgesells_host_id'])) {
				$host_id = $item['nudgesells_host_id'];
			}
		}

		if ($upgrade_item_key && isset($items[$upgrade_item_key]['nudgesells_upgrade_rule'])) {
			$rule  = $items[$upgrade_item_key]['nudgesells_upgrade_rule'];
			$product = $items[$upgrade_item_key]['data'];
			$resolved = NudgeSells_Pricing_Engine::resolve_effective_prices($this->get_fresh_product($product), $rule['discount_type'], $rule['discount_value']);
			$base_price = $resolved['original'];
			$final      = $resolved['final'];
			$product->set_price($final);

			if ($final < $base_price) {
				$product->set_sale_price($final);

				$product->set_date_on_sale_from('');
				$product->set_date_on_sale_to('');
			}
		}

		if (empty($addon_items) || ! $host_id) {
			return;
		}

		if (! $parent_present) {

			foreach ($addon_items as $item) {
				$fresh    = $this->get_fresh_product($item['data']);
				$resolved = NudgeSells_Pricing_Engine::resolve_effective_prices($fresh, 'percent', 0);
				$item['data']->set_price($resolved['final']);
				if ($fresh->is_on_sale()) {
					$item['data']->set_sale_price((float) $fresh->get_sale_price());
					$item['data']->set_date_on_sale_from('');
					$item['data']->set_date_on_sale_to('');
				} else {

					$item['data']->set_sale_price('');
				}
			}
			return;
		}

		$rules = NudgeSells_Rules_Repository::get_rules($host_id, $host_variation_id);

		$selected = array();
		foreach ($addon_items as $key => $item) {
			$rule = isset($item['nudgesells_addon_rule']) ? $item['nudgesells_addon_rule'] : null;
			if (! $rule) {
				continue;
			}
			$product    = $item['data'];
			$fresh      = $this->get_fresh_product($product);
			$discount_value = $rule['discount_value'] ?? 0;
			$resolved   = NudgeSells_Pricing_Engine::resolve_effective_prices($fresh, $rule['discount_type'] ?? 'percent', $discount_value);

			$base_price = floatval($discount_value) > 0 ? $resolved['base'] : $resolved['final'];
			$selected[$key] = array(
				'product_id' => $item['nudgesells_addon_product_id'],
				'qty'        => $item['quantity'],
				'unit_price' => $base_price,
			);
		}

		$live_addon_rules = array();
		foreach ($rules['addons'] as $r) {
			$live_addon_rules[$r['product_id']] = $r;
		}
		foreach ($selected as $key => $line) {
			if (! isset($live_addon_rules[$line['product_id']])) {
				$rules['addons'][] = $addon_items[$key]['nudgesells_addon_rule'] + array('product_id' => $line['product_id']);
			}
		}

		$result = NudgeSells_Pricing_Engine::calculate($rules, $selected);

		foreach ($result['lines'] as $key => $line) {
			if (isset($addon_items[$key])) {
				$addon_items[$key]['data']->set_price($line['unit_price_final']);
				if ($line['unit_price_final'] < $line['unit_price_original']) {
					$addon_items[$key]['data']->set_sale_price($line['unit_price_final']);
					$addon_items[$key]['data']->set_date_on_sale_from('');
					$addon_items[$key]['data']->set_date_on_sale_to('');
				}
			}
		}
	}

	/**
	 * Filter cart item price.
	 *
	 * @param mixed $price_html
	 * @param mixed $cart_item
	 * @param mixed $cart_item_key
	 *
	 * @return mixed
	 * @since 1.0.0
	 */
	public function filter_cart_item_price($price_html, $cart_item, $cart_item_key)
	{
		if (empty($cart_item['nudgesells_role']) || ! in_array($cart_item['nudgesells_role'], array('addon', 'upgrade'), true)) {
			return $price_html;
		}

		$product = $cart_item['data'];
		if (! $product instanceof WC_Product) {
			return $price_html;
		}

		$fresh    = $this->get_fresh_product($product);
		$original = (float) $fresh->get_regular_price();
		if (! $original) {
			$original = (float) $fresh->get_price();
		}
		$current  = (float) $product->get_price();

		if ($original <= $current) {
			return $price_html;
		}

		return wc_format_sale_price(
			$this->format_price($original, $product),
			$this->format_price($current, $product)
		);
	}

	/**
	 * Format price.
	 *
	 * @param mixed $amount
	 * @param WC_Product $product
	 *
	 * @return mixed
	 * @since 1.0.0
	 */
	private function format_price($amount, WC_Product $product)
	{
		if (class_exists('WC_Subscriptions_Product') && WC_Subscriptions_Product::is_subscription($product)) {
			return WC_Subscriptions_Product::get_price_string($product, array('price' => wc_price($amount)));
		}
		return wc_price($amount);
	}

	/**
	 * Flag addon name.
	 *
	 * Prepends a visual "belongs to the main product" indicator to
	 * addon/upgrade line items in the cart/checkout, so the relationship
	 * reads clearly even though WooCommerce renders each cart item as its
	 * own independent table row with no native nesting support.
	 *
	 * @param mixed $name
	 * @param mixed $cart_item
	 * @param mixed $cart_item_key
	 *
	 * @return mixed
	 * @since 1.0.0
	 */
	public function flag_addon_name($name, $cart_item, $cart_item_key)
	{
		if (empty($cart_item['nudgesells_role']) || 'addon' !== $cart_item['nudgesells_role']) {
			return $name;
		}
		return '<span class="nudgesells-cart-item-indent" aria-hidden="true">&#8627;</span>' . $name;
	}

	/**
	 * Add a CSS class to each cart/checkout line item row identifying its
	 * role in a NudgeSells group (host/addon/upgrade), so the frontend
	 * stylesheet can style the visual hierarchy between them.
	 *
	 * @param string $class
	 * @param array  $cart_item
	 * @param string $cart_item_key
	 *
	 * @return string
	 * @since 1.0.0
	 */
	public function cart_item_class($class, $cart_item, $cart_item_key)
	{
		if (empty($cart_item['nudgesells_group'])) {
			return $class;
		}
		$role = $cart_item['nudgesells_role'] ?? '';
		if (! in_array($role, array('host', 'addon'), true)) {
			return $class;
		}
		return $class . ' nudgesells-cart-item nudgesells-cart-item--' . $role;
	}

	/**
	 * Show the actual dollar amount saved on each discounted addon/upgrade
	 * line item, rather than the previous generic "discount applied at
	 * checkout" placeholder text - computed the same way
	 * filter_cart_item_price() derives the struck-through price, so the
	 * two always agree with each other.
	 *
	 * @param array $item_data
	 * @param array $cart_item
	 *
	 * @return array
	 * @since 1.0.0
	 */
	public function add_savings_item_data($item_data, $cart_item)
	{
		if (empty($cart_item['nudgesells_role']) || ! in_array($cart_item['nudgesells_role'], array('addon', 'upgrade'), true)) {
			return $item_data;
		}

		$product = $cart_item['data'];
		if (! $product instanceof WC_Product) {
			return $item_data;
		}

		$fresh    = $this->get_fresh_product($product);
		$original = (float) $fresh->get_regular_price();
		if (! $original) {
			$original = (float) $fresh->get_price();
		}
		$current = (float) $product->get_price();

		if ($original <= $current) {
			return $item_data;
		}

		$qty   = (int) $cart_item['quantity'];
		$saved = ($original - $current) * $qty;

		if ($saved <= 0) {
			return $item_data;
		}

		$item_data[] = array(
			'key'     => __('Discount applied', 'nudgesells'),
			'value'   => wc_price(-$saved),
			'display' => '<span class="nudgesells-cart-savings-note">' . wc_price(-$saved) . '</span>',
		);

		return $item_data;
	}
}
