// Simple modal loader + select append helper for "add new" links.
(function () {
	let modal;
	let currentTrigger = null;
	const modalStack = [];
	let activeModalRequest = null;
	let confirmModal = null;
	let confirmResolver = null;
	let confirmSettled = false;

	function ensureModal() {
		if (modal) return modal;
		const overlay = document.createElement("div");
		overlay.id = "tw-modal-overlay";
		overlay.className =
			"fixed inset-0 z-[9999] hidden items-center justify-center bg-slate-900/50 p-4 opacity-0 transition-opacity duration-200";
		overlay.innerHTML = `
      <div class="relative w-full max-w-2xl rounded-xl bg-white dark:bg-slate-800 shadow-2xl border border-slate-200 dark:border-slate-700 overflow-hidden scale-95 opacity-0 transition-all duration-200" data-modal-panel>
        <div class="flex items-center justify-between px-5 py-3 border-b border-slate-200 dark:border-slate-700">
          <h3 class="text-sm font-semibold text-slate-900 dark:text-slate-100" data-modal-title>Modal</h3>
          <button type="button" data-modal-close class="text-slate-400 hover:text-slate-600 dark:hover:text-slate-300">
            <svg class="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
            </svg>
          </button>
        </div>
        <div class="p-5 max-h-[75vh] overflow-y-auto" data-modal-body></div>
      </div>
    `;
		document.body.appendChild(overlay);
		overlay.addEventListener("click", (e) => {
			if (
				e.target === overlay ||
				e.target.hasAttribute("data-modal-close") ||
				e.target.getAttribute("data-dismiss") === "modal"
			) {
				closeModal();
			}
		});
		modal = {
			overlay,
			panelEl: overlay.querySelector("[data-modal-panel]"),
			titleEl: overlay.querySelector("[data-modal-title]"),
			bodyEl: overlay.querySelector("[data-modal-body]"),
		};
		return modal;
	}

	function ensureConfirmModal() {
		if (confirmModal) return confirmModal;
		const overlay = document.createElement("div");
		overlay.id = "tw-confirm-overlay";
		overlay.className =
			"fixed inset-0 z-[10000] hidden opacity-0 transition-opacity duration-150";
		overlay.innerHTML = `
      <div class="pointer-events-auto absolute w-[min(24rem,calc(100vw-1.5rem))] rounded-2xl border border-slate-200 bg-white shadow-2xl shadow-slate-950/20 transition-all duration-150 scale-95 opacity-0 dark:border-slate-700 dark:bg-slate-900" data-confirm-panel>
        <span class="absolute h-3 w-3 rotate-45 border-l border-t border-slate-200 bg-white dark:border-slate-700 dark:bg-slate-900" data-confirm-arrow></span>
        <div class="flex items-start gap-4 border-b border-slate-200 px-5 py-4 dark:border-slate-700">
          <div data-confirm-icon-wrap class="flex h-11 w-11 items-center justify-center rounded-full bg-rose-50 text-rose-600 ring-1 ring-rose-100 dark:bg-rose-950/30 dark:text-rose-300 dark:ring-rose-900/40">
            <i data-confirm-icon data-lucide="trash-2" class="h-5 w-5"></i>
          </div>
          <div class="min-w-0 flex-1">
            <h3 class="text-base font-semibold text-slate-900 dark:text-slate-100" data-confirm-title>Confirm delete</h3>
            <p class="mt-1 text-sm leading-6 text-slate-600 dark:text-slate-300" data-confirm-message></p>
          </div>
          <button type="button" data-confirm-close class="rounded-lg p-2 text-slate-400 transition hover:bg-slate-100 hover:text-slate-700 dark:hover:bg-slate-800 dark:hover:text-slate-200">
            <i data-lucide="x" class="h-4 w-4"></i>
          </button>
        </div>
        <div class="flex items-center justify-end gap-3 px-5 py-4">
          <button type="button" data-confirm-cancel class="inline-flex items-center gap-2 rounded-xl border border-slate-200 bg-slate-100 px-4 py-2.5 text-sm font-semibold text-slate-700 transition hover:bg-slate-200 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-200 dark:hover:bg-slate-700">
            <i data-lucide="x" class="h-4 w-4"></i>
            <span>${window.confirm_cancel || "Cancel"}</span>
          </button>
          <button type="button" data-confirm-accept class="inline-flex items-center gap-2 rounded-xl border border-rose-200 bg-rose-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm transition hover:bg-rose-500 dark:border-rose-900/40 dark:bg-rose-500 dark:hover:bg-rose-400">
            <i data-lucide="trash-2" class="h-4 w-4"></i>
            <span>${window.confirm_delete || "Delete"}</span>
          </button>
        </div>
      </div>
		`;
		document.body.appendChild(overlay);
		overlay.addEventListener("click", (e) => {
			if (e.target === overlay) {
				resolveConfirm(false);
			}
		});
		confirmModal = {
			overlay,
			panelEl: overlay.querySelector("[data-confirm-panel]"),
			arrowEl: overlay.querySelector("[data-confirm-arrow]"),
			titleEl: overlay.querySelector("[data-confirm-title]"),
			messageEl: overlay.querySelector("[data-confirm-message]"),
			iconWrapEl: overlay.querySelector("[data-confirm-icon-wrap]"),
			iconEl: overlay.querySelector("[data-confirm-icon]"),
			acceptEl: overlay.querySelector("[data-confirm-accept]"),
			cancelEl: overlay.querySelector("[data-confirm-cancel]"),
		};
		const acceptConfirm = (e) => {
			e.preventDefault();
			e.stopPropagation();
			resolveConfirm(true);
		};
		const cancelConfirm = (e) => {
			e.preventDefault();
			e.stopPropagation();
			resolveConfirm(false);
		};
		confirmModal.acceptEl.addEventListener("pointerdown", acceptConfirm);
		confirmModal.acceptEl.addEventListener("click", acceptConfirm);
		confirmModal.cancelEl.addEventListener("pointerdown", cancelConfirm);
		confirmModal.cancelEl.addEventListener("click", cancelConfirm);
		const closeBtn = overlay.querySelector("[data-confirm-close]");
		if (closeBtn) {
			closeBtn.addEventListener("pointerdown", cancelConfirm);
			closeBtn.addEventListener("click", cancelConfirm);
		}
		document.addEventListener("keydown", (e) => {
			if (!confirmModal || confirmModal.overlay.classList.contains("hidden")) {
				return;
			}
			if (e.key === "Escape") {
				resolveConfirm(false);
			}
		});
		return confirmModal;
	}

	function resolveConfirm(result) {
		if (!confirmModal) return;
		if (confirmSettled) return;
		confirmSettled = true;
		const m = confirmModal;
		const fn = confirmResolver;
		confirmResolver = null;
		if (fn) {
			fn(!!result);
		}
		m.overlay.classList.add("opacity-0");
		m.panelEl.classList.add("opacity-0", "scale-95");
		setTimeout(() => {
			m.overlay.classList.add("hidden");
			m.overlay.classList.remove("block");
			m.messageEl.textContent = "";
			document.body.classList.remove("overflow-hidden");
		}, 180);
	}

	function escapeHtml(value) {
		return String(value == null ? "" : value)
			.replace(/&/g, "&amp;")
			.replace(/</g, "&lt;")
			.replace(/>/g, "&gt;")
			.replace(/"/g, "&quot;")
			.replace(/'/g, "&#39;");
	}

	window.erpConfirmDialog = function (message, options) {
		const m = ensureConfirmModal();
		confirmSettled = false;
		const opts = options || {};
		const action = opts.action || "confirm";
		const variants = {
			delete: {
				icon: "trash-2",
				iconClass: "flex h-11 w-11 items-center justify-center rounded-full bg-rose-50 text-rose-600 ring-1 ring-rose-100 dark:bg-rose-950/30 dark:text-rose-300 dark:ring-rose-900/40",
				buttonClass: "inline-flex items-center gap-2 rounded-xl border border-rose-200 bg-rose-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm transition hover:bg-rose-500 dark:border-rose-900/40 dark:bg-rose-500 dark:hover:bg-rose-400",
			},
			publish: {
				icon: "check",
				iconClass: "flex h-11 w-11 items-center justify-center rounded-full bg-emerald-50 text-emerald-600 ring-1 ring-emerald-100 dark:bg-emerald-950/30 dark:text-emerald-300 dark:ring-emerald-900/40",
				buttonClass: "inline-flex items-center gap-2 rounded-xl border border-emerald-200 bg-emerald-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm transition hover:bg-emerald-500 dark:border-emerald-900/40 dark:bg-emerald-500 dark:hover:bg-emerald-400",
			},
			unpublish: {
				icon: "x",
				iconClass: "flex h-11 w-11 items-center justify-center rounded-full bg-amber-50 text-amber-600 ring-1 ring-amber-100 dark:bg-amber-950/30 dark:text-amber-300 dark:ring-amber-900/40",
				buttonClass: "inline-flex items-center gap-2 rounded-xl border border-amber-200 bg-amber-500 px-4 py-2.5 text-sm font-semibold text-white shadow-sm transition hover:bg-amber-400 dark:border-amber-900/40 dark:bg-amber-500 dark:hover:bg-amber-400",
			},
			confirm: {
				icon: "check-circle",
				iconClass: "flex h-11 w-11 items-center justify-center rounded-full bg-sky-50 text-sky-600 ring-1 ring-sky-100 dark:bg-sky-950/30 dark:text-sky-300 dark:ring-sky-900/40",
				buttonClass: "inline-flex items-center gap-2 rounded-xl border border-sky-200 bg-sky-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm transition hover:bg-sky-500 dark:border-sky-900/40 dark:bg-sky-500 dark:hover:bg-sky-400",
			},
				copy: {
					icon: "copy",
					iconClass: "flex h-11 w-11 items-center justify-center rounded-full bg-cyan-50 text-cyan-700 ring-1 ring-cyan-100 dark:bg-cyan-950/30 dark:text-cyan-300 dark:ring-cyan-900/40",
					buttonClass: "inline-flex items-center gap-2 rounded-xl border border-cyan-200 bg-cyan-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm transition hover:bg-cyan-500 dark:border-cyan-900/40 dark:bg-cyan-500 dark:hover:bg-cyan-400",
				},
				convert: {
					icon: "arrow-right-left",
					iconClass: "flex h-11 w-11 items-center justify-center rounded-full bg-violet-50 text-violet-700 ring-1 ring-violet-100 dark:bg-violet-950/30 dark:text-violet-300 dark:ring-violet-900/40",
					buttonClass: "inline-flex items-center gap-2 rounded-xl border border-violet-200 bg-violet-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm transition hover:bg-violet-500 dark:border-violet-900/40 dark:bg-violet-500 dark:hover:bg-violet-400",
				},
			};
		const variant = variants[action] || variants.confirm;
		m.titleEl.textContent = opts.title || "Confirm delete";
		m.messageEl.textContent = message || "Are you sure?";
		if (m.iconWrapEl) {
			m.iconWrapEl.className = variant.iconClass;
		}
		if (m.iconEl) {
			m.iconEl.setAttribute("data-lucide", variant.icon);
		}
		if (m.acceptEl) {
			const label = opts.acceptLabel || window.confirm_delete || "Delete";
			m.acceptEl.className = variant.buttonClass;
			m.acceptEl.querySelector("span").textContent = label;
			const acceptIcon = m.acceptEl.querySelector("i");
			if (acceptIcon) {
				acceptIcon.setAttribute("data-lucide", variant.icon);
			}
		}
		const cancelBtn = m.overlay.querySelector("[data-confirm-cancel] span");
		if (cancelBtn) {
			cancelBtn.textContent = opts.cancelLabel || window.confirm_cancel || "Cancel";
		}
		const trigger = opts.trigger || null;
		const placePopover = function () {
			const pad = 12;
			const gap = 10;
			const panel = m.panelEl;
			const arrow = m.arrowEl;
			const trigRect = trigger && typeof trigger.getBoundingClientRect === "function"
				? trigger.getBoundingClientRect()
				: null;
			const panelRect = panel.getBoundingClientRect();
			let top = pad;
			let left = pad;
			let arrowTop = 18;
			let arrowLeft = 18;
			let placement = "right";

			if (trigRect) {
				const spaceRight = window.innerWidth - trigRect.right - pad;
				const spaceLeft = trigRect.left - pad;
				const spaceBelow = window.innerHeight - trigRect.bottom - pad;
				const spaceAbove = trigRect.top - pad;
				const desiredTop = Math.round(trigRect.top + (trigRect.height / 2) - (panelRect.height / 2));

				if (spaceRight >= panelRect.width + gap) {
					placement = "right";
					left = Math.min(Math.round(trigRect.right + gap), window.innerWidth - panelRect.width - pad);
					top = Math.max(pad, Math.min(desiredTop, window.innerHeight - panelRect.height - pad));
					arrowLeft = -6;
					arrowTop = Math.max(16, Math.min(panelRect.height / 2 - 6, panelRect.height - 24));
				} else if (spaceLeft >= panelRect.width + gap) {
					placement = "left";
					left = Math.max(pad, Math.round(trigRect.left - panelRect.width - gap));
					top = Math.max(pad, Math.min(desiredTop, window.innerHeight - panelRect.height - pad));
					arrowLeft = panelRect.width - 6;
					arrowTop = Math.max(16, Math.min(panelRect.height / 2 - 6, panelRect.height - 24));
				} else if (spaceBelow >= panelRect.height + gap) {
					placement = "bottom";
					left = Math.max(pad, Math.min(Math.round(trigRect.left + (trigRect.width / 2) - (panelRect.width / 2)), window.innerWidth - panelRect.width - pad));
					top = Math.min(Math.round(trigRect.bottom + gap), window.innerHeight - panelRect.height - pad);
					arrowTop = -6;
					arrowLeft = Math.max(16, Math.min(panelRect.width / 2 - 6, panelRect.width - 24));
				} else {
					placement = "top";
					left = Math.max(pad, Math.min(Math.round(trigRect.left + (trigRect.width / 2) - (panelRect.width / 2)), window.innerWidth - panelRect.width - pad));
					top = Math.max(pad, Math.round(trigRect.top - panelRect.height - gap));
					arrowTop = panelRect.height - 6;
					arrowLeft = Math.max(16, Math.min(panelRect.width / 2 - 6, panelRect.width - 24));
				}
			}

			panel.style.top = top + "px";
			panel.style.left = left + "px";
			panel.style.right = "auto";
			panel.style.transform = "none";
			panel.dataset.placement = placement;
			if (arrow) {
				arrow.dataset.placement = placement;
				arrow.style.top = arrowTop + "px";
				arrow.style.left = arrowLeft + "px";
			}
		};
		m.overlay.classList.remove("hidden");
		m.overlay.classList.add("block");
		document.body.classList.add("overflow-hidden");
		m.panelEl.style.position = "fixed";
		requestAnimationFrame(() => {
			placePopover();
			m.overlay.classList.remove("opacity-0");
			m.panelEl.classList.remove("opacity-0", "scale-95");
			if (window.lucide && typeof window.lucide.createIcons === "function") {
				window.lucide.createIcons({ root: m.overlay });
			}
		});
		return new Promise((resolve) => {
			confirmResolver = resolve;
		});
	};

	function getModalLoadingHtml(title) {
		const label = title || "Loading";
		return `
			<div class="flex min-h-[14rem] items-center justify-center px-4 py-10" data-modal-loading>
				<div class="w-full max-w-sm text-center">
					<div class="mx-auto flex h-14 w-14 items-center justify-center rounded-2xl border border-slate-200 bg-slate-50 text-indigo-600 shadow-sm dark:border-slate-700 dark:bg-slate-900/70 dark:text-indigo-300">
						<span class="h-6 w-6 animate-spin rounded-full border-2 border-current border-t-transparent"></span>
					</div>
					<h4 class="mt-4 text-sm font-semibold text-slate-900 dark:text-slate-100">${escapeHtml(label)}</h4>
					<p class="mt-1 text-sm text-slate-500 dark:text-slate-400">Preparing the form, please wait...</p>
					<div class="mt-6 space-y-2">
						<div class="h-2 rounded-full bg-slate-100 dark:bg-slate-800"></div>
						<div class="mx-auto h-2 w-4/5 rounded-full bg-slate-100 dark:bg-slate-800"></div>
						<div class="mx-auto h-2 w-3/5 rounded-full bg-slate-100 dark:bg-slate-800"></div>
					</div>
				</div>
			</div>
		`;
	}

	function getModalErrorHtml(message) {
		return `
			<div class="flex min-h-[14rem] items-center justify-center px-4 py-10" data-modal-error>
				<div class="w-full max-w-sm text-center">
					<div class="mx-auto flex h-14 w-14 items-center justify-center rounded-2xl border border-rose-200 bg-rose-50 text-rose-600 shadow-sm dark:border-rose-900/50 dark:bg-rose-950/30 dark:text-rose-300">
						<i data-lucide="alert-triangle" class="h-6 w-6"></i>
					</div>
					<h4 class="mt-4 text-sm font-semibold text-slate-900 dark:text-slate-100">Unable to load this modal</h4>
					<p class="mt-1 text-sm leading-6 text-slate-500 dark:text-slate-400">${escapeHtml(message || "Please check your connection and try again.")}</p>
					<div class="mt-5 flex justify-center gap-2">
						<button type="button" data-modal-retry class="inline-flex items-center gap-2 rounded-xl bg-indigo-600 px-4 py-2 text-sm font-semibold text-white shadow-sm transition hover:bg-indigo-500">
							<i data-lucide="refresh-cw" class="h-4 w-4"></i>
							<span>Retry</span>
						</button>
						<button type="button" data-modal-close class="inline-flex items-center gap-2 rounded-xl border border-slate-200 bg-white px-4 py-2 text-sm font-semibold text-slate-700 transition hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-200 dark:hover:bg-slate-800">
							<i data-lucide="x" class="h-4 w-4"></i>
							<span>Close</span>
						</button>
					</div>
				</div>
			</div>
		`;
	}

	function setTriggerLoading(trigger, isLoading) {
		if (!trigger) return;
		if (isLoading) {
			trigger.dataset.modalLoading = "1";
			trigger.setAttribute("aria-busy", "true");
			if ("disabled" in trigger) {
				trigger.disabled = true;
			}
			trigger.classList.add("pointer-events-none", "opacity-70");
			return;
		}
		delete trigger.dataset.modalLoading;
		trigger.removeAttribute("aria-busy");
		if ("disabled" in trigger) {
			trigger.disabled = false;
		}
		trigger.classList.remove("pointer-events-none", "opacity-70");
	}

	function showModalShell(trigger, html, options) {
		const m = ensureModal();
		const opts = options || {};
		if (!opts.replace && !m.overlay.classList.contains("hidden")) {
			const clean = sanitizeModalBody(m.bodyEl);
			modalStack.push({
				title: m.titleEl.textContent,
				body: clean,
				size: m.panelEl.className,
				trigger: currentTrigger,
			});
		}
		// Always reset current modal content to prevent wrapper duplication.
		m.bodyEl.innerHTML = "";
		currentTrigger = trigger;
		m.titleEl.textContent = trigger.dataset.modalTitle || "Add Item";
		m.bodyEl.innerHTML = html;
		applyModalSize(trigger.dataset.modalSize);
		m.overlay.classList.remove("hidden");
		m.overlay.classList.add("flex");
		document.body.classList.add("overflow-hidden");
		requestAnimationFrame(() => {
			m.overlay.classList.remove("opacity-0");
			m.panelEl.classList.remove("opacity-0", "scale-95");
		});
		if (
			window.FormValidator &&
			typeof window.FormValidator.init === "function"
		) {
			window.FormValidator.init();
		}
		if (window.lucide && typeof window.lucide.createIcons === "function") {
			window.lucide.createIcons();
		}
		return m;
	}

	function openModal(trigger, html) {
		const m = showModalShell(trigger, html, { replace: true });
		if (window.erpEnsureFeaturePlugins) {
			window.erpEnsureFeaturePlugins(m.bodyEl)
				.then(function () {
					initModalSelects(m.bodyEl);
				})
				.catch(function (error) {
					console.warn("Modal feature plugins failed", error);
				});
		}
		initModalSelects(m.bodyEl);
		if (window.initLookup) {
			window.initLookup(m.bodyEl);
		}
		if (window.initGroupedLookup) {
			window.initGroupedLookup(m.bodyEl);
		}
		if (window.initEditors) {
			window.initEditors(m.bodyEl);
		}
		if (
			window.FormValidator &&
			typeof window.FormValidator.init === "function"
		) {
			window.FormValidator.init();
		}
		if (
			window.initDatePickers &&
			typeof window.initDatePickers === "function"
		) {
			window.initDatePickers();
		}
		if (window.initUploader && typeof window.initUploader === "function") {
			window.initUploader();
		}
		if (window.initPermissionUI) {
			window.initPermissionUI(m.bodyEl);
		}
		document.dispatchEvent(
			new CustomEvent("app:modal-loaded", {
				detail: {
					trigger,
					bodyEl: m.bodyEl,
					panelEl: m.panelEl,
					title: m.titleEl.textContent,
				},
			})
		);
	}

	function showModalLoading(trigger, replace) {
		const title = trigger.dataset.modalLoadingText || trigger.dataset.modalTitle || "Loading";
		showModalShell(trigger, getModalLoadingHtml(title), { replace: !!replace });
	}

	function showModalError(trigger, message) {
		const m = showModalShell(trigger, getModalErrorHtml(message), { replace: true });
		const retry = m.bodyEl.querySelector("[data-modal-retry]");
		if (retry) {
			retry.addEventListener("click", function () {
				loadModal(trigger);
			});
		}
		if (window.lucide && typeof window.lucide.createIcons === "function") {
			window.lucide.createIcons({ root: m.bodyEl });
		}
	}

	function initModalSelects(container) {
		if (!container) return;
		container
			.querySelectorAll('[data-selectpicker-wrapper="true"]')
			.forEach((el) => el.remove());
		const selects = container.querySelectorAll("select.tw-selectpicker");
		if (!selects.length) return;
		selects.forEach((el) => {
			cleanupSelectpicker(el);
			if (window.refreshSelectpicker) {
				window.refreshSelectpicker(el);
			} else if (window.jQuery && window.jQuery.fn && typeof window.jQuery.fn.selectpicker === "function") {
				window.jQuery(el).selectpicker();
			}
		});
	}

	function findSelectpickerWrapper(select) {
		if (!select) return null;
		const next = select.nextElementSibling;
		if (!next) return null;
		if (next.dataset && next.dataset.selectpickerWrapper === "true")
			return next;
		if (
			next.classList.contains("relative") &&
			next.querySelector("button") &&
			next.querySelector("ul")
		) {
			return next;
		}
		return null;
	}

	function cleanupSelectpicker(select) {
		if (!select) return;
		if (select.dataset && select.dataset.enhanced === "true") {
			delete select.dataset.enhanced;
		}
		const next = findSelectpickerWrapper(select);
		if (next) next.remove();
		select.classList.remove("hidden");
		select.style.display = "";
		select.style.visibility = "";
	}

	function applyModalSize(size) {
		if (!modal || !modal.panelEl) return;
		const map = {
			sm: "max-w-md",
			md: "max-w-2xl",
			lg: "max-w-4xl",
			xl: "max-w-6xl",
			"2xl": "max-w-7xl",
			"3xl": "max-w-8xl",
			full: "max-w-none",
		};
		const cls = map[(size || "md").toLowerCase()] || map.md;
		const panel = modal.panelEl;
		Object.values(map).forEach((c) => panel.classList.remove(c));
		panel.classList.add(cls);
	}

	function closeModal() {
		if (!modal) return;
		activeModalRequest = null;
		if (currentTrigger) {
			setTriggerLoading(currentTrigger, false);
		}
		if (modalStack.length) {
			const prev = modalStack.pop();
			currentTrigger = prev.trigger || null;
			modal.titleEl.textContent = prev.title || "Modal";
			modal.bodyEl.innerHTML = prev.body || "";
			modal.panelEl.className = prev.size || modal.panelEl.className;
			if (
				window.FormValidator &&
				typeof window.FormValidator.init === "function"
			) {
				window.FormValidator.init();
			}
			if (window.lucide && typeof window.lucide.createIcons === "function") {
				window.lucide.createIcons();
			}
			if (
				window.initDatePickers &&
				typeof window.initDatePickers === "function"
			) {
				window.initDatePickers();
			}
			if (window.initUploader && typeof window.initUploader === "function") {
				window.initUploader();
			}
			initModalSelects(modal.bodyEl);
			if (window.initLookup) {
				window.initLookup(modal.bodyEl);
			}
			if (window.initGroupedLookup) {
				window.initGroupedLookup(modal.bodyEl);
			}
			if (window.initEditors) {
				window.initEditors(modal.bodyEl);
			}
			if (window.initPermissionUI) {
				window.initPermissionUI(modal.bodyEl);
			}
			document.dispatchEvent(
				new CustomEvent("app:modal-loaded", {
					detail: {
						trigger: currentTrigger,
						bodyEl: modal.bodyEl,
						panelEl: modal.panelEl,
						title: modal.titleEl.textContent,
					},
				})
			);
			return;
		}
		modal.overlay.classList.add("opacity-0");
		modal.panelEl.classList.add("opacity-0", "scale-95");
		setTimeout(() => {
			modal.overlay.classList.add("hidden");
			modal.overlay.classList.remove("flex");
			modal.bodyEl.innerHTML = "";
			currentTrigger = null;
			document.body.classList.remove("overflow-hidden");
			document.dispatchEvent(new CustomEvent("app:modal-closed"));
		}, 200);
	}

	function resolveSelect(target) {
		if (!target) return null;
		if (
			target.startsWith("#") ||
			target.startsWith(".") ||
			target.startsWith("[")
		) {
			return document.querySelector(target);
		}
		return document.querySelector(`select[name="${target}"]`);
	}

	function updateSelectDirect(select, value, label) {
		if (!select) return false;
		const nextValue = value == null ? "" : String(value);
		if (!select.multiple) {
			Array.from(select.options).forEach((o) => {
				o.selected = false;
				o.removeAttribute("selected");
			});
		}
		let opt = Array.from(select.options).find((o) => String(o.value) === nextValue);
		if (!opt) {
			opt = new Option(label, nextValue, true, true);
			opt.setAttribute("selected", "selected");
			select.add(opt);
		} else {
			opt.text = label;
			opt.value = nextValue;
			opt.selected = true;
			opt.setAttribute("selected", "selected");
		}
		select.value = nextValue;
		select.dispatchEvent(new Event("change", { bubbles: true }));
		if (window.refreshSelectpicker) {
			window.refreshSelectpicker(select);
		} else if (window.jQuery && window.jQuery.fn && typeof window.jQuery.fn.selectpicker === "function") {
			window.jQuery(select).selectpicker("refresh");
		}
		return true;
	}

	function updateSelectInContainer(
		container,
		selectName,
		value,
		label,
		labelKey
	) {
		if (!container) return false;
		let select = null;
		if (
			selectName.startsWith("#") ||
			selectName.startsWith(".") ||
			selectName.startsWith("[")
		) {
			select = container.querySelector(selectName);
		} else {
			select = container.querySelector(`select[name="${selectName}"]`);
		}
		if (!select) return false;
		select.classList.remove("hidden");

		const wrapper = findSelectpickerWrapper(select);
		if (wrapper) wrapper.remove();
		return updateSelectDirect(select, value, label);
	}

	function resolveLookupWrapper(target, scope) {
		if (!target) return null;
		const root = scope || document;
		if (
			target.startsWith("#") ||
			target.startsWith(".") ||
			target.startsWith("[")
		) {
			return root.querySelector(target);
		}
		const hidden =
			root.querySelector(`input[type="hidden"][name="${target}"]`) ||
			root.querySelector(`[data-lookup-hidden][name="${target}"]`) ||
			root.querySelector(`[data-project-lookup-hidden][name="${target}"]`);
		return hidden ? hidden.closest("[data-lookup],[data-project-lookup]") : null;
	}

	function updateLookupWrapper(wrapper, value, label) {
		if (!wrapper) return false;
		const hidden =
			wrapper.querySelector("[data-lookup-hidden]") ||
			wrapper.querySelector("[data-project-lookup-hidden]");
		const input =
			wrapper.querySelector("[data-lookup-input]") ||
			wrapper.querySelector("[data-project-lookup-input]");
		const status =
			wrapper.querySelector("[data-lookup-status]") ||
			wrapper.querySelector("[data-project-lookup-status]");
		if (hidden) hidden.value = value;
		if (input) input.value = label || "";
		if (status) status.textContent = "";
		if (hidden) {
			hidden.dispatchEvent(new Event("change", { bubbles: true }));
		}
		if (input) {
			input.dispatchEvent(new Event("change", { bubbles: true }));
		}
		const event = new CustomEvent("lookup:change", {
			detail: { id: value, label: label, wrapper: wrapper },
		});
		wrapper.dispatchEvent(event);
		document.dispatchEvent(event);
		return true;
	}

	function updateLookupInContainer(container, lookupName, value, label) {
		if (!container) return false;
		const wrapper = resolveLookupWrapper(lookupName, container);
		return updateLookupWrapper(wrapper, value, label);
	}

	function resolveGroupedLookupWrapper(target, scope) {
		if (!target) return null;
		const root = scope || document;
		if (
			target.startsWith("#") ||
			target.startsWith(".") ||
			target.startsWith("[")
		) {
			return root.querySelector(target);
		}
		const hidden = root.querySelector(
			`[data-grouped-lookup-hidden][name="${target}"]`
		);
		return hidden ? hidden.closest("[data-grouped-lookup]") : null;
	}

	function appendGroupedLookupSource(wrapper, value, label, groupLabel, metaLabel) {
		const sourceNode = wrapper
			? wrapper.querySelector("[data-grouped-lookup-source]")
			: null;
		if (!sourceNode) return;
		let groups = [];
		try {
			groups = JSON.parse(sourceNode.textContent || "[]");
		} catch (err) {
			groups = [];
		}
		if (!Array.isArray(groups)) groups = [];
		const nextValue = value == null ? "" : String(value);
		const nextLabel = label == null ? "" : String(label);
		if (nextValue === "" || nextLabel === "") return;
		const exists = groups.some(function (group) {
			return Array.isArray(group.items) && group.items.some(function (item) {
				return String(item && item.id) === nextValue;
			});
		});
		if (exists) return;
		const targetGroupLabel = groupLabel ? String(groupLabel) : "New";
		let targetGroup = groups.find(function (group) {
			return String(group && group.label) === targetGroupLabel;
		});
		if (!targetGroup) {
			targetGroup = { label: targetGroupLabel, items: [] };
			groups.unshift(targetGroup);
		}
		if (!groups.length) {
			groups.push(targetGroup);
		}
		targetGroup.items = Array.isArray(targetGroup.items) ? targetGroup.items : [];
		targetGroup.items.unshift({
			id: nextValue,
			text: nextLabel,
			name: nextLabel,
			meta: metaLabel ? String(metaLabel) : "",
		});
		sourceNode.textContent = JSON.stringify(groups);
	}

	function updateGroupedLookupWrapper(wrapper, value, label, groupLabel, metaLabel) {
		if (!wrapper) return false;
		const hidden = wrapper.querySelector("[data-grouped-lookup-hidden]");
		const input = wrapper.querySelector("[data-grouped-lookup-input]");
		const clearButton = wrapper.querySelector("[data-grouped-lookup-clear]");
		if (hidden) hidden.value = value == null ? "" : String(value);
		if (input) input.value = label || "";
		if (clearButton) {
			const hasValue = Boolean(value) || Boolean(label);
			clearButton.classList.toggle("hidden", !hasValue);
			clearButton.classList.toggle("flex", hasValue);
		}
		appendGroupedLookupSource(wrapper, value, label, groupLabel, metaLabel);
		if (hidden) {
			hidden.dispatchEvent(new Event("change", { bubbles: true }));
		}
		if (input) {
			input.dispatchEvent(new Event("change", { bubbles: true }));
		}
		const event = new CustomEvent("groupedLookup:change", {
			detail: { id: value, label: label, wrapper: wrapper },
		});
		wrapper.dispatchEvent(event);
		document.dispatchEvent(event);
		return true;
	}

	function updateGroupedLookupInContainer(container, lookupName, value, label, groupLabel, metaLabel) {
		if (!container) return false;
		const wrapper = resolveGroupedLookupWrapper(lookupName, container);
		return updateGroupedLookupWrapper(wrapper, value, label, groupLabel, metaLabel);
	}

	function sanitizeModalBody(container) {
		if (!container) return "";
		const clone = container.cloneNode(true);
		const readyAttrs = [
			"data-lookup-ready",
			"data-project-lookup-ready",
			"data-grouped-lookup-ready",
			"data-items-ready",
			"data-editor-ready",
		];
		readyAttrs.forEach((attr) => {
			if (clone.hasAttribute && clone.hasAttribute(attr)) {
				clone.removeAttribute(attr);
			}
			clone.querySelectorAll(`[${attr}]`).forEach((el) => {
				el.removeAttribute(attr);
			});
		});
		clone
			.querySelectorAll('[data-selectpicker-wrapper="true"]')
			.forEach((el) => el.remove());
		clone.querySelectorAll("select.tw-selectpicker").forEach((sel) => {
			const wrapper = findSelectpickerWrapper(sel);
			if (wrapper) wrapper.remove();
			sel.classList.remove("hidden");
			sel.style.display = "";
			sel.style.visibility = "";
			if (sel.dataset && sel.dataset.enhanced) {
				delete sel.dataset.enhanced;
			}
		});
		clone.querySelectorAll("input.ts-datepicker, input.datetimepicker").forEach((input) => {
			if (input.dataset && input.dataset.enhanced) {
				delete input.dataset.enhanced;
			}
		});
		return clone.innerHTML;
	}

	function getTextFromResponse(data, labelKey) {
		if (!data || typeof data !== "object") return null;
		const preferredKeys = [
			labelKey,
			"name",
			"text",
			"label",
			"title",
			"group",
			"manufacturer",
			"customer_group",
			"category",
			"categories",
			"method_name",
			"account_name",
			"payment_method",
			"state_reason",
			"reason",
			"source",
			"department",
			"deptname",
			"designation",
			"designations",
			"designation_name",
			"leave_category",
			"account_sub_type",
		].filter(Boolean);
		for (const key of preferredKeys) {
			if (typeof data[key] === "string" && data[key].trim() !== "") {
				return data[key];
			}
		}
		const ignoredKeys = new Set([
			"status",
			"message",
			"url",
			"redirect_url",
			"redirectUrl",
			"id",
			"total",
			"items",
			"data",
		]);
		for (const [key, value] of Object.entries(data)) {
			if (ignoredKeys.has(key) || /_id$/i.test(key)) continue;
			if (typeof value === "string" && value.trim() !== "") {
				return value;
			}
		}
		return null;
	}

	async function handleSubmit(form) {
		if (!currentTrigger) return;
		const submitButton = form.querySelector('button[type="submit"], input[type="submit"]');
		const originalButtonHtml = submitButton ? submitButton.innerHTML : "";
		const originalButtonValue = submitButton ? submitButton.value : "";
		const action = form.getAttribute("action") || "";
		const method = (form.getAttribute("method") || "post").toUpperCase();
		const body = new FormData(form);

		if (submitButton) {
			submitButton.disabled = true;
			submitButton.classList.add("opacity-70", "cursor-not-allowed");
			if (submitButton.tagName === "INPUT") {
				submitButton.value = submitButton.dataset.loadingText || "Saving...";
			} else {
				submitButton.innerHTML = submitButton.dataset.loadingText || "Saving...";
			}
		}

		let payload = null;
		try {
			const res = await fetch(action, {
				method,
				body,
				headers: { "X-Requested-With": "XMLHttpRequest" },
			});
			const text = await res.text();
			try {
				payload = JSON.parse(text);
			} catch (err) {
				payload = null;
			}

			if (!payload || !payload.status) {
				if (window.toaster) {
					window.toaster.error("Could not save. Please try again.");
				} else {
					alert("Could not save. Please try again.");
				}
				return;
			}
		} finally {
			if (!payload || payload.status !== "success") {
				form.dataset.modalSubmitting = "0";
				if (submitButton) {
					submitButton.disabled = false;
					submitButton.classList.remove("opacity-70", "cursor-not-allowed");
					if (submitButton.tagName === "INPUT") {
						submitButton.value = originalButtonValue || "Save";
					} else {
						submitButton.innerHTML = originalButtonHtml || "Save";
					}
				}
			}
		}

		if (!payload || !payload.status) {
			return;
		}

		if (payload.status === "success") {
			if (typeof window.erpInvalidateDatatableCache === "function") {
				window.erpInvalidateDatatableCache();
			}
			const selectName = currentTrigger.dataset.modalSelect || "";
			const select = resolveSelect(selectName);
			const lookupName = currentTrigger.dataset.modalLookup || "";
			const valueKey = currentTrigger.dataset.modalValueKey || "id";
			const labelKey = currentTrigger.dataset.modalLabelKey || "";
			const value = payload[valueKey];
			const url = payload.url || "";
			const label = getTextFromResponse(payload, labelKey);
			const groupLabel = payload.group || payload.department || payload.deptname || "";
			const metaLabel = payload.meta || payload.designation || payload.designations || "";

			if (value && label) {
				const updated = select
					? updateSelectDirect(select, value, label)
					: false;

				if (updated && select) {
					select.dispatchEvent(new Event("change"));
				} else if (lookupName) {
					const wrapper = resolveLookupWrapper(lookupName, document);
					const groupedWrapper = resolveGroupedLookupWrapper(lookupName, document);
					const updatedLookup =
						updateLookupWrapper(wrapper, value, label) ||
						updateGroupedLookupWrapper(groupedWrapper, value, label, groupLabel, metaLabel);
					if (!updatedLookup && modalStack.length) {
						const last = modalStack[modalStack.length - 1];
						const wrapEl = document.createElement("div");
						wrapEl.innerHTML = last.body || "";
						if (
							updateLookupInContainer(wrapEl, lookupName, value, label) ||
							updateGroupedLookupInContainer(wrapEl, lookupName, value, label, groupLabel, metaLabel)
						) {
							last.body = wrapEl.innerHTML;
						}
					}
				} else if (modalStack.length) {
					const last = modalStack[modalStack.length - 1];
					const wrapper = document.createElement("div");
					wrapper.innerHTML = last.body || "";
					if (
						updateSelectInContainer(wrapper, selectName, value, label, labelKey)
					) {
						last.body = wrapper.innerHTML;
					}
				}
			}

			if (window.toaster) {
				window.toaster.success(payload.message || "Saved.");
			}

			if (Array.isArray(payload.patch)) {
				payload.patch.forEach(function (item) {
					if (!item || !item.selector) return;
					var target = document.querySelector(item.selector);
					if (!target) return;
					if (typeof item.html === "string") {
						if (item.mode === "replace") {
							target.outerHTML = item.html;
						} else {
							target.innerHTML = item.html;
						}
					}
				});
				if (window.updateIcons) {
					window.updateIcons(document);
				}
				if (typeof window.refreshAppUi === "function") {
					window.refreshAppUi(document);
				}
			}

			if (url) {
				window.location.href = base_url + url;
			}

			const shouldRefresh = !!select && !!value && !!label;
			const hasStack = modalStack.length > 0;
			const shouldReloadPage =
				(form && form.dataset && form.dataset.modalRefreshPage === "1") ||
				payload.reload_page === true ||
				payload.reloadPage === true;
			const shouldReloadTicketState =
				payload.reload_ticket_state_report === true ||
				payload.reloadTicketStateReport === true;
			closeModal();
			window.setTimeout(function () {
				if (shouldReloadTicketState && typeof window.ins_data === "function") {
					window.ins_data(base_url + "admin/tickets/tickets_state_report");
				}
				if (!window.jQuery || !window.jQuery.fn || !window.jQuery.fn.dataTable) {
					if (shouldReloadPage) {
						window.location.reload();
					}
					return;
				}
				window.jQuery(".DataTables").each(function () {
					if (window.jQuery.fn.dataTable.isDataTable(this)) {
						window.jQuery(this).DataTable().ajax.reload(null, false);
					}
				});
				if (shouldReloadPage) {
					window.location.reload();
				}
			}, 50);
			if (shouldRefresh && !hasStack) {
				if (window.refreshSelectpicker) {
					window.refreshSelectpicker(select);
				} else if (window.jQuery && window.jQuery.fn && typeof window.jQuery.fn.selectpicker === "function") {
					window.jQuery(select).selectpicker("refresh");
				}
			}

			return;
		}

		if (window.toaster) {
			window.toaster.error(payload.message || "Failed to save.");
		} else {
			alert(payload.message || "Failed to save.");
		}
	}

	function handleManualItemSubmit(form) {
		const getFieldValue = function (name) {
			const input = form.querySelector('[name="' + name + '"]');
			return input ? input.value : "";
		};
		const itemName = getFieldValue("item_name").trim();
		if (!itemName) {
			const input = form.querySelector('[name="item_name"]');
			if (input) input.focus();
			return;
		}

		let taxLookup = {};
		try {
			taxLookup = JSON.parse(form.dataset.taxLookupMap || "{}") || {};
		} catch (error) {
			taxLookup = {};
		}
		const taxValues = Array.from(form.querySelectorAll('[data-lookup-hidden-list] input[name="tax_rates_id[]"]'))
			.map(function (input) {
				return taxLookup[input.value] || "";
			})
			.filter(Boolean);

		const payload = {
			item_name: itemName,
			item_desc: getFieldValue("item_desc").trim(),
			quantity: getFieldValue("qty") || "1",
			unit_cost: getFieldValue("rate") || "0",
			unit: getFieldValue("unit").trim(),
			taxname: taxValues,
			saved_items_id: 0,
			items_id: "",
		};
		const activeTable = document.querySelector(".prodcutTable[data-terget]");
		const targetType = activeTable ? activeTable.getAttribute("data-terget") : "purchase";
		if (typeof window.addManualSalesItem === "function" && window.addManualSalesItem(payload, targetType)) {
			closeModal();
		}
	}

	async function loadModal(trigger) {
		const url = trigger.dataset.modalUrl;
		if (!url) return;
		if (trigger.dataset.modalLoading === "1") return;

		const requestToken = {};
		activeModalRequest = requestToken;
		const replaceCurrentState = modal
			&& !modal.overlay.classList.contains("hidden")
			&& modal.bodyEl
			&& (modal.bodyEl.querySelector("[data-modal-loading]") || modal.bodyEl.querySelector("[data-modal-error]"));

		setTriggerLoading(trigger, true);
		showModalLoading(trigger, replaceCurrentState);

		try {
			const res = await fetch(url, {
				headers: { "X-Requested-With": "XMLHttpRequest" },
			});
			if (!res.ok) {
				throw new Error(`Request failed (${res.status})`);
			}
			const text = await res.text();
			if (activeModalRequest !== requestToken) return;
			const doc = new DOMParser().parseFromString(text, "text/html");
			const html = doc.body ? doc.body.innerHTML : text;
			openModal(trigger, html);
		} catch (error) {
			if (activeModalRequest !== requestToken) return;
			showModalError(trigger, error && error.message ? error.message : "Please check your connection and try again.");
		} finally {
			if (activeModalRequest === requestToken) {
				activeModalRequest = null;
			}
			setTriggerLoading(trigger, false);
		}
	}

		document.addEventListener("click", (e) => {
			const trigger = e.target.closest("[data-modal-url]");
			if (!trigger) return;
			if (trigger.getAttribute("data-confirm-message") || trigger.getAttribute("data-confirm")) {
				return;
			}
			e.preventDefault();
			loadModal(trigger);
		});
		document.addEventListener("erp:open-modal", (e) => {
			const trigger = e.detail && e.detail.trigger;
			if (!trigger || !trigger.dataset || !trigger.dataset.modalUrl) return;
			loadModal(trigger);
		});

	document.addEventListener("click", (e) => {
		const toggle = e.target.closest("[data-password-toggle]");
		if (!toggle) return;
		e.preventDefault();
		const inputId = toggle.getAttribute("data-password-toggle");
		const input = inputId ? document.getElementById(inputId) : null;
		if (!input) return;
		const shouldShow = input.type === "password";
		input.type = shouldShow ? "text" : "password";
		toggle.setAttribute("aria-label", shouldShow ? "Hide password" : "Show password");
		toggle.setAttribute("title", shouldShow ? "Hide password" : "Show password");
		const icon = toggle.querySelector("[data-lucide]");
		if (icon) {
			icon.setAttribute("data-lucide", shouldShow ? "eye-off" : "eye");
			if (window.lucide && typeof window.lucide.createIcons === "function") {
				window.lucide.createIcons({ root: toggle });
			}
		}
	});

	document.addEventListener("click", (e) => {
		if (
			e.target.closest("[data-modal-close]") ||
			e.target.getAttribute("data-dismiss") === "modal"
		) {
			e.preventDefault();
			closeModal();
		}
	});

	document.addEventListener("submit", (e) => {
		if (!modal || !modal.overlay.contains(e.target)) return;
		if (e.target.getAttribute("data-modal-native-submit") === "true") return;
		e.preventDefault();
		e.stopPropagation();
		if (typeof e.stopImmediatePropagation === "function") {
			e.stopImmediatePropagation();
		}
		if (e.target.dataset.manualItemForm === "1") {
			handleManualItemSubmit(e.target);
			return;
		}
		if (e.target.dataset.modalSubmitting === "1") return;
		if (
			window.FormValidator &&
			typeof window.FormValidator.validateForm === "function"
		) {
			if (!window.FormValidator.validateForm(e.target)) {
				return;
			}
		}
		e.target.dataset.modalSubmitting = "1";
		handleSubmit(e.target);
	});
	document.addEventListener("keydown", (e) => {
		if (e.key === "Escape") closeModal();
	});
})();
