/**
 * Niwa AI — Lead Form Generator Builder
 *
 * Hydrates the admin builder UI from the localized schema, maintains builder
 * state in the DOM, provides step/question/rule/option CRUD + drag reorder,
 * and serializes the full schema into a single hidden JSON input on submit.
 *
 * Relies on:
 *   - jQuery (bundled with wp-admin)
 *   - jQuery UI sortable (enqueued via dependency)
 *   - window.niwaLeadFormBuilder (localized config)
 *   - wp.media (completion media picker)
 */
(function ($) {
	'use strict';

	var CFG = window.niwaLeadFormBuilder || {};
	var SCHEMA = CFG.schema || {};
	var MATCH_TYPES = CFG.matchTypes || ['equals', 'contains', 'regex', 'any_of'];
	var QUESTION_TYPES = CFG.questionTypes || ['text', 'textarea', 'email', 'phone', 'number', 'choice_single', 'choice_multi', 'appointment'];
	var COMPLETE_SENTINEL = CFG.completeSentinel || '__complete__';
	var CHOICE_TYPES = ['choice_single', 'choice_multi'];
	var APPOINTMENT_TYPE = 'appointment';
	var SCHEDULER_CONFIG = CFG.schedulerConfig || {};
	var branchModeGroupSeq = 0;

	function log() {
		if (!window.console || !window.console.log) return;
		var args = Array.prototype.slice.call(arguments);
		args.unshift('[Niwa LF]');
		window.console.log.apply(window.console, args);
	}

	function warn() {
		if (!window.console || !window.console.warn) return;
		var args = Array.prototype.slice.call(arguments);
		args.unshift('[Niwa LF]');
		window.console.warn.apply(window.console, args);
	}

	function shortId(prefix) {
		var r = Math.floor(Math.random() * 0xffffffff).toString(16);
		while (r.length < 8) { r = '0' + r; }
		return prefix + '_' + r.slice(0, 8);
	}

	function sanitizeSlug(raw, prefix) {
		var s = (raw || '').toString().toLowerCase().replace(/[^a-z0-9_]/g, '_').replace(/^_+|_+$/g, '');
		if (s.length > 64) { s = s.slice(0, 64); }
		if (!s && prefix) { s = shortId(prefix); }
		return s;
	}

	// ─────────────────────────────────────────────────────────────
	// Boot
	// ─────────────────────────────────────────────────────────────
	$(function () {
		var $form = $('#niwa-lf-form');
		var $builder = $('#niwa-lf-builder');
		var $stepsHost = $('#niwa-lf-steps');
		var stepTpl = document.getElementById('niwa-lf-step-tpl');
		var questionTpl = document.getElementById('niwa-lf-question-tpl');
		var optionTpl = document.getElementById('niwa-lf-option-tpl');
		var ruleTpl = document.getElementById('niwa-lf-rule-tpl');

		if (!$form.length || !$builder.length || !$stepsHost.length || !stepTpl || !questionTpl || !optionTpl || !ruleTpl) {
			warn('builder container missing');
			return;
		}

		hydrateBehavior(SCHEMA);
		hydrateSteps(SCHEMA);
		makeStepsSortable();
		refreshEmptyState();
		refreshAllCandidateSelectors();

		// Card A bindings.
		$('#niwa-lf-add-step').on('click', function (e) {
			e.preventDefault();
			var $step = addStep({ id: shortId('s'), label: '', questions: [] });
			$step.find('[data-step-label]').trigger('focus');
			refreshAllCandidateSelectors();
			refreshEmptyState();
		});

		// Delegated events.
		$stepsHost.on('click', '[data-delete-step]', function (e) {
			e.preventDefault();
			e.stopPropagation();
			if (!window.confirm(tr('Delete this step and all its questions?', 'Delete this step?'))) return;
			$(this).closest('.niwa-lf-step').remove();
			refreshAllCandidateSelectors();
			refreshEmptyState();
		});

		$stepsHost.on('input', '[data-step-label]', function () {
			var $step = $(this).closest('.niwa-lf-step');
			var val = ($(this).val() || '').toString();
			$step.find('> details > summary > [data-title]').text(val || tr('Untitled step'));
		});

		$stepsHost.on('click', '[data-add-question]', function (e) {
			e.preventDefault();
			var $step = $(this).closest('.niwa-lf-step');
			var $q = addQuestion($step.find('> details > .niwa-lf-step__body > [data-questions]'), {
				id: shortId('q'),
				label: '',
				type: 'text',
				required: 1,
				placeholder: '',
				options: [],
				appointment: { service_ids: [], team_member_ids: [] },
				validation: { min: null, max: null, regex: '' },
				conditional: { enabled: 0, rules: [], llm_enabled: 0, llm_instruction: '', llm_candidates: [] }
			});
			$q.find('[data-q-label]').trigger('focus');
			updateQuestionCount($step);
			refreshAllCandidateSelectors();
		});

		$stepsHost.on('click', '[data-delete-question]', function (e) {
			e.preventDefault();
			e.stopPropagation();
			if (!window.confirm(tr('Delete this question?'))) return;
			var $q = $(this).closest('.niwa-lf-question');
			var $step = $q.closest('.niwa-lf-step');
			$q.remove();
			updateQuestionCount($step);
			refreshAllCandidateSelectors();
		});

		$stepsHost.on('input', '[data-q-label]', function () {
			var $q = $(this).closest('.niwa-lf-question');
			var val = ($(this).val() || '').toString();
			$q.find('> details > summary > [data-title]').text(val || tr('Untitled question'));
			refreshAllCandidateSelectors();
		});

		$stepsHost.on('change', '[data-q-type]', function () {
			var $q = $(this).closest('.niwa-lf-question');
			syncOptionsVisibility($q);
		});

		$stepsHost.on('click', '[data-add-option]', function (e) {
			e.preventDefault();
			var $q = $(this).closest('.niwa-lf-question');
			addOption($q.find('[data-options]'), { value: '', label: '' });
		});

		$stepsHost.on('click', '[data-delete-option]', function (e) {
			e.preventDefault();
			$(this).closest('.niwa-lf-option-row').remove();
		});

		$stepsHost.on('change', '[data-branch-mode]', function () {
			var $q = $(this).closest('.niwa-lf-question');
			$q.find('[data-branch-mode]').not(this).prop('checked', false);
			$(this).prop('checked', true);
			syncBranchMode($q);
		});

		$stepsHost.on('click', '[data-add-rule]', function (e) {
			e.preventDefault();
			var $q = $(this).closest('.niwa-lf-question');
			addRule($q.find('[data-rules]'), { match_type: 'equals', match_value: '', next_question: '' });
			refreshCandidateSelector($q);
		});

		$stepsHost.on('click', '[data-delete-rule]', function (e) {
			e.preventDefault();
			$(this).closest('.niwa-lf-rule').remove();
		});

		$stepsHost.on('change', '[data-rule-match-type]', function () {
			var $rule = $(this).closest('.niwa-lf-rule');
			updateRuleHelp($rule);
		});

		$stepsHost.on('click', '[data-candidates-select-all]', function (e) {
			e.preventDefault();
			var $q = $(this).closest('.niwa-lf-question');
			$q.find('[data-llm-candidates] input[type="checkbox"]').prop('checked', true);
		});

		$stepsHost.on('click', '[data-candidates-clear]', function (e) {
			e.preventDefault();
			var $q = $(this).closest('.niwa-lf-question');
			$q.find('[data-llm-candidates] input[type="checkbox"]').prop('checked', false);
		});

		// Completion media picker.
		wireMediaPicker();

		// Submit hook: serialize schema to hidden JSON field.
		$form.on('submit', function (e) {
			var err = validateBuilder();
			if (err) {
				e.preventDefault();
				window.alert(err);
				return false;
			}
			var schema = serializeSchema();
			$('#niwa-lf-json-out').val(JSON.stringify(schema));
		});
	});

	// ─────────────────────────────────────────────────────────────
	// Hydration
	// ─────────────────────────────────────────────────────────────
	function hydrateBehavior(schema) {
		$('#niwa-lf-enabled').prop('checked', !!schema.enabled);
		$('input[name="niwa-lf-mode"][value="' + (schema.mode || 'chat') + '"]').prop('checked', true);
		$('#niwa-lf-trigger').val(schema.trigger || 'after_first_user_msg');
		$('#niwa-lf-trigger-delay').val(parseInt(schema.trigger_delay_ms, 10) || 0);
		$('#niwa-lf-trigger-description').val(schema.trigger_description || '');
		$('#niwa-lf-partial-submit').prop('checked', !!schema.partial_submit);
		$('#niwa-lf-completion-text').val(schema.completion_text || '');
	}

	function hydrateSteps(schema) {
		var steps = Array.isArray(schema.steps) ? schema.steps : [];
		steps.forEach(function (s) { addStep(s); });
	}

	function addStep(step) {
		var tpl = document.getElementById('niwa-lf-step-tpl');
		var frag = tpl.content.cloneNode(true);
		var $step = $(frag.firstElementChild);
		var stepId = sanitizeSlug(step.id, 's');
		$step.attr('data-step-id', stepId);
		$step.find('> details > .niwa-lf-step__body > .niwa-field > [data-step-label]').val(step.label || '');
		$step.find('> details > summary > [data-title]').text(step.label || tr('Untitled step'));

		var $qHost = $step.find('> details > .niwa-lf-step__body > [data-questions]');
		var questions = Array.isArray(step.questions) ? step.questions : [];
		questions.forEach(function (q) { addQuestion($qHost, q); });

		$('#niwa-lf-steps').append($step);
		updateQuestionCount($step);
		makeQuestionsSortable($qHost);
		return $step;
	}

	function addQuestion($qHost, q) {
		var tpl = document.getElementById('niwa-lf-question-tpl');
		var frag = tpl.content.cloneNode(true);
		var $q = $(frag.firstElementChild);
		var qid = sanitizeSlug(q.id, 'q');
		$q.attr('data-question-id', qid);
		$q.find('[data-id-display]').text(qid);
		branchModeGroupSeq += 1;
		$q.find('[data-branch-mode]').attr('name', 'niwa-lf-branch-mode-' + qid + '-' + branchModeGroupSeq);
		$q.find('[data-q-label]').val(q.label || '');
		$q.find('> details > summary > [data-title]').text(q.label || tr('Untitled question'));

		var type = QUESTION_TYPES.indexOf(q.type) >= 0 ? q.type : 'text';
		$q.find('[data-q-type]').val(type);
		$q.find('[data-q-required]').prop('checked', !!q.required);
		$q.find('[data-q-placeholder]').val(q.placeholder || '');
		hydrateAppointmentOptions($q, q.appointment || {});

		// Options.
		var $optHost = $q.find('[data-options]');
		var opts = Array.isArray(q.options) ? q.options : [];
		opts.forEach(function (o) { addOption($optHost, o); });

		// Validation.
		var v = q.validation || {};
		if (v.min !== null && v.min !== undefined && v.min !== '') { $q.find('[data-v-min]').val(v.min); }
		if (v.max !== null && v.max !== undefined && v.max !== '') { $q.find('[data-v-max]').val(v.max); }
		$q.find('[data-v-regex]').val(v.regex || '');

		// Branching (conditional logic + AI).
		var c = q.conditional || {};
		var mode = computeBranchMode(!!c.enabled, !!c.llm_enabled);
		$q.find('[data-branch-mode][value="' + mode + '"]').prop('checked', true);
		$q.find('[data-llm-instruction]').val(c.llm_instruction || '');

		var $ruleHost = $q.find('[data-rules]');
		var rules = Array.isArray(c.rules) ? c.rules : [];
		rules.forEach(function (r) { addRule($ruleHost, r); });

		$qHost.append($q);
		syncOptionsVisibility($q);
		syncBranchMode($q);
		// Candidate selectors filled after full hydration via refreshAllCandidateSelectors().
		$q.data('llmCandidates', Array.isArray(c.llm_candidates) ? c.llm_candidates.slice() : []);
		return $q;
	}

	// ─────────────────────────────────────────────────────────────
	// Branching mode helpers
	// ─────────────────────────────────────────────────────────────
	function computeBranchMode(hasRules, hasAI) {
		if (hasRules && hasAI) return 'rules_ai';
		if (hasRules) return 'rules';
		if (hasAI) return 'ai';
		return 'none';
	}

	function readBranchMode($q) {
		var val = $q.find('[data-branch-mode]:checked').val();
		if (val === 'rules' || val === 'ai' || val === 'rules_ai') return val;
		return 'none';
	}

	function syncBranchMode($q) {
		var mode = readBranchMode($q);
		var showRules = (mode === 'rules' || mode === 'rules_ai');
		var showAI    = (mode === 'ai' || mode === 'rules_ai');
		$q.find('[data-branch-section="rules"]').prop('hidden', !showRules);
		$q.find('[data-branch-section="ai"]').prop('hidden', !showAI);
		$q.find('[data-mode-card]').each(function () {
			var $card = $(this);
			var isActive = ($card.attr('data-mode-card') === mode);
			$card.toggleClass('is-active', isActive);
		});
	}

	function addOption($host, o) {
		var tpl = document.getElementById('niwa-lf-option-tpl');
		var frag = tpl.content.cloneNode(true);
		var $row = $(frag.firstElementChild);
		$row.find('[data-opt-value]').val(o && o.value ? o.value : '');
		$row.find('[data-opt-label]').val(o && o.label ? o.label : '');
		$host.append($row);
		return $row;
	}

	function addRule($host, r) {
		var tpl = document.getElementById('niwa-lf-rule-tpl');
		var frag = tpl.content.cloneNode(true);
		var $row = $(frag.firstElementChild);
		var mt = MATCH_TYPES.indexOf(r.match_type) >= 0 ? r.match_type : 'equals';
		$row.find('[data-rule-match-type]').val(mt);
		var mv = r.match_value;
		if (Array.isArray(mv)) { mv = mv.join(', '); }
		$row.find('[data-rule-match-value]').val(mv || '');
		updateRuleHelp($row);
		$row.data('pendingNext', r.next_question || '');
		$host.append($row);
		return $row;
	}

	function updateRuleHelp($rule) {
		var mt = ($rule.find('[data-rule-match-type]').val() || 'equals').toString();
		var $help = $rule.find('[data-rule-help]');
		var $value = $rule.find('[data-rule-match-value]');
		var text = '';
		var placeholder = tr('value');
		switch (mt) {
			case 'contains':
				text = tr('Matches when the answer contains this text (case-insensitive).');
				placeholder = tr('text to look for');
				break;
			case 'any_of':
				text = tr('Comma-separated list. Matches when the answer equals any of these values.');
				placeholder = tr('e.g. Yes, Sure, Ok');
				break;
			case 'regex':
				text = tr('Advanced: a regular expression applied to the raw answer string.');
				placeholder = '^yes$';
				break;
			case 'equals':
			default:
				text = tr('Matches when the answer exactly equals this value (case-insensitive).');
				placeholder = tr('exact value');
				break;
		}
		$help.text(text);
		$value.attr('placeholder', placeholder);

		// Extra context: for choice questions show available option values inline.
		var $q = $rule.closest('.niwa-lf-question');
		if ($q.length && (mt === 'equals' || mt === 'any_of')) {
			var optValues = [];
			$q.find('[data-options] .niwa-lf-option-row').each(function () {
				var v = ($(this).find('[data-opt-value]').val() || '').toString();
				var l = ($(this).find('[data-opt-label]').val() || '').toString();
				var pick = v || l;
				if (pick) { optValues.push(pick); }
			});
			if (optValues.length) {
				$help.text(text + ' ' + tr('Available options:') + ' ' + optValues.join(', '));
			}
		}
	}

	// ─────────────────────────────────────────────────────────────
	// Options visibility & counts
	// ─────────────────────────────────────────────────────────────
	function syncOptionsVisibility($q) {
		var t = $q.find('[data-q-type]').val();
		var isChoice = CHOICE_TYPES.indexOf(t) >= 0;
		$q.find('[data-options-wrap]').toggle(isChoice);
		$q.find('[data-appointment-wrap]').prop('hidden', t !== APPOINTMENT_TYPE);
	}

	function hydrateAppointmentOptions($q, appointment) {
		var services = Array.isArray(SCHEDULER_CONFIG.services) ? SCHEDULER_CONFIG.services : [];
		var team = Array.isArray(SCHEDULER_CONFIG.teamMembers) ? SCHEDULER_CONFIG.teamMembers : [];
		var selectedServices = Array.isArray(appointment.service_ids) ? appointment.service_ids.map(String) : [];
		var selectedTeam = Array.isArray(appointment.team_member_ids) ? appointment.team_member_ids.map(String) : [];
		var $services = $q.find('[data-appointment-services]').empty();
		var $team = $q.find('[data-appointment-team]').empty();

		function addChecks($host, rows, selected, kind) {
			rows.forEach(function (row) {
				if (!row || row.id === undefined || row.id === null) return;
				var id = String(row.id);
				var $label = $('<label class="niwa-lf-appointment-option"></label>');
				var $input = $('<input type="checkbox" />')
					.attr('data-appointment-' + kind, '')
					.val(id)
					.prop('checked', selected.indexOf(id) >= 0);
				var title = String(row.name || row.label || id);
				var meta = '';
				if (kind === 'service' && (row.duration_minutes || row.durationMinutes)) meta = String(row.duration_minutes || row.durationMinutes) + ' min';
				if (kind === 'team' && row.email) meta = String(row.email);
				$label.append($input).append($('<span></span>').text(title));
				if (meta) $label.append($('<small></small>').text(meta));
				$host.append($label);
			});
		}

		addChecks($services, services, selectedServices, 'service');
		addChecks($team, team, selectedTeam, 'team');
		$q.find('[data-appointment-empty]').prop('hidden', services.length > 0 || team.length > 0);
	}

	function updateQuestionCount($step) {
		var n = $step.find('.niwa-lf-question').length;
		$step.find('> details > summary > [data-qcount]').text(n + ' ' + tr('questions'));
	}

	function refreshEmptyState() {
		var n = $('#niwa-lf-steps .niwa-lf-step').length;
		$('#niwa-lf-empty').toggle(n === 0);
	}

	// ─────────────────────────────────────────────────────────────
	// Candidate/next-question selectors
	// ─────────────────────────────────────────────────────────────
	function allQuestions() {
		var out = [];
		$('#niwa-lf-steps .niwa-lf-step').each(function () {
			var $step = $(this);
			var stepLabel = ($step.find('[data-step-label]').val() || tr('Untitled step')).toString();
			$step.find('.niwa-lf-question').each(function () {
				var $q = $(this);
				var id = $q.attr('data-question-id');
				var label = ($q.find('[data-q-label]').val() || tr('Untitled question')).toString();
				out.push({ id: id, label: label, stepLabel: stepLabel });
			});
		});
		return out;
	}

	function refreshAllCandidateSelectors() {
		$('#niwa-lf-steps .niwa-lf-question').each(function () {
			refreshCandidateSelector($(this));
		});
	}

	function refreshCandidateSelector($q) {
		var currentId = $q.attr('data-question-id');
		var all = allQuestions();
		var candidates = all.filter(function (it) { return it.id !== currentId; });

		// LLM candidate checklist.
		var $llm = $q.find('[data-llm-candidates]');
		var $empty = $q.find('[data-candidates-empty]');
		var prevSelected = [];
		$llm.find('input[type="checkbox"]:checked').each(function () { prevSelected.push(this.value); });
		if (!prevSelected.length) {
			var pending = $q.data('llmCandidates');
			if (Array.isArray(pending) && pending.length) {
				prevSelected = pending.slice();
				$q.data('llmCandidates', null);
			}
		}
		$llm.empty();
		if (!candidates.length) {
			$empty.prop('hidden', false);
		} else {
			$empty.prop('hidden', true);
			candidates.forEach(function (c) {
				var checked = prevSelected.indexOf(c.id) >= 0;
				var $row = $(
					'<label class="niwa-lf-candidate">' +
						'<input type="checkbox" value="' + escapeAttr(c.id) + '"' + (checked ? ' checked' : '') + ' />' +
						'<span class="niwa-lf-candidate__body">' +
							'<span class="niwa-lf-candidate__label"></span>' +
							'<code class="niwa-lf-candidate__id"></code>' +
						'</span>' +
					'</label>'
				);
				$row.find('.niwa-lf-candidate__label').text(c.label || tr('Untitled question'));
				$row.find('.niwa-lf-candidate__id').text(c.id);
				$llm.append($row);
			});
		}

		// Rule next-question selects.
		$q.find('[data-rules] .niwa-lf-rule').each(function () {
			var $rule = $(this);
			var $sel = $rule.find('[data-rule-next]');
			var prev = $sel.val() || $rule.data('pendingNext') || '';
			$sel.empty();
			$sel.append($('<option>').val('').text('— ' + tr('select next question') + ' —'));
			candidates.forEach(function (c) {
				$sel.append($('<option>').val(c.id).text(c.label ? (c.label + ' (' + c.id + ')') : c.id));
			});
			$sel.append($('<option>').val(COMPLETE_SENTINEL).text(tr('Complete form')));
			if (prev) { $sel.val(prev); }
			$rule.removeData('pendingNext');
			updateRuleHelp($rule);
		});
	}

	function escapeAttr(s) {
		return (s || '').toString().replace(/[&<>"']/g, function (ch) {
			return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[ch];
		});
	}

	// ─────────────────────────────────────────────────────────────
	// Sortable
	// ─────────────────────────────────────────────────────────────
	function makeStepsSortable() {
		if (!$.fn.sortable) return;
		$('#niwa-lf-steps').sortable({
			handle: '.niwa-lf-drag',
			placeholder: 'niwa-lf-sort-placeholder',
			axis: 'y',
			tolerance: 'pointer',
			stop: function () { refreshAllCandidateSelectors(); }
		});
	}

	function makeQuestionsSortable($qHost) {
		if (!$.fn.sortable) return;
		$qHost.sortable({
			handle: '.niwa-lf-drag',
			placeholder: 'niwa-lf-sort-placeholder',
			axis: 'y',
			tolerance: 'pointer',
			connectWith: '.niwa-lf-questions',
			stop: function (e, ui) {
				var $step = $(ui.item).closest('.niwa-lf-step');
				updateQuestionCount($step);
				$('#niwa-lf-steps .niwa-lf-step').each(function () { updateQuestionCount($(this)); });
				refreshAllCandidateSelectors();
			}
		});
	}

	// ─────────────────────────────────────────────────────────────
	// Validation
	// ─────────────────────────────────────────────────────────────
	function validateBuilder() {
		var steps = $('#niwa-lf-steps .niwa-lf-step');
		if (!steps.length) {
			// allow empty form (disabled state) — but if enabled, warn.
			if ($('#niwa-lf-enabled').is(':checked')) {
				return tr('Form is enabled but has no steps. Add at least one step or disable the form.');
			}
			return '';
		}
		var err = '';
		steps.each(function () {
			if (err) return;
			var $step = $(this);
			var label = ($step.find('[data-step-label]').val() || '').toString().trim();
			if (!label) {
				err = tr('Every step needs a label.');
				return;
			}
			var $qs = $step.find('.niwa-lf-question');
			$qs.each(function () {
				if (err) return;
				var $q = $(this);
				var qLabel = ($q.find('[data-q-label]').val() || '').toString().trim();
				if (!qLabel) {
					err = tr('Every question needs a label.');
					return;
				}
				var t = $q.find('[data-q-type]').val();
				if (CHOICE_TYPES.indexOf(t) >= 0) {
					var hasOpt = false;
					$q.find('[data-options] .niwa-lf-option-row').each(function () {
						var v = ($(this).find('[data-opt-value]').val() || '').toString().trim();
						var l = ($(this).find('[data-opt-label]').val() || '').toString().trim();
						if (v || l) { hasOpt = true; }
					});
					if (!hasOpt) {
						err = tr('Choice-type questions need at least one option.');
					}
				}

				var mode = readBranchMode($q);
				if ((mode === 'ai' || mode === 'rules_ai')) {
					var candCount = $q.find('[data-llm-candidates] input[type="checkbox"]:checked').length;
					var allCount = $q.find('[data-llm-candidates] input[type="checkbox"]').length;
					if (allCount > 0 && candCount === 0) {
						err = tr('AI routing is on for "%s" but no candidate questions are ticked. Tick the questions the AI is allowed to route to, or switch the mode.').replace('%s', qLabel);
					}
				}
			});
		});
		return err;
	}

	// ─────────────────────────────────────────────────────────────
	// Serialize
	// ─────────────────────────────────────────────────────────────
	function serializeSchema() {
		var schema = {
			enabled: $('#niwa-lf-enabled').is(':checked') ? 1 : 0,
			mode: $('input[name="niwa-lf-mode"]:checked').val() || 'chat',
			trigger: $('#niwa-lf-trigger').val() || 'after_first_user_msg',
			trigger_delay_ms: parseInt($('#niwa-lf-trigger-delay').val(), 10) || 0,
			trigger_description: ($('#niwa-lf-trigger-description').val() || '').toString(),
			partial_submit: $('#niwa-lf-partial-submit').is(':checked') ? 1 : 0,
			completion_text: ($('#niwa-lf-completion-text').val() || '').toString(),
			completion_media_id: parseInt($('#niwa_lf_completion_media_id').val(), 10) || 0,
			steps: []
		};

		$('#niwa-lf-steps .niwa-lf-step').each(function () {
			var $step = $(this);
			var step = {
				id: sanitizeSlug($step.attr('data-step-id'), 's'),
				label: ($step.find('[data-step-label]').val() || '').toString(),
				questions: []
			};
			$step.find('.niwa-lf-question').each(function () {
				var $q = $(this);
				var mode = readBranchMode($q);
				var rulesEnabled = (mode === 'rules' || mode === 'rules_ai') ? 1 : 0;
				var aiEnabled    = (mode === 'ai' || mode === 'rules_ai') ? 1 : 0;
				var q = {
					id: sanitizeSlug($q.attr('data-question-id'), 'q'),
					label: ($q.find('[data-q-label]').val() || '').toString(),
					type: $q.find('[data-q-type]').val() || 'text',
					required: $q.find('[data-q-required]').is(':checked') ? 1 : 0,
					placeholder: ($q.find('[data-q-placeholder]').val() || '').toString(),
					options: [],
					appointment: {
						service_ids: [],
						team_member_ids: []
					},
					validation: {
						min: readIntOrNull($q.find('[data-v-min]').val()),
						max: readIntOrNull($q.find('[data-v-max]').val()),
						regex: ($q.find('[data-v-regex]').val() || '').toString()
					},
					conditional: {
						enabled: rulesEnabled,
						rules: [],
						llm_enabled: aiEnabled,
						llm_instruction: ($q.find('[data-llm-instruction]').val() || '').toString(),
						llm_candidates: []
					}
				};

				$q.find('[data-options] .niwa-lf-option-row').each(function () {
					var v = ($(this).find('[data-opt-value]').val() || '').toString();
					var l = ($(this).find('[data-opt-label]').val() || '').toString();
					if (!v && !l) return;
					q.options.push({ value: v || l, label: l || v });
				});

				$q.find('[data-appointment-service]:checked').each(function () {
					q.appointment.service_ids.push(String(this.value));
				});
				$q.find('[data-appointment-team]:checked').each(function () {
					q.appointment.team_member_ids.push(String(this.value));
				});

				$q.find('[data-rules] .niwa-lf-rule').each(function () {
					var $r = $(this);
					var mt = $r.find('[data-rule-match-type]').val() || 'equals';
					var mvRaw = ($r.find('[data-rule-match-value]').val() || '').toString();
					var mv = mvRaw;
					if (mt === 'any_of') {
						mv = mvRaw.split(',').map(function (s) { return s.trim(); }).filter(Boolean);
					}
					var next = ($r.find('[data-rule-next]').val() || '').toString();
					if (!next) return;
					q.conditional.rules.push({ match_type: mt, match_value: mv, next_question: next });
				});

				$q.find('[data-llm-candidates] input[type="checkbox"]:checked').each(function () {
					q.conditional.llm_candidates.push(this.value);
				});

				step.questions.push(q);
			});
			schema.steps.push(step);
		});

		return schema;
	}

	function readIntOrNull(v) {
		if (v === '' || v === null || typeof v === 'undefined') return null;
		var n = parseInt(v, 10);
		return isNaN(n) ? null : n;
	}

	// ─────────────────────────────────────────────────────────────
	// Completion media picker (wp.media)
	// ─────────────────────────────────────────────────────────────
	function wireMediaPicker() {
		var frame = null;
		var $id = $('#niwa_lf_completion_media_id');
		var $preview = $('#niwa_lf_completion_media_preview');
		var $remove = $('#niwa_lf_completion_media_remove');

		$('#niwa_lf_completion_media_btn').on('click', function (e) {
			e.preventDefault();
			if (!window.wp || !window.wp.media) {
				warn('wp.media not available');
				return;
			}
			if (frame) { frame.open(); return; }
			frame = window.wp.media({
				title: tr('Choose completion media'),
				button: { text: tr('Use this media') },
				multiple: false
			});
			frame.on('select', function () {
				var att = frame.state().get('selection').first().toJSON();
				var mime = att.mime || '';
				var type = att.type || '';
				var isImage = type === 'image' || mime.indexOf('image/') === 0;
				var isVideo = type === 'video' || mime.indexOf('video/') === 0;
				if (!isImage && !isVideo) {
					window.alert(tr('Please choose an image, GIF, or video file.'));
					return;
				}
				$id.val(att.id);
				var html = '';
				if (isImage) {
					html = '<img src="' + att.url + '" alt="" style="max-width:200px;max-height:150px;display:block;border-radius:8px;" />';
				} else {
					html = '<video controls style="max-width:200px;max-height:150px;display:block;border-radius:8px;"><source src="' + att.url + '" type="' + (mime || 'video/mp4') + '" /></video>';
				}
				if (att.filename) {
					html += '<p style="margin:6px 0 0;font-size:13px;"><strong>' + att.filename + '</strong></p>';
				}
				$preview.html(html);
				$remove.show();
			});
			frame.open();
		});

		$remove.on('click', function (e) {
			e.preventDefault();
			$id.val('');
			$preview.empty();
			$remove.hide();
		});
	}

	// ─────────────────────────────────────────────────────────────
	// i18n shim — uses CFG.i18n if present, else fallback to the string itself.
	// ─────────────────────────────────────────────────────────────
	function tr(key, fallback) {
		if (CFG.i18n && typeof CFG.i18n[key] === 'string') return CFG.i18n[key];
		return fallback || key;
	}
})(jQuery);
