<?php
/**
 * Plugin Name:  Peptio – Frontend Warehouse Stock Routing
 * Description:  Routes storefront stock, cart validation and order stock movements to the correct Peptio warehouse with cart-wide HQ fallback, concurrency protection, refund-safe restoration and dispatch integration helpers.
 * Version:      1.1.0
 * Author:       Peptio
 *
 * CODE SNIPPETS VERSION:
 * Paste into a PHP snippet without adding <?php.
 * Set the snippet to run everywhere.
 *
 * REQUIREMENTS:
 * - Peptio – Multi-Warehouse Core v1.2.0 or later.
 *
 * IMPORTANT:
 * - Replace the previous v1.0.1 snippet with this version.
 * - Do NOT run both versions simultaneously.
 * - This snippet does nothing unless the Core setting
 *   "Enable multi-warehouse mode" is switched on.
 * - HQ remains native WooCommerce stock.
 * - Europe stock is read from the Peptio warehouse stock table.
 * - EU orders do NOT use WooCommerce native stock reduction.
 * - EU orders use the Peptio warehouse movement engine.
 * - HQ fallback applies to the COMPLETE CART, never individual lines.
 */

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


/* ==========================================================================
   LOAD GUARD
   ========================================================================== */

if (defined('PEPTI_FRONTEND_WAREHOUSE_ROUTING_LOADED')) {
	return;
}

define(
	'PEPTI_FRONTEND_WAREHOUSE_ROUTING_LOADED',
	true
);

define(
	'PEPTI_FWR_VERSION',
	'1.1.0'
);


/* ==========================================================================
   ORDER / ITEM META
   ========================================================================== */

define(
	'PEPTI_FWR_ORDER_WAREHOUSE',
	'_pepti_fulfilment_warehouse'
);

define(
	'PEPTI_FWR_ORDER_WAREHOUSE_NAME',
	'_pepti_fulfilment_warehouse_name'
);

define(
	'PEPTI_FWR_ORDER_COUNTRY',
	'_pepti_routing_country'
);

define(
	'PEPTI_FWR_ORDER_DEDUCTED',
	'_pepti_warehouse_stock_deducted'
);

define(
	'PEPTI_FWR_ORDER_RESTORED',
	'_pepti_warehouse_stock_restored'
);

define(
	'PEPTI_FWR_ORDER_DEDUCTION_TIME',
	'_pepti_warehouse_stock_deducted_at'
);

define(
	'PEPTI_FWR_ITEM_REDUCED',
	'_pepti_fwr_reduced_stock'
);


/* ==========================================================================
   LOCK / RETRY CONFIGURATION
   ========================================================================== */

define(
	'PEPTI_FWR_STOCK_LOCK_NAME',
	'pepti_fwr_eu_stock'
);

define(
	'PEPTI_FWR_STOCK_LOCK_TIMEOUT',
	8
);

define(
	'PEPTI_FWR_RETRY_HOOK',
	'pepti_fwr_retry_order_stock_reduction'
);


/* ==========================================================================
   CORE / ENABLED CHECKS
   ========================================================================== */

/**
 * Is the Multi-Warehouse Core switched on?
 */
function pepti_fwr_is_enabled() {

	return (
		function_exists(
			'pepti_mw_is_enabled'
		)
		&& (bool)
			pepti_mw_is_enabled()
	);
}


/**
 * Verify every Core function required by this router exists.
 */
function pepti_fwr_core_available() {

	return (
		function_exists(
			'pepti_mw_get_settings'
		)
		&& function_exists(
			'pepti_mw_get_stock'
		)
		&& function_exists(
			'pepti_mw_set_stock'
		)
		&& function_exists(
			'pepti_mw_stock_owner_id'
		)
		&& function_exists(
			'pepti_mw_get_warehouses'
		)
		&& function_exists(
			'pepti_mw_is_valid_warehouse'
		)
	);
}


/**
 * Should warehouse routing currently operate?
 */
function pepti_fwr_active() {

	return (
		pepti_fwr_is_enabled()
		&& pepti_fwr_core_available()
		&& class_exists(
			'WooCommerce'
		)
	);
}


/**
 * Retrieve Core settings safely.
 */
function pepti_fwr_settings() {

	if (
		!function_exists(
			'pepti_mw_get_settings'
		)
	) {
		return [];
	}

	$settings =
		pepti_mw_get_settings();

	return is_array($settings)
		? $settings
		: [];
}


/* ==========================================================================
   WAREHOUSE CONFIGURATION
   ========================================================================== */

/**
 * Europe country list configured in Multi-Warehouse Core.
 */
function pepti_fwr_eu_countries() {

	$settings =
		pepti_fwr_settings();

	$countries =
		$settings[
			'eu_country_codes'
		]
		?? [];

	if (!is_array($countries)) {
		$countries = [];
	}

	$countries =
		array_values(
			array_unique(
				array_filter(
					array_map(
						static function(
							$country
						) {

							$country =
								strtoupper(
									sanitize_text_field(
										$country
									)
								);

							return $country;
						},
						$countries
					)
				)
			)
		);

	return (array)
		apply_filters(
			'pepti_fwr_eu_countries',
			$countries
		);
}


/**
 * Is whole-cart HQ fallback enabled?
 */
function pepti_fwr_fallback_to_hq_enabled() {

	$settings =
		pepti_fwr_settings();

	return !empty(
		$settings[
			'fallback_to_hq'
		]
	);
}


/**
 * Should customers see the warehouse-origin notice?
 */
function pepti_fwr_show_customer_notice() {

	$settings =
		pepti_fwr_settings();

	return !empty(
		$settings[
			'show_customer_notice'
		]
	);
}


/**
 * Normalised warehouse list.
 */
function pepti_fwr_warehouses() {

	if (
		!function_exists(
			'pepti_mw_get_warehouses'
		)
	) {
		return [];
	}

	$raw =
		pepti_mw_get_warehouses();

	if (!is_array($raw)) {
		return [];
	}

	$warehouses = [];

	foreach (
		$raw
		as $key => $warehouse
	) {

		if (is_array($warehouse)) {

			$code =
				sanitize_key(
					$warehouse['code']
					?? (
						is_string($key)
							? $key
							: ''
					)
				);

			$name =
				sanitize_text_field(
					$warehouse['name']
					?? strtoupper($code)
				);

		} elseif (
			is_string($warehouse)
		) {

			$code =
				sanitize_key(
					is_string($key)
						? $key
						: $warehouse
				);

			$name =
				sanitize_text_field(
					$warehouse
				);

		} else {
			continue;
		}

		if ($code === '') {
			continue;
		}

		$warehouses[$code] = [
			'code' => $code,

			'name' =>
				$name !== ''
					? $name
					: strtoupper($code),
		];
	}

	return $warehouses;
}


/**
 * Safe default warehouse.
 */
function pepti_fwr_default_warehouse() {

	$default =
		sanitize_key(
			(string)
			apply_filters(
				'pepti_mw_default_warehouse',
				'hq'
			)
		);

	$warehouses =
		pepti_fwr_warehouses();

	if (
		$default !== ''
		&& isset(
			$warehouses[$default]
		)
	) {
		return $default;
	}

	if (
		isset(
			$warehouses['hq']
		)
	) {
		return 'hq';
	}

	$keys =
		array_keys(
			$warehouses
		);

	return isset($keys[0])
		? sanitize_key(
			$keys[0]
		)
		: 'hq';
}


/**
 * Regional warehouse before stock fallback is considered.
 */
function pepti_fwr_warehouse_for_country(
	$country
) {

	$country =
		strtoupper(
			sanitize_text_field(
				$country
			)
		);

	$warehouse =
		in_array(
			$country,
			pepti_fwr_eu_countries(),
			true
		)
			? 'eu'
			: 'hq';

	if (
		!function_exists(
			'pepti_mw_is_valid_warehouse'
		)
		|| !pepti_mw_is_valid_warehouse(
			$warehouse
		)
	) {

		$warehouse =
			pepti_fwr_default_warehouse();
	}

	$filtered =
		sanitize_key(
			(string)
			apply_filters(
				'pepti_fwr_warehouse_for_country',
				$warehouse,
				$country
			)
		);

	if (
		function_exists(
			'pepti_mw_is_valid_warehouse'
		)
		&& pepti_mw_is_valid_warehouse(
			$filtered
		)
	) {
		return $filtered;
	}

	return $warehouse;
}


/**
 * Human-readable warehouse name.
 */
function pepti_fwr_warehouse_name(
	$warehouse
) {

	$warehouse =
		sanitize_key(
			$warehouse
		);

	$warehouses =
		pepti_fwr_warehouses();

	return (
		$warehouses[$warehouse]['name']
		?? strtoupper($warehouse)
	);
}


/* ==========================================================================
   CUSTOMER COUNTRY
   ========================================================================== */

/**
 * Current customer delivery country.
 */
function pepti_fwr_customer_country() {

	$country = '';

	if (
		function_exists('WC')
		&& WC()->customer
	) {

		$country =
			WC()->customer
				->get_shipping_country();

		if ($country === '') {

			$country =
				WC()->customer
					->get_billing_country();
		}
	}

	if (
		$country === ''
		&& function_exists('WC')
		&& WC()->session
	) {

		$country =
			(string)
			WC()->session->get(
				'pepti_fwr_country',
				''
			);
	}

	if (
		$country === ''
		&& class_exists(
			'WC_Geolocation'
		)
	) {

		$location =
			WC_Geolocation::geolocate_ip();

		if (
			is_array($location)
			&& !empty(
				$location['country']
			)
		) {

			$country =
				$location['country'];
		}
	}

	return strtoupper(
		sanitize_text_field(
			$country
		)
	);
}


/* ==========================================================================
   PRODUCT / STOCK HELPERS
   ========================================================================== */

/**
 * Resolve the actual stock-owning product ID.
 */
function pepti_fwr_owner_id(
	$product_or_id
) {

	if (
		!function_exists(
			'wc_get_product'
		)
		|| !function_exists(
			'pepti_mw_stock_owner_id'
		)
	) {
		return 0;
	}

	$product =
		$product_or_id
		instanceof WC_Product
			? $product_or_id
			: wc_get_product(
				absint(
					$product_or_id
				)
			);

	if (
		!$product
		instanceof WC_Product
	) {
		return 0;
	}

	$owner_id =
		pepti_mw_stock_owner_id(
			$product
		);

	return is_numeric($owner_id)
		? max(
			0,
			(int) $owner_id
		)
		: 0;
}


/**
 * Return the stock-owning WC_Product.
 */
function pepti_fwr_owner_product(
	$product_or_id
) {

	$owner_id =
		pepti_fwr_owner_id(
			$product_or_id
		);

	if ($owner_id <= 0) {
		return null;
	}

	$product =
		wc_get_product(
			$owner_id
		);

	return $product
		instanceof WC_Product
			? $product
			: null;
}


/**
 * Determine whether warehouse stock should control this product.
 *
 * IMPORTANT:
 * v1.0.1 effectively treated almost every valid WooCommerce
 * product as stock managed because every product has an owner ID.
 *
 * v1.1.0 only takes over stock behaviour when the actual stock
 * owner is configured to manage stock.
 */
function pepti_fwr_product_uses_stock(
	$product
) {

	if (
		!$product
		instanceof WC_Product
	) {
		return false;
	}

	$owner =
		pepti_fwr_owner_product(
			$product
		);

	if (
		!$owner
		instanceof WC_Product
	) {
		return false;
	}

	return (bool)
		$owner->managing_stock();
}


/**
 * Raw warehouse quantity.
 */
function pepti_fwr_stock(
	$product_or_id,
	$warehouse = ''
) {

	if (!pepti_fwr_active()) {
		return 0.0;
	}

	$owner_id =
		pepti_fwr_owner_id(
			$product_or_id
		);

	if ($owner_id <= 0) {
		return 0.0;
	}

	$warehouse =
		$warehouse !== ''
			? sanitize_key(
				$warehouse
			)
			: pepti_fwr_current_warehouse();

	if (
		!pepti_mw_is_valid_warehouse(
			$warehouse
		)
	) {
		return 0.0;
	}

	return (float)
		pepti_mw_get_stock(
			$warehouse,
			$owner_id
		);
}


/**
 * Pretty quantity formatter.
 */
function pepti_fwr_qty(
	$quantity
) {

	$quantity =
		(float) $quantity;

	if (
		(float) (int) $quantity
		=== $quantity
	) {
		return (string)
			(int) $quantity;
	}

	return wc_format_decimal(
		$quantity,
		2
	);
}


/* ==========================================================================
   CART STOCK AGGREGATION
   ========================================================================== */

/**
 * Aggregate cart quantities by stock-owner ID.
 *
 * $overrides can be:
 *
 * [
 *     owner_id => quantity
 * ]
 *
 * An override REPLACES the current quantity for that owner.
 */
function pepti_fwr_cart_stock_totals(
	array $overrides = []
) {

	$totals = [];

	if (
		!function_exists('WC')
		|| !WC()->cart
	) {
		return $totals;
	}

	foreach (
		WC()->cart->get_cart()
		as $cart_item_key =>
		$cart_item
	) {

		$product =
			$cart_item['data']
			?? null;

		if (
			!$product
			instanceof WC_Product
		) {
			continue;
		}

		if (
			!pepti_fwr_product_uses_stock(
				$product
			)
		) {
			continue;
		}

		$owner_id =
			pepti_fwr_owner_id(
				$product
			);

		if ($owner_id <= 0) {
			continue;
		}

		if (
			!isset(
				$totals[$owner_id]
			)
		) {

			$owner =
				wc_get_product(
					$owner_id
				);

			$totals[$owner_id] = [
				'product' =>
					$owner
					instanceof WC_Product
						? $owner
						: $product,

				'quantity' =>
					0.0,
			];
		}

		$totals[
			$owner_id
		]['quantity'] +=
			max(
				0,
				(float)
				(
					$cart_item[
						'quantity'
					]
					?? 0
				)
			);
	}

	foreach (
		$overrides
		as $owner_id =>
		$quantity
	) {

		$owner_id =
			absint(
				$owner_id
			);

		if ($owner_id <= 0) {
			continue;
		}

		$product =
			wc_get_product(
				$owner_id
			);

		if (
			!$product
			instanceof WC_Product
			|| !pepti_fwr_product_uses_stock(
				$product
			)
		) {
			continue;
		}

		$totals[$owner_id] = [
			'product' =>
				$product,

			'quantity' =>
				max(
					0,
					(float) $quantity
				),
		];
	}

	return $totals;
}


/**
 * Does a complete set of quantities fit in one warehouse?
 */
function pepti_fwr_warehouse_can_fulfil(
	$warehouse,
	$totals
) {

	$warehouse =
		sanitize_key(
			$warehouse
		);

	if (
		!pepti_fwr_active()
		|| !pepti_mw_is_valid_warehouse(
			$warehouse
		)
	) {
		return false;
	}

	foreach (
		(array) $totals
		as $entry
	) {

		$product =
			$entry['product']
			?? null;

		$quantity =
			(float)
			(
				$entry['quantity']
				?? 0
			);

		if (
			!$product
			instanceof WC_Product
			|| !pepti_fwr_product_uses_stock(
				$product
			)
			|| $quantity <= 0
		) {
			continue;
		}

		if (
			$product->backorders_allowed()
		) {
			continue;
		}

		$available =
			pepti_fwr_stock(
				$product,
				$warehouse
			);

		if (
			$quantity
			> $available
			+ 0.00001
		) {
			return false;
		}
	}

	return true;
}


/**
 * Resolve a warehouse for a known country and known complete cart.
 *
 * Europe:
 * - Prefer Europe.
 * - If Europe cannot fulfil the COMPLETE cart and fallback is enabled,
 *   use HQ only if HQ can fulfil the COMPLETE cart.
 * - Otherwise remain EU so validation can correctly block checkout.
 *
 * ROW/UK:
 * - HQ.
 */
function pepti_fwr_resolve_warehouse_for_totals(
	$country,
	$totals
) {
	$country = strtoupper(sanitize_text_field($country));
	$regional = $country !== '' ? pepti_fwr_warehouse_for_country($country) : pepti_fwr_default_warehouse();
	if (!$totals || pepti_fwr_warehouse_can_fulfil($regional, $totals)) return $regional;
	if (!pepti_fwr_fallback_to_hq_enabled()) return $regional;
	$fallback = class_exists('MI0PS_Warehouses') ? MI0PS_Warehouses::default_code() : 'hq';
	if ($fallback !== $regional && pepti_fwr_warehouse_can_fulfil($fallback, $totals)) return $fallback;
	return $regional;
}


/**
 * Resolve warehouse for the current cart.
 */
function pepti_fwr_resolve_cart_warehouse(
	$country = '',
	$totals = null
) {

	$country =
		$country !== ''
			? strtoupper(
				sanitize_text_field(
					$country
				)
			)
			: pepti_fwr_customer_country();

	if ($totals === null) {

		$totals =
			pepti_fwr_cart_stock_totals();
	}

	if ($country === '') {

		if (
			function_exists('WC')
			&& WC()->session
		) {

			$session_warehouse =
				sanitize_key(
					(string)
					WC()->session->get(
						'pepti_fwr_warehouse',
						''
					)
				);

			if (
				$session_warehouse !== ''
				&& pepti_mw_is_valid_warehouse(
					$session_warehouse
				)
			) {
				return $session_warehouse;
			}
		}

		$country =
			pepti_fwr_customer_country();
	}

	return pepti_fwr_resolve_warehouse_for_totals(
		$country,
		$totals
	);
}


/**
 * Current storefront warehouse.
 */
function pepti_fwr_current_warehouse() {

	if (!pepti_fwr_active()) {
		return 'hq';
	}

	$country =
		pepti_fwr_customer_country();

	$warehouse =
		pepti_fwr_resolve_cart_warehouse(
			$country
		);

	if (
		!pepti_mw_is_valid_warehouse(
			$warehouse
		)
	) {

		$warehouse =
			pepti_fwr_default_warehouse();
	}

	if (
		function_exists('WC')
		&& WC()->session
	) {

		WC()->session->set(
			'pepti_fwr_warehouse',
			$warehouse
		);

		if ($country !== '') {

			WC()->session->set(
				'pepti_fwr_country',
				$country
			);
		}
	}

	return $warehouse;
}


/* ==========================================================================
   DATABASE STOCK LOCK
   ========================================================================== */

/**
 * Acquire a MySQL/MariaDB advisory lock.
 *
 * This serialises custom EU inventory mutations so simultaneous
 * checkout requests cannot both read the same EU quantity and then
 * deduct from the same starting value.
 */
function pepti_fwr_acquire_stock_lock(
	$timeout = PEPTI_FWR_STOCK_LOCK_TIMEOUT
) {

	global $wpdb;

	$timeout =
		max(
			0,
			(int) $timeout
		);

	$result =
		$wpdb->get_var(
			$wpdb->prepare(
				'SELECT GET_LOCK(%s, %d)',
				PEPTI_FWR_STOCK_LOCK_NAME,
				$timeout
			)
		);

	return (
		(string) $result
		=== '1'
	);
}


/**
 * Release warehouse stock mutex.
 */
function pepti_fwr_release_stock_lock() {

	global $wpdb;

	$wpdb->get_var(
		$wpdb->prepare(
			'SELECT RELEASE_LOCK(%s)',
			PEPTI_FWR_STOCK_LOCK_NAME
		)
	);
}


/* ==========================================================================
   FRONTEND PRODUCT AVAILABILITY
   ========================================================================== */

/**
 * Override product stock quantity with regional warehouse quantity.
 */
add_filter(
	'woocommerce_product_get_stock_quantity',
	function(
		$quantity,
		$product
	) {

		if (
			!pepti_fwr_active()
			|| is_admin()
			|| !$product
				instanceof WC_Product
			|| !pepti_fwr_product_uses_stock(
				$product
			)
		) {
			return $quantity;
		}

		return pepti_fwr_stock(
			$product
		);
	},
	100,
	2
);


/**
 * Variation stock quantity.
 */
add_filter(
	'woocommerce_product_variation_get_stock_quantity',
	function(
		$quantity,
		$product
	) {

		if (
			!pepti_fwr_active()
			|| is_admin()
			|| !$product
				instanceof WC_Product
			|| !pepti_fwr_product_uses_stock(
				$product
			)
		) {
			return $quantity;
		}

		return pepti_fwr_stock(
			$product
		);
	},
	100,
	2
);


/**
 * Product in-stock state.
 */
add_filter(
	'woocommerce_product_is_in_stock',
	function(
		$in_stock,
		$product
	) {

		if (
			!pepti_fwr_active()
			|| is_admin()
			|| !$product
				instanceof WC_Product
			|| !pepti_fwr_product_uses_stock(
				$product
			)
		) {
			return $in_stock;
		}

		if (
			$product
				->backorders_allowed()
		) {
			return true;
		}

		return (
			pepti_fwr_stock(
				$product
			)
			> 0
		);
	},
	100,
	2
);


/**
 * Product stock status.
 */
add_filter(
	'woocommerce_product_get_stock_status',
	function(
		$status,
		$product
	) {

		if (
			!pepti_fwr_active()
			|| is_admin()
			|| !$product
				instanceof WC_Product
			|| !pepti_fwr_product_uses_stock(
				$product
			)
		) {
			return $status;
		}

		if (
			$product
				->backorders_allowed()
		) {
			return 'onbackorder';
		}

		return (
			pepti_fwr_stock(
				$product
			)
			> 0
		)
			? 'instock'
			: 'outofstock';
	},
	100,
	2
);


/**
 * Variation stock status.
 */
add_filter(
	'woocommerce_product_variation_get_stock_status',
	function(
		$status,
		$product
	) {

		if (
			!pepti_fwr_active()
			|| is_admin()
			|| !$product
				instanceof WC_Product
			|| !pepti_fwr_product_uses_stock(
				$product
			)
		) {
			return $status;
		}

		if (
			$product
				->backorders_allowed()
		) {
			return 'onbackorder';
		}

		return (
			pepti_fwr_stock(
				$product
			)
			> 0
		)
			? 'instock'
			: 'outofstock';
	},
	100,
	2
);


/**
 * Customer availability text.
 */
add_filter(
	'woocommerce_get_availability_text',
	function(
		$text,
		$product
	) {

		if (
			!pepti_fwr_active()
			|| !$product
				instanceof WC_Product
			|| !pepti_fwr_product_uses_stock(
				$product
			)
		) {
			return $text;
		}

		$stock =
			pepti_fwr_stock(
				$product
			);

		if (
			$stock <= 0
			&& !$product
				->backorders_allowed()
		) {

			return __(
				'Out of stock',
				'woocommerce'
			);
		}

		if (
			$stock > 0
			&& $stock <= 5
		) {

			return sprintf(
				__(
					'Only %s left in stock',
					'woocommerce'
				),
				wc_format_stock_quantity_for_display(
					$stock,
					$product
				)
			);
		}

		return __(
			'In stock',
			'woocommerce'
		);
	},
	100,
	2
);


/**
 * Regional quantity limit.
 */
add_filter(
	'woocommerce_quantity_input_max',
	function(
		$max,
		$product
	) {

		if (
			!pepti_fwr_active()
			|| !$product
				instanceof WC_Product
			|| !pepti_fwr_product_uses_stock(
				$product
			)
			|| $product
				->backorders_allowed()
		) {
			return $max;
		}

		$stock =
			max(
				0,
				pepti_fwr_stock(
					$product
				)
			);

		if (
			$max === ''
			|| $max === null
			|| (float) $max <= 0
		) {
			return $stock;
		}

		return min(
			(float) $max,
			$stock
		);
	},
	100,
	2
);


/**
 * Variable-product availability.
 */
add_filter(
	'woocommerce_available_variation',
	function(
		$data,
		$product,
		$variation
	) {

		if (
			!pepti_fwr_active()
			|| !$variation
				instanceof WC_Product_Variation
			|| !pepti_fwr_product_uses_stock(
				$variation
			)
		) {
			return $data;
		}

		$stock =
			pepti_fwr_stock(
				$variation
			);

		if (
			!$variation
				->backorders_allowed()
		) {

			$data['max_qty'] =
				max(
					0,
					$stock
				);
		}

		$data['is_in_stock'] =
			$variation
				->backorders_allowed()
			|| $stock > 0;

		$data['availability_html'] =
			wc_get_stock_html(
				$variation
			);

		return $data;
	},
	100,
	3
);


/* ==========================================================================
   CHECKOUT COUNTRY / SESSION ROUTING
   ========================================================================== */

/**
 * Save currently resolved checkout route in the WC session.
 */
function pepti_fwr_update_session_route(
	$country
) {

	if (
		!pepti_fwr_active()
		|| !function_exists('WC')
		|| !WC()->session
	) {
		return;
	}

	$country =
		strtoupper(
			sanitize_text_field(
				$country
			)
		);

	if ($country === '') {
		return;
	}

	$warehouse =
		pepti_fwr_resolve_cart_warehouse(
			$country
		);

	WC()->session->set(
		'pepti_fwr_country',
		$country
	);

	WC()->session->set(
		'pepti_fwr_warehouse',
		$warehouse
	);
}


/**
 * Classic checkout address refresh.
 */
add_action(
	'woocommerce_checkout_update_order_review',
	function(
		$posted_data
	) {

		if (
			!pepti_fwr_active()
		) {
			return;
		}

		$data = [];

		parse_str(
			(string) $posted_data,
			$data
		);

		$country =
			$data[
				'shipping_country'
			]
			?? $data[
				'billing_country'
			]
			?? '';

		pepti_fwr_update_session_route(
			$country
		);
	},
	5
);


/* ==========================================================================
   CART VALIDATION HELPERS
   ========================================================================== */

/**
 * Display a warehouse-stock error for one product.
 */
function pepti_fwr_stock_notice(
	$product,
	$available
) {

	if (
		!$product
		instanceof WC_Product
	) {
		return;
	}

	wc_add_notice(
		sprintf(
			__(
				'There is not enough warehouse stock for “%1$s”. Available: %2$s.',
				'woocommerce'
			),
			$product->get_name(),
			wc_format_stock_quantity_for_display(
				max(
					0,
					(float) $available
				),
				$product
			)
		),
		'error'
	);
}


/**
 * Validate complete quantities against a chosen warehouse.
 */
function pepti_fwr_validate_totals(
	$totals,
	$warehouse,
	$show_notices = true
) {

	$valid = true;

	foreach (
		(array) $totals
		as $entry
	) {

		$product =
			$entry['product']
			?? null;

		$quantity =
			(float)
			(
				$entry['quantity']
				?? 0
			);

		if (
			!$product
			instanceof WC_Product
			|| !pepti_fwr_product_uses_stock(
				$product
			)
			|| $quantity <= 0
			|| $product
				->backorders_allowed()
		) {
			continue;
		}

		$available =
			pepti_fwr_stock(
				$product,
				$warehouse
			);

		if (
			$quantity
			<= $available
			+ 0.00001
		) {
			continue;
		}

		$valid = false;

		if ($show_notices) {

			pepti_fwr_stock_notice(
				$product,
				$available
			);
		}
	}

	return $valid;
}


/* ==========================================================================
   ADD-TO-CART VALIDATION
   ========================================================================== */

/**
 * Validate prospective complete cart.
 *
 * This fixes v1.0.1 behaviour where Europe could reject an item
 * before the router had the opportunity to test whether HQ could
 * fulfil the complete cart.
 */
add_filter(
	'woocommerce_add_to_cart_validation',
	function(
		$passed,
		$product_id,
		$quantity,
		$variation_id = 0,
		$variations = []
	) {

		if (
			!$passed
			|| !pepti_fwr_active()
		) {
			return $passed;
		}

		$product =
			wc_get_product(
				$variation_id > 0
					? $variation_id
					: $product_id
			);

		if (
			!$product
			instanceof WC_Product
			|| !pepti_fwr_product_uses_stock(
				$product
			)
			|| $product
				->backorders_allowed()
		) {
			return $passed;
		}

		$owner_id =
			pepti_fwr_owner_id(
				$product
			);

		if ($owner_id <= 0) {
			return $passed;
		}

		$totals =
			pepti_fwr_cart_stock_totals();

		$existing =
			isset(
				$totals[$owner_id]
			)
				? (float)
					$totals[
						$owner_id
					]['quantity']
				: 0.0;

		$owner_product =
			pepti_fwr_owner_product(
				$product
			);

		$totals[$owner_id] = [
			'product' =>
				$owner_product
				instanceof WC_Product
					? $owner_product
					: $product,

			'quantity' =>
				$existing
				+ max(
					0,
					(float) $quantity
				),
		];

		$country =
			pepti_fwr_customer_country();

		$warehouse =
			pepti_fwr_resolve_warehouse_for_totals(
				$country,
				$totals
			);

		if (
			pepti_fwr_validate_totals(
				$totals,
				$warehouse,
				false
			)
		) {

			if (
				function_exists('WC')
				&& WC()->session
			) {

				WC()->session->set(
					'pepti_fwr_warehouse',
					$warehouse
				);
			}

			return true;
		}

		$available =
			pepti_fwr_stock(
				$product,
				$warehouse
			);

		pepti_fwr_stock_notice(
			$product,
			$available
		);

		return false;
	},
	100,
	5
);


/* ==========================================================================
   CART UPDATE VALIDATION
   ========================================================================== */

add_filter(
	'woocommerce_update_cart_validation',
	function(
		$passed,
		$cart_item_key,
		$values,
		$quantity
	) {

		if (
			!$passed
			|| !pepti_fwr_active()
		) {
			return $passed;
		}

		$product =
			$values['data']
			?? null;

		if (
			!$product
			instanceof WC_Product
			|| !pepti_fwr_product_uses_stock(
				$product
			)
			|| $product
				->backorders_allowed()
		) {
			return $passed;
		}

		$owner_id =
			pepti_fwr_owner_id(
				$product
			);

		if ($owner_id <= 0) {
			return $passed;
		}

		/*
		 * Rebuild totals while replacing the quantity represented
		 * by the cart line being edited.
		 */
		$totals = [];

		if (
			function_exists('WC')
			&& WC()->cart
		) {

			foreach (
				WC()->cart->get_cart()
				as $key =>
				$cart_item
			) {

				$item_product =
					$cart_item['data']
					?? null;

				if (
					!$item_product
					instanceof WC_Product
					|| !pepti_fwr_product_uses_stock(
						$item_product
					)
				) {
					continue;
				}

				$item_owner =
					pepti_fwr_owner_id(
						$item_product
					);

				if (
					$item_owner <= 0
				) {
					continue;
				}

				if (
					!isset(
						$totals[
							$item_owner
						]
					)
				) {

					$owner =
						wc_get_product(
							$item_owner
						);

					$totals[
						$item_owner
					] = [
						'product' =>
							$owner
							instanceof WC_Product
								? $owner
								: $item_product,

						'quantity' =>
							0.0,
					];
				}

				$item_quantity =
					$key === $cart_item_key
						? max(
							0,
							(float)
							$quantity
						)
						: max(
							0,
							(float)
							(
								$cart_item[
									'quantity'
								]
								?? 0
							)
						);

				$totals[
					$item_owner
				]['quantity'] +=
					$item_quantity;
			}
		}

		$warehouse =
			pepti_fwr_resolve_warehouse_for_totals(
				pepti_fwr_customer_country(),
				$totals
			);

		$valid =
			pepti_fwr_validate_totals(
				$totals,
				$warehouse,
				!$passed
					? false
					: true
			);

		if (
			$valid
			&& function_exists('WC')
			&& WC()->session
		) {

			WC()->session->set(
				'pepti_fwr_warehouse',
				$warehouse
			);
		}

		return (
			$passed
			&& $valid
		);
	},
	100,
	4
);


/* ==========================================================================
   CART / CHECKOUT VALIDATION
   ========================================================================== */

function pepti_fwr_validate_cart() {

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

	$totals =
		pepti_fwr_cart_stock_totals();

	$country =
		pepti_fwr_customer_country();

	$warehouse =
		pepti_fwr_resolve_warehouse_for_totals(
			$country,
			$totals
		);

	if (WC()->session) {

		WC()->session->set(
			'pepti_fwr_warehouse',
			$warehouse
		);

		if ($country !== '') {

			WC()->session->set(
				'pepti_fwr_country',
				$country
			);
		}
	}

	pepti_fwr_validate_totals(
		$totals,
		$warehouse,
		true
	);
}


add_action(
	'woocommerce_check_cart_items',
	'pepti_fwr_validate_cart',
	100
);


add_action(
	'woocommerce_after_checkout_validation',
	function(
		$data,
		$errors
	) {

		if (
			!pepti_fwr_active()
		) {
			return;
		}

		$country =
			$data[
				'shipping_country'
			]
			?? $data[
				'billing_country'
			]
			?? '';

		if ($country !== '') {

			pepti_fwr_update_session_route(
				$country
			);
		}

		pepti_fwr_validate_cart();
	},
	100,
	2
);


/* ==========================================================================
   ORDER WAREHOUSE HELPERS
   ========================================================================== */

/**
 * Get immutable fulfilment warehouse from an order.
 */
function pepti_fwr_get_order_warehouse(
	$order_or_id
) {

	$order =
		$order_or_id
		instanceof WC_Order
			? $order_or_id
			: wc_get_order(
				absint(
					$order_or_id
				)
			);

	if (
		!$order
		instanceof WC_Order
	) {
		return '';
	}

	$warehouse =
		sanitize_key(
			(string)
			$order->get_meta(
				PEPTI_FWR_ORDER_WAREHOUSE,
				true
			)
		);

	if (
		$warehouse !== ''
		&& function_exists(
			'pepti_mw_is_valid_warehouse'
		)
		&& pepti_mw_is_valid_warehouse(
			$warehouse
		)
	) {
		return $warehouse;
	}

	return '';
}


/**
 * Fulfilment warehouse name for downstream modules.
 */
function pepti_fwr_get_order_warehouse_name(
	$order_or_id
) {

	$order =
		$order_or_id
		instanceof WC_Order
			? $order_or_id
			: wc_get_order(
				absint(
					$order_or_id
				)
			);

	if (
		!$order
		instanceof WC_Order
	) {
		return '';
	}

	$name =
		sanitize_text_field(
			(string)
			$order->get_meta(
				PEPTI_FWR_ORDER_WAREHOUSE_NAME,
				true
			)
		);

	if ($name !== '') {
		return $name;
	}

	$warehouse =
		pepti_fwr_get_order_warehouse(
			$order
		);

	return $warehouse !== ''
		? pepti_fwr_warehouse_name(
			$warehouse
		)
		: '';
}


/**
 * Printer destination helper for the future warehouse dispatch module.
 */
function pepti_fwr_get_order_printer_email(
	$order_or_id
) {

	$warehouse =
		pepti_fwr_get_order_warehouse(
			$order_or_id
		);

	if (
		$warehouse === ''
		|| !function_exists(
			'pepti_mw_get_printer_email'
		)
	) {
		return '';
	}

	return sanitize_email(
		pepti_mw_get_printer_email(
			$warehouse
		)
	);
}


/* ==========================================================================
   ORDER WAREHOUSE ASSIGNMENT
   ========================================================================== */

/**
 * Assign warehouse once.
 *
 * Once a valid warehouse has been written to an order it becomes
 * authoritative. Later Store API/classic checkout callbacks do not
 * silently change it.
 */
function pepti_fwr_assign_order_warehouse(
	$order,
	$country = ''
) {

	if (
		!pepti_fwr_active()
		|| !$order
			instanceof WC_Order
	) {
		return '';
	}

	$existing =
		pepti_fwr_get_order_warehouse(
			$order
		);

	if ($existing !== '') {

		/*
		 * Repair missing display-name meta without rerouting.
		 */
		$existing_name =
			sanitize_text_field(
				(string)
				$order->get_meta(
					PEPTI_FWR_ORDER_WAREHOUSE_NAME,
					true
				)
			);

		if ($existing_name === '') {

			$order->update_meta_data(
				PEPTI_FWR_ORDER_WAREHOUSE_NAME,
				pepti_fwr_warehouse_name(
					$existing
				)
			);
		}

		return $existing;
	}

	$country =
		strtoupper(
			sanitize_text_field(
				$country
			)
		);

	if ($country === '') {

		$country =
			strtoupper(
				sanitize_text_field(
					$order
						->get_shipping_country()
					?: $order
						->get_billing_country()
					?: pepti_fwr_customer_country()
				)
			);
	}

	$warehouse =
		$country !== ''
			? pepti_fwr_resolve_cart_warehouse(
				$country
			)
			: pepti_fwr_current_warehouse();

	if (
		!pepti_mw_is_valid_warehouse(
			$warehouse
		)
	) {

		$warehouse =
			pepti_fwr_default_warehouse();
	}

	$order->update_meta_data(
		PEPTI_FWR_ORDER_WAREHOUSE,
		$warehouse
	);

	$order->update_meta_data(
		PEPTI_FWR_ORDER_WAREHOUSE_NAME,
		pepti_fwr_warehouse_name(
			$warehouse
		)
	);

	$order->update_meta_data(
		PEPTI_FWR_ORDER_COUNTRY,
		$country
	);

	return $warehouse;
}


/**
 * Classic Checkout.
 */
add_action(
	'woocommerce_checkout_create_order',
	function(
		$order,
		$data
	) {

		if (
			!$order
			instanceof WC_Order
		) {
			return;
		}

		$country =
			$data[
				'shipping_country'
			]
			?? $data[
				'billing_country'
			]
			?? '';

		pepti_fwr_assign_order_warehouse(
			$order,
			$country
		);
	},
	20,
	2
);


/**
 * WooCommerce Checkout Blocks / Store API.
 */
add_action(
	'woocommerce_store_api_checkout_update_order_meta',
	function(
		$order
	) {

		if (
			!$order
			instanceof WC_Order
		) {
			return;
		}

		$country =
			$order
				->get_shipping_country()
			?: $order
				->get_billing_country();

		pepti_fwr_assign_order_warehouse(
			$order,
			$country
		);
	},
	20,
	1
);


/* ==========================================================================
   ORDER ADMIN DISPLAY
   ========================================================================== */

add_action(
	'woocommerce_admin_order_data_after_shipping_address',
	function(
		$order
	) {

		if (
			!$order
			instanceof WC_Order
		) {
			return;
		}

		$warehouse =
			pepti_fwr_get_order_warehouse(
				$order
			);

		if ($warehouse === '') {
			return;
		}

		$name =
			pepti_fwr_get_order_warehouse_name(
				$order
			);

		$country =
			sanitize_text_field(
				(string)
				$order->get_meta(
					PEPTI_FWR_ORDER_COUNTRY,
					true
				)
			);

		$printer =
			pepti_fwr_get_order_printer_email(
				$order
			);

		echo '<div style="
			margin-top:12px;
			padding:10px 12px;
			border:1px solid #dbe5ef;
			border-radius:9px;
			background:#f8fafc;
		">';

		echo '<p style="margin:0 0 5px;">';

		echo '<strong>Fulfilment warehouse:</strong> ';

		echo esc_html(
			$name !== ''
				? $name
				: strtoupper(
					$warehouse
				)
		);

		echo '</p>';

		if ($country !== '') {

			echo '<p style="margin:0 0 5px;">';

			echo '<strong>Routing country:</strong> ';

			echo esc_html(
				$country
			);

			echo '</p>';
		}

		if ($printer !== '') {

			echo '<p style="margin:0;">';

			echo '<strong>Warehouse printer:</strong> ';

			echo esc_html(
				$printer
			);

			echo '</p>';
		}

		echo '</div>';
	}
);


/* ==========================================================================
   STOP NATIVE WOO REDUCTION FOR EU
   ========================================================================== */

/**
 * HQ:
 * WooCommerce native reduction continues normally.
 *
 * EU:
 * Native reduction must be stopped because HQ's native _stock value
 * must not be altered for a European warehouse order.
 */
add_filter(
	'woocommerce_can_reduce_order_stock',
	function(
		$can_reduce,
		$order
	) {

		if (
			!pepti_fwr_active()
			|| !$order
				instanceof WC_Order
		) {
			return $can_reduce;
		}

		$warehouse =
			pepti_fwr_get_order_warehouse(
				$order
			);

		if ($warehouse === 'eu') {
			return false;
		}

		return $can_reduce;
	},
	100,
	2
);


/**
 * Also stop WooCommerce from trying to restore native HQ inventory
 * for an EU order.
 */
add_filter(
	'woocommerce_can_restore_order_stock',
	function(
		$can_restore,
		$order
	) {

		if (
			!pepti_fwr_active()
			|| !$order
				instanceof WC_Order
		) {
			return $can_restore;
		}

		if (
			pepti_fwr_get_order_warehouse(
				$order
			) === 'eu'
		) {
			return false;
		}

		return $can_restore;
	},
	100,
	2
);


/* ==========================================================================
   ORDER REQUIREMENTS
   ========================================================================== */

/**
 * Aggregate an order into stock-owner requirements.
 */
function pepti_fwr_order_requirements(
	$order
) {

	$requirements = [];

	if (
		!$order
		instanceof WC_Order
	) {
		return $requirements;
	}

	foreach (
		$order->get_items(
			'line_item'
		)
		as $item_id =>
		$item
	) {

		if (
			!$item
			instanceof WC_Order_Item_Product
		) {
			continue;
		}

		$product =
			$item->get_product();

		if (
			!$product
			instanceof WC_Product
			|| !pepti_fwr_product_uses_stock(
				$product
			)
		) {
			continue;
		}

		$owner_id =
			pepti_fwr_owner_id(
				$product
			);

		$quantity =
			max(
				0,
				(float)
				$item->get_quantity()
			);

		if (
			$owner_id <= 0
			|| $quantity <= 0
		) {
			continue;
		}

		if (
			!isset(
				$requirements[
					$owner_id
				]
			)
		) {

			$requirements[
				$owner_id
			] = 0.0;
		}

		$requirements[
			$owner_id
		] +=
			$quantity;
	}

	return $requirements;
}


/**
 * Is any custom stock still marked as deducted on this EU order?
 */
function pepti_fwr_order_has_reduced_stock(
	$order
) {

	if (
		!$order
		instanceof WC_Order
	) {
		return false;
	}

	foreach (
		$order->get_items(
			'line_item'
		)
		as $item
	) {

		if (
			!$item
			instanceof WC_Order_Item_Product
		) {
			continue;
		}

		$reduced =
			(float)
			$item->get_meta(
				PEPTI_FWR_ITEM_REDUCED,
				true
			);

		if ($reduced > 0.00001) {
			return true;
		}
	}

	return false;
}


/* ==========================================================================
   RETRY SCHEDULER
   ========================================================================== */

function pepti_fwr_schedule_reduction_retry(
	$order_id
) {

	$order_id =
		absint(
			$order_id
		);

	if ($order_id <= 0) {
		return;
	}

	if (
		wp_next_scheduled(
			PEPTI_FWR_RETRY_HOOK,
			[$order_id]
		)
	) {
		return;
	}

	wp_schedule_single_event(
		time() + 15,
		PEPTI_FWR_RETRY_HOOK,
		[$order_id]
	);
}


add_action(
	PEPTI_FWR_RETRY_HOOK,
	'pepti_fwr_reduce_order_stock',
	10,
	1
);


/* ==========================================================================
   EU ORDER STOCK DEDUCTION
   ========================================================================== */

/**
 * Deduct an EU order from custom Europe inventory.
 *
 * Concurrency strategy:
 * 1. Acquire global EU warehouse advisory lock.
 * 2. Reload order.
 * 3. Recheck idempotency meta while holding lock.
 * 4. Validate every required product.
 * 5. Apply all changes.
 * 6. Roll back completed changes if any write fails.
 * 7. Mark individual order lines as reduced.
 * 8. Mark order deduction complete.
 */
function pepti_fwr_reduce_order_stock(
	$order_id
) {

	if (!pepti_fwr_active()) {
		return;
	}

	$order_id =
		absint(
			$order_id
		);

	if ($order_id <= 0) {
		return;
	}

	$order =
		wc_get_order(
			$order_id
		);

	if (
		!$order
		instanceof WC_Order
	) {
		return;
	}

	/*
	 * Old/manual orders may not yet have warehouse meta.
	 */
	$warehouse =
		pepti_fwr_get_order_warehouse(
			$order
		);

	if ($warehouse === '') {

		pepti_fwr_assign_order_warehouse(
			$order
		);

		$order->save();

		$warehouse =
			pepti_fwr_get_order_warehouse(
				$order
			);
	}

	if ($warehouse !== 'eu') {
		return;
	}

	/*
	 * Quick idempotency test before waiting for the lock.
	 */
	if (
		$order->get_meta(
			PEPTI_FWR_ORDER_DEDUCTED,
			true
		) === 'yes'
	) {
		return;
	}

	$locked =
		pepti_fwr_acquire_stock_lock();

	if (!$locked) {

		pepti_fwr_schedule_reduction_retry(
			$order_id
		);

		return;
	}

	try {

		/*
		 * Reload after acquiring the stock mutex.
		 * Another callback may have completed the reduction
		 * while this request was waiting.
		 */
		$order =
			wc_get_order(
				$order_id
			);

		if (
			!$order
			instanceof WC_Order
		) {
			return;
		}

		if (
			pepti_fwr_get_order_warehouse(
				$order
			) !== 'eu'
		) {
			return;
		}

		if (
			$order->get_meta(
				PEPTI_FWR_ORDER_DEDUCTED,
				true
			) === 'yes'
		) {
			return;
		}

		$requirements =
			pepti_fwr_order_requirements(
				$order
			);

		if (!$requirements) {

			$order->update_meta_data(
				PEPTI_FWR_ORDER_DEDUCTED,
				'yes'
			);

			$order->update_meta_data(
				PEPTI_FWR_ORDER_DEDUCTION_TIME,
				current_time(
					'mysql'
				)
			);

			$order->save();

			return;
		}

		/*
		 * Validate complete order before making any writes.
		 */
		foreach (
			$requirements
			as $product_id =>
			$quantity
		) {

			$available =
				(float)
				pepti_mw_get_stock(
					'eu',
					$product_id
				);

			if (
				$available
				+ 0.00001
				< $quantity
			) {

				$order->add_order_note(
					sprintf(
						'EU stock deduction stopped: product #%d requires %s but Europe has only %s available.',
						$product_id,
						pepti_fwr_qty(
							$quantity
						),
						pepti_fwr_qty(
							$available
						)
					)
				);

				return;
			}
		}

		$changes = [];

		foreach (
			$requirements
			as $product_id =>
			$quantity
		) {

			$before =
				(float)
				pepti_mw_get_stock(
					'eu',
					$product_id
				);

			$after =
				$before
				- $quantity;

			$result =
				pepti_mw_set_stock(
					'eu',
					$product_id,
					$after,
					[
						'movement_type' =>
							'order_fulfilment',

						'reference_type' =>
							'woocommerce_order',

						'reference_id' =>
							$order->get_id(),

						'note' =>
							sprintf(
								'Order #%s allocated to %s.',
								$order
									->get_order_number(),
								pepti_fwr_warehouse_name(
									'eu'
								)
							),

						'log_movement' =>
							true,
					]
				);

			if (
				is_wp_error(
					$result
				)
				|| $result === false
				|| $result === null
			) {

				foreach (
					array_reverse(
						$changes
					)
					as $rollback
				) {

					pepti_mw_set_stock(
						'eu',
						$rollback[
							'product_id'
						],
						$rollback[
							'before'
						],
						[
							'movement_type' =>
								'order_stock_rollback',

							'reference_type' =>
								'woocommerce_order',

							'reference_id' =>
								$order->get_id(),

							'note' =>
								'Automatic rollback after failed EU order deduction.',

							'log_movement' =>
								true,
						]
					);
				}

				$order->add_order_note(
					'EU warehouse stock deduction failed and completed warehouse changes were rolled back.'
				);

				return;
			}

			$changes[] = [
				'product_id' =>
					$product_id,

				'before' =>
					$before,
			];
		}

		/*
		 * Store the reduced quantity against every order line.
		 *
		 * This is intentionally line based, while actual stock
		 * deduction above is owner-ID aggregated.
		 */
		foreach (
			$order->get_items(
				'line_item'
			)
			as $item
		) {

			if (
				!$item
				instanceof WC_Order_Item_Product
			) {
				continue;
			}

			$product =
				$item->get_product();

			if (
				!$product
				instanceof WC_Product
				|| !pepti_fwr_product_uses_stock(
					$product
				)
			) {
				continue;
			}

			$item->update_meta_data(
				PEPTI_FWR_ITEM_REDUCED,
				max(
					0,
					(float)
					$item->get_quantity()
				)
			);

			$item->save();
		}

		$order->update_meta_data(
			PEPTI_FWR_ORDER_DEDUCTED,
			'yes'
		);

		$order->update_meta_data(
			PEPTI_FWR_ORDER_DEDUCTION_TIME,
			current_time(
				'mysql'
			)
		);

		$order->delete_meta_data(
			PEPTI_FWR_ORDER_RESTORED
		);

		$order->save();

		$order->add_order_note(
			'Warehouse stock deducted from '
			. pepti_fwr_warehouse_name(
				'eu'
			)
			. '.'
		);

	} finally {

		pepti_fwr_release_stock_lock();
	}
}


/**
 * Payment / status triggers.
 *
 * They may all fire.
 * Idempotency protection means only the first valid execution deducts.
 */
add_action(
	'woocommerce_payment_complete',
	'pepti_fwr_reduce_order_stock',
	20
);

add_action(
	'woocommerce_order_status_processing',
	'pepti_fwr_reduce_order_stock',
	20
);

add_action(
	'woocommerce_order_status_on-hold',
	'pepti_fwr_reduce_order_stock',
	20
);

add_action(
	'woocommerce_order_status_completed',
	'pepti_fwr_reduce_order_stock',
	20
);


/* ==========================================================================
   EU STOCK RESTORATION
   ========================================================================== */

/**
 * Restore quantity from an individual EU order item.
 *
 * Never restores more than that line still has marked as reduced.
 */
function pepti_fwr_restore_item_quantity(
	$order,
	$item,
	$quantity,
	$reason
) {

	if (
		!pepti_fwr_active()
		|| !$order
			instanceof WC_Order
		|| !$item
			instanceof WC_Order_Item_Product
	) {
		return false;
	}

	if (
		pepti_fwr_get_order_warehouse(
			$order
		) !== 'eu'
	) {
		return false;
	}

	$product =
		$item->get_product();

	if (
		!$product
		instanceof WC_Product
		|| !pepti_fwr_product_uses_stock(
			$product
		)
	) {
		return false;
	}

	$owner_id =
		pepti_fwr_owner_id(
			$product
		);

	if ($owner_id <= 0) {
		return false;
	}

	$requested =
		max(
			0,
			(float) $quantity
		);

	if ($requested <= 0) {
		return false;
	}

	$reduced =
		max(
			0,
			(float)
			$item->get_meta(
				PEPTI_FWR_ITEM_REDUCED,
				true
			)
		);

	$restore =
		min(
			$requested,
			$reduced
		);

	if ($restore <= 0) {
		return false;
	}

	$locked =
		pepti_fwr_acquire_stock_lock();

	if (!$locked) {
		return false;
	}

	try {

		/*
		 * Re-read the order/item after obtaining lock.
		 */
		$fresh_order =
			wc_get_order(
				$order->get_id()
			);

		if (
			!$fresh_order
			instanceof WC_Order
		) {
			return false;
		}

		$fresh_item =
			$fresh_order->get_item(
				$item->get_id()
			);

		if (
			!$fresh_item
			instanceof WC_Order_Item_Product
		) {
			return false;
		}

		$fresh_reduced =
			max(
				0,
				(float)
				$fresh_item->get_meta(
					PEPTI_FWR_ITEM_REDUCED,
					true
				)
			);

		$restore =
			min(
				$requested,
				$fresh_reduced
			);

		if ($restore <= 0) {
			return false;
		}

		$fresh_product =
			$fresh_item->get_product();

		if (
			!$fresh_product
			instanceof WC_Product
		) {
			return false;
		}

		$fresh_owner =
			pepti_fwr_owner_id(
				$fresh_product
			);

		if ($fresh_owner <= 0) {
			return false;
		}

		$before =
			(float)
			pepti_mw_get_stock(
				'eu',
				$fresh_owner
			);

		$result =
			pepti_mw_set_stock(
				'eu',
				$fresh_owner,
				$before + $restore,
				[
					'movement_type' =>
						'order_stock_restored',

					'reference_type' =>
						'woocommerce_order',

					'reference_id' =>
						$fresh_order
							->get_id(),

					'note' =>
						sprintf(
							'Order #%s: %s.',
							$fresh_order
								->get_order_number(),
							sanitize_text_field(
								$reason
							)
						),

					'log_movement' =>
						true,
				]
			);

		if (
			is_wp_error(
				$result
			)
			|| $result === false
			|| $result === null
		) {
			return false;
		}

		$fresh_item->update_meta_data(
			PEPTI_FWR_ITEM_REDUCED,
			max(
				0,
				$fresh_reduced
				- $restore
			)
		);

		$fresh_item->save();

		return true;

	} finally {

		pepti_fwr_release_stock_lock();
	}
}


/**
 * Restore every still-outstanding reduced line.
 *
 * Used for failed/cancelled orders.
 */
function pepti_fwr_restore_remaining_order_stock(
	$order_id
) {

	if (!pepti_fwr_active()) {
		return;
	}

	$order =
		wc_get_order(
			absint(
				$order_id
			)
		);

	if (
		!$order
		instanceof WC_Order
	) {
		return;
	}

	if (
		pepti_fwr_get_order_warehouse(
			$order
		) !== 'eu'
	) {
		return;
	}

	if (
		$order->get_meta(
			PEPTI_FWR_ORDER_DEDUCTED,
			true
		) !== 'yes'
	) {
		return;
	}

	$restored_any =
		false;

	foreach (
		$order->get_items(
			'line_item'
		)
		as $item
	) {

		if (
			!$item
			instanceof WC_Order_Item_Product
		) {
			continue;
		}

		$remaining =
			max(
				0,
				(float)
				$item->get_meta(
					PEPTI_FWR_ITEM_REDUCED,
					true
				)
			);

		if ($remaining <= 0) {
			continue;
		}

		if (
			pepti_fwr_restore_item_quantity(
				$order,
				$item,
				$remaining,
				'remaining stock restored after order status change'
			)
		) {

			$restored_any =
				true;
		}
	}

	if (!$restored_any) {
		return;
	}

	/*
	 * Reload so item meta reflects restorations.
	 */
	$order =
		wc_get_order(
			$order->get_id()
		);

	if (
		!$order
		instanceof WC_Order
	) {
		return;
	}

	$order->update_meta_data(
		PEPTI_FWR_ORDER_RESTORED,
		'yes'
	);

	$order->save();

	$order->add_order_note(
		'Remaining warehouse stock restored to '
		. pepti_fwr_warehouse_name(
			'eu'
		)
		. '.'
	);
}


/**
 * Cancellation restores EU stock.
 */
add_action(
	'woocommerce_order_status_cancelled',
	'pepti_fwr_restore_remaining_order_stock',
	20
);


/**
 * Failed payment/status restores EU stock.
 */
add_action(
	'woocommerce_order_status_failed',
	'pepti_fwr_restore_remaining_order_stock',
	20
);


/* ==========================================================================
   FAILED PAYMENT RETRY SUPPORT
   ========================================================================== */

/**
 * If a failed order had its EU stock fully restored and payment
 * subsequently succeeds/retries, permit a fresh deduction.
 */
function pepti_fwr_prepare_failed_order_retry(
	$order_id
) {

	if (!pepti_fwr_active()) {
		return;
	}

	$order =
		wc_get_order(
			absint(
				$order_id
			)
		);

	if (
		!$order
		instanceof WC_Order
		|| pepti_fwr_get_order_warehouse(
			$order
		) !== 'eu'
	) {
		return;
	}

	if (
		pepti_fwr_order_has_reduced_stock(
			$order
		)
	) {
		return;
	}

	if (
		$order->get_meta(
			PEPTI_FWR_ORDER_RESTORED,
			true
		) !== 'yes'
	) {
		return;
	}

	$order->delete_meta_data(
		PEPTI_FWR_ORDER_DEDUCTED
	);

	$order->delete_meta_data(
		PEPTI_FWR_ORDER_RESTORED
	);

	$order->delete_meta_data(
		PEPTI_FWR_ORDER_DEDUCTION_TIME
	);

	$order->save();

	$order->add_order_note(
		'Warehouse stock state reopened for successful payment retry.'
	);
}


/*
 * Priority 5 runs before the normal destination status
 * deduction hook at priority 20.
 */
add_action(
	'woocommerce_order_status_failed_to_processing',
	'pepti_fwr_prepare_failed_order_retry',
	5
);

add_action(
	'woocommerce_order_status_failed_to_on-hold',
	'pepti_fwr_prepare_failed_order_retry',
	5
);

add_action(
	'woocommerce_order_status_failed_to_completed',
	'pepti_fwr_prepare_failed_order_retry',
	5
);


/* ==========================================================================
   PARTIAL / FULL REFUND RESTORATION
   ========================================================================== */

/**
 * Refund-created handler.
 *
 * IMPORTANT:
 * Only restore custom EU stock when WooCommerce's refund itself
 * says stock should be restocked.
 *
 * This avoids v1.0.1 behaviour where a monetary refund could add
 * stock back even when "Restock refunded items" was deliberately off.
 */
add_action(
	'woocommerce_refund_created',
	function(
		$refund_id,
		$args
	) {

		if (!pepti_fwr_active()) {
			return;
		}

		/*
		 * Respect WooCommerce refund restock choice.
		 */
		$restock_items =
			is_array($args)
			&& !empty(
				$args[
					'restock_items'
				]
			);

		if (!$restock_items) {
			return;
		}

		$refund =
			wc_get_order(
				absint(
					$refund_id
				)
			);

		if (
			!$refund
			instanceof WC_Order_Refund
		) {
			return;
		}

		$order =
			wc_get_order(
				$refund
					->get_parent_id()
			);

		if (
			!$order
			instanceof WC_Order
			|| pepti_fwr_get_order_warehouse(
				$order
			) !== 'eu'
		) {
			return;
		}

		if (
			$order->get_meta(
				PEPTI_FWR_ORDER_DEDUCTED,
				true
			) !== 'yes'
		) {
			return;
		}

		$restored_any =
			false;

		foreach (
			$refund->get_items(
				'line_item'
			)
			as $refund_item
		) {

			if (
				!$refund_item
				instanceof WC_Order_Item_Product
			) {
				continue;
			}

			$original_item_id =
				absint(
					$refund_item->get_meta(
						'_refunded_item_id',
						true
					)
				);

			if (
				$original_item_id <= 0
			) {
				continue;
			}

			$original_item =
				$order->get_item(
					$original_item_id
				);

			if (
				!$original_item
				instanceof WC_Order_Item_Product
			) {
				continue;
			}

			$quantity =
				abs(
					(float)
					$refund_item
						->get_quantity()
				);

			if ($quantity <= 0) {
				continue;
			}

			if (
				pepti_fwr_restore_item_quantity(
					$order,
					$original_item,
					$quantity,
					'refund stock restoration'
				)
			) {

				$restored_any =
					true;
			}
		}

		if ($restored_any) {

			$order =
				wc_get_order(
					$order->get_id()
				);

			if (
				$order
				instanceof WC_Order
			) {

				$order->add_order_note(
					'Refunded item stock restored to '
					. pepti_fwr_warehouse_name(
						'eu'
					)
					. '.'
				);
			}
		}
	},
	20,
	2
);


/* ==========================================================================
   CUSTOMER WAREHOUSE NOTICE
   ========================================================================== */

add_action(
	'woocommerce_single_product_summary',
	function() {

		if (
			!pepti_fwr_active()
			|| !pepti_fwr_show_customer_notice()
		) {
			return;
		}

		global $product;

		if (
			$product
			instanceof WC_Product
			&& !pepti_fwr_product_uses_stock(
				$product
			)
		) {
			return;
		}

		$warehouse =
			pepti_fwr_current_warehouse();

		if ($warehouse === 'eu') {

			echo '<p class="pepti-fwr-origin" style="
				margin-top:8px;
				font-size:13px;
			">';

			echo esc_html__(
				'Ships from our European distribution centre.',
				'woocommerce'
			);

			echo '</p>';

		} elseif (
			$warehouse === 'hq'
		) {

			echo '<p class="pepti-fwr-origin" style="
				margin-top:8px;
				font-size:13px;
			">';

			echo esc_html__(
				'Ships from our main distribution centre.',
				'woocommerce'
			);

			echo '</p>';
		}
	},
	22
);


/* ==========================================================================
   FRONTEND SESSION / CHECKOUT REFRESH
   ========================================================================== */

add_action(
	'wp_enqueue_scripts',
	function() {

		if (!pepti_fwr_active()) {
			return;
		}

		wp_register_script(
			'pepti-fwr-refresh',
			'',
			['jquery'],
			PEPTI_FWR_VERSION,
			true
		);

		wp_enqueue_script(
			'pepti-fwr-refresh'
		);

		wp_add_inline_script(
			'pepti-fwr-refresh',
			"
			jQuery(function($){

				let peptiFwrRefreshing = false;

				$(document.body).on(
					'country_to_state_changed',
					function(){

						if (peptiFwrRefreshing) {
							return;
						}

						peptiFwrRefreshing = true;

						$(document.body).trigger(
							'update_checkout'
						);

						window.setTimeout(
							function(){
								peptiFwrRefreshing = false;
							},
							250
						);
					}
				);

				$(document.body).on(
					'updated_checkout',
					function(){

						$(document.body).trigger(
							'wc_fragment_refresh'
						);
					}
				);

			});
			"
		);
	}
);


/* ==========================================================================
   INTEGRATION FILTERS
   ========================================================================== */

/**
 * Allows future modules to retrieve the warehouse for an order.
 */
add_filter(
	'pepti_fwr_order_warehouse',
	function(
		$warehouse,
		$order_or_id
	) {

		$resolved =
			pepti_fwr_get_order_warehouse(
				$order_or_id
			);

		return $resolved !== ''
			? $resolved
			: $warehouse;
	},
	10,
	2
);


/**
 * Allows future dispatch modules to retrieve the printer destination.
 */
add_filter(
	'pepti_fwr_order_printer_email',
	function(
		$email,
		$order_or_id
	) {

		$resolved =
			pepti_fwr_get_order_printer_email(
				$order_or_id
			);

		return $resolved !== ''
			? $resolved
			: $email;
	},
	10,
	2
);