/* global wp, grecaptcha, jQuery, wc, wppsGrWooCheckoutCallback */
/**
 * WooCommerce checkout reCAPTCHA lifecycle.
 *
 * v2: owns explicit render for #wpps-grecaptcha-woo-checkout so Store API
 *     extension data is set (generic V2Init must not claim this widget).
 * v3: executes a fresh token on Blocks validation / classic place-order.
 */
(function () {
	var WIDGET_ID = 'wpps-grecaptcha-woo-checkout';
	var RENDERED_ATTR = 'data-wpps-rendered';
	var WIDGET_ATTR = 'data-wpps-widget-id';
	var readyTimer = null;

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

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

	function isV3() {
		return cfg().version === 'v3';
	}

	function hasGrecaptcha() {
		return typeof grecaptcha !== 'undefined' && typeof grecaptcha.ready === 'function';
	}

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

	function setRecaptchaExtensionData(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 unlockSubmit() {
		if (typeof wppsGrWooCheckoutCallback === 'function') {
			try {
				wppsGrWooCheckoutCallback();
			} catch (e) {}
		}
	}

	function whenGrecaptchaReady(cb) {
		if (hasGrecaptcha()) {
			grecaptcha.ready(cb);
			return;
		}
		if (readyTimer) {
			return;
		}
		var attempts = 0;
		readyTimer = setInterval(function () {
			attempts += 1;
			if (hasGrecaptcha()) {
				clearInterval(readyTimer);
				readyTimer = null;
				grecaptcha.ready(cb);
			} else if (attempts >= 100) {
				clearInterval(readyTimer);
				readyTimer = null;
			}
		}, 100);
	}

	function readV2Token(el) {
		if (!el || typeof grecaptcha === 'undefined' || typeof grecaptcha.getResponse !== 'function') {
			return '';
		}
		var wid = el.getAttribute(WIDGET_ATTR);
		try {
			if (wid !== null && wid !== '') {
				var fromId = grecaptcha.getResponse(parseInt(wid, 10));
				if (fromId) {
					return String(fromId);
				}
			}
		} catch (e) {}
		var input =
			(el.querySelector && el.querySelector('textarea.g-recaptcha-response')) ||
			document.querySelector('#' + WIDGET_ID + ' textarea.g-recaptcha-response') ||
			document.querySelector('form.checkout textarea.g-recaptcha-response, form.woocommerce-checkout textarea.g-recaptcha-response');
		return input && input.value ? String(input.value) : '';
	}

	function renderV2Widget(force) {
		var el = widgetEl();
		if (!el || typeof grecaptcha === 'undefined' || typeof grecaptcha.render !== 'function') {
			return false;
		}
		if (!force && el.getAttribute(RENDERED_ATTR) === '1' && el.getAttribute(WIDGET_ATTR)) {
			return true;
		}
		var c = cfg();
		var sk = c.siteKey || el.getAttribute('data-sitekey') || '';
		if (!sk) {
			return false;
		}

		var existing = el.getAttribute(WIDGET_ATTR);
		if (existing !== null && existing !== '') {
			try {
				grecaptcha.reset(parseInt(existing, 10));
				setRecaptchaExtensionData('');
				return true;
			} catch (e) {
				el.innerHTML = '';
				el.removeAttribute(RENDERED_ATTR);
				el.removeAttribute(WIDGET_ATTR);
			}
		} else if (force) {
			el.innerHTML = '';
			el.removeAttribute(RENDERED_ATTR);
			el.removeAttribute(WIDGET_ATTR);
		}

		if (el.getAttribute(RENDERED_ATTR) === '1') {
			return true;
		}

		try {
			var id = grecaptcha.render(el, {
				sitekey: sk,
				theme: el.getAttribute('data-theme') || c.theme || 'light',
				size: el.getAttribute('data-size') || c.size || 'normal',
				callback: function (tok) {
					setRecaptchaExtensionData(tok || '');
					unlockSubmit();
				},
				'expired-callback': function () {
					setRecaptchaExtensionData('');
				},
				'error-callback': function () {
					setRecaptchaExtensionData('');
				},
			});
			el.setAttribute(WIDGET_ATTR, String(id));
			el.setAttribute(RENDERED_ATTR, '1');
			el.setAttribute('data-wpps-grecaptcha-done', '1');
			return true;
		} catch (e2) {
			return false;
		}
	}

	function resetV2Widget() {
		var el = widgetEl();
		if (!el || typeof grecaptcha === 'undefined') {
			return;
		}
		var wid = el.getAttribute(WIDGET_ATTR);
		if (wid !== null && wid !== '' && typeof grecaptcha.reset === 'function') {
			try {
				grecaptcha.reset(parseInt(wid, 10));
				setRecaptchaExtensionData('');
				return;
			} catch (e) {}
		}
		renderV2Widget(true);
		setRecaptchaExtensionData('');
	}

	function executeV3Token() {
		var c = cfg();
		return new Promise(function (resolve, reject) {
			if (!c.siteKey || typeof grecaptcha === 'undefined' || !grecaptcha.execute) {
				reject(new Error('unavailable'));
				return;
			}
			grecaptcha.ready(function () {
				grecaptcha
					.execute(c.siteKey, { action: c.action || 'woo_checkout' })
					.then(function (tok) {
						setRecaptchaExtensionData(tok || '');
						var hidden = document.querySelector(
							'#' + WIDGET_ID + '.wpps-recaptcha-v3-token, #' + WIDGET_ID + '[name="g-recaptcha-response"], .woocommerce-checkout .wpps-recaptcha-v3-token[data-action="woo_checkout"], form.checkout .wpps-recaptcha-v3-token[data-action="woo_checkout"]'
						);
						if (hidden) {
							hidden.value = tok || '';
						}
						resolve(tok || '');
					})
					.catch(reject);
			});
		});
	}

	function registerBlocksValidation() {
		if (typeof wc === 'undefined' || !wc.blocksCheckoutEvents || typeof wc.blocksCheckoutEvents.onCheckoutValidation !== 'function') {
			return;
		}
		wc.blocksCheckoutEvents.onCheckoutValidation(function () {
			return new Promise(function (resolve) {
				whenGrecaptchaReady(function () {
					if (isV3()) {
						executeV3Token()
							.then(function (tok) {
								if (!tok) {
									resolve({
										errorMessage: cfg().v3FailedMessage || 'reCAPTCHA verification failed.',
									});
									return;
								}
								resolve(true);
							})
							.catch(function () {
								resolve({
									errorMessage: cfg().v3UnavailableMessage || 'reCAPTCHA is unavailable.',
								});
							});
						return;
					}

					if (!widgetEl()) {
						resolve(true);
						return;
					}
					renderV2Widget(false);
					var token = readV2Token(widgetEl());
					if (!token) {
						resolve({
							errorMessage: cfg().missingTokenMessage || 'Please complete the reCAPTCHA verification to complete your order.',
						});
						return;
					}
					setRecaptchaExtensionData(token);
					resolve(true);
				});
			});
		}, 5);
	}

	function bootBlocksV2() {
		if (typeof wp === 'undefined' || !wp.data || isV3()) {
			return;
		}
		wp.data.subscribe(function () {
			var el = widgetEl();
			if (!el) {
				return;
			}
			if (el.getAttribute(RENDERED_ATTR) !== '1') {
				renderV2Widget(false);
			}
		}, 'wc/store/cart');
		renderV2Widget(false);
	}

	jQuery(document.body).on('checkout_error', function () {
		whenGrecaptchaReady(function () {
			if (isV3()) {
				setRecaptchaExtensionData('');
				return;
			}
			setTimeout(resetV2Widget, 400);
		});
	});

	jQuery(document.body).on('updated_checkout', function () {
		whenGrecaptchaReady(function () {
			if (isV3()) {
				return;
			}
			var el = widgetEl();
			if (!el || el.getAttribute(RENDERED_ATTR) !== '1' || !el.querySelector('iframe')) {
				setTimeout(function () {
					renderV2Widget(true);
				}, 250);
			}
		});
	});

	/**
	 * Themes sometimes force `textarea { display:block !important }`, which reveals
	 * Google's injected g-recaptcha-response next to Place Order. Keep it hidden.
	 */
	function ensureResponseHidden() {
		if (document.getElementById('wpps-gr-hide-response')) {
			return;
		}
		var style = document.createElement('style');
		style.id = 'wpps-gr-hide-response';
		style.textContent =
			'textarea.g-recaptcha-response{display:none!important;position:absolute!important;' +
			'width:1px!important;height:1px!important;min-height:0!important;padding:0!important;' +
			'margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;' +
			'border:0!important;opacity:0!important;visibility:hidden!important;resize:none!important;}';
		(document.head || document.documentElement).appendChild(style);
	}

	function boot() {
		ensureResponseHidden();
		whenGrecaptchaReady(function () {
			registerBlocksValidation();
			if (!isV3()) {
				bootBlocksV2();
				renderV2Widget(false);
			}
		});
	}

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