/* global turnstile, jQuery */
/**
 * Refresh Turnstile widgets after AJAX form submissions.
 *
 * Turnstile tokens are single-use: once a submission is verified server-side the
 * token is redeemed, but AJAX forms stay on the page with the stale widget. The
 * next attempt then posts a dead token and fails verification even though the
 * visitor did everything right ("works the first time, fails after that").
 *
 * Listens for each supported form plugin's post-submission signal and resets the
 * widget(s) inside the affected form. Gravity Forms replaces the form HTML
 * entirely on AJAX validation errors, so its fresh container is rendered rather
 * than reset (Turnstile's implicit scan only runs once at page load).
 *
 * The WooCommerce checkout widget (`wpps-turnstile-explicit`) is excluded —
 * woo-checkout.js owns that lifecycle.
 */
(function () {
	function widgetsWithin(root) {
		if (!root || typeof root.querySelectorAll !== 'function') {
			root = document;
		}
		return Array.prototype.filter.call(
			root.querySelectorAll('.cf-turnstile.wpps-turnstile-widget'),
			function (el) {
				return !el.classList.contains('wpps-turnstile-explicit');
			}
		);
	}

	/**
	 * Clear disable-submit unlock styles so the stylesheet lock applies again
	 * until the visitor solves the fresh challenge.
	 */
	function relockSubmits(root) {
		var form = root;
		if (root && root.nodeType === 1 && root.tagName !== 'FORM') {
			form = root.closest ? root.closest('form') : null;
		}
		if (!form || typeof form.querySelectorAll !== 'function') {
			return;
		}
		form.querySelectorAll('button, input[type="submit"], .wpps-bf-submit').forEach(function (el) {
			el.style.removeProperty('pointer-events');
			el.style.removeProperty('opacity');
		});
	}

	function refresh(root) {
		if (typeof turnstile === 'undefined' || typeof turnstile.render !== 'function') {
			return;
		}
		widgetsWithin(root).forEach(function (el) {
			try {
				if (el.querySelector('iframe, input[name="cf-turnstile-response"]')) {
					turnstile.reset(el);
				} else {
					// Never rendered (e.g. Gravity Forms swapped in fresh markup after
					// the implicit scan already ran). Options come from data-* attributes.
					turnstile.render(el);
				}
			} catch (e) {
				try {
					turnstile.remove(el);
					turnstile.render(el);
				} catch (e2) {}
			}
		});
		relockSubmits(root);
	}

	// Basic Forms fires both a jQuery event and a native CustomEvent — collapse duplicates.
	var lastRefreshAt = 0;
	var lastRefreshRoot = null;
	function refreshOnce(root) {
		var now = Date.now();
		if (root === lastRefreshRoot && now - lastRefreshAt < 150) {
			return;
		}
		lastRefreshRoot = root;
		lastRefreshAt = now;
		refresh(root);
	}

	// Contact Form 7: real DOM event, fires after every AJAX submission outcome.
	document.addEventListener('wpcf7submit', function (e) {
		refreshOnce(e.target);
	});

	// Basic Forms also dispatches a native CustomEvent (works even if jQuery loads late).
	document.addEventListener('wpps_bf_submit_complete', function (e) {
		refreshOnce(e.target);
	});

	function bindJqueryHandlers() {
		if (typeof jQuery === 'undefined') {
			return;
		}
		// Basic Forms (jQuery event from frontend.js).
		jQuery(document).on('wpps_bf_submit_complete', function (e) {
			refreshOnce(e.target);
		});

		// WPForms with AJAX submissions enabled.
		jQuery(document).on('wpformsAjaxSubmitFailed wpformsAjaxSubmitSuccess', function (e) {
			refreshOnce(e.target);
		});

		// Elementor Pro forms (events are jQuery-triggered on the form and bubble).
		jQuery(document).on('submit_success error', 'form.elementor-form', function () {
			refreshOnce(this);
		});

		// Fluent Forms.
		jQuery(document).on('fluentform_submission_failed fluentform_submission_success', function (e) {
			refreshOnce(e.target);
		});

		// Gravity Forms (AJAX mode): fires after the replaced form markup lands.
		jQuery(document).on('gform_post_render', function (e, formId) {
			var wrapper = formId ? document.getElementById('gform_wrapper_' + formId) : null;
			refreshOnce(wrapper || document);
		});
	}

	if (typeof jQuery !== 'undefined') {
		bindJqueryHandlers();
	} else {
		document.addEventListener('DOMContentLoaded', bindJqueryHandlers);
	}
})();
