(function () {
	'use strict';

	var config = window.subinAutoReplyRules || {};
	var strings = config.strings || {};
	var state = {
		fields: [],
		emailFields: [],
		operators: {},
		mergeTags: {},
		limits: {},
		expectedVersion: 0,
		dirty: false,
		editRevision: 0,
		saveGeneration: 0,
		loadingState: false,
		loadGeneration: 0,
		previewGeneration: 0,
		previewTimer: null,
		lastMergeTarget: null,
		lastSubmitter: null,
		initialTemplateId: 0,
		sharedDesignId: 0,
		sharedSubject: '',
		sharedPreheader: '',
		sharedBody: '',
		designPickerReturnFocus: null,
		designPickerBackground: [],
		designInsertGeneration: 0,
		designInsertControl: null,
		designSearchTimer: null,
		designSearchGeneration: 0,
		designPage: 1,
		designQuery: '',
		designHasMore: false,
		bodyEditorBound: false,
		bodyEditorBindAttempts: 0,
		suppressEditorEvents: false,
		validationTargets: [],
		validationMessageSerial: 0
	};
	var root;
	var form;

	document.addEventListener('DOMContentLoaded', init);

	function init() {
		root = document.querySelector('[data-subin-auto-reply-rules-editor]');
		form = root ? root.querySelector('[data-subin-auto-reply-rule-form]') : null;
		if (!root || !form) {
			return;
		}

		readEmbeddedRule();
		state.initialTemplateId = positiveInteger(inputValue('template_id'));
		bindEditor();
		bindBodyEditor();
		syncDerivedFields();
		updateMessageMode();

		if (window.fetch && config.ajaxUrl && config.nonce && config.actions) {
			// The server-rendered rule is authoritative; AJAX only hydrates form schema.
			loadInitialState(false);
		}
	}

	function readEmbeddedRule() {
		var island = document.getElementById('subin-auto-reply-rule-data');
		if (!island) {
			return;
		}
		try {
			var embedded = JSON.parse(island.textContent || '{}');
			var rule = embedded && embedded.rule ? embedded.rule : {};
			state.expectedVersion = positiveInteger(rule.rule_version || rule.expected_version || 0);
			state.sharedDesignId = positiveInteger(rule.template_id || 0);
			state.sharedSubject = String(rule.subject || '');
			state.sharedPreheader = String(rule.preheader || '');
			state.sharedBody = String(rule.body || '');
		} catch (error) {
			state.expectedVersion = 0;
		}
	}

	function bindEditor() {
		form.addEventListener('click', function (event) {
			var submitter = event.target.closest('[data-subin-save-intent]');
			if (submitter) {
				state.lastSubmitter = submitter;
			}

			var add = event.target.closest('[data-subin-add-condition]');
			if (add) {
				event.preventDefault();
				addConditionAndFocus();
				return;
			}

			var remove = event.target.closest('[data-subin-remove-condition]');
			if (remove) {
				event.preventDefault();
				var row = remove.closest('[data-subin-condition-row]');
				if (row) {
					removeConditionAndFocus(row);
				}
				return;
			}

			var merge = event.target.closest('[data-subin-merge-tag]');
			if (merge) {
				event.preventDefault();
				insertMergeTag(merge.getAttribute('data-subin-merge-tag') || '');
				return;
			}

			var openPicker = event.target.closest('[data-subin-open-design-picker]');
			if (openPicker) {
				event.preventDefault();
				openDesignPicker(openPicker);
				return;
			}

			if (event.target.closest('[data-subin-close-design-picker]')) {
				event.preventDefault();
				closeDesignPicker();
				return;
			}

			var insert = event.target.closest('[data-subin-insert-design]');
			if (insert) {
				event.preventDefault();
				insertDesign(insert, false);
				return;
			}

			var detach = event.target.closest('[data-subin-detach-design]');
			if (detach) {
				event.preventDefault();
				insertDesign(detach, true);
				return;
			}

			var loadMoreDesigns = event.target.closest('[data-subin-load-more-designs]');
			if (loadMoreDesigns) {
				event.preventDefault();
				window.clearTimeout(state.designSearchTimer);
				state.designSearchTimer = null;
				loadDesignChoices(state.designQuery, state.designPage + 1, true);
				return;
			}

			if (event.target.closest('[data-subin-refresh-preview]')) {
				event.preventDefault();
				preview();
				return;
			}
			if (event.target.closest('[data-subin-simulate]')) {
				event.preventDefault();
				simulate();
				return;
			}
			if (event.target.closest('[data-subin-test-send]')) {
				event.preventDefault();
				testSend();
			}
		});

		form.addEventListener('focusin', function (event) {
			if (event.target.matches('[data-subin-merge-target]')) {
				state.lastMergeTarget = event.target;
			} else if (event.target.matches('#subin_rule_body_editor')) {
				state.lastMergeTarget = event.target;
			}
		});

		form.addEventListener('change', function (event) {
			var target = event.target;
			if (target.matches('[data-subin-sample-field]')) {
				schedulePreview();
				return;
			}
			if (target.matches('#subin-rule-provider')) {
				loadInitialState(false, true);
				markDirty();
				return;
			}
			if (target.matches('[data-subin-rule-form-select]')) {
				ensureRuleName();
				loadInitialState(false, false);
				markDirty();
				return;
			}
			if (target.matches('[data-subin-condition-field]')) {
				conditionFieldChanged(target.closest('[data-subin-condition-row]'));
				syncConditions();
				markDirty();
				schedulePreview();
				return;
			}
			if (target.matches('[data-subin-condition-operator]')) {
				refreshConditionValue(target.closest('[data-subin-condition-row]'));
				syncConditions();
				markDirty();
				schedulePreview();
				return;
			}
			if (target.matches('[data-subin-condition-mode], [data-subin-condition-value], [data-subin-condition-range], [data-subin-condition-choice]')) {
				syncConditions();
				markDirty();
				schedulePreview();
				return;
			}
			if (target.matches('[data-subin-delivery-mode]')) {
				updateDeliveryState();
			}
			if (target.matches('[data-subin-delay-minutes]')) {
				syncDelay();
			}

			markDirty();
			schedulePreview();
		});

		form.addEventListener('input', function (event) {
			if (event.target.matches('[data-subin-design-search]')) {
				scheduleDesignSearch(event.target.value || '');
				return;
			}
			if (event.target.matches('[data-subin-sample-field]')) {
				schedulePreview();
				return;
			}
			if (event.target.matches('[data-subin-test-email]')) {
				return;
			}
			if (event.target.matches('[data-subin-condition-value], [data-subin-condition-range], [data-subin-delay-minutes]')) {
				syncDerivedFields();
			}
			markDirty();
			schedulePreview();
		});

		form.addEventListener('submit', function (event) {
			syncDerivedFields();
			if (!window.fetch || !config.ajaxUrl || !config.actions || !config.actions.save) {
				return;
			}
			event.preventDefault();
			var submitter = event.submitter || state.lastSubmitter || form.querySelector('[data-subin-save-intent="publish"], [data-subin-save-intent="save"]');
			saveRule(submitter);
		});

		window.addEventListener('beforeunload', function (event) {
			if (!state.dirty) {
				return;
			}
			event.preventDefault();
			event.returnValue = '';
		});

		document.addEventListener('keydown', function (event) {
			var picker = form.querySelector('[data-subin-design-picker]');
			if (event.key === 'Escape' && picker && !picker.hidden) {
				event.preventDefault();
				closeDesignPicker();
				return;
			}
			if (event.key === 'Tab' && picker && !picker.hidden) {
				trapPickerFocus(event, picker);
			}
		});
	}

	function trapPickerFocus(event, picker) {
		var controls = Array.prototype.filter.call(
			picker.querySelectorAll('a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'),
			function (control) { return !control.hidden && control.offsetParent !== null; }
		);
		if (!controls.length) {
			return;
		}
		var first = controls[0];
		var last = controls[controls.length - 1];
		if (event.shiftKey && document.activeElement === first) {
			event.preventDefault();
			last.focus();
		} else if (!event.shiftKey && document.activeElement === last) {
			event.preventDefault();
			first.focus();
		}
	}

	function loadInitialState(hydrateRule, providerChanged) {
		var generation = state.loadGeneration + 1;
		state.loadGeneration = generation;
		var provider = selectedProvider();
		var formId = providerChanged ? '' : selectedFormId();
		var ruleIdInput = form.querySelector('[data-subin-rule-id]');
		var ruleId = hydrateRule && ruleIdInput ? positiveInteger(ruleIdInput.value) : 0;
		state.loadingState = true;
		setFeedback(value('loading', 'Loading…'));

		request(config.actions.initialState, {
			rule_id: ruleId,
			provider: provider,
			form_id: formId
		}).then(function (data) {
			if (generation !== state.loadGeneration) {
				return;
			}
			applyInitialState(data || {}, hydrateRule && !state.dirty, providerChanged);
			state.loadingState = false;
			if (!state.dirty) {
				setFeedback('');
			}
			preview();
		}).catch(function (error) {
			if (generation !== state.loadGeneration) {
				return;
			}
			state.loadingState = false;
			setFeedback(error.message, true);
		});
	}

	function applyInitialState(data, hydrateRule, providerChanged) {
		var rule = data.rule || {};
		state.fields = Array.isArray(data.fields) ? data.fields : [];
		state.emailFields = Array.isArray(data.email_fields) ? data.email_fields : [];
		state.operators = data.operators && typeof data.operators === 'object' ? data.operators : {};
		state.mergeTags = data.merge_tags && typeof data.merge_tags === 'object' ? data.merge_tags : {};
		state.limits = data.limits && typeof data.limits === 'object' ? data.limits : {};

		var providerSelect = form.querySelector('#subin-rule-provider');
		if (hydrateRule && providerSelect && rule.provider) {
			providerSelect.value = rule.provider;
		}
		var desiredForm = hydrateRule ? String(rule.form_id || '') : (providerChanged ? '' : selectedFormId());
		populateForms(Array.isArray(data.forms) ? data.forms : [], desiredForm);
		populateEmailFields(state.emailFields, hydrateRule ? String(rule.visitor_email_field || '') : selectedEmailField());

		if (hydrateRule) {
			applyRuleResource(rule);
			state.expectedVersion = positiveInteger(rule.expected_version || 0);
			state.dirty = false;
		}
		if (state.emailFields.length === 1 && !selectedEmailField() && (!hydrateRule || !positiveInteger(rule.id))) {
			var recipient = form.querySelector('[data-subin-rule-email-field-select]');
			if (recipient) {
				recipient.value = String(state.emailFields[0].key || '');
			}
		}

		refreshConditionRows();
		renderMergeTags();
		renderSampleFields();
		ensureRuleName();
		updateDeliveryState();
	}

	function applyRuleResource(rule) {
		if (!positiveInteger(rule.id) && state.initialTemplateId && !positiveInteger(rule.template_id)) {
			rule.template_id = state.initialTemplateId;
		}
		setValue('expected_version', String(rule.expected_version || rule.rule_version || 0));
		setValue('name', rule.name || '');
		setValue('template_id', String(rule.template_id || 0));
		setValue('subject', rule.subject || '');
		setValue('preheader', rule.preheader || '');
		setBodyContent(rule.body || '');
		state.sharedDesignId = positiveInteger(rule.template_id || 0);
		state.sharedSubject = String(rule.subject || '');
		state.sharedPreheader = String(rule.preheader || '');
		state.sharedBody = String(rule.body || '');
		setValue('priority', String(rule.priority === 0 ? 0 : (rule.priority || 100)));
		setValue('from_name', rule.from && rule.from.name ? rule.from.name : '');
		setValue('from_email', rule.from && rule.from.email ? rule.from.email : '');
		setValue('reply_to_name', rule.reply_to && rule.reply_to.name ? rule.reply_to.name : '');
		setValue('reply_to_email', rule.reply_to && rule.reply_to.email ? rule.reply_to.email : '');
		var enabled = form.querySelector('[data-subin-rule-enabled]');
		if (enabled) {
			enabled.checked = Boolean(rule.enabled);
		}
		var status = form.querySelector('[data-subin-rule-status]');
		if (status) {
			status.value = rule.status || 'draft';
		}
		var deliveryMode = form.querySelector('[data-subin-delivery-mode]');
		if (deliveryMode) {
			deliveryMode.value = rule.delivery && rule.delivery.mode === 'delay' ? 'delay' : 'immediate';
		}
		var delay = positiveInteger(rule.delivery && rule.delivery.delay_seconds ? rule.delivery.delay_seconds : 0);
		var delayMinutes = form.querySelector('[data-subin-delay-minutes]');
		if (delayMinutes) {
			delayMinutes.value = String(Math.max(1, Math.ceil(delay / 60)));
		}
		var conditionMode = form.querySelector('[data-subin-condition-mode]');
		if (conditionMode) {
			conditionMode.value = rule.condition_match === 'any' ? 'any' : 'all';
		}
		var conditions = rule.conditions && Array.isArray(rule.conditions.conditions) ? rule.conditions.conditions : [];
		renderConditions(conditions);
		var idInput = form.querySelector('[data-subin-rule-id]');
		if (idInput) {
			idInput.value = String(rule.id || 0);
		}
		updateStatePresentation(rule);
		updateMessageMode();
	}

	function updateMessageMode() {
		var shared = form.querySelector('[data-subin-shared-design]');
		var editor = form.querySelector('[data-subin-message-editor]');
		var templateId = positiveInteger(inputValue('template_id'));
		if (templateId > 0 && !shared) {
			setValue('template_id', '0');
			templateId = 0;
		}
		var isShared = templateId > 0 && Boolean(shared);
		if (shared) {
			shared.hidden = !isShared;
		}
		if (editor) {
			editor.hidden = isShared;
		}
	}

	function openDesignPicker(trigger) {
		var picker = form.querySelector('[data-subin-design-picker]');
		if (!picker) {
			return;
		}
		state.designPickerReturnFocus = trigger || null;
		picker.hidden = false;
		picker.setAttribute('aria-hidden', 'false');
		isolateDesignPickerBackground(picker);
		if (document.body) {
			document.body.classList.add('subin-design-picker-open');
		}
		var search = picker.querySelector('[data-subin-design-search]');
		if (search) {
			search.focus();
		}
	}

	function closeDesignPicker(cancelPending) {
		if (cancelPending !== false) {
			state.designInsertGeneration += 1;
			if (state.designInsertControl) {
				setBusy([state.designInsertControl], false);
				state.designInsertControl = null;
			}
		}
		var picker = form.querySelector('[data-subin-design-picker]');
		var wasOpen = Boolean(picker && !picker.hidden);
		if (picker) {
			picker.hidden = true;
			picker.setAttribute('aria-hidden', 'true');
		}
		if (document.body) {
			document.body.classList.remove('subin-design-picker-open');
		}
		restoreDesignPickerBackground();
		if (!wasOpen) {
			return;
		}
		setDesignFeedback('');
		if (state.designPickerReturnFocus && typeof state.designPickerReturnFocus.focus === 'function') {
			state.designPickerReturnFocus.focus();
		}
		state.designPickerReturnFocus = null;
	}

	function isolateDesignPickerBackground(picker) {
		restoreDesignPickerBackground();
		var current = picker;
		while (current && current !== document.body && current.parentNode) {
			var parent = current.parentNode;
			Array.prototype.forEach.call(parent.children || [], function (sibling) {
				if (sibling === current || !sibling || typeof sibling.setAttribute !== 'function') {
					return;
				}
				var hadInert = typeof sibling.hasAttribute === 'function'
					? sibling.hasAttribute('inert')
					: sibling.getAttribute('inert') !== null;
				var hadAriaHidden = typeof sibling.hasAttribute === 'function'
					? sibling.hasAttribute('aria-hidden')
					: sibling.getAttribute('aria-hidden') !== null;
				state.designPickerBackground.push({
					node: sibling,
					hadInert: hadInert,
					inert: sibling.getAttribute('inert'),
					hadAriaHidden: hadAriaHidden,
					ariaHidden: sibling.getAttribute('aria-hidden')
				});
				sibling.setAttribute('inert', '');
				sibling.setAttribute('aria-hidden', 'true');
			});
			current = parent;
		}
	}

	function restoreDesignPickerBackground() {
		state.designPickerBackground.forEach(function (entry) {
			if (!entry.node || typeof entry.node.setAttribute !== 'function') {
				return;
			}
			if (entry.hadInert) {
				entry.node.setAttribute('inert', entry.inert === null ? '' : entry.inert);
			} else {
				entry.node.removeAttribute('inert');
			}
			if (entry.hadAriaHidden) {
				entry.node.setAttribute('aria-hidden', entry.ariaHidden === null ? '' : entry.ariaHidden);
			} else {
				entry.node.removeAttribute('aria-hidden');
			}
		});
		state.designPickerBackground = [];
	}

	function filterDesigns(query) {
		var normalized = String(query || '').trim().toLocaleLowerCase();
		var cards = form.querySelectorAll('[data-subin-design-card]');
		var visible = 0;
		Array.prototype.forEach.call(cards, function (card) {
			var haystack = String(card.getAttribute('data-design-search') || '').toLocaleLowerCase();
			var match = !normalized || haystack.indexOf(normalized) !== -1;
			card.hidden = !match;
			if (match) {
				visible += 1;
			}
		});
		Array.prototype.forEach.call(form.querySelectorAll('[data-subin-design-group]'), function (group) {
			var groupCards = group.querySelectorAll('[data-subin-design-card]');
			var groupVisible = Array.prototype.some.call(groupCards, function (card) { return !card.hidden; });
			group.hidden = !groupVisible;
		});
		var noResults = form.querySelector('[data-subin-design-no-results]');
		if (noResults) {
			noResults.hidden = visible > 0;
		}
	}

	function scheduleDesignSearch(query) {
		var scheduledQuery = String(query || '').trim();
		var generation = state.designSearchGeneration + 1;
		state.designSearchGeneration = generation;
		state.designQuery = scheduledQuery;
		state.designPage = 0;
		state.designHasMore = false;
		filterDesigns(scheduledQuery);
		window.clearTimeout(state.designSearchTimer);
		var button = form.querySelector('[data-subin-load-more-designs]');
		setBusy(button ? [button] : [], false);
		if (button) {
			button.hidden = true;
		}
		if (!config.actions || !config.actions.listDesigns) {
			return;
		}
		state.designSearchTimer = window.setTimeout(function () {
			loadDesignChoices(scheduledQuery, 1, false, generation);
		}, 250);
	}

	function loadDesignChoices(query, page, append, scheduledGeneration) {
		if (!config.actions || !config.actions.listDesigns) {
			return;
		}
		var generation = positiveInteger(scheduledGeneration);
		if (generation > 0 && generation !== state.designSearchGeneration) {
			return;
		}
		if (generation === 0) {
			generation = state.designSearchGeneration + 1;
			state.designSearchGeneration = generation;
		}
		var button = form.querySelector('[data-subin-load-more-designs]');
		var buttonHadFocus = Boolean(button && document.activeElement === button);
		setBusy(button ? [button] : [], true);
		setDesignFeedback(value('loadingDesigns', 'Loading saved designs…'));
		request(config.actions.listDesigns, {
			search: String(query || ''),
			page: Math.max(1, positiveInteger(page) || 1),
			per_page: 24
		}).then(function (data) {
			if (generation !== state.designSearchGeneration) {
				return;
			}
			var designs = Array.isArray(data.designs) ? data.designs : [];
			var pagination = data.pagination && typeof data.pagination === 'object' ? data.pagination : {};
			state.designQuery = String(query || '');
			state.designPage = Math.max(1, positiveInteger(pagination.page || page) || 1);
			state.designHasMore = state.designPage < positiveInteger(pagination.total_pages || 0);
			var firstNewControl = renderSavedDesigns(designs, Boolean(append));
			setBusy(button ? [button] : [], false);
			if (button) {
				button.hidden = !state.designHasMore;
			}
			if (buttonHadFocus && button && button.hidden) {
				if (firstNewControl && typeof firstNewControl.focus === 'function') {
					firstNewControl.focus();
				} else {
					var search = form.querySelector('[data-subin-design-search]');
					if (search && typeof search.focus === 'function') {
						search.focus();
					}
				}
			}
			setDesignFeedback('');
			filterDesigns(state.designQuery);
		}).catch(function (error) {
			if (generation !== state.designSearchGeneration) {
				return;
			}
			setBusy(button ? [button] : [], false);
			setDesignFeedback(error.message, true);
		});
	}

	function renderSavedDesigns(designs, append) {
		var grid = form.querySelector('[data-subin-saved-design-grid]');
		if (!grid) {
			return null;
		}
		if (!append) {
			grid.textContent = '';
		}
		var firstNewControl = null;
		designs.forEach(function (design) {
			var article = document.createElement('article');
			article.className = 'subin-auto-reply-design-card';
			article.setAttribute('data-subin-design-card', '');
			article.setAttribute('data-design-search', String(design.name || ''));
			var badge = document.createElement('span');
			badge.textContent = value('savedDesign', 'Saved');
			var heading = document.createElement('h5');
			heading.textContent = String(design.name || '');
			var description = document.createElement('p');
			description.textContent = value('savedDesignDescription', 'Reusable email layout and content');
			var insert = document.createElement('button');
			insert.type = 'button';
			insert.className = 'button button-primary';
			insert.setAttribute('data-subin-insert-design', '');
			insert.setAttribute('data-design-type', 'saved');
			insert.setAttribute('data-design-id', String(positiveInteger(design.id)));
			insert.textContent = value('insertDesign', 'Insert and customize');
			if (!firstNewControl) {
				firstNewControl = insert;
			}
			article.appendChild(badge);
			article.appendChild(heading);
			article.appendChild(description);
			article.appendChild(insert);
			grid.appendChild(article);
		});
		var empty = form.querySelector('[data-subin-saved-design-empty]');
		if (empty) {
			empty.hidden = grid.querySelectorAll('[data-subin-design-card]').length > 0;
		}
		return firstNewControl;
	}

	function insertDesign(control, detach) {
		var type = detach ? 'saved' : String(control.getAttribute('data-design-type') || '');
		var shared = form.querySelector('[data-subin-shared-design]');
		var designId = detach && shared
			? positiveInteger(shared.getAttribute('data-design-id'))
			: positiveInteger(control.getAttribute('data-design-id'));
		var designKey = detach ? '' : String(control.getAttribute('data-design-key') || '');
		var currentSubject = inputValue('subject');
		var currentPreheader = inputValue('preheader');
		var currentBody = getBodyContent();

		if (!detach && currentBody.trim() !== '') {
			if (!window.confirm(value('replaceMessage', 'Replace the current email body with this design?'))) {
				return;
			}
		}

		var generation = state.designInsertGeneration + 1;
		state.designInsertGeneration = generation;
		if (state.designInsertControl && state.designInsertControl !== control) {
			setBusy([state.designInsertControl], false);
		}
		state.designInsertControl = control;
		setBusy([control], true);
		setDesignFeedback(value('loadingDesign', 'Loading design…'));
		request(config.actions.loadDesign, {
			design_type: type,
			design_id: designId,
			design_key: designKey
		}).then(function (data) {
			if (generation !== state.designInsertGeneration) {
				return;
			}
			setBusy([control], false);
			state.designInsertControl = null;
			var design = data && data.design ? data.design : {};
			var body = detach && currentBody.trim() !== '' ? currentBody : String(design.body || '');
			setValue('template_id', '0');
			setBodyContent(body);
			state.sharedDesignId = 0;
			state.sharedSubject = currentSubject;
			state.sharedPreheader = currentPreheader;
			state.sharedBody = body;
			updateMessageMode();
			clearMissingDesignWarning();
			if (detach) {
				focusMessageEditor();
			} else {
				closeDesignPicker(false);
				focusMessageEditor();
			}
			markDirty();
			preview();
			setFeedback(value('designInserted', 'Design content inserted. Subject and preview text were kept.'));
		}).catch(function (error) {
			if (generation !== state.designInsertGeneration) {
				return;
			}
			setBusy([control], false);
			state.designInsertControl = null;
			setDesignFeedback(error.message, true);
			if (detach) {
				setFeedback(error.message, true);
			}
		});
	}

	function focusMessageEditor() {
		var editor = bodyEditor();
		if (editor && bodyEditorIsVisual(editor) && typeof editor.focus === 'function') {
			editor.focus();
			return;
		}
		var body = form.querySelector('#subin_rule_body_editor') || form.querySelector('[name="body"]');
		if (body && typeof body.focus === 'function') {
			body.focus();
			return;
		}
		var subject = form.querySelector('#subin-rule-subject') || form.querySelector('[name="subject"]');
		if (subject && typeof subject.focus === 'function') {
			subject.focus();
		}
	}

	function clearMissingDesignWarning() {
		var warning = form.querySelector('[data-subin-missing-design-warning]');
		if (warning) {
			warning.hidden = true;
		}
	}

	function setDesignFeedback(message, isError) {
		var feedback = form.querySelector('[data-subin-design-feedback]');
		if (!feedback) {
			return;
		}
		feedback.textContent = message || '';
		feedback.classList.toggle('is-error', Boolean(isError));
	}

	function populateForms(forms, selected) {
		var select = form.querySelector('[data-subin-rule-form-select]');
		if (!select) {
			return;
		}
		clearOptions(select, value('chooseForm', 'Select form'));
		forms.forEach(function (item) {
			var option = document.createElement('option');
			option.value = String(item.id || '');
			option.textContent = String(item.label || item.id || '');
			option.setAttribute('data-subin-provider', selectedProvider());
			select.appendChild(option);
		});
		select.value = String(selected || '');
	}

	function populateEmailFields(fields, selected) {
		var select = form.querySelector('[data-subin-rule-email-field-select]');
		if (!select) {
			return;
		}
		clearOptions(select, value('chooseEmail', 'Select visitor email field'));
		fields.forEach(function (field) {
			var option = document.createElement('option');
			option.value = String(field.key || '');
			option.textContent = String(field.label || field.key || '');
			option.setAttribute('data-subin-provider', selectedProvider());
			option.setAttribute('data-subin-form-value', selectedFormId());
			select.appendChild(option);
		});
		select.disabled = fields.length === 0;
		select.value = String(selected || '');
	}

	function clearOptions(select, placeholder) {
		while (select.firstChild) {
			select.removeChild(select.firstChild);
		}
		var empty = document.createElement('option');
		empty.value = '';
		empty.textContent = placeholder;
		select.appendChild(empty);
	}

	function renderConditions(conditions) {
		var list = form.querySelector('[data-subin-condition-list]');
		if (!list) {
			return;
		}
		list.textContent = '';
		conditions.forEach(addCondition);
		updateConditionSummary();
		syncConditions();
	}

	function addCondition(condition) {
		var list = form.querySelector('[data-subin-condition-list]');
		if (!list) {
			return null;
		}
		var max = positiveInteger(state.limits.max_conditions || 20) || 20;
		if (list.querySelectorAll('[data-subin-condition-row]').length >= max) {
			return null;
		}

		var row = document.createElement('div');
		row.className = 'subin-auto-reply-condition-row';
		row.setAttribute('data-subin-condition-row', '');

		var handle = document.createElement('span');
		handle.className = 'subin-auto-reply-condition-row__handle';
		handle.setAttribute('aria-hidden', 'true');
		handle.textContent = '⋮⋮';
		row.appendChild(handle);

		var fieldSelect = createSelect('data-subin-condition-field');
		appendOption(fieldSelect, '', value('chooseField', 'Choose field'));
		state.fields.forEach(function (field) {
			appendOption(fieldSelect, String(field.key || ''), String(field.label || field.key || ''));
		});
		if (condition.field && !optionExists(fieldSelect, condition.field)) {
			appendOption(fieldSelect, condition.field, condition.field);
		}
		fieldSelect.value = String(condition.field || '');
		row.appendChild(wrapControl(fieldSelect, 'Field'));

		var typeSelect = createSelect('data-subin-condition-type');
		['text', 'email', 'url', 'tel', 'textarea', 'choice', 'select', 'radio', 'checkbox', 'consent', 'number', 'date', 'datetime', 'time'].forEach(function (type) {
			appendOption(typeSelect, type, titleize(type));
		});
		typeSelect.value = String(condition.type || fieldType(condition.field) || 'text');
		typeSelect.disabled = true;
		row.appendChild(wrapControl(typeSelect, value('fieldType', 'Field type')));

		var operatorSelect = createSelect('data-subin-condition-operator');
		row.appendChild(wrapControl(operatorSelect, value('operator', 'Operator')));

		var valueHost = document.createElement('div');
		valueHost.className = 'subin-auto-reply-condition-value';
		valueHost.setAttribute('data-subin-condition-value-host', '');
		row.appendChild(valueHost);

		var remove = document.createElement('button');
		remove.type = 'button';
		remove.className = 'button-link subin-auto-reply-condition-remove';
		remove.setAttribute('data-subin-remove-condition', '');
		remove.setAttribute('aria-label', value('removeCondition', 'Remove condition'));
		var icon = document.createElement('span');
		icon.className = 'dashicons dashicons-no-alt';
		icon.setAttribute('aria-hidden', 'true');
		remove.appendChild(icon);
		row.appendChild(remove);

		list.appendChild(row);
		populateOperators(row, String(condition.operator || 'equals'));
		refreshConditionValue(row, condition.value);
		updateConditionSummary();
		syncConditions();
		return row;
	}

	function addConditionAndFocus() {
		var row = addCondition({});
		if (!row) {
			return;
		}
		markDirty();
		focusConditionRow(row);
	}

	function removeConditionAndFocus(row) {
		var rows = Array.prototype.slice.call(form.querySelectorAll('[data-subin-condition-row]'));
		var index = rows.indexOf(row);
		var focusRow = index >= 0 ? (rows[index + 1] || rows[index - 1] || null) : null;
		row.remove();
		updateConditionSummary();
		syncConditions();
		markDirty();
		schedulePreview();
		if (focusRow) {
			focusConditionRow(focusRow);
			return;
		}
		var add = form.querySelector('[data-subin-add-condition]');
		if (add && typeof add.focus === 'function') {
			add.focus();
		}
	}

	function focusConditionRow(row) {
		var field = row ? row.querySelector('[data-subin-condition-field]') : null;
		if (field && typeof field.focus === 'function') {
			field.focus();
		}
	}

	function refreshConditionRows() {
		form.querySelectorAll('[data-subin-condition-row]').forEach(function (row) {
			var select = row.querySelector('[data-subin-condition-field]');
			var current = select ? select.value : '';
			if (!select) {
				return;
			}
			clearOptions(select, value('chooseField', 'Choose field'));
			state.fields.forEach(function (field) {
				appendOption(select, String(field.key || ''), String(field.label || field.key || ''));
			});
			if (current && !optionExists(select, current)) {
				appendOption(select, current, current);
			}
			select.value = current;
			conditionFieldChanged(row, true);
		});
	}

	function conditionFieldChanged(row, preserveValue) {
		if (!row) {
			return;
		}
		var fieldSelect = row.querySelector('[data-subin-condition-field]');
		var typeSelect = row.querySelector('[data-subin-condition-type]');
		var oldValue = preserveValue ? readConditionValue(row) : '';
		if (typeSelect) {
			typeSelect.value = fieldType(fieldSelect ? fieldSelect.value : '') || 'text';
		}
		populateOperators(row, row.querySelector('[data-subin-condition-operator]') ? row.querySelector('[data-subin-condition-operator]').value : 'equals');
		refreshConditionValue(row, oldValue);
	}

	function populateOperators(row, selected) {
		var typeSelect = row.querySelector('[data-subin-condition-type]');
		var operatorSelect = row.querySelector('[data-subin-condition-operator]');
		if (!operatorSelect) {
			return;
		}
		var type = typeSelect ? typeSelect.value : 'text';
		var operators = Array.isArray(state.operators[type]) ? state.operators[type] : defaultOperators(type);
		operatorSelect.textContent = '';
		operators.forEach(function (operator) {
			appendOption(operatorSelect, String(operator.key || operator), String(operator.label || titleize(operator.key || operator)));
		});
		operatorSelect.value = optionExists(operatorSelect, selected) ? selected : (operatorSelect.options[0] ? operatorSelect.options[0].value : 'equals');
	}

	function refreshConditionValue(row, suppliedValue) {
		if (!row) {
			return;
		}
		var host = row.querySelector('[data-subin-condition-value-host]');
		var operatorSelect = row.querySelector('[data-subin-condition-operator]');
		var fieldSelect = row.querySelector('[data-subin-condition-field]');
		var typeSelect = row.querySelector('[data-subin-condition-type]');
		if (!host) {
			return;
		}
		var previous = suppliedValue !== undefined ? suppliedValue : readConditionValue(row);
		var operator = operatorSelect ? operatorSelect.value : 'equals';
		var type = typeSelect ? typeSelect.value : 'text';
		var schema = findField(fieldSelect ? fieldSelect.value : '');
		host.textContent = '';

		if (operator === 'is_empty' || operator === 'is_not_empty') {
			var noValue = document.createElement('span');
			noValue.className = 'subin-auto-reply-condition-no-value';
			noValue.textContent = '—';
			host.appendChild(noValue);
			return;
		}
		if (operator === 'between' || operator === 'not_between') {
			var range = Array.isArray(previous) ? previous : String(previous || '').split(',').map(function (item) { return item.trim(); });
			var first = conditionInput(type, range[0] || '');
			var second = conditionInput(type, range[1] || '');
			first.setAttribute('data-subin-condition-range', 'start');
			second.setAttribute('data-subin-condition-range', 'end');
			host.appendChild(first);
			var separator = document.createElement('span');
			separator.textContent = value('and', 'and');
			host.appendChild(separator);
			host.appendChild(second);
			return;
		}

		if (schema && Array.isArray(schema.options) && schema.options.length) {
			var choice = document.createElement('select');
			choice.setAttribute('data-subin-condition-choice', '');
			var multiple = operator === 'in' || operator === 'not_in';
			choice.multiple = multiple;
			schema.options.forEach(function (option) {
				appendOption(choice, String(option.value || ''), String(option.label || option.value || ''));
			});
			var values = Array.isArray(previous) ? previous.map(String) : [String(previous || '')];
			Array.prototype.forEach.call(choice.options, function (option) {
				option.selected = values.indexOf(option.value) !== -1;
			});
			host.appendChild(choice);
			return;
		}

		if (type === 'consent') {
			var consent = document.createElement('select');
			consent.setAttribute('data-subin-condition-choice', '');
			appendOption(consent, '1', value('checked', 'Checked'));
			appendOption(consent, '0', value('notChecked', 'Not checked'));
			consent.value = String(previous) === '0' ? '0' : '1';
			host.appendChild(consent);
			return;
		}

		var input = conditionInput(type, Array.isArray(previous) ? previous.join(', ') : String(previous || ''));
		input.setAttribute('data-subin-condition-value', '');
		input.placeholder = value('value', 'Value');
		host.appendChild(input);
	}

	function conditionInput(type, current) {
		var input = document.createElement('input');
		input.type = type === 'number' ? 'number' : (type === 'date' ? 'date' : (type === 'time' ? 'time' : (type === 'datetime' ? 'datetime-local' : 'text')));
		input.value = current;
		return input;
	}

	function collectConditions() {
		var conditions = [];
		form.querySelectorAll('[data-subin-condition-row]').forEach(function (row) {
			var field = row.querySelector('[data-subin-condition-field]');
			var type = row.querySelector('[data-subin-condition-type]');
			var operator = row.querySelector('[data-subin-condition-operator]');
			conditions.push({
				field: field ? field.value : '',
				type: type ? type.value : 'text',
				operator: operator ? operator.value : 'equals',
				value: readConditionValue(row)
			});
		});
		return conditions;
	}

	function readConditionValue(row) {
		var ranges = row.querySelectorAll('[data-subin-condition-range]');
		if (ranges.length) {
			return Array.prototype.map.call(ranges, function (input) { return input.value; });
		}
		var choice = row.querySelector('[data-subin-condition-choice]');
		if (choice) {
			if (choice.multiple) {
				return Array.prototype.filter.call(choice.options, function (option) { return option.selected; }).map(function (option) { return option.value; });
			}
			return choice.value;
		}
		var input = row.querySelector('[data-subin-condition-value]');
		return input ? input.value : '';
	}

	function syncConditions() {
		var mode = form.querySelector('[data-subin-condition-mode]');
		var hidden = form.querySelector('[data-subin-conditions-json]');
		if (hidden) {
			hidden.value = JSON.stringify({ version: 1, mode: mode && mode.value === 'any' ? 'any' : 'all', conditions: collectConditions() });
		}
	}

	function updateConditionSummary() {
		var count = form.querySelectorAll('[data-subin-condition-row]').length;
		var badge = form.querySelector('[data-subin-condition-count]');
		var empty = form.querySelector('[data-subin-condition-empty]');
		var add = form.querySelector('[data-subin-add-condition]');
		if (badge) {
			badge.textContent = count ? String(count) : value('always', 'Always');
		}
		if (empty) {
			empty.hidden = count > 0;
		}
		if (add) {
			add.disabled = count >= (positiveInteger(state.limits.max_conditions || 20) || 20);
		}
	}

	function renderMergeTags() {
		var host = form.querySelector('[data-subin-merge-tags]');
		if (!host) {
			return;
		}
		host.textContent = '';
		Object.keys(state.mergeTags).forEach(function (group) {
			var tags = Array.isArray(state.mergeTags[group]) ? state.mergeTags[group] : [];
			tags.forEach(function (item) {
				var button = document.createElement('button');
				button.type = 'button';
				button.className = 'button button-small';
				button.setAttribute('data-subin-merge-tag', String(item.tag || ''));
				button.title = String(item.label || item.tag || '');
				button.textContent = String(item.tag || '');
				host.appendChild(button);
			});
		});
	}

	function insertMergeTag(tag) {
		if (!tag) {
			return;
		}
		if (state.lastMergeTarget === 'body-editor') {
			var visualEditor = bodyEditor();
			if (visualEditor && bodyEditorIsVisual(visualEditor)) {
				visualEditor.execCommand('mceInsertContent', false, tag);
				visualEditor.save();
				markDirty();
				schedulePreview();
				return;
			}
		}
		var target = state.lastMergeTarget;
		if (target === 'body-editor') {
			target = form.querySelector('#subin_rule_body_editor') || form.querySelector('[name="body"]');
		}
		if (!target || typeof target.value !== 'string') {
			target = form.querySelector('#subin-rule-subject') || form.querySelector('#subin_rule_body_editor') || form.querySelector('[name="body"]');
		}
		if (!target || typeof target.value !== 'string') {
			return;
		}
		var start = typeof target.selectionStart === 'number' ? target.selectionStart : target.value.length;
		var end = typeof target.selectionEnd === 'number' ? target.selectionEnd : start;
		target.value = target.value.slice(0, start) + tag + target.value.slice(end);
		target.selectionStart = start + tag.length;
		target.selectionEnd = target.selectionStart;
		target.focus();
		markDirty();
		schedulePreview();
	}

	function bodyEditor() {
		if (!window.tinymce || typeof window.tinymce.get !== 'function') {
			return null;
		}
		return window.tinymce.get('subin_rule_body_editor') || null;
	}

	function bodyEditorIsVisual(editor) {
		if (!editor) {
			return false;
		}
		return typeof editor.isHidden !== 'function' || !editor.isHidden();
	}

	function bindBodyEditor() {
		var editor = bodyEditor();
		if (!editor) {
			state.bodyEditorBindAttempts += 1;
			if (state.bodyEditorBindAttempts < 40) {
				window.setTimeout(bindBodyEditor, 100);
			}
			return;
		}
		if (state.bodyEditorBound) {
			return;
		}
		state.bodyEditorBound = true;
		editor.on('focus', function () {
			state.lastMergeTarget = 'body-editor';
		});
		editor.on('change input undo redo', function () {
			editor.save();
			if (state.suppressEditorEvents) {
				return;
			}
			markDirty();
			schedulePreview();
		});
	}

	function getBodyContent() {
		var editor = bodyEditor();
		if (editor && bodyEditorIsVisual(editor)) {
			editor.save();
		}
		return inputValue('body');
	}

	function setBodyContent(content) {
		content = String(content || '');
		setValue('body', content);
		var editor = bodyEditor();
		if (editor) {
			state.suppressEditorEvents = true;
			editor.setContent(content);
			editor.save();
			state.suppressEditorEvents = false;
		}
	}

	function hasMeaningfulBody(content) {
		var text = String(content || '')
			.replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, '')
			.replace(/<[^>]+>/g, '')
			.replace(/&nbsp;|&#160;/gi, ' ')
			.trim();
		return text !== '' || /<img\b/i.test(String(content || ''));
	}

	function renderSampleFields() {
		var host = form.querySelector('[data-subin-sample-fields]');
		if (!host) {
			return;
		}
		host.textContent = '';
		if (!state.fields.length) {
			var empty = document.createElement('p');
			empty.textContent = value('sampleChooseForm', 'Choose a form to add sample values.');
			host.appendChild(empty);
			return;
		}
		state.fields.forEach(function (field) {
			var label = document.createElement('label');
			var title = document.createElement('span');
			title.textContent = String(field.label || field.key || '');
			label.appendChild(title);
			var control;
			if (Array.isArray(field.options) && field.options.length) {
				control = document.createElement('select');
				if (field.type === 'checkbox') {
					control.multiple = true;
				}
				field.options.forEach(function (option) {
					appendOption(control, String(option.value || ''), String(option.label || option.value || ''));
				});
			} else if (field.type === 'consent') {
				control = document.createElement('select');
				appendOption(control, '1', value('checked', 'Checked'));
				appendOption(control, '0', value('notChecked', 'Not checked'));
			} else {
				control = conditionInput(field.type || 'text', sampleDefault(field));
			}
			control.setAttribute('data-subin-sample-field', String(field.key || ''));
			control.setAttribute('data-field-type', String(field.type || 'text'));
			label.appendChild(control);
			host.appendChild(label);
		});
	}

	function sampleDefault(field) {
		if (field.type === 'email') {
			return 'visitor@example.com';
		}
		if (field.type === 'number') {
			return '1';
		}
		if (field.type === 'date') {
			return new Date().toISOString().slice(0, 10);
		}
		if (field.type === 'time') {
			return '09:00';
		}
		return '';
	}

	function collectSample() {
		var fields = {};
		form.querySelectorAll('[data-subin-sample-field]').forEach(function (control) {
			var key = control.getAttribute('data-subin-sample-field') || '';
			if (!key) {
				return;
			}
			if (control.multiple) {
				fields[key] = Array.prototype.filter.call(control.options, function (option) { return option.selected; }).map(function (option) { return option.value; });
			} else {
				fields[key] = control.value;
			}
		});
		var recipient = fields[selectedEmailField()];
		return { fields: fields, visitor_email: typeof recipient === 'string' ? recipient : '' };
	}

	function collectRule() {
		ensureRuleName();
		syncDerivedFields();
		var body = getBodyContent();
		return {
			id: positiveInteger(inputValue('rule_id')),
			expected_version: state.expectedVersion,
			name: inputValue('name'),
			provider: selectedProvider(),
			form_id: selectedFormId(),
			visitor_email_field: selectedEmailField(),
			template_id: positiveInteger(inputValue('template_id')),
			status: inputValue('status') || 'draft',
			enabled: Boolean(form.querySelector('[data-subin-rule-enabled]') && form.querySelector('[data-subin-rule-enabled]').checked),
			priority: positiveInteger(inputValue('priority')),
			condition_match: form.querySelector('[data-subin-condition-mode]') && form.querySelector('[data-subin-condition-mode]').value === 'any' ? 'any' : 'all',
			conditions: {
				version: 1,
				mode: form.querySelector('[data-subin-condition-mode]') && form.querySelector('[data-subin-condition-mode]').value === 'any' ? 'any' : 'all',
				conditions: collectConditions()
			},
			subject: inputValue('subject'),
			preheader: inputValue('preheader'),
			body: body,
			from: { name: inputValue('from_name'), email: inputValue('from_email') },
			reply_to: { name: inputValue('reply_to_name'), email: inputValue('reply_to_email') },
			delivery: {
				mode: form.querySelector('[data-subin-delivery-mode]') && form.querySelector('[data-subin-delivery-mode]').value === 'delay' ? 'delay' : 'immediate',
				delay_seconds: positiveInteger(form.querySelector('[data-subin-delay-seconds]') ? form.querySelector('[data-subin-delay-seconds]').value : 0)
			}
		};
	}

	function saveRule(submitter) {
		clearValidation();
		var rawIntent = submitter ? (submitter.getAttribute('data-subin-save-intent') || submitter.value || '') : 'publish';
		var intent = rawIntent === 'draft' ? 'save_draft' : 'publish';
		var rule = collectRule();
		var submittedRevision = state.editRevision;
		var generation = state.saveGeneration + 1;
		state.saveGeneration = generation;
		if (rawIntent === 'publish') {
			rule.enabled = true;
			var enabled = form.querySelector('[data-subin-rule-enabled]');
			if (enabled) {
				enabled.checked = true;
			}
		}
		setBusy(form.querySelectorAll('[data-subin-save-intent]'), true);
		setFeedback(value('saving', 'Saving…'));

		request(config.actions.save, { payload: JSON.stringify({ rule: rule, intent: intent }) }).then(function (data) {
			if (generation !== state.saveGeneration) {
				return;
			}
			setBusy(form.querySelectorAll('[data-subin-save-intent]'), false);
			if (data.rule) {
				if (submittedRevision === state.editRevision) {
					state.expectedVersion = positiveInteger(data.rule.expected_version || data.rule.rule_version || 0);
					applyRuleResource(data.rule);
					updateEditorUrl(data.rule.id || 0);
				} else {
					applySavedRuleIdentity(data.rule);
				}
			}
			if (submittedRevision === state.editRevision) {
				state.dirty = false;
				setFeedback(data.message || value('saved', 'Rule saved.'));
			} else {
				state.dirty = true;
				setFeedback(value('savedWithNewerChanges', 'Saved. You still have newer unsaved changes.'));
			}
			preview();
		}).catch(function (error) {
			if (generation !== state.saveGeneration) {
				return;
			}
			setBusy(form.querySelectorAll('[data-subin-save-intent]'), false);
			showValidation(error.data && error.data.fields ? error.data.fields : {});
			setFeedback(error.message, true);
		});
	}

	function applySavedRuleIdentity(rule) {
		state.expectedVersion = positiveInteger(rule.expected_version || rule.rule_version || state.expectedVersion);
		setValue('expected_version', String(state.expectedVersion));
		var idInput = form.querySelector('[data-subin-rule-id]');
		if (idInput) {
			idInput.value = String(rule.id || 0);
		}
		var status = form.querySelector('[data-subin-rule-status]');
		if (status) {
			status.value = rule.status || 'draft';
		}
		updateStatePresentation(rule);
		updateEditorUrl(rule.id || 0);
	}

	function preview() {
		var generation = state.previewGeneration + 1;
		var refresh = form.querySelector('[data-subin-refresh-preview]');
		state.previewGeneration = generation;
		if (!canPreview() || !config.actions.preview) {
			setBusy(refresh ? [refresh] : [], false);
			return;
		}
		setBusy(refresh ? [refresh] : [], true);
		setPreviewDiagnostics(value('previewing', 'Generating preview…'));
		request(config.actions.preview, { payload: JSON.stringify({ rule: collectRule(), sample: collectSample() }) }).then(function (data) {
			if (generation !== state.previewGeneration) {
				return;
			}
			setBusy(refresh ? [refresh] : [], false);
			var subject = form.querySelector('[data-subin-preview-subject]');
			var preheader = form.querySelector('[data-subin-preview-preheader]');
			var frame = form.querySelector('[data-subin-preview-frame]');
			if (subject) {
				subject.textContent = data.subject || value('previewTitle', 'Auto-reply preview');
			}
			if (preheader) {
				preheader.textContent = data.preheader || '';
				preheader.hidden = !data.preheader;
			}
			if (frame) {
				frame.srcdoc = data.body_html || '<!doctype html><html><body></body></html>';
			}
			renderPreviewWarnings(data.warnings || []);
		}).catch(function (error) {
			if (generation !== state.previewGeneration) {
				return;
			}
			setBusy(refresh ? [refresh] : [], false);
			setPreviewDiagnostics(error.message, true);
		});
	}

	function simulate() {
		if (!canPreview() || !config.actions.simulate) {
			return;
		}
		var button = form.querySelector('[data-subin-simulate]');
		var result = form.querySelector('[data-subin-simulation-result]');
		setBusy(button ? [button] : [], true);
		if (result) {
			result.className = 'subin-auto-reply-simulation-result';
			result.textContent = value('simulating', 'Running safe simulation…');
		}
		request(config.actions.simulate, { payload: JSON.stringify({ rule: collectRule(), sample: collectSample() }) }).then(function (data) {
			setBusy(button ? [button] : [], false);
			if (result) {
				result.className = 'subin-auto-reply-simulation-result ' + (data.matched ? 'is-match' : 'is-no-match');
				result.textContent = data.matched ? value('matched', 'Rule matched the sample submission.') : value('notMatched', 'Rule did not match the sample submission.');
			}
		}).catch(function (error) {
			setBusy(button ? [button] : [], false);
			if (result) {
				result.className = 'subin-auto-reply-simulation-result is-error';
				result.textContent = error.message;
			}
		});
	}

	function testSend() {
		var input = form.querySelector('[data-subin-test-email]');
		var button = form.querySelector('[data-subin-test-send]');
		var result = form.querySelector('[data-subin-test-result]');
		if (!input || !input.checkValidity()) {
			if (input) {
				input.reportValidity();
			}
			return;
		}
		setBusy(button ? [button] : [], true);
		if (result) {
			result.textContent = value('sending', 'Sending test…');
		}
		request(config.actions.testSend, { payload: JSON.stringify({ rule: collectRule(), sample: collectSample(), to: input.value }) }).then(function (data) {
			setBusy(button ? [button] : [], false);
			if (result) {
				result.textContent = data.message || value('testSent', 'Test email sent.');
				var link = document.createElement('a');
				link.href = config.urls && config.urls.emailLog ? config.urls.emailLog : 'admin.php?page=subin-email-log';
				link.textContent = value('openEmailLog', 'Open Email Log');
				result.appendChild(document.createTextNode(' '));
				result.appendChild(link);
			}
		}).catch(function (error) {
			setBusy(button ? [button] : [], false);
			if (result) {
				result.textContent = error.message;
			}
		});
	}

	function request(action, params) {
		var body = new FormData();
		body.append('action', action || '');
		body.append('nonce', config.nonce || '');
		Object.keys(params || {}).forEach(function (key) {
			body.append(key, params[key]);
		});

		return window.fetch(config.ajaxUrl, {
			method: 'POST',
			credentials: 'same-origin',
			body: body
		}).then(function (response) {
			return response.text().then(function (text) {
				var generic = value('invalidResponse', 'The server returned an invalid auto-reply response.');
				if (!text || !text.trim()) {
					throw responseError(generic, {}, response.status);
				}
				var parsed;
				try {
					parsed = JSON.parse(text);
				} catch (error) {
					throw responseError(generic, {}, response.status);
				}
				if (!parsed || typeof parsed !== 'object' || typeof parsed.success !== 'boolean' || !parsed.data || typeof parsed.data !== 'object') {
					throw responseError(generic, {}, response.status);
				}
				if (!response.ok || !parsed.success) {
					throw responseError(parsed.data.message || value('requestFailed', 'The auto-reply request could not be completed.'), parsed.data, response.status);
				}
				return parsed.data;
			});
		}).catch(function (error) {
			if (error && error.isAutoReplyError) {
				throw error;
			}
			throw responseError(value('requestFailed', 'The auto-reply request could not be completed. Reload the page and try again.'), {}, 0);
		});
	}

	function responseError(message, data, status) {
		var error = new Error(message);
		error.data = data || {};
		error.status = status || 0;
		error.isAutoReplyError = true;
		return error;
	}

	function showValidation(fields) {
		clearValidation();
		var firstControl = null;
		var firstPath = '';
		Object.keys(fields || {}).forEach(function (path) {
			var control = controlForError(path);
			if (!control) {
				return;
			}
			control.classList.add('is-invalid');
			rememberValidationTarget(control);
			control.setAttribute('aria-invalid', 'true');
			var message = document.createElement('span');
			state.validationMessageSerial += 1;
			var messageId = 'subin-auto-reply-api-error-' + String(state.validationMessageSerial);
			message.className = 'subin-auto-reply-inline-error';
			message.setAttribute('id', messageId);
			message.setAttribute('data-subin-api-error', '');
			message.textContent = fields[path] && fields[path].message ? fields[path].message : 'Review this field.';
			(control.closest('label') || control.parentNode).appendChild(message);
			validationAriaTargets(path, control).forEach(function (target) {
				rememberValidationTarget(target);
				target.setAttribute('aria-invalid', 'true');
				addDescribedBy(target, messageId);
			});
			revealParentDetails(control);
			if (!firstControl) {
				firstControl = control;
				firstPath = path;
			}
		});
		if (firstControl) {
			focusValidationControl(firstControl, firstPath);
		}
	}

	function validationAriaTargets(path, control) {
		if (path !== 'body' && path !== 'content') {
			return [control];
		}
		var editor = bodyEditor();
		if (editor && bodyEditorIsVisual(editor)) {
			var frame = editor.iframeElement || form.querySelector('#subin_rule_body_editor_ifr');
			if (!frame && typeof editor.getContentAreaContainer === 'function') {
				var container = editor.getContentAreaContainer();
				frame = container && typeof container.querySelector === 'function' ? container.querySelector('iframe') : null;
			}
			if (frame) {
				return [frame];
			}
		}
		var body = form.querySelector('#subin_rule_body_editor') || form.querySelector('[name="body"]');
		return body ? [body] : [control];
	}

	function rememberValidationTarget(target) {
		if (!target || typeof target.getAttribute !== 'function') {
			return;
		}
		var remembered = state.validationTargets.some(function (entry) { return entry.node === target; });
		if (remembered) {
			return;
		}
		state.validationTargets.push({
			node: target,
			hadInvalid: target.hasAttribute('aria-invalid'),
			invalid: target.getAttribute('aria-invalid'),
			hadDescribedBy: target.hasAttribute('aria-describedby'),
			describedBy: target.getAttribute('aria-describedby')
		});
	}

	function addDescribedBy(target, messageId) {
		var ids = String(target.getAttribute('aria-describedby') || '').split(/\s+/).filter(Boolean);
		if (ids.indexOf(messageId) === -1) {
			ids.push(messageId);
		}
		target.setAttribute('aria-describedby', ids.join(' '));
	}

	function revealParentDetails(control) {
		var current = control;
		while (current && typeof current.closest === 'function') {
			var details = current.closest('details');
			if (!details) {
				return;
			}
			details.open = true;
			current = details.parentNode;
		}
	}

	function focusValidationControl(control, path) {
		revealParentDetails(control);
		if (path === 'body' || path === 'content') {
			var editor = bodyEditor();
			if (editor && bodyEditorIsVisual(editor) && typeof editor.focus === 'function') {
				editor.focus();
				return;
			}
			var body = form.querySelector('#subin_rule_body_editor') || form.querySelector('[name="body"]');
			if (body && typeof body.focus === 'function') {
				body.focus();
				return;
			}
		}
		if (control && typeof control.focus === 'function') {
			control.focus();
		}
	}

	function clearValidation() {
		form.querySelectorAll('.is-invalid').forEach(function (control) {
			control.classList.remove('is-invalid');
		});
		state.validationTargets.forEach(function (entry) {
			if (!entry.node || typeof entry.node.setAttribute !== 'function') {
				return;
			}
			if (entry.hadInvalid) {
				entry.node.setAttribute('aria-invalid', entry.invalid === null ? '' : entry.invalid);
			} else {
				entry.node.removeAttribute('aria-invalid');
			}
			if (entry.hadDescribedBy) {
				entry.node.setAttribute('aria-describedby', entry.describedBy === null ? '' : entry.describedBy);
			} else {
				entry.node.removeAttribute('aria-describedby');
			}
		});
		state.validationTargets = [];
		form.querySelectorAll('[data-subin-api-error]').forEach(function (message) { message.remove(); });
	}

	function controlForError(path) {
		var map = {
			name: '[name="name"]', provider: '[name="provider"]', form_id: '[name="form_id"]',
			visitor_email_field: '[name="visitor_email_field"]', template_id: '[data-subin-open-design-picker]',
			body: '[data-subin-rule-wp-editor]', content: '[data-subin-rule-wp-editor]', subject: '[name="subject"]', preheader: '[name="preheader"]', priority: '[name="priority"]',
			'from.name': '[name="from_name"]', 'from.email': '[name="from_email"]',
			'reply_to.name': '[name="reply_to_name"]', 'reply_to.email': '[name="reply_to_email"]',
			'delivery.mode': '[name="delivery_mode"]', 'delivery.delay_seconds': '[name="delay_minutes"]'
		};
		if (map[path]) {
			return form.querySelector(map[path]);
		}
		var match = /^conditions\.(\d+)\.(field|type|operator|value)$/.exec(path);
		if (match) {
			var rows = form.querySelectorAll('[data-subin-condition-row]');
			var row = rows[positiveInteger(match[1])];
			if (!row) {
				return null;
			}
			var selector = match[2] === 'field' ? '[data-subin-condition-field]' : (match[2] === 'type' ? '[data-subin-condition-type]' : (match[2] === 'operator' ? '[data-subin-condition-operator]' : '[data-subin-condition-value], [data-subin-condition-choice], [data-subin-condition-range]'));
			return row.querySelector(selector);
		}
		return null;
	}

	function renderPreviewWarnings(warnings) {
		var host = form.querySelector('[data-subin-preview-diagnostics]');
		if (!host) {
			return;
		}
		host.textContent = '';
		if (!Array.isArray(warnings) || !warnings.length) {
			var clear = document.createElement('span');
			clear.className = 'is-clear';
			clear.textContent = value('previewReady', 'Preview ready. No merge-tag warnings.');
			host.appendChild(clear);
			return;
		}
		var list = document.createElement('ul');
		warnings.forEach(function (warning) {
			var item = document.createElement('li');
			item.textContent = String(warning.message || warning.code || value('previewWarning', 'Preview warning'));
			list.appendChild(item);
		});
		host.appendChild(list);
	}

	function setPreviewDiagnostics(message, isError) {
		var host = form.querySelector('[data-subin-preview-diagnostics]');
		if (!host) {
			return;
		}
		host.classList.toggle('is-error', Boolean(isError));
		host.textContent = message || '';
	}

	function canPreview() {
		if (!selectedProvider() || !selectedFormId() || !selectedEmailField()) {
			setPreviewDiagnostics(value('previewSelect', 'Select a form and visitor email field to preview.'));
			return false;
		}
		return true;
	}

	function schedulePreview() {
		window.clearTimeout(state.previewTimer);
		state.previewTimer = window.setTimeout(preview, 550);
	}

	function syncDerivedFields() {
		syncConditions();
		syncDelay();
	}

	function syncDelay() {
		var mode = form.querySelector('[data-subin-delivery-mode]');
		var minutes = form.querySelector('[data-subin-delay-minutes]');
		var hidden = form.querySelector('[data-subin-delay-seconds]');
		if (hidden) {
			hidden.value = mode && mode.value === 'delay' ? String(Math.max(1, positiveInteger(minutes ? minutes.value : 1)) * 60) : '0';
		}
	}

	function updateDeliveryState() {
		var mode = form.querySelector('[data-subin-delivery-mode]');
		var field = form.querySelector('[data-subin-delay-field]');
		if (field) {
			field.hidden = !mode || mode.value !== 'delay';
		}
		syncDelay();
	}

	function updateStatePresentation(rule) {
		var badge = form.querySelector('[data-subin-rule-state-badge]');
		var title = form.querySelector('[data-subin-save-title]');
		var primary = form.querySelector('[data-subin-save-intent]:last-child');
		var published = rule && rule.status === 'published';
		if (badge) {
			badge.textContent = published ? (rule.enabled ? value('publishedEnabled', 'Published · enabled') : value('publishedDisabled', 'Published · disabled')) : value('draft', 'Draft');
			badge.className = 'subin-auto-reply-state-text ' + (published && rule.enabled ? 'is-enabled' : (published ? 'is-disabled' : 'is-draft'));
		}
		if (title) {
			title.textContent = published ? value('publishedRule', 'Published rule') : value('draftRule', 'Draft rule');
		}
		if (primary) {
			primary.setAttribute('data-subin-save-intent', published ? 'save' : 'publish');
			primary.value = published ? 'save' : 'publish';
			primary.textContent = published ? value('saveChanges', 'Save changes') : value('publishEnable', 'Publish & enable');
		}
	}

	function updateEditorUrl(ruleId) {
		if (!ruleId || !window.history || !window.URL) {
			return;
		}
		var url = new URL(window.location.href);
		url.searchParams.set('rule_view', 'edit');
		url.searchParams.set('id', String(ruleId));
		window.history.replaceState({}, '', url.toString());
	}

	function ensureRuleName() {
		var name = form.querySelector('[name="name"]');
		var formSelect = form.querySelector('[data-subin-rule-form-select]');
		if (!name || name.value.trim() || !formSelect || !formSelect.value) {
			return;
		}
		var option = formSelect.options[formSelect.selectedIndex];
		if (option) {
			name.value = option.textContent.replace(/^.*?\s-\s/, '') + ' auto-reply';
		}
	}

	function markDirty() {
		state.editRevision += 1;
		state.dirty = true;
		setFeedback(value('unsaved', 'You have unsaved changes.'));
	}

	function setFeedback(message, isError) {
		var feedback = form.querySelector('[data-subin-rule-feedback]');
		if (!feedback) {
			return;
		}
		feedback.textContent = message || '';
		feedback.classList.toggle('is-error', Boolean(isError));
	}

	function setBusy(controls, busy) {
		Array.prototype.forEach.call(controls || [], function (control) {
			if (!control) {
				return;
			}
			control.disabled = busy;
			control.setAttribute('aria-busy', busy ? 'true' : 'false');
		});
	}

	function selectedProvider() {
		var select = form.querySelector('#subin-rule-provider');
		return select ? String(select.value || '').split('|')[0] : '';
	}

	function selectedFormId() {
		var select = form.querySelector('[data-subin-rule-form-select]');
		var parts = select ? String(select.value || '').split('|') : [];
		return parts.length > 1 ? parts[parts.length - 1] : (parts[0] || '');
	}

	function selectedEmailField() {
		var select = form.querySelector('[data-subin-rule-email-field-select]');
		var parts = select ? String(select.value || '').split('|') : [];
		return parts.length > 2 ? parts[parts.length - 1] : (parts[0] || '');
	}

	function inputValue(name) {
		var input = form.querySelector('[name="' + name + '"]');
		return input ? String(input.value || '') : '';
	}

	function setValue(name, newValue) {
		var input = form.querySelector('[name="' + name + '"]');
		if (input) {
			input.value = newValue;
		}
	}

	function findField(key) {
		for (var i = 0; i < state.fields.length; i += 1) {
			if (String(state.fields[i].key || '') === String(key || '')) {
				return state.fields[i];
			}
		}
		return null;
	}

	function fieldType(key) {
		var field = findField(key);
		return field ? String(field.type || 'text') : '';
	}

	function createSelect(attribute) {
		var select = document.createElement('select');
		select.setAttribute(attribute, '');
		return select;
	}

	function wrapControl(control, labelText) {
		var label = document.createElement('label');
		var text = document.createElement('span');
		text.className = 'screen-reader-text';
		text.textContent = labelText;
		label.appendChild(text);
		label.appendChild(control);
		return label;
	}

	function appendOption(select, optionValue, label) {
		var option = document.createElement('option');
		option.value = optionValue;
		option.textContent = label;
		select.appendChild(option);
	}

	function optionExists(select, optionValue) {
		return Array.prototype.some.call(select.options || [], function (option) { return option.value === String(optionValue || ''); });
	}

	function defaultOperators(type) {
		var common = [{ key: 'equals', label: 'Is' }, { key: 'not_equals', label: 'Is not' }, { key: 'is_empty', label: 'Is empty' }, { key: 'is_not_empty', label: 'Is not empty' }];
		if (['text', 'email', 'url', 'tel', 'textarea'].indexOf(type) !== -1) {
			return common.concat([{ key: 'contains', label: 'Contains' }, { key: 'not_contains', label: 'Does not contain' }]);
		}
		return common;
	}

	function positiveInteger(raw) {
		var number = parseInt(raw, 10);
		return isFinite(number) && number >= 0 ? number : 0;
	}

	function titleize(raw) {
		return String(raw || '').replace(/_/g, ' ').replace(/\b\w/g, function (letter) { return letter.toUpperCase(); });
	}

	function value(key, fallback) {
		return typeof strings[key] === 'string' && strings[key] ? strings[key] : fallback;
	}
}());
