/* global wp, turnstile, jQuery, wc, turnstileWooCheckoutCallback */
/**
 * WooCommerce checkout Turnstile lifecycle.
 *
 * Classic: keeps the widget alive across updated_checkout / checkout_error and
 * always re-renders with data-action so siteverify action checks pass.
 * Blocks: pushes the token into Store API extension data (forms are not POSTed).
 *
 * interaction-only: widget stays hidden until Cloudflare needs a click. Place-order
 * must wait for the background token (or a visible challenge) instead of failing immediately.
 */
(function () {
	var WIDGET_ID = 'cf-turnstile-woo-checkout';
	var INIT_ATTR = 'data-wpps-init';
	var WIDGET_ATTR = 'data-wpps-widget-id';
	var readyTimer = null;
	var lastToken = '';
	var tokenWaiters = [];

	function ns() {
		return typeof wpPsTurnstileWoo !== 'undefined' && wpPsTurnstileWoo.ns
			? wpPsTurnstileWoo.ns
			: 'wp-powersuite-turnstile';
	}

	function cfg() {
		return typeof wpPsTurnstileWoo !== 'undefined' ? wpPsTurnstileWoo : {};
	}

	function hasTurnstileApi() {
		return typeof turnstile !== 'undefined' && typeof turnstile.render === 'function';
	}

	function widgetEl() {
		return document.getElementById(WIDGET_ID);
	}

	function missingTokenMessage() {
		return (
			(cfg().missingTokenMessage && String(cfg().missingTokenMessage)) ||
			'Please complete the security verification to complete your order.'
		);
	}

	function setTurnstileExtensionData(token) {
		lastToken = token || '';
		if (typeof wp === 'undefined' || !wp.data) {
			return;
		}
		var dispatch = wp.data.dispatch('wc/store/checkout');
		if (!dispatch) {
			return;
		}
		var payload = { token: token || '' };
		if (typeof dispatch.setExtensionData === 'function') {
			dispatch.setExtensionData(ns(), payload);
		} else if (typeof dispatch.__internalSetExtensionData === 'function') {
			dispatch.__internalSetExtensionData(ns(), payload);
		}
	}

	function notifyTokenWaiters(token) {
		if (!tokenWaiters.length) {
			return;
		}
		var list = tokenWaiters.slice();
		tokenWaiters = [];
		list.forEach(function (fn) {
			try {
				fn(token || '');
			} catch (e) {}
		});
	}

	function unlockSubmit() {
		if (typeof turnstileWooCheckoutCallback === 'function') {
			try {
				turnstileWooCheckoutCallback();
			} catch (e) {}
		}
	}

	function readTokenFromDom(el) {
		if (lastToken) {
			return lastToken;
		}
		if (!el || !hasTurnstileApi()) {
			return '';
		}
		var wid = el.getAttribute(WIDGET_ATTR);
		try {
			if (wid && typeof turnstile.getResponse === 'function') {
				var fromId = turnstile.getResponse(wid);
				if (fromId) {
					return String(fromId);
				}
			}
		} catch (e) {}
		try {
			if (typeof turnstile.getResponse === 'function') {
				var fromEl = turnstile.getResponse(el);
				if (fromEl) {
					return String(fromEl);
				}
			}
		} catch (e2) {}
		var input =
			(el.closest('form') && el.closest('form').querySelector('input[name="cf-turnstile-response"]')) ||
			document.querySelector('#' + WIDGET_ID + ' ~ input[name="cf-turnstile-response"], input[name="cf-turnstile-response"]');
		return input && input.value ? String(input.value) : '';
	}

	function renderOptsFor(el, appearanceOverride) {
		var c = cfg();
		var sitekey = el.getAttribute('data-sitekey') || c.siteKey || '';
		if (!sitekey) {
			return null;
		}
		var action = el.getAttribute('data-action') || c.action || 'woo_checkout';
		var appearance = appearanceOverride || el.getAttribute('data-appearance') || c.appearance || 'interaction-only';
		var opts = {
			sitekey: sitekey,
			theme: el.getAttribute('data-theme') || c.theme || 'auto',
			size: el.getAttribute('data-size') || c.apiSize || 'normal',
			appearance: appearance,
			action: action,
			retry: 'auto',
			'refresh-expired': 'auto',
			callback: function (token) {
				setTurnstileExtensionData(token || '');
				unlockSubmit();
				notifyTokenWaiters(token || '');
			},
			'expired-callback': function () {
				setTurnstileExtensionData('');
			},
			'error-callback': function () {
				setTurnstileExtensionData('');
			},
		};
		var lang = el.getAttribute('data-language') || c.language || '';
		if (lang) {
			opts.language = lang;
		}
		var refreshTimeout = el.getAttribute('data-refresh-timeout') || c.refreshTimeout || '';
		if (refreshTimeout) {
			opts['refresh-timeout'] = refreshTimeout;
		}
		return opts;
	}

	function markRendered(el, widgetId) {
		el.setAttribute(INIT_ATTR, 'true');
		if (widgetId !== undefined && widgetId !== null && widgetId !== '') {
			el.setAttribute(WIDGET_ATTR, String(widgetId));
		}
	}

	function clearRendered(el) {
		if (!el) {
			return;
		}
		el.removeAttribute(INIT_ATTR);
		el.removeAttribute(WIDGET_ATTR);
	}

	function renderCheckoutWidget(force, appearanceOverride) {
		if (!hasTurnstileApi()) {
			return false;
		}
		var el = widgetEl();
		if (!el) {
			return false;
		}
		if (!force && !appearanceOverride && el.getAttribute(INIT_ATTR) === 'true' && el.getAttribute(WIDGET_ATTR)) {
			return true;
		}
		var opts = renderOptsFor(el, appearanceOverride);
		if (!opts) {
			return false;
		}

		var existingId = el.getAttribute(WIDGET_ATTR);
		if (existingId) {
			try {
				turnstile.remove(existingId);
			} catch (e) {}
			clearRendered(el);
		} else {
			try {
				turnstile.remove(el);
			} catch (e2) {}
			clearRendered(el);
		}

		try {
			var id = turnstile.render(el, opts);
			markRendered(el, id);
			if (appearanceOverride) {
				el.setAttribute('data-appearance', appearanceOverride);
			}
			return true;
		} catch (e3) {
			try {
				var id2 = turnstile.render('#' + WIDGET_ID, opts);
				markRendered(el, id2);
				if (appearanceOverride) {
					el.setAttribute('data-appearance', appearanceOverride);
				}
				return true;
			} catch (e4) {
				return false;
			}
		}
	}

	function resetCheckoutWidget() {
		if (!hasTurnstileApi()) {
			return;
		}
		var el = widgetEl();
		if (!el) {
			return;
		}
		lastToken = '';
		setTurnstileExtensionData('');
		var wid = el.getAttribute(WIDGET_ATTR);
		if (wid && typeof turnstile.reset === 'function') {
			try {
				turnstile.reset(wid);
				return;
			} catch (e) {}
		}
		renderCheckoutWidget(true);
	}

	function whenTurnstileReady(cb, onTimeout) {
		if (hasTurnstileApi()) {
			cb();
			return;
		}
		var attempts = 0;
		var timer = setInterval(function () {
			attempts += 1;
			if (hasTurnstileApi()) {
				clearInterval(timer);
				if (readyTimer === timer) {
					readyTimer = null;
				}
				cb();
			} else if (attempts >= 100) {
				clearInterval(timer);
				if (readyTimer === timer) {
					readyTimer = null;
				}
				if (typeof onTimeout === 'function') {
					onTimeout();
				}
			}
		}, 100);
		readyTimer = timer;
	}

	function syncExtensionTokenFromWidget() {
		var el = widgetEl();
		var token = readTokenFromDom(el);
		if (token) {
			setTurnstileExtensionData(token);
		}
		return token;
	}

	/**
	 * Wait for a Turnstile token. interaction-only often finishes in the background
	 * a moment after render; failing immediately produces a false "verification" error
	 * with no visible widget.
	 */
	function waitForCheckoutToken(timeoutMs) {
		return new Promise(function (resolve) {
			var existing = syncExtensionTokenFromWidget();
			if (existing) {
				resolve(existing);
				return;
			}

			var settled = false;
			var started = Date.now();

			function finish(token) {
				if (settled) {
					return;
				}
				settled = true;
				resolve(token || '');
			}

			tokenWaiters.push(finish);

			var poll = setInterval(function () {
				var tok = syncExtensionTokenFromWidget();
				if (tok) {
					clearInterval(poll);
					finish(tok);
					return;
				}
				if (Date.now() - started >= timeoutMs) {
					clearInterval(poll);
					finish('');
				}
			}, 150);
		});
	}

	function revealWidgetForInteraction() {
		// If the invisible challenge never produced a token, force a visible widget
		// so the shopper can complete verification instead of looping on a blank error.
		renderCheckoutWidget(true, 'always');
	}

	function obtainCheckoutToken() {
		return new Promise(function (resolve) {
			whenTurnstileReady(
				function () {
					if (!widgetEl()) {
						resolve('');
						return;
					}
					renderCheckoutWidget(false);

					// Kick a deferred challenge if the site uses execution=execute.
					try {
						var el = widgetEl();
						var wid = el && el.getAttribute(WIDGET_ATTR);
						if (wid && typeof turnstile.execute === 'function') {
							turnstile.execute(wid);
						}
					} catch (e) {}

					waitForCheckoutToken(10000).then(function (token) {
						if (token) {
							resolve(token);
							return;
						}
						// Still empty: show an always-visible challenge and wait a bit more.
						revealWidgetForInteraction();
						waitForCheckoutToken(15000).then(resolve);
					});
				},
				function () {
					resolve('');
				}
			);
		});
	}

	function registerBlocksValidation() {
		if (typeof wc === 'undefined' || !wc.blocksCheckoutEvents || typeof wc.blocksCheckoutEvents.onCheckoutValidation !== 'function') {
			return;
		}
		wc.blocksCheckoutEvents.onCheckoutValidation(function () {
			return obtainCheckoutToken().then(function (token) {
				if (!widgetEl()) {
					return true;
				}
				if (!token) {
					revealWidgetForInteraction();
					return {
						errorMessage: missingTokenMessage(),
					};
				}
				setTurnstileExtensionData(token);
				return true;
			});
		}, 5);
	}

	function registerClassicPlaceOrderGate() {
		if (typeof jQuery === 'undefined') {
			return;
		}
		jQuery(document.body).on('checkout_place_order', function () {
			if (!widgetEl()) {
				return true;
			}
			var existing = syncExtensionTokenFromWidget();
			if (existing) {
				return true;
			}
			// Block this submit; obtain token, then re-trigger place order.
			var $form = jQuery('form.checkout');
			if ($form.data('wppsTurnstileSubmitting')) {
				return true;
			}
			$form.data('wppsTurnstileSubmitting', true);
			obtainCheckoutToken().then(function (token) {
				$form.data('wppsTurnstileSubmitting', false);
				if (!token) {
					revealWidgetForInteraction();
					var msg = missingTokenMessage();
					jQuery('.woocommerce-NoticeGroup-checkout').remove();
					$form.prepend(
						'<div class="woocommerce-NoticeGroup woocommerce-NoticeGroup-checkout"><ul class="woocommerce-error" role="alert"><li>' +
							jQuery('<div>').text(msg).html() +
							'</li></ul></div>'
					);
					jQuery(document.body).trigger('checkout_error');
					var top = $form.offset() ? $form.offset().top - 100 : 0;
					jQuery('html, body').animate({ scrollTop: top }, 300);
					return;
				}
				setTurnstileExtensionData(token);
				var input =
					document.querySelector('form.checkout input[name="cf-turnstile-response"]') ||
					document.querySelector('input[name="cf-turnstile-response"]');
				if (input) {
					input.value = token;
				}
				$form.trigger('submit');
			});
			return false;
		});
	}

	function bootBlocks() {
		if (typeof wp === 'undefined' || !wp.data) {
			return;
		}
		registerBlocksValidation();
		wp.data.subscribe(function () {
			var el = widgetEl();
			if (!el) {
				return;
			}
			if (el.getAttribute(INIT_ATTR) !== 'true' || !el.getAttribute(WIDGET_ATTR)) {
				renderCheckoutWidget(false);
			}
		}, 'wc/store/cart');
		renderCheckoutWidget(false);
	}

	jQuery(document.body).on('checkout_error', function () {
		whenTurnstileReady(function () {
			setTimeout(function () {
				lastToken = '';
				resetCheckoutWidget();
			}, 400);
		});
	});

	jQuery(document.body).on('updated_checkout', function () {
		whenTurnstileReady(function () {
			var el = widgetEl();
			if (!el || el.getAttribute(INIT_ATTR) !== 'true' || !el.innerHTML || !el.innerHTML.trim()) {
				setTimeout(function () {
					lastToken = '';
					renderCheckoutWidget(true);
				}, 250);
			}
		});
	});

	function boot() {
		whenTurnstileReady(function () {
			renderCheckoutWidget(false);
			bootBlocks();
			registerClassicPlaceOrderGate();
		});
	}

	if (document.readyState === 'loading') {
		document.addEventListener('DOMContentLoaded', boot);
	} else {
		boot();
	}
})();
