<?php // phpcs:ignore WordPress.Files.FileName.NotHyphenatedLowercase, WordPress.Files.FileName.InvalidClassFileName
/**
 * Wholesale pricing cart hook handler.
 *
 * @package Autoship\Modules\WholesalePricing\Services
 * @since   2.13.0
 */

namespace Autoship\Modules\WholesalePricing\Services;

/**
 * Handles cart-specific hook callbacks for wholesale pricing.
 *
 * Contains the callbacks registered with WooCommerce/WWP cart filters.
 * Uses deferred notice collection to avoid duplicate notices and
 * preserve other WC notices (e.g. coupon success).
 *
 * @package Autoship\Modules\WholesalePricing\Services
 * @since   2.13.0
 */
class WholesaleCartHandler {

	/**
	 * The pricing service.
	 *
	 * @var WholesalePricingServiceInterface
	 */
	private WholesalePricingServiceInterface $pricing;

	/**
	 * The notice manager.
	 *
	 * @var WholesaleNoticeManager
	 */
	private WholesaleNoticeManager $notices;

	/**
	 * Constructor.
	 *
	 * @param WholesalePricingServiceInterface $pricing The pricing service.
	 * @param WholesaleNoticeManager           $notices The notice manager.
	 */
	public function __construct(
		WholesalePricingServiceInterface $pricing,
		WholesaleNoticeManager $notices
	) {
		$this->pricing = $pricing;
		$this->notices = $notices;
	}

	/**
	 * Adjusts the general wholesale price for autoship cart items.
	 *
	 * Hooked to wwp_filter_get_custom_product_type_wholesale_price.
	 *
	 * @param float    $wholesale_price     The current wholesale price.
	 * @param array    $cart_item           The cart item data.
	 * @param array    $user_wholesale_role The user's wholesale role.
	 * @param \WC_Cart $cart_object         The cart object.
	 *
	 * @return float The adjusted wholesale price.
	 */
	public function adjust_cart_item_general_wholesale_price( $wholesale_price, $cart_item, $user_wholesale_role, $cart_object ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
		if ( empty( $cart_item['autoship_frequency_type'] ) || empty( $cart_item['autoship_frequency'] ) ) {
			return $wholesale_price;
		}

		$product_id = $cart_item['data']->get_id();
		$price      = ( '' !== $wholesale_price && null !== $wholesale_price ) ? (float) $wholesale_price : null;
		$adjusted   = $this->pricing->adjust_price( $product_id, $price );

		// When adjust_price returns null (no wholesale price applies),
		// return the original value so WWP's strict empty-string check
		// ('' !== $wholesale_price) continues to see '' and does not
		// mistakenly activate the wholesale pricing context.
		return null !== $adjusted ? $adjusted : $wholesale_price;
	}

	/**
	 * Adjusts the wholesale price for autoship cart items with validation.
	 *
	 * Hooked to wwp_filter_wholesale_price_cart. Collects notices for deferred
	 * display instead of calling wc_add_notice() inline.
	 *
	 * @param array    $values              The wholesale price values.
	 * @param int      $product_id          The product ID.
	 * @param array    $user_wholesale_role The user's wholesale role.
	 * @param array    $cart_item           The cart item data.
	 * @param \WC_Cart $cart_object         The cart object.
	 *
	 * @return array The adjusted wholesale price values.
	 */
	public function adjust_cart_item_wholesale_price( $values, $product_id, $user_wholesale_role, $cart_item, $cart_object ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
		if ( empty( $cart_item['autoship_frequency_type'] ) || empty( $cart_item['autoship_frequency'] ) ) {
			return $values;
		}

		$base_price     = isset( $values['wholesale_price'] ) && '' !== $values['wholesale_price'] ? (float) $values['wholesale_price'] : null;
		$adjusted_price = $this->pricing->adjust_price( $product_id, $base_price );

		if ( null === $adjusted_price ) {
			return $values;
		}

		$result = $this->pricing->validate(
			$user_wholesale_role,
			$cart_item['quantity'],
			round( $adjusted_price * $cart_item['quantity'], 2 ),
			$product_id
		);

		if ( $result->passed() ) {
			$values['wholesale_price'] = $adjusted_price;
		} elseif ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
			// During Store API requests, keep the wholesale price so
			// WWPP's block endpoint data (extensions.wwpp_block.notices)
			// can format the correct price in the notice message.
			// The block-page JS reads this data to display the notice.
			$values['wholesale_price'] = $adjusted_price;
		} else {
			// On full page loads, clear the wholesale price so WWP skips
			// its own per-product validation and does not generate a
			// duplicate notice via printWCNotice().
			$values['wholesale_price'] = '';
		}

		// Collect / dismiss notices ONLY after wp_loaded has fired.
		//
		// WWP hooks apply_product_wholesale_price_to_cart to both
		// woocommerce_cart_loaded_from_session (during init) and
		// woocommerce_before_calculate_totals (during calculate_totals).
		//
		// The session-load invocation runs BEFORE WC processes the
		// "Update Cart" POST (WC_Form_Handler::update_cart_action at
		// wp_loaded priority 20), so it sees stale quantities.  Any
		// notice collected at that point would be wrong and would
		// persist even after the customer meets MOQ.
		//
		// By gating on wp_loaded we guarantee notices are only built
		// from authoritative, post-update cart data.
		if ( did_action( 'wp_loaded' ) ) {
			if ( $result->passed() ) {
				$this->notices->dismiss( $product_id );
			} else {
				$notice = $this->notices->build_notice( $result, $product_id, $user_wholesale_role );

				$notice = apply_filters(
					'autoship_filter_wholesale_price_requirement_failure_notice',
					$notice,
					$result->get_requirements()->get_minimum_order_items(),
					$result->get_requirements()->get_minimum_order_price(),
					$cart_item['quantity'],
					round( $adjusted_price * $cart_item['quantity'], 2 ),
					$user_wholesale_role
				);

				if ( ! empty( $notice ) ) {
					$this->notices->collect( $product_id, $notice );
				}
			}
		}

		return $values;
	}

	/**
	 * Flush collected wholesale notices.
	 *
	 * Hooked to woocommerce_after_calculate_totals at priority 99.
	 *
	 * @return void
	 */
	public function flush_notices(): void {
		$this->notices->flush();
	}
}
