/**
 * Basic Forms - Frontend submission and UI helpers.
 *
 * @package BasicForms
 * @since 1.0.0
 */
(function ($) {
	'use strict';

	function syncPhoneFields($form) {
		$form.find('.wpps-bf-phone-wrap').each(function () {
			var $w = $(this);
			var code = $w.find('.wpps-bf-phone-country-value').val();
			var num = ($w.find('.wpps-bf-phone-number').val() || '').trim();
			$w.find('.wpps-bf-phone-value').val(num ? '+' + code + ' ' + num : '');
		});
	}

	function bindPhonePicker($scope) {
		$scope.on('click', '.wpps-bf-phone-country-trigger', function (e) {
			e.preventDefault();
			var $trigger = $(this);
			var $wrap = $trigger.closest('.wpps-bf-phone-country');
			var $list = $wrap.find('.wpps-bf-phone-country-list');
			$('.wpps-bf-phone-country-list').not($list).attr('hidden', 'hidden').find('.wpps-bf-phone-country-search').val('');
			$('.wpps-bf-phone-country-trigger').not($trigger).attr('aria-expanded', 'false');
			var wasHidden = $list.attr('hidden') === 'hidden';
			$list.attr('hidden', wasHidden ? null : 'hidden');
			$trigger.attr('aria-expanded', wasHidden ? 'true' : 'false');
			if (wasHidden) {
				var code = $wrap.find('.wpps-bf-phone-country-value').val();
				var iso = $trigger.data('iso');
				$wrap.find('.wpps-bf-phone-country-search').val('').focus();
				$wrap.find('.wpps-bf-phone-country-option').removeClass('is-selected').attr('aria-selected', 'false').show();
				$wrap.find('.wpps-bf-phone-country-option[data-code="' + code + '"][data-iso="' + iso + '"]').addClass('is-selected').attr('aria-selected', 'true');
			} else {
				$wrap.find('.wpps-bf-phone-country-search').val('');
				$wrap.find('.wpps-bf-phone-country-option').show();
			}
		});

		$scope.on('input', '.wpps-bf-phone-country-search', function () {
			var q = ($(this).val() || '').toLowerCase().trim();
			var $wrap = $(this).closest('.wpps-bf-phone-country-list');
			$wrap.find('.wpps-bf-phone-country-option').each(function () {
				var $opt = $(this);
				var show = !q || (($opt.data('search') || '').indexOf(q) >= 0);
				$opt.toggle(show);
			});
		});

		$scope.on('click', '.wpps-bf-phone-country-search', function (e) {
			e.stopPropagation();
		});

		$scope.on('keydown', '.wpps-bf-phone-country-search', function (e) {
			var $wrap = $(this).closest('.wpps-bf-phone-country');
			if (e.key === 'Escape') {
				$wrap.find('.wpps-bf-phone-country-list').attr('hidden', 'hidden');
				$wrap.find('.wpps-bf-phone-country-trigger').attr('aria-expanded', 'false').focus();
			} else if (e.key === 'Enter') {
				e.preventDefault();
				var $first = $wrap.find('.wpps-bf-phone-country-option:visible').first();
				if ($first.length) {
					$first.trigger('click');
				}
			}
		});

		$scope.on('click', '.wpps-bf-phone-country-option', function () {
			var $opt = $(this);
			if ($opt.is(':hidden')) {
				return;
			}
			var code = $opt.data('code');
			var iso = $opt.data('iso');
			var $wrap = $opt.closest('.wpps-bf-phone-country');
			$wrap.find('.wpps-bf-phone-country-value').val(code);
			$wrap.find('.wpps-bf-phone-country-trigger').data('iso', iso).attr('data-iso', iso).find('.wpps-bf-phone-flag').attr('src', 'https://flagcdn.com/w40/' + iso + '.png').end().find('.wpps-bf-phone-code').text('+' + code);
			$wrap.find('.wpps-bf-phone-country-option').removeClass('is-selected').attr('aria-selected', 'false');
			$opt.addClass('is-selected').attr('aria-selected', 'true');
			$wrap.find('.wpps-bf-phone-country-search').val('');
			$wrap.find('.wpps-bf-phone-country-list').attr('hidden', 'hidden');
			$wrap.find('.wpps-bf-phone-country-trigger').attr('aria-expanded', 'false');
		});

		$(document).on('click', function (e) {
			if (!$(e.target).closest('.wpps-bf-phone-country').length) {
				$('.wpps-bf-phone-country-list').attr('hidden', true);
				$('.wpps-bf-phone-country-trigger').attr('aria-expanded', 'false');
			}
		});
	}

	function bindConditionalFields($form) {
		// inputs (robust regardless of wrapper CSS classes).
		function readFieldValue($fieldWrap) {
			if (!$fieldWrap.length) {
				return '';
			}

			if ($fieldWrap.find('input[type="radio"]').length) {
				return $fieldWrap.find('input[type="radio"]:checked').val() || '';
			}

			var $checkboxes = $fieldWrap.find('input[type="checkbox"]');
			if ($checkboxes.length) {
				var checkedVals = $checkboxes.filter(':checked').map(function () {
					return $(this).val();
				}).get();
				if ($checkboxes.length === 1) {
					return checkedVals.length ? (checkedVals[0] || '1') : '';
				}
				return checkedVals.join(', ');
			}

			var $select = $fieldWrap.find('select');
			if ($select.length) {
				var val = $select.val();
				return (Array.isArray(val) ? val.join(', ') : (val || '')).toString();
			}

			if ($fieldWrap.find('.wpps-bf-phone-number').length) {
				return ($fieldWrap.find('.wpps-bf-phone-number').val() || '').trim();
			}

			var $input = $fieldWrap.find('input, textarea').not('[type="hidden"]').first();
			return ($input.val() || '').toString().trim();
		}

		function matchesRule(rule, actual) {
			// Equality and containment are case-insensitive so "Other" matches "other".
			var expected = (rule.value || '').toString();
			var actualCi = actual.toLowerCase();
			var expectedCi = expected.toLowerCase();
			switch (rule.operator) {
				case 'not_equals':
					return actualCi !== expectedCi;
				case 'contains':
					return expectedCi !== '' && actualCi.indexOf(expectedCi) >= 0;
				case 'not_empty':
					return actual !== '';
				case 'empty':
					return actual === '';
				default:
					return actualCi === expectedCi;
			}
		}

		// Accept both the legacy single-rule shape and the multi-rule shape.
		function normalizeShowIf(parsed) {
			if (!parsed || typeof parsed !== 'object') {
				return null;
			}
			var rules = [];
			var relation = (String(parsed.relation || 'and').toLowerCase() === 'or') ? 'or' : 'and';
			if (Array.isArray(parsed.rules)) {
				rules = parsed.rules.filter(function (r) { return r && r.field_id; });
			} else if (parsed.field_id) {
				rules = [parsed];
			}
			return rules.length ? { relation: relation, rules: rules } : null;
		}

		function evaluateRules(config) {
			var isOr = config.relation === 'or';
			var result = !isOr; // AND starts true, OR starts false.
			for (var i = 0; i < config.rules.length; i++) {
				var rule = config.rules[i];
				var $controller = $form.find('[data-field-id="' + rule.field_id + '"]').first();
				var match = matchesRule(rule, readFieldValue($controller));
				if (isOr) {
					result = result || match;
					if (result) { break; }
				} else {
					result = result && match;
					if (!result) { break; }
				}
			}
			return result;
		}

		function updateConditionalFields() {
			$form.find('.wpps-bf-field-conditional').each(function () {
				var $target = $(this);
				var rawRule = $target.attr('data-show-if');
				if (!rawRule) {
					return;
				}
				var parsed;
				try {
					parsed = JSON.parse(rawRule);
				} catch (err) {
					return;
				}
				var config = normalizeShowIf(parsed);
				if (!config) {
					return;
				}
				var show = evaluateRules(config);
				var $box = $target.closest('.wpps-bf-field-wrap');
				if (!$box.length) {
					$box = $target;
				}
				// 'none' hides. Wraps start hidden via server-rendered inline style.
				$box.css('display', show ? '' : 'none');
				$target.find('input, select, textarea').each(function () {
					var $input = $(this);
					if ($input.data('wppsBfRequired') === undefined && $input.prop('required')) {
						$input.data('wppsBfRequired', true);
					}
					if ($input.data('wppsBfRequired')) {
						$input.prop('required', show);
					}
					$input.prop('disabled', !show);
				});
			});
		}

		$form.on('input change', 'input, select, textarea', updateConditionalFields);
		updateConditionalFields();
		return updateConditionalFields;
	}

	function bindFileUpload($wrapper) {
		$wrapper.find('.wpps-bf-file-dropzone').on('click', function () {
			var forId = $(this).data('for');
			if (forId) {
				$('#' + forId).trigger('click');
			}
		});

		$wrapper.find('.wpps-bf-file-input').on('change', function () {
			var $wrap = $(this).closest('.wpps-bf-file-upload');
			var $preview = $wrap.find('.wpps-bf-file-preview');
			var files = this.files;
			if (files && files.length) {
				var names = [];
				for (var i = 0; i < files.length; i++) {
					names.push(files[i].name);
				}
				$preview.text(names.join(', ')).show();
			} else {
				$preview.empty().hide();
			}
		});

		$wrapper.find('.wpps-bf-file-dropzone').on('dragover dragenter', function (e) {
			e.preventDefault();
			e.stopPropagation();
			$(this).addClass('wpps-bf-dragover');
		});

		$wrapper.find('.wpps-bf-file-dropzone').on('dragleave drop', function (e) {
			e.preventDefault();
			e.stopPropagation();
			$(this).removeClass('wpps-bf-dragover');
		});

		$wrapper.find('.wpps-bf-file-dropzone').on('drop', function (e) {
			var forId = $(this).data('for');
			if (forId && e.originalEvent && e.originalEvent.dataTransfer && e.originalEvent.dataTransfer.files && e.originalEvent.dataTransfer.files.length) {
				var input = document.getElementById(forId);
				if (input && typeof DataTransfer !== 'undefined') {
					var dt = new DataTransfer();
					for (var j = 0; j < e.originalEvent.dataTransfer.files.length; j++) {
						dt.items.add(e.originalEvent.dataTransfer.files[j]);
					}
					input.files = dt.files;
					$(input).trigger('change');
				}
			}
		});
	}

	/**
	 * Wait for a Turnstile token that is still being fetched, then call done().
	 *
	 * Turnstile tokens are single-use: after a submission the widget resets and
	 * needs a fresh Cloudflare round trip before its hidden input refills. Firing
	 * the AJAX immediately would post an empty token and fail verification. The
	 * field is re-queried each tick because a widget re-render replaces it.
	 * reCAPTCHA needs no wait — its checkbox only fills on user interaction.
	 */
	function waitForCaptchaToken(formEl, done) {
		var maxWaitMs = 5000;
		var stepMs = 100;

		function pendingField() {
			var field = formEl.querySelector('input[name="cf-turnstile-response"]');
			return field && !field.value ? field : null;
		}

		if (!pendingField()) {
			done();
			return;
		}
		var waited = 0;
		var timer = setInterval(function () {
			waited += stepMs;
			if (!pendingField() || waited >= maxWaitMs) {
				clearInterval(timer);
				done();
			}
		}, stepMs);
	}

	function applyTimingToken($form, token) {
		if (!token) {
			return;
		}
		$form.find('input[name="wpps_bf_timing_token"]').each(function () {
			this.value = token;
			this.defaultValue = token;
		});
	}

	function notifyCaptchaRefresh($form, formEl) {
		$form.trigger('wpps_bf_submit_complete');
		if (formEl && typeof formEl.dispatchEvent === 'function') {
			try {
				formEl.dispatchEvent(new CustomEvent('wpps_bf_submit_complete', { bubbles: true }));
			} catch (err) {}
		}
	}

	function initForm($wrapper) {
		var formId = parseInt($wrapper.data('form-id'), 10) || 0;
		var config = window.wppsBfFrontendData && window.wppsBfFrontendData[formId] ? window.wppsBfFrontendData[formId] : {};
		var $form = $wrapper.find('.wpps-bf-form');
		var $msg = $form.find('.wpps-bf-message');
		var $btn = $form.find('.wpps-bf-submit');
		var btnText = config.submitText || $btn.text();

		bindFileUpload($wrapper);
		var refreshConditionalFields = bindConditionalFields($form);

		$form.on('submit', function (e) {
			e.preventDefault();
			syncPhoneFields($form);

			$msg.hide().removeClass('wpps-bf-success wpps-bf-error').text('');

			var formEl = this;
			var pendingTurnstile = formEl.querySelector('input[name="cf-turnstile-response"]');
			var waitingForToken = !!(pendingTurnstile && !pendingTurnstile.value);

			// Don't show "Submitting…" during the Turnstile refill wait — that made
			// file uploads feel stuck before the request even started.
			$btn.prop('disabled', true);
			if (waitingForToken) {
				$btn.text(config.verifyingText || 'Verifying…');
			}

			waitForCaptchaToken(formEl, function () {
				$btn.text(config.sendingText || 'Submitting...');
				sendSubmission(formEl);
			});
		});

		function sendSubmission(formEl) {
			// Built after any captcha wait so the freshest token is included.
			var fd = new FormData(formEl);
			fd.append('action', 'submit_basic_form');
			fd.append('form_id', formId);

			$.ajax({
				url: config.ajaxUrl || (window.wppsBfFrontendData && window.wppsBfFrontendData.ajaxUrl) || '',
				type: 'POST',
				data: fd,
				processData: false,
				contentType: false,
				success: function (r) {
					if (r.success) {
						if (r.data.redirect_url) {
							$msg.addClass('wpps-bf-success').text(config.sendingText || config.redirectText || 'Submitting...').show();
							setTimeout(function () {
								window.location.href = r.data.redirect_url;
							}, 500);
						} else {
							$msg.addClass('wpps-bf-success').text(r.data.message).show();
							$form[0].reset();
							// reset() restores the original (now redeemed) timing token — replace it.
							applyTimingToken($form, r.data && r.data.timing_token ? r.data.timing_token : '');
							if (typeof refreshConditionalFields === 'function') {
								refreshConditionalFields();
							}
							$wrapper.find('.wpps-bf-phone-country-value').val('1');
							$wrapper.find('.wpps-bf-phone-country-trigger').data('iso', 'us').attr('data-iso', 'us').find('.wpps-bf-phone-flag').attr('src', 'https://flagcdn.com/w40/us.png').end().find('.wpps-bf-phone-code').text('+1');
							$wrapper.find('.wpps-bf-phone-country-list').attr('hidden', 'hidden');
							$btn.prop('disabled', false).text(btnText);
							// The form stays on the page for another submission; captcha modules
							// listen for this to refresh their single-use tokens.
							notifyCaptchaRefresh($form, formEl);
						}
					} else {
						$msg.addClass('wpps-bf-error').text(r.data && r.data.message ? r.data.message : (config.errorText || 'An error occurred.')).show();
						$('html, body').animate({ scrollTop: $msg.offset().top - 50 }, 300);
						$btn.prop('disabled', false).text(btnText);
						notifyCaptchaRefresh($form, formEl);
					}
				},
				error: function () {
					$msg.addClass('wpps-bf-error').text(config.networkErrorText || 'An error occurred. Please try again.').show();
					$btn.prop('disabled', false).text(btnText);
					notifyCaptchaRefresh($form, formEl);
				}
			});
		}
	}

	$(function () {
		bindPhonePicker($(document));
		$('.wpps-bf-wrapper').each(function () {
			initForm($(this));
		});
	});
})(jQuery);
