<?php // phpcs:ignore WordPress.Files.FileName.NotHyphenatedLowercase, WordPress.Files.FileName.InvalidClassFileName
/**
 * Wholesale Pricing Module.
 *
 * Provides wholesale pricing validation, price adjustments, and
 * cart notification management for WooCommerce Wholesale Prices integration.
 *
 * @package Autoship\Modules\WholesalePricing
 * @since   2.13.0
 */

namespace Autoship\Modules\WholesalePricing;

use Autoship\Core\ModuleInterface;
use Autoship\Core\Plugin;
use Autoship\Core\ServiceContainer;
use Autoship\Modules\WholesalePricing\Compatibility\LegacyWholesalePricingAdapter;
use Autoship\Modules\WholesalePricing\Services\WholesaleAdminHandler;
use Autoship\Modules\WholesalePricing\Services\WholesaleBulkService;
use Autoship\Modules\WholesalePricing\Services\WholesaleCartHandler;
use Autoship\Modules\WholesalePricing\Services\WholesaleCheckoutHandler;
use Autoship\Modules\WholesalePricing\Services\WholesaleDisplayHandler;
use Autoship\Modules\WholesalePricing\Services\WholesaleNativeUIHandler;
use Autoship\Modules\WholesalePricing\Services\WholesaleNoticeManager;
use Autoship\Modules\WholesalePricing\Services\WholesalePricingService;
use Autoship\Modules\WholesalePricing\Services\WholesalePricingServiceInterface;
use Autoship\Modules\WholesalePricing\Services\WholesaleRequirementsResolver;
use Autoship\Repositories\WholesaleDiscountRepositoryInterface;
use Autoship\Repositories\Implementations\WordPressWholesaleDiscountRepository;

/**
 * Wholesale Pricing Module.
 *
 * Registers services and hooks for wholesale pricing functionality.
 *
 * @package Autoship\Modules\WholesalePricing
 * @since   2.13.0
 */
class WholesalePricingModule implements ModuleInterface {

	/**
	 * Register the module services in the container.
	 *
	 * @param ServiceContainer $container The service container.
	 *
	 * @return void
	 */
	public function register( ServiceContainer $container ): void {
		$container->register(
			WholesaleDiscountRepositoryInterface::class,
			function () {
				return new WordPressWholesaleDiscountRepository();
			}
		);

		$container->register(
			WholesaleRequirementsResolver::class,
			function () {
				return new WholesaleRequirementsResolver();
			}
		);

		$container->register(
			WholesalePricingServiceInterface::class,
			function () {
				$c = Plugin::get_service_container();
				return new WholesalePricingService(
					$c->get( WholesaleRequirementsResolver::class ),
					$c->get( WholesaleDiscountRepositoryInterface::class )
				);
			}
		);

		$container->register(
			WholesaleNoticeManager::class,
			function () {
				$c = Plugin::get_service_container();
				return new WholesaleNoticeManager(
					$c->get( WholesalePricingServiceInterface::class )
				);
			}
		);

		$container->register(
			WholesaleCartHandler::class,
			function () {
				$c = Plugin::get_service_container();
				return new WholesaleCartHandler(
					$c->get( WholesalePricingServiceInterface::class ),
					$c->get( WholesaleNoticeManager::class )
				);
			}
		);

		$container->register(
			LegacyWholesalePricingAdapter::class,
			function () {
				return new LegacyWholesalePricingAdapter();
			}
		);

		$container->register(
			WholesaleAdminHandler::class,
			function () {
				$c = Plugin::get_service_container();
				return new WholesaleAdminHandler(
					$c->get( WholesaleDiscountRepositoryInterface::class )
				);
			}
		);

		$container->register(
			WholesaleNativeUIHandler::class,
			function () {
				$c = Plugin::get_service_container();
				return new WholesaleNativeUIHandler(
					$c->get( WholesalePricingServiceInterface::class ),
					$c->get( LegacyWholesalePricingAdapter::class )
				);
			}
		);

		$container->register(
			WholesaleDisplayHandler::class,
			function () {
				$c = Plugin::get_service_container();
				return new WholesaleDisplayHandler(
					$c->get( WholesalePricingServiceInterface::class )
				);
			}
		);

		$container->register(
			WholesaleCheckoutHandler::class,
			function () {
				$c = Plugin::get_service_container();
				return new WholesaleCheckoutHandler(
					$c->get( WholesalePricingServiceInterface::class ),
					$c->get( LegacyWholesalePricingAdapter::class )
				);
			}
		);

		$container->register(
			WholesaleBulkService::class,
			function () {
				$c = Plugin::get_service_container();
				return new WholesaleBulkService(
					$c->get( WholesalePricingServiceInterface::class ),
					$c->get( LegacyWholesalePricingAdapter::class )
				);
			}
		);
	}

	/**
	 * Boot the module.
	 *
	 * @param ServiceContainer $container The service container.
	 *
	 * @return void
	 */
	public function boot( ServiceContainer $container ): void {
		// Admin hooks (always registered in admin context).
		if ( is_admin() ) {
			$this->register_admin_hooks( $container );
		}

		// Frontend hooks (non-admin or AJAX).
		if ( ! is_admin() || defined( 'DOING_AJAX' ) ) {
			add_action(
				'woocommerce_init',
				function () use ( $container ) {
					$this->register_frontend_hooks( $container );
				}
			);
		}

		// Remove duplicate legacy hooks AFTER they are registered.
		// The legacy autoship_wholesale_pricing_init() runs at
		// plugins_loaded priority 100 (after the modern boot at 10).
		// Admin hooks are registered immediately in load(), frontend
		// hooks in a woocommerce_init callback.  Using woocommerce_init
		// priority 20 catches both admin and frontend legacy hooks.
		add_action(
			'woocommerce_init',
			function () {
				$this->remove_legacy_hooks();
			},
			20
		);
	}

	/**
	 * Register admin hooks.
	 *
	 * @param ServiceContainer $container The service container.
	 *
	 * @return void
	 */
	private function register_admin_hooks( ServiceContainer $container ): void {
		$admin_handler = $container->get( WholesaleAdminHandler::class );
		$bulk_service  = $container->get( WholesaleBulkService::class );

		// Product edit fields.
		add_action( 'autoship_after_print_product_custom_fields', array( $admin_handler, 'schedule_order_wholesale_discount' ), 10, 1 );
		add_action( 'autoship_after_print_variable_product_custom_fields', array( $admin_handler, 'schedule_order_variation_wholesale_discount' ), 10, 3 );

		// Save handlers.
		add_action( 'woocommerce_process_product_meta', array( $admin_handler, 'save_product_custom_options' ), 10, 1 );
		add_action( 'woocommerce_save_product_variation', array( $admin_handler, 'save_product_custom_options' ), 10, 2 );

		// Bulk actions and export.
		add_action( 'admin_init', array( $admin_handler, 'download_export_file' ) );
		add_filter( 'autoship_batch_update_products_actions', array( $bulk_service, 'add_update_scheduled_orders_wholesale_pricing_action' ), 15 );

		add_action( 'autoship_after_autoship_admin_utilities', array( $bulk_service, 'wholesale_bulk_admin_utilities_html' ), 10, 2 );
		add_action( 'autoship_set_latest_export_file', array( $bulk_service, 'set_wholesale_pricing_updates_export_file' ) );
		add_filter( 'autoship_batch_update_products_autoship_bulk_update_scheduled_orders_wholesale_pricing_args', array( $bulk_service, 'batch_update_scheduled_orders_wholesale_pricing_added_args' ), 10, 1 );
	}

	/**
	 * Remove legacy hooks registered by Autoship_Cloud_Wholesale_Pricing
	 * that are purely duplicated by the modern module.
	 *
	 * The legacy class may have registered its hooks when QPilot remote
	 * flags did not include 'wholesale_pricing' (causing the feature-flag
	 * guard in autoship_wholesale_pricing_init() to pass).
	 *
	 * NOTE: The legacy wwp_filter_wholesale_price_cart (priority 10) and
	 * flush_wholesale_notices (priority 99) are intentionally kept — the
	 * classic-page rendering path depends on them to populate the WC
	 * session via wc_add_notice().
	 *
	 * @return void
	 */
	private function remove_legacy_hooks(): void {
		if ( ! class_exists( 'Autoship_Cloud_Wholesale_Pricing' ) ) {
			return;
		}
		$legacy = \Autoship_Cloud_Wholesale_Pricing::get_instance();

		// Admin hooks (define_admin_hooks).
		remove_action( 'autoship_after_print_product_custom_fields', array( $legacy, 'schedule_order_wholesale_discount' ), 10 );
		remove_action( 'autoship_after_print_variable_product_custom_fields', array( $legacy, 'schedule_order_variation_wholesale_discount' ), 10 );
		remove_action( 'woocommerce_process_product_meta', array( $legacy, 'save_product_custom_options' ), 10 );
		remove_action( 'woocommerce_save_product_variation', array( $legacy, 'save_product_custom_options' ), 10 );
		remove_action( 'admin_init', array( $legacy, 'download_export_file' ) );
		remove_filter( 'autoship_batch_update_products_actions', array( $legacy, 'add_update_scheduled_orders_wholesale_pricing_action' ), 15 );
		remove_action( 'autoship_after_autoship_admin_utilities', array( $legacy, 'autoship_wholesale_bulk_admin_utilities_html' ), 10 );
		remove_action( 'autoship_set_latest_export_file', array( $legacy, 'set_wholesale_pricing_updates_export_file' ) );
		remove_filter( 'autoship_batch_update_products_autoship_bulk_update_scheduled_orders_wholesale_pricing_args', array( $legacy, 'autoship_batch_update_scheduled_orders_wholesale_pricing_added_args' ), 10 );

		// Frontend hooks that are purely duplicated by the modern module.
		// Native UI hooks.
		remove_filter( 'autoship_create_scheduled_order_item_data', array( $legacy, 'autoship_add_schedule_items_wholesale_price' ), 10 );
		remove_filter( 'autoship_update_schedule_items_changes', array( $legacy, 'autoship_update_schedule_items_wholesale_price' ), 10 );
		remove_filter( 'autoship_scheduled_order_form_item_add_display_name', array( $legacy, 'autoship_product_dropdown_get_price_range' ), 10 );
		remove_filter( 'autoship_scheduled_order_form_add_item_price', array( $legacy, 'autoship_added_product_html_get_price' ), 10 );
		remove_filter( 'autoship_scheduled_order_form_item_quantity_field', array( $legacy, 'autoship_enable_schedule_items_wholesale_price_input' ), 10 );
		// Display hooks.
		remove_filter( 'autoship_all_prices_array', array( $legacy, 'autoship_all_prices_array_w_wholesale' ), 10 );
		remove_filter( 'autoship_discount_checkout_and_recurring_same', array( $legacy, 'autoship_checkout_recurring_discount_wholesale_string' ), 10 );
		remove_filter( 'autoship_product_discount_data', array( $legacy, 'autoship_adjust_displayed_wholesale_string_price' ), 10 );
		// General wholesale price hook.
		remove_filter( 'wwp_filter_get_custom_product_type_wholesale_price', array( $legacy, 'autoship_adjust_cart_item_general_wholesale_price' ), 10 );
		// Checkout hook.
		remove_filter( 'autoship_get_scheduled_order_data_full_item_data', array( $legacy, 'autoship_update_scheduled_order_full_item_data' ), 10 );

		// Intentionally kept (classic-page session rendering depends on these):
		// - wwp_filter_wholesale_price_cart at priority 10 (legacy cart handler).
		// - woocommerce_after_calculate_totals at priority 99 (legacy flush via wc_add_notice).
	}

	/**
	 * Register frontend hooks for cart, checkout, native UI, and display.
	 *
	 * @param ServiceContainer $container The service container.
	 *
	 * @return void
	 */
	private function register_frontend_hooks( ServiceContainer $container ): void {
		$cart_handler     = $container->get( WholesaleCartHandler::class );
		$notice_manager   = $container->get( WholesaleNoticeManager::class );
		$native_handler   = $container->get( WholesaleNativeUIHandler::class );
		$display_handler  = $container->get( WholesaleDisplayHandler::class );
		$checkout_handler = $container->get( WholesaleCheckoutHandler::class );

		// Cart & Checkout hooks.
		add_filter( 'wwp_filter_get_custom_product_type_wholesale_price', array( $cart_handler, 'adjust_cart_item_general_wholesale_price' ), 10, 4 );
		// Priority 999 ensures this runs AFTER all WWPP pricing filters
		// (quantity-based at 11, category at 100, role at 200, add-ons at 500).
		// A lower priority lets those filters override our wholesale_price
		// clearing, re-enabling WWPP's own notice via printWCNotice().
		add_filter( 'wwp_filter_wholesale_price_cart', array( $cart_handler, 'adjust_cart_item_wholesale_price' ), 999, 5 );
		add_action( 'woocommerce_after_calculate_totals', array( $cart_handler, 'flush_notices' ), 99 );

		// Render wholesale notices directly into the store-notices block,
		// bypassing the WC session to avoid duplication via hydration.
		add_filter( 'render_block_woocommerce/store-notices', array( $notice_manager, 'render_notice_block' ) );

		// On classic shortcode pages woocommerce_output_all_notices() renders
		// session notices at woocommerce_before_cart / woocommerce_before_checkout_form
		// priority 10.  But calculate_totals() — which is where the legacy
		// flush adds fresh notices via wc_add_notice() — only runs later
		// inside the shortcode.  This creates a one-page-load delay.
		//
		// Fix: at priority 1 (before woocommerce_output_all_notices at 10),
		// scrub stale notices from the previous request, then force an
		// early calculate_totals() so the legacy flush can add a fresh
		// notice before woocommerce_output_all_notices() renders.
		// Scrub stale wholesale notices from the WC session at wp_loaded —
		// before any template rendering or WC form handlers that read the
		// session.  This ensures that after "Update Cart" with MOQ met,
		// the stale notice from the previous page load is gone before the
		// redirect page renders.
		add_action( 'wp_loaded', array( $notice_manager, 'remove_stale_wholesale_notices' ) );

		// Classic shortcode hooks — fire early_scrub_and_recalculate before
		// woocommerce_output_all_notices at priority 10.
		add_action( 'woocommerce_before_cart', array( $this, 'early_scrub_and_recalculate' ), 1 );
		add_action( 'woocommerce_before_checkout_form_cart_notices', array( $this, 'early_scrub_and_recalculate' ), 1 );

		// Block-based pages: the classic hooks above don't fire, and the
		// woocommerce/store-notices block may not exist in the template.
		// Force early calculate_totals() at template_redirect so
		// current_notices is populated, then inject the notice HTML
		// directly into the cart/checkout block output.
		add_action( 'template_redirect', array( $this, 'early_calculate_for_block_pages' ) );
		// On block pages, prepend notices into the cart/checkout block HTML.
		add_filter( 'render_block_woocommerce/cart', array( $notice_manager, 'prepend_notices_to_block' ) );
		add_filter( 'render_block_woocommerce/checkout', array( $notice_manager, 'prepend_notices_to_block' ) );

		// JS to remove stale wholesale notices after AJAX cart mutations.
		add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_wholesale_notice_script' ) );

		// Native UI hooks.
		add_filter( 'autoship_create_scheduled_order_item_data', array( $native_handler, 'add_schedule_items_wholesale_price' ), 10, 5 );
		add_filter( 'autoship_update_schedule_items_changes', array( $native_handler, 'update_schedule_items_wholesale_price' ), 10, 5 );
		add_filter( 'autoship_scheduled_order_form_item_add_display_name', array( $native_handler, 'product_dropdown_get_price_range' ), 10, 5 );
		add_filter( 'autoship_scheduled_order_form_add_item_price', array( $native_handler, 'added_product_html_get_price' ), 10, 3 );
		add_filter( 'autoship_scheduled_order_form_item_quantity_field', array( $native_handler, 'enable_schedule_items_wholesale_price_input' ), 10, 3 );

		// Display hooks.
		add_filter( 'autoship_all_prices_array', array( $display_handler, 'all_prices_array_w_wholesale' ), 10, 3 );
		add_filter( 'autoship_discount_checkout_and_recurring_same', array( $display_handler, 'checkout_recurring_discount_wholesale_string' ), 10, 4 );
		add_filter( 'autoship_product_discount_data', array( $display_handler, 'adjust_displayed_wholesale_string_price' ), 10, 2 );

		// Checkout hooks.
		add_filter( 'autoship_get_scheduled_order_data_full_item_data', array( $checkout_handler, 'update_scheduled_order_full_item_data' ), 10, 3 );
	}

	/**
	 * Scrub stale wholesale notices then force an early calculate_totals().
	 *
	 * Hooked to woocommerce_before_cart and woocommerce_before_checkout_form
	 * at priority 1 — before woocommerce_output_all_notices() at priority 10.
	 *
	 * On classic shortcode pages, woocommerce_output_all_notices() renders
	 * session notices BEFORE the cart shortcode triggers calculate_totals().
	 * Without this, fresh notices from the legacy flush (wc_add_notice)
	 * would only appear on the NEXT page load — one step behind.
	 *
	 * By scrubbing stale notices first and then forcing calculate_totals(),
	 * the legacy flush can add a fresh, accurate notice to the session
	 * before woocommerce_output_all_notices() renders at priority 10.
	 *
	 * @return void
	 */
	public function early_scrub_and_recalculate(): void {
		// Guard: only run once per request.  The checkout template fires
		// both woocommerce_before_checkout_form_cart_notices and
		// woocommerce_before_checkout_form — both trigger this callback.
		static $done = false;
		if ( $done ) {
			return;
		}
		$done = true;

		if ( ! function_exists( 'WC' ) || null === WC()->cart ) {
			return;
		}

		$container      = \Autoship\Core\Plugin::get_service_container();
		$notice_manager = $container->get( WholesaleNoticeManager::class );

		// Remove stale wholesale notices from the previous request.
		$notice_manager->remove_stale_wholesale_notices();

		// Force cart calculation so the legacy flush_wholesale_notices()
		// can add a fresh notice (via wc_add_notice) to the session
		// before woocommerce_output_all_notices() renders at priority 10.
		WC()->cart->calculate_totals();

		// Clear current_notices so that prepend_notices_to_block() and
		// render_notice_block() do not inject a duplicate — the session
		// notice will be rendered by woocommerce_output_all_notices().
		$notice_manager->clear_current();
	}

	/**
	 * Force early calculate_totals() on block-based cart/checkout pages.
	 *
	 * On block pages the classic woocommerce_before_cart hook does not fire,
	 * and the store-notices block renders BEFORE the cart block triggers
	 * calculate_totals().  By forcing calculation at template_redirect
	 * (before any blocks render), both the legacy session notices and the
	 * modern current_notices are populated in time for render_notice_block().
	 *
	 * Skipped on classic shortcode pages (handled by early_scrub_and_recalculate)
	 * and non-cart/checkout pages.
	 *
	 * @return void
	 */
	public function early_calculate_for_block_pages(): void {
		if ( ! function_exists( 'WC' ) || null === WC()->cart ) {
			return;
		}

		if ( ! function_exists( 'is_cart' ) || ( ! is_cart() && ! is_checkout() ) ) {
			return;
		}

		$container      = \Autoship\Core\Plugin::get_service_container();
		$notice_manager = $container->get( WholesaleNoticeManager::class );

		// Scrub stale notices from the previous request.
		$notice_manager->remove_stale_wholesale_notices();

		// Force early cart calculation so that both the legacy flush
		// (wc_add_notice → session) and the modern flush (current_notices)
		// are populated before any blocks or templates render.
		WC()->cart->calculate_totals();

		// Enqueue the reactive block notice script.  This runs on EVERY
		// block cart/checkout page load — the JS reads WWPP's extension
		// data from Store API responses to show/hide the notice when the
		// user changes quantities via the +/- buttons (AJAX, no reload).
		$this->enqueue_block_reactive_notice_script();
	}

	/**
	 * Enqueue a wp_footer script for block cart/checkout pages that
	 * reactively shows/hides wholesale notices.
	 *
	 * The script intercepts every Store API mutation (fetch).  WWPP's
	 * extension data on each cart item (extensions.wwpp_block.notices)
	 * is non-null when the MOQ is not met.  The script reads the notice
	 * message directly from that data — no PHP-generated HTML needed.
	 *
	 * @return void
	 */
	private function enqueue_block_reactive_notice_script(): void {
		add_action(
			'wp_footer',
			function () {
				$script = <<<'JS'
(function(){
	var NOTICE_CLASS = 'autoship-wholesale-block-notice';

	function removeWholesaleNotices(){
		document.querySelectorAll('.' + NOTICE_CLASS).forEach(function(el){ el.remove(); });
		document.querySelectorAll('.wwpp-notice').forEach(function(el){
			var info = el.closest('.woocommerce-info');
			if(info){ var w = info.closest('.woocommerce-notices-wrapper'); info.remove(); if(w && !w.children.length) w.remove(); }
		});
		document.querySelectorAll('.wc-block-components-notice-banner.is-info').forEach(function(el){
			var text = el.textContent || '';
			if(text.indexOf('minimum order quantity') !== -1 || text.indexOf('minimum order subtotal') !== -1){
				var c = el.closest('.wc-block-components-notices');
				el.remove();
				if(c && !c.children.length) c.remove();
			}
		});
	}

	function showNotice(message){
		removeWholesaleNotices();
		var target = document.querySelector('.wp-block-woocommerce-cart, .wp-block-woocommerce-checkout');
		if(!target) return;
		var html = '<div class="woocommerce-notices-wrapper ' + NOTICE_CLASS + '">'
			+ '<div class="woocommerce-info">' + message + '</div></div>';
		target.insertAdjacentHTML('beforebegin', html);
	}

	/**
	 * Extract the first wholesale MOQ failure message from a Store API
	 * response.  Handles both direct and batch response structures.
	 */
	function getWholesaleFailureMessage(data){
		/* Collect all item arrays — direct response or batch. */
		var itemSets = [];
		if(data && data.items){ itemSets.push(data.items); }
		if(data && data.responses){
			for(var r = 0; r < data.responses.length; r++){
				var body = data.responses[r] && data.responses[r].body;
				if(body && body.items){ itemSets.push(body.items); }
			}
		}
		for(var s = 0; s < itemSets.length; s++){
			var items = itemSets[s];
			for(var i = 0; i < items.length; i++){
				var ext = items[i].extensions;
				if(ext && ext.wwpp_block && ext.wwpp_block.notices
					&& ext.wwpp_block.notices.message){
					return ext.wwpp_block.notices.message;
				}
			}
		}
		return '';
	}

	function handleResponse(data){
		var msg = getWholesaleFailureMessage(data);
		if(msg){
			showNotice(msg);
		} else {
			removeWholesaleNotices();
		}
	}

	/* Skip entirely on classic shortcode pages (no block target element). */
	if(!document.querySelector('.wp-block-woocommerce-cart, .wp-block-woocommerce-checkout')){ return; }

	/* --- Intercept Store API mutations (quantity +/-, remove, etc). --- */
	var nativeFetch = window.fetch;
	window.fetch = function(input, init){
		var url = '';
		var method = 'GET';

		if(typeof input === 'string'){ url = input; }
		else if(input && input.url){ url = input.url; method = input.method || 'GET'; }
		if(init && init.method){ method = init.method; }
		method = method.toUpperCase();

		return nativeFetch.apply(this, arguments).then(function(response){
			if(method === 'GET' || url.indexOf('/wc/store') === -1){
				return response;
			}
			response.clone().json().then(handleResponse).catch(function(){});
			return response;
		});
	};

})();
JS;
				echo '<script>' . $script . '</script>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
			}
		);
	}

	/**
	 * Remove legacy cart and notice hooks from src/wholesale-pricing.php.
	 *
	 * The legacy Autoship_Cloud_Wholesale_Pricing class registers its own
	 * wwp_filter_wholesale_price_cart handler (priority 10) and a
	 * flush_wholesale_notices() that calls wc_add_notice(), putting
	 * wholesale notices into the WC session.  This conflicts with the
	 * modern notice lifecycle (collect → flush → render_notice_block)
	 * and causes duplication and stale notices.
	 *
	 * @return void
	 */
	private function remove_legacy_cart_hooks(): void {
		if ( ! class_exists( 'Autoship_Cloud_Wholesale_Pricing' ) ) {
			return;
		}

		$legacy = \Autoship_Cloud_Wholesale_Pricing::get_instance();

		if ( null === $legacy ) {
			return;
		}

		remove_filter( 'wwp_filter_wholesale_price_cart', array( $legacy, 'autoship_adjust_cart_item_wholesale_price' ), 10 );
		remove_filter( 'wwp_filter_get_custom_product_type_wholesale_price', array( $legacy, 'autoship_adjust_cart_item_general_wholesale_price' ), 10 );
		remove_action( 'woocommerce_after_calculate_totals', array( $legacy, 'flush_wholesale_notices' ), 99 );
	}

	/**
	 * Perform operations on plugin deactivation.
	 *
	 * @param ServiceContainer $container The service container.
	 *
	 * @return void
	 */
	public function deactivate( ServiceContainer $container ): void {
	}

	/**
	 * Perform operations on plugin uninstall.
	 *
	 * @param ServiceContainer $container The service container.
	 *
	 * @return void
	 */
	public function uninstall( ServiceContainer $container ): void {
	}

	/**
	 * Enqueue JS that removes stale wholesale notices after AJAX
	 * cart mutations on the block cart page.
	 *
	 * The block cart updates via the Store API (fetch) without a page
	 * reload, so the server-rendered notice HTML becomes stale.  This
	 * script intercepts Store API cart mutation responses and removes
	 * any wholesale notice elements from the DOM.
	 *
	 * @return void
	 */
	public function enqueue_wholesale_notice_script(): void {
		if ( ! function_exists( 'is_cart' ) || ( ! is_cart() && ! is_checkout() ) ) {
			return;
		}

		$script = <<<'JS'
(function () {
	/**
	 * Remove all wholesale pricing notices from the DOM.
	 *
	 * Targets two different renderings:
	 * 1. Server-rendered: .woocommerce-info containing .wwpp-notice
	 * 2. React-rendered:  .wc-block-components-notice-banner whose
	 *    text matches wholesale requirement phrases.
	 */
	function removeWholesaleNotices() {
		/* 1. Server-rendered notices (store-notices block). */
		document.querySelectorAll('.wwpp-notice').forEach(function (el) {
			var info = el.closest('.woocommerce-info');
			if (info) {
				var wrapper = info.closest('.woocommerce-notices-wrapper');
				info.remove();
				if (wrapper && wrapper.children.length === 0) {
					wrapper.remove();
				}
			}
		});

		/* 2. React-rendered notices (WC Blocks cart). */
		document.querySelectorAll('.wc-block-components-notice-banner.is-info').forEach(function (el) {
			var text = el.textContent || '';
			if (text.indexOf('minimum order quantity') !== -1 || text.indexOf('minimum order subtotal') !== -1) {
				var container = el.closest('.wc-block-components-notices');
				el.remove();
				if (container && container.children.length === 0) {
					container.remove();
				}
			}
		});
	}

	var nativeFetch = window.fetch;

	window.fetch = function (input, init) {
		var url = '';
		var method = 'GET';

		if (typeof input === 'string') {
			url = input;
		} else if (input && input.url) {
			url = input.url;
			method = input.method || 'GET';
		}

		if (init && init.method) {
			method = init.method;
		}

		method = method.toUpperCase();

		return nativeFetch.apply(this, arguments).then(function (response) {
			if (method === 'GET' || url.indexOf('/wc/store') === -1) {
				return response;
			}

			/*
			 * After any cart mutation the server-rendered wholesale notice
			 * is stale.  Remove it — the next full page load will re-render
			 * if requirements are still not met.
			 */
			response.clone().json().then(function () {
				removeWholesaleNotices();
			}).catch(function () {});

			return response;
		});
	};
})();
JS;

		add_action(
			'wp_footer',
			function () use ( $script ) {
				echo '<script>' . $script . '</script>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
			}
		);
	}
}
