/**
 * Kramar — Invoice Template Builder
 *
 * 3-panel drag-and-drop editor mirroring the Email Builder architecture.
 * Palette (left) | Canvas (center) | Settings (right)
 *
 * @package Kramar
 * @since   1.0.0
 */

(function () {
	'use strict';

	var cfg         = window.kramarInvoiceBuilder || {};
	var ajaxUrl     = cfg.ajaxUrl || '';
	var nonce       = cfg.nonce || '';
	var docType     = cfg.docType || 'invoice';
	var blockTypes  = cfg.blockTypes || {};
	var lockedTypes = cfg.locked || [];
	var i18n        = cfg.i18n || {};
	var hasOrder    = cfg.hasOrder !== false;

	// ─── State ────────────────────────────────────────────────────────────

	var state = {
		blocks: [],
		selectedBlockId: null,
		dirty: false,
		undoStack: [],
		redoStack: [],
		rendered: {},      // blockId -> server-rendered HTML (real PDF markup)
		footerHtml: '',    // server-rendered footer inner HTML
		renderTimer: null,
		renderSeq: 0,
		renderNotice: '',  // banner shown when the server can't build a real preview
	};

	// ─── Helpers ──────────────────────────────────────────────────────────

	function uid() {
		return 'b' + Math.random().toString(36).slice(2, 9);
	}

	function esc(s) {
		if (s == null) return '';
		return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
	}

	function closest(el, sel) {
		while (el && el !== document) {
			if (el.matches && el.matches(sel)) return el;
			el = el.parentNode;
		}
		return null;
	}

	function findBlock(id) {
		for (var i = 0; i < state.blocks.length; i++) {
			if (state.blocks[i].id === id) return state.blocks[i];
		}
		return null;
	}

	function findBlockIndex(id) {
		for (var i = 0; i < state.blocks.length; i++) {
			if (state.blocks[i].id === id) return i;
		}
		return -1;
	}

	function isLocked(type) {
		return lockedTypes.indexOf(type) !== -1;
	}

	function createBlock(type) {
		var def = blockTypes[type] || {};
		var defaults = def.defaults || {};
		var settings = {};
		for (var k in defaults) {
			if (Object.prototype.hasOwnProperty.call(defaults, k)) {
				settings[k] = defaults[k];
			}
		}
		return { id: uid(), type: type, settings: settings };
	}

	// ─── Snapshot / Undo / Redo ───────────────────────────────────────────

	function snapshot() {
		state.undoStack.push(JSON.stringify(state.blocks));
		if (state.undoStack.length > 50) state.undoStack.shift();
		state.redoStack = [];
	}

	function undo() {
		if (!state.undoStack.length) return;
		state.redoStack.push(JSON.stringify(state.blocks));
		state.blocks = JSON.parse(state.undoStack.pop());
		state.selectedBlockId = null;
		renderCanvas();
		renderSettings(null);
		markDirty();
		scheduleRender();
	}

	function redo() {
		if (!state.redoStack.length) return;
		state.undoStack.push(JSON.stringify(state.blocks));
		state.blocks = JSON.parse(state.redoStack.pop());
		state.selectedBlockId = null;
		renderCanvas();
		renderSettings(null);
		markDirty();
		scheduleRender();
	}

	// ─── Dirty Flag ──────────────────────────────────────────────────────

	function markDirty() {
		state.dirty = true;
		var btn = document.querySelector('.kramar-eb-toolbar__save');
		if (btn) btn.classList.add('kramar-eb-toolbar__save--dirty');
	}

	function clearDirty() {
		state.dirty = false;
		var btn = document.querySelector('.kramar-eb-toolbar__save');
		if (btn) btn.classList.remove('kramar-eb-toolbar__save--dirty');
	}

	// ─── Init ─────────────────────────────────────────────────────────────

	function init() {
		state.blocks = Array.isArray(cfg.blocks) ? cfg.blocks.slice() : [];
		state.blocks.forEach(function (b) {
			if (!b.id) b.id = uid();
			if (!b.settings) b.settings = {};
		});

		renderCanvas();
		bindToolbar();
		bindPalette();
		bindCanvasDelegated();
		bindKeyboard();
		bindBeforeUnload();

		var firstEditable = null;
		for (var i = 0; i < state.blocks.length; i++) {
			if (!isLocked(state.blocks[i].type)) { firstEditable = state.blocks[i]; break; }
		}
		if (firstEditable) selectBlock(firstEditable.id);

		// Pull the real, server-rendered document into the canvas.
		scheduleRender(true);
	}

	// ─── Server Render (single source of truth) ───────────────────────────

	// Debounced: ask the server for the real PDF markup of each block so the
	// canvas is a faithful WYSIWYG of the final document (no JS re-implementation).
	function scheduleRender(immediate) {
		if (!hasOrder) { renderCanvas(); return; }
		if (state.renderTimer) { clearTimeout(state.renderTimer); }
		state.renderTimer = setTimeout(doRender, immediate ? 0 : 220);
	}

	function doRender() {
		state.renderTimer = null;
		var seq = ++state.renderSeq;

		var body = new FormData();
		body.append('action', 'kramar_invoice_render');
		body.append('_ajax_nonce', nonce);
		body.append('doc_type', docType);
		body.append('blocks', JSON.stringify(state.blocks));

		fetch(ajaxUrl, { method: 'POST', body: body })
			.then(function (r) { return r.json(); })
			.then(function (r) {
				if (seq !== state.renderSeq) return; // a newer render superseded us
				if (r && r.success && r.data && r.data.blocks) {
					var map = {};
					for (var i = 0; i < r.data.blocks.length; i++) {
						map[r.data.blocks[i].id] = r.data.blocks[i].html;
					}
					state.rendered = map;
					state.footerHtml = r.data.footer || '';
					state.renderNotice = '';
					renderCanvas();
				} else if (r && r.data && (r.data.reason === 'no_refund' || r.data.reason === 'no_order')) {
					// No real data to render (e.g. a credit note with no refund yet):
					// show empty-content placeholders + a hint instead of a stuck "Rendering…".
					var empties = {};
					for (var j = 0; j < state.blocks.length; j++) {
						empties[state.blocks[j].id] = '';
					}
					state.rendered = empties;
					state.footerHtml = '';
					state.renderNotice = r.data.reason === 'no_refund'
						? (i18n.noRefundData || 'Refund an order to preview the credit note with real refunded amounts.')
						: (i18n.noOrderData || 'Create a WooCommerce order to preview this template with real data.');
					renderCanvas();
				}
			})
			.catch(function () { /* keep last good render */ });
	}

	// ─── Canvas Render ───────────────────────────────────────────────────

	function renderCanvas() {
		var container = document.getElementById('kramar-ib-canvas-container');
		var empty = document.getElementById('kramar-ib-canvas-empty');
		if (!container) return;

		if (!state.blocks.length) {
			container.innerHTML = '';
			if (empty) empty.style.display = '';
			return;
		}
		if (empty) empty.style.display = 'none';

		var html = '';
		if (!hasOrder) {
			html += '<div class="kramar-ib-canvas-banner">' +
				esc(i18n.noOrderData || 'Create a WooCommerce order to preview this template with real data.') +
			'</div>';
		} else if (state.renderNotice) {
			html += '<div class="kramar-ib-canvas-banner">' + esc(state.renderNotice) + '</div>';
		}
		for (var i = 0; i < state.blocks.length; i++) {
			html += renderBlockHTML(state.blocks[i]);
		}
		html += renderCanvasFooter();
		container.innerHTML = html;

		if (state.selectedBlockId) {
			var el = container.querySelector('[data-block-id="' + state.selectedBlockId + '"]');
			if (el) el.classList.add('kramar-ib-block--selected');
		}
	}

	function renderBlockHTML(block) {
		var required = isLocked(block.type);
		var selectedClass = block.id === state.selectedBlockId ? ' kramar-ib-block--selected' : '';
		var requiredClass = required ? ' kramar-ib-block--required' : '';
		var requiredBadge = required
			? '<span class="kramar-ib-block__required" title="' + esc(i18n.locked || 'Required by EU law') + '">' + esc(i18n.locked || 'Required by EU law') + '</span>'
			: '';

		return '<div class="kramar-ib-block kramar-ib-block--' + esc(block.type) + requiredClass + selectedClass + '" data-block-id="' + esc(block.id) + '">' +
			'<div class="kramar-ib-block__actions">' +
				requiredBadge +
				'<span class="dashicons dashicons-move kramar-ib-block__handle" title="' + esc(i18n.dragToReorder || 'Drag to Reorder') + '"></span>' +
				'<span class="dashicons dashicons-trash kramar-ib-block__delete" title="Delete"></span>' +
			'</div>' +
			'<div class="kramar-ib-block__content">' +
				renderBlockContent(block) +
			'</div>' +
			'<div class="kramar-ib-block__hint">' + esc(i18n.clickToEdit || 'Click to Edit') + ' &middot; ' + esc(i18n.dragToReorder || 'Drag to Reorder') + '</div>' +
		'</div>';
	}

	// The canvas shows the EXACT markup the PDF renderer produces, fetched from
	// the server (see doRender). Until it arrives we show a light placeholder.
	function renderBlockContent(block) {
		if (Object.prototype.hasOwnProperty.call(state.rendered, block.id)) {
			var html = state.rendered[block.id];
			if (html && html.replace(/\s/g, '') !== '') {
				return html;
			}
			// Block renders to nothing for this order (e.g. totals on packing slip).
			var def0 = blockTypes[block.type] || {};
			return '<div class="kramar-ib-block__placeholder">' +
				esc(def0.label || block.type) +
				' <span style="opacity:.6;">(' + esc(i18n.empty || 'no content for this order') + ')</span></div>';
		}
		var def = blockTypes[block.type] || {};
		return '<div class="kramar-ib-block__placeholder">' + esc(def.label || block.type) + ' — ' + esc(i18n.rendering || 'Rendering…') + '</div>';
	}

	function renderCanvasFooter() {
		if (!state.footerHtml) return '';
		return '<div class="kramar-ib-canvas-footer">' + state.footerHtml + '</div>';
	}

	// ─── Select Block ─────────────────────────────────────────────────────

	function selectBlock(id) {
		state.selectedBlockId = id;
		var container = document.getElementById('kramar-ib-canvas-container');
		if (container) {
			var all = container.querySelectorAll('.kramar-ib-block');
			for (var i = 0; i < all.length; i++) {
				all[i].classList.remove('kramar-ib-block--selected');
			}
			if (id) {
				var el = container.querySelector('[data-block-id="' + id + '"]');
				if (el) el.classList.add('kramar-ib-block--selected');
			}
		}
		renderSettings(id ? findBlock(id) : null);
	}

	// ─── Settings Panel ──────────────────────────────────────────────────

	function renderSettings(block) {
		var title = document.getElementById('kramar-ib-settings-title');
		var body = document.getElementById('kramar-ib-settings-body');
		if (!body) return;

		if (!block) {
			if (title) title.textContent = 'Block Settings';
			body.innerHTML = '<div class="kramar-ib-settings__empty"><span class="dashicons dashicons-admin-generic" style="font-size:32px;width:32px;height:32px;color:#d1d5db;"></span><p style="color:#9ca3af;margin:8px 0 0;">' + esc(i18n.noBlockSelected || 'Select a block to edit its settings.') + '</p></div>';
			return;
		}

		var def = blockTypes[block.type] || {};
		if (title) title.textContent = (def.label || block.type) + ' Settings';

		body.innerHTML = renderSettingsForm(block);
		bindSettingsFields(block.id);
	}

	function renderSettingsForm(block) {
		var s = block.settings || {};
		var html = '<div class="kramar-ib-settings-form" data-block-id="' + esc(block.id) + '">';

		switch (block.type) {
			case 'header':
				html += fieldToggle('show_logo', 'Show logo', s.show_logo !== false);
				html += '<p class="kramar-ib-field__hint">Logo is loaded from your Email Manager template settings.</p>';
				break;

			case 'title':
				html += '<p class="kramar-ib-field__hint">Automatically shows "INVOICE", "PACKING SLIP", or "CREDIT NOTE" based on document type.</p>';
				break;

			case 'addresses':
				html += fieldToggle('show_shipping', 'Show shipping address', s.show_shipping !== false);
				html += fieldToggle('show_email', 'Show customer email', !!s.show_email);
				html += fieldToggle('show_phone', 'Show customer phone', s.show_phone !== false);
				html += fieldToggle('show_vat_id', 'Show customer VAT ID', s.show_vat_id !== false);
				break;

			case 'line-items':
				html += fieldToggle('show_sku', 'Show SKU', s.show_sku !== false);
				html += fieldToggle('show_vat', 'Show VAT column', s.show_vat !== false);
				html += fieldToggle('show_image', 'Show product images', !!s.show_image);
				break;

			case 'totals':
				html += fieldToggle('show_shipping', 'Show shipping row', s.show_shipping !== false);
				html += fieldToggle('show_discount', 'Show discount row', s.show_discount !== false);
				break;

			case 'notes':
				html += fieldToggle('show_customer_note', 'Show customer note', s.show_customer_note !== false);
				html += fieldTextarea('custom_text', 'Additional text', s.custom_text || '');
				break;

			case 'text':
				html += fieldTextarea('content', 'Content', s.content || '');
				break;

			case 'divider':
				html += '<p class="kramar-ib-field__hint">Horizontal separator line between sections.</p>';
				break;
		}

		if (isLocked(block.type)) {
			html += '<div class="kramar-ib-required-notice">' + esc(i18n.locked || 'Required by EU law') + '</div>';
		}

		html += '</div>';
		return html;
	}

	function field(key, label, value) {
		return '<div class="kramar-ib-field">' +
			'<label class="kramar-ib-field__label">' + esc(label) + '</label>' +
			'<input type="text" class="kramar-ib-field__input" data-field="' + esc(key) + '" value="' + esc(value) + '" />' +
		'</div>';
	}

	function fieldToggle(key, label, checked) {
		return '<div class="kramar-ib-field kramar-ib-field--toggle">' +
			'<label class="kramar-ib-field__toggle-label">' +
				'<input type="checkbox" class="kramar-ib-field__toggle" data-field="' + esc(key) + '"' + (checked ? ' checked' : '') + ' />' +
				'<span>' + esc(label) + '</span>' +
			'</label>' +
		'</div>';
	}

	function fieldTextarea(key, label, value) {
		return '<div class="kramar-ib-field">' +
			'<label class="kramar-ib-field__label">' + esc(label) + '</label>' +
			'<textarea class="kramar-ib-field__input kramar-ib-field__textarea" data-field="' + esc(key) + '" rows="3">' + esc(value) + '</textarea>' +
		'</div>';
	}

	function fieldSelect(key, label, value, options) {
		var opts = '';
		for (var i = 0; i < options.length; i++) {
			var sel = options[i][0] === value ? ' selected' : '';
			opts += '<option value="' + esc(options[i][0]) + '"' + sel + '>' + esc(options[i][1]) + '</option>';
		}
		return '<div class="kramar-ib-field">' +
			'<label class="kramar-ib-field__label">' + esc(label) + '</label>' +
			'<select class="kramar-ib-field__input kramar-ib-field__select" data-field="' + esc(key) + '">' + opts + '</select>' +
		'</div>';
	}

	// ─── Settings Binding ────────────────────────────────────────────────

	function bindSettingsFields(blockId) {
		var body = document.getElementById('kramar-ib-settings-body');
		if (!body) return;

		var inputs = body.querySelectorAll('.kramar-ib-field__input, .kramar-ib-field__textarea, .kramar-ib-field__toggle');
		for (var i = 0; i < inputs.length; i++) {
			(function (input) {
				var fieldKey = input.getAttribute('data-field');
				if (!fieldKey) return;
				var evtName = input.type === 'checkbox' ? 'change' : 'input';
				input.addEventListener(evtName, function () {
					var block = findBlock(blockId);
					if (!block) return;
					snapshot();
					block.settings[fieldKey] = input.type === 'checkbox' ? input.checked : input.value;
					scheduleRender();
					markDirty();
				});
			})(inputs[i]);
		}
	}

	// ─── Palette ─────────────────────────────────────────────────────────

	function bindPalette() {
		var palette = document.getElementById('kramar-ib-palette');
		if (!palette) return;

		palette.addEventListener('click', function (e) {
			var item = closest(e.target, '.kramar-ib-palette-item');
			if (!item) return;
			var type = item.getAttribute('data-block-type');
			if (!type) return;

			snapshot();
			var block = createBlock(type);
			var selIdx = state.selectedBlockId ? findBlockIndex(state.selectedBlockId) : -1;
			if (selIdx >= 0) {
				state.blocks.splice(selIdx + 1, 0, block);
			} else {
				state.blocks.push(block);
			}
			renderCanvas();
			selectBlock(block.id);
			markDirty();
			scheduleRender();
			scrollBlockIntoView(block.id);
		});

		palette.addEventListener('dragstart', function (e) {
			var item = closest(e.target, '.kramar-ib-palette-item');
			if (!item) return;
			e.dataTransfer.setData('text/plain', 'palette:' + item.getAttribute('data-block-type'));
			e.dataTransfer.effectAllowed = 'copy';
		});
	}

	// ─── Canvas Drag and Drop ────────────────────────────────────────────

	var dragState = {
		placeholder: null,
		insertIndex: -1,
	};

	function bindCanvasDelegated() {
		var container = document.getElementById('kramar-ib-canvas-container');
		if (!container) return;

		// Click delegation.
		container.addEventListener('click', function (e) {
			if (closest(e.target, '.kramar-ib-block__delete')) {
				var blockEl = closest(e.target, '.kramar-ib-block');
				if (blockEl) deleteBlock(blockEl.getAttribute('data-block-id'));
				return;
			}
			var blockEl2 = closest(e.target, '.kramar-ib-block');
			if (blockEl2 && !closest(e.target, '.kramar-ib-block__actions')) {
				selectBlock(blockEl2.getAttribute('data-block-id'));
			}
		});

		// Drag-over from palette.
		container.addEventListener('dragover', function (e) {
			e.preventDefault();
			e.dataTransfer.dropEffect = 'copy';
			updateDropPlaceholder(container, e.clientY);
		});

		container.addEventListener('dragleave', function (e) {
			if (!container.contains(e.relatedTarget)) {
				removePlaceholder();
				dragState.insertIndex = -1;
			}
		});

		container.addEventListener('drop', function (e) {
			e.preventDefault();
			var data = e.dataTransfer.getData('text/plain');
			removePlaceholder();

			if (data && data.indexOf('palette:') === 0) {
				var type = data.slice(8);
				snapshot();
				var block = createBlock(type);
				var idx = dragState.insertIndex >= 0 ? dragState.insertIndex : state.blocks.length;
				state.blocks.splice(idx, 0, block);
				renderCanvas();
				selectBlock(block.id);
				markDirty();
				scheduleRender();
			}
			dragState.insertIndex = -1;
		});

		// Mousedown-based drag for block reorder.
		var reorderDrag = { active: false, blockId: null, ghost: null, startY: 0 };

		container.addEventListener('mousedown', function (e) {
			var handle = closest(e.target, '.kramar-ib-block__handle');
			if (!handle) return;
			e.preventDefault();

			var blockEl = closest(handle, '.kramar-ib-block');
			if (!blockEl) return;

			reorderDrag.blockId = blockEl.getAttribute('data-block-id');
			reorderDrag.startY = e.clientY;
			reorderDrag.active = false;

			var ghost = blockEl.cloneNode(true);
			ghost.style.cssText = 'position:fixed;pointer-events:none;opacity:0.7;z-index:99999;width:' + blockEl.offsetWidth + 'px;box-shadow:0 4px 20px rgba(0,0,0,.15);border-radius:6px;left:' + blockEl.getBoundingClientRect().left + 'px;top:' + e.clientY + 'px;transform:translateY(-50%);';
			reorderDrag.ghost = ghost;

			function onMove(ev) {
				if (!reorderDrag.active && Math.abs(ev.clientY - reorderDrag.startY) < 5) return;

				if (!reorderDrag.active) {
					reorderDrag.active = true;
					document.body.appendChild(ghost);
					blockEl.style.opacity = '0.3';
				}

				ghost.style.top = ev.clientY + 'px';
				updateDropPlaceholder(container, ev.clientY);
			}

			function onUp() {
				document.removeEventListener('mousemove', onMove);
				document.removeEventListener('mouseup', onUp);

				if (reorderDrag.active && reorderDrag.ghost) {
					reorderDrag.ghost.remove();
					blockEl.style.opacity = '';

					var fromIdx = findBlockIndex(reorderDrag.blockId);
					var toIdx = dragState.insertIndex;

					if (fromIdx !== -1 && toIdx !== -1 && fromIdx !== toIdx) {
						snapshot();
						var moved = state.blocks.splice(fromIdx, 1)[0];
						if (toIdx > fromIdx) toIdx--;
						state.blocks.splice(toIdx, 0, moved);
						renderCanvas();
						selectBlock(reorderDrag.blockId);
						markDirty();
					}
				}

				removePlaceholder();
				dragState.insertIndex = -1;
				reorderDrag.active = false;
				reorderDrag.blockId = null;
				reorderDrag.ghost = null;
			}

			document.addEventListener('mousemove', onMove);
			document.addEventListener('mouseup', onUp);
		});
	}

	function updateDropPlaceholder(container, clientY) {
		var blocks = container.querySelectorAll('.kramar-ib-block');
		var insertIndex = state.blocks.length;

		for (var i = 0; i < blocks.length; i++) {
			var rect = blocks[i].getBoundingClientRect();
			var mid = rect.top + rect.height / 2;
			if (clientY < mid) {
				insertIndex = i;
				break;
			}
		}

		dragState.insertIndex = insertIndex;

		if (!dragState.placeholder) {
			dragState.placeholder = document.createElement('div');
			dragState.placeholder.className = 'kramar-ib-drop-placeholder';
		}

		if (insertIndex < blocks.length) {
			container.insertBefore(dragState.placeholder, blocks[insertIndex]);
		} else {
			container.appendChild(dragState.placeholder);
		}
	}

	function removePlaceholder() {
		if (dragState.placeholder && dragState.placeholder.parentNode) {
			dragState.placeholder.parentNode.removeChild(dragState.placeholder);
		}
		dragState.placeholder = null;
	}

	// ─── Block Actions ───────────────────────────────────────────────────

	function deleteBlock(id) {
		var block = findBlock(id);
		if (!block) return;
		snapshot();
		state.blocks.splice(findBlockIndex(id), 1);
		if (state.selectedBlockId === id) {
			state.selectedBlockId = null;
			renderSettings(null);
		}
		renderCanvas();
		markDirty();
	}

	function scrollBlockIntoView(id) {
		var el = document.querySelector('[data-block-id="' + id + '"]');
		if (el && el.scrollIntoView) {
			el.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
		}
	}

	// ─── Toolbar ─────────────────────────────────────────────────────────

	function bindToolbar() {
		var saveBtn = document.querySelector('.kramar-eb-toolbar__save');
		if (saveBtn) saveBtn.addEventListener('click', saveBlocks);

		var previewBtn = document.querySelector('.kramar-ib-toolbar__preview');
		if (previewBtn) previewBtn.addEventListener('click', openPreview);
	}

	// ─── Save ────────────────────────────────────────────────────────────

	function saveBlocks() {
		var btn = document.querySelector('.kramar-eb-toolbar__save');
		if (btn) { btn.disabled = true; btn.textContent = 'Saving...'; }

		var body = new FormData();
		body.append('action', 'kramar_invoice_save_template');
		body.append('_ajax_nonce', nonce);
		body.append('doc_type', docType);
		body.append('blocks', JSON.stringify(state.blocks));

		fetch(ajaxUrl, { method: 'POST', body: body })
			.then(function (r) { return r.json(); })
			.then(function (r) {
				if (btn) { btn.disabled = false; btn.innerHTML = '<span class="dashicons dashicons-saved"></span> Save'; }
				if (r.success) { clearDirty(); showNotice(i18n.saved || 'Template saved!', 'success'); }
				else showNotice(i18n.error || 'Save failed.', 'error');
			})
			.catch(function () {
				if (btn) { btn.disabled = false; btn.innerHTML = '<span class="dashicons dashicons-saved"></span> Save'; }
				showNotice(i18n.error || 'Save failed.', 'error');
			});
	}

	// ─── Preview Modal ───────────────────────────────────────────────────

	function openPreview() {
		var modal = document.getElementById('kramar-ib-preview-modal');
		if (!modal) {
			modal = createPreviewModal();
			document.body.appendChild(modal);
		}
		modal.style.display = 'flex';

		var iframe = modal.querySelector('.kramar-ib-preview-iframe');
		iframe.srcdoc = '<p style="padding:40px;text-align:center;font-family:sans-serif;color:#787c82;">Loading preview...</p>';

		var body = new FormData();
		body.append('action', 'kramar_invoice_preview');
		body.append('_ajax_nonce', nonce);
		body.append('doc_type', docType);
		body.append('blocks', JSON.stringify(state.blocks));

		fetch(ajaxUrl, { method: 'POST', body: body })
			.then(function (r) { return r.json(); })
			.then(function (r) {
				if (r.success && r.data && r.data.html) {
					iframe.srcdoc = r.data.html;
				} else if (r && r.data && r.data.reason === 'no_refund') {
					iframe.srcdoc = '<p style="padding:40px;text-align:center;font-family:sans-serif;color:#92400e;">' +
						esc(i18n.noRefundData || 'Refund an order to preview the credit note with real refunded amounts.') + '</p>';
				} else if (r && r.data && r.data.reason === 'no_order') {
					iframe.srcdoc = '<p style="padding:40px;text-align:center;font-family:sans-serif;color:#92400e;">' +
						esc(i18n.noOrderData || 'Create a WooCommerce order to preview this template with real data.') + '</p>';
				} else {
					iframe.srcdoc = '<p style="padding:40px;text-align:center;color:#d63638;">Preview failed.</p>';
				}
			})
			.catch(function () {
				iframe.srcdoc = '<p style="padding:40px;text-align:center;color:#d63638;">Preview failed.</p>';
			});
	}

	function createPreviewModal() {
		var modal = document.createElement('div');
		modal.id = 'kramar-ib-preview-modal';
		modal.style.cssText = 'position:fixed;inset:0;z-index:100000;display:flex;align-items:center;justify-content:center;';

		var overlay = document.createElement('div');
		overlay.style.cssText = 'position:absolute;inset:0;background:rgba(0,0,0,.6);';
		overlay.addEventListener('click', function () { modal.style.display = 'none'; });

		var content = document.createElement('div');
		content.style.cssText = 'position:relative;background:#fff;border-radius:8px;width:92vw;max-width:900px;height:90vh;display:flex;flex-direction:column;overflow:hidden;z-index:1;';

		var toolbar = document.createElement('div');
		toolbar.style.cssText = 'display:flex;align-items:center;gap:12px;padding:12px 16px;border-bottom:1px solid #dcdcde;';
		toolbar.innerHTML = '<strong style="flex:1;">Invoice Preview (with real order data)</strong>' +
			'<button type="button" class="button" style="font-size:18px;line-height:1;" onclick="this.closest(\'#kramar-ib-preview-modal\').style.display=\'none\'">&times;</button>';

		var iframeWrap = document.createElement('div');
		iframeWrap.style.cssText = 'flex:1;background:#c8cacf;overflow:auto;';

		var iframe = document.createElement('iframe');
		iframe.className = 'kramar-ib-preview-iframe';
		iframe.sandbox = 'allow-same-origin';
		iframe.style.cssText = 'border:none;width:100%;height:100%;background:#c8cacf;';

		iframeWrap.appendChild(iframe);
		content.appendChild(toolbar);
		content.appendChild(iframeWrap);
		modal.appendChild(overlay);
		modal.appendChild(content);

		return modal;
	}

	// ─── Keyboard ────────────────────────────────────────────────────────

	function bindKeyboard() {
		document.addEventListener('keydown', function (e) {
			var ctrl = /Mac/.test(navigator.platform) ? e.metaKey : e.ctrlKey;
			if (ctrl && !e.shiftKey && e.key === 's') { e.preventDefault(); saveBlocks(); }
			if (ctrl && !e.shiftKey && e.key === 'z') { e.preventDefault(); undo(); }
			if (ctrl && (e.key === 'y' || (e.shiftKey && e.key === 'z'))) { e.preventDefault(); redo(); }
			if (e.key === 'Delete' || e.key === 'Backspace') {
				if (state.selectedBlockId && document.activeElement === document.body) {
					e.preventDefault();
					deleteBlock(state.selectedBlockId);
				}
			}
		});
	}

	function bindBeforeUnload() {
		window.addEventListener('beforeunload', function (e) {
			if (state.dirty) { e.returnValue = 'Unsaved changes.'; return e.returnValue; }
		});
	}

	// ─── Notices ─────────────────────────────────────────────────────────

	function showNotice(msg, type) {
		var n = document.createElement('div');
		n.className = 'kramar-ib-toast kramar-ib-toast--' + (type || 'info');
		n.textContent = msg;
		document.body.appendChild(n);
		setTimeout(function () { if (n.parentNode) n.remove(); }, 3000);
	}

	// ─── Boot ────────────────────────────────────────────────────────────

	if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
	else init();

})();
