// 帳號管理「代入帳號」畫中畫視窗管理器 ==========================================================
// 由外框 FatherPage.html 載入(浮在整個平台上,管理員切頁也不會關);4_3 帳號管理呼叫 parent.ImpWin.open()。
// 每個視窗內是一個以被代入帳號身分登入的完整 FatherPage(iframe,網址 ?impw=<視窗id>&impticket=<票證>&imppage=<頁面>)。
// 代入視窗的登入資訊放在 sessionStorage "__imp.<視窗id>." 命名空間(見 FatherPage.js 的 LS),不碰管理員的 localStorage。
// 桌面:拖曳標題列移動、八方向改大小、最大化/還原、最小化成右下角小卡、關閉;手機:全螢幕覆蓋 + 返回(縮成小卡)。
// 關閉 = 以代入 token 呼叫 LogOut 立即撤銷代入 session;管理員登出 / 離開頁面 → 全部撤銷。
// DOM 一律 createElement/textContent;樣式在 ImpersonateWin.css,JS 只設定拖曳/改大小必需的位置與尺寸。
(function () {
	if (window.ImpWin) return;
	var MAX = 4;
	var NS = "__imp.";
	var wins = [];
	var zTop = 100000;
	var seq = 0;
	var isImpFrame = typeof IMP_MODE !== "undefined" && IMP_MODE;	//代入視窗內的 FatherPage 不提供再開代入視窗

	function L(n, fb) {
		try { var t = window.getLanguageString("/FatherPage.html", n); return t || fb; } catch (e) { return fb; }
	}
	function phone() {
		try { if (window.isphone && window.isphone !== "0") return true; } catch (e) { }
		return window.innerWidth < 700;
	}
	function el(tag, cls, text) {
		var e = document.createElement(tag);
		if (cls) e.className = cls;
		if (text != null) e.textContent = text;
		return e;
	}
	var SVGNS = "http://www.w3.org/2000/svg";
	function icon(d) {
		var s = document.createElementNS(SVGNS, "svg");
		s.setAttribute("viewBox", "0 0 16 16");
		s.setAttribute("aria-hidden", "true");
		var p = document.createElementNS(SVGNS, "path");
		p.setAttribute("d", d);
		s.appendChild(p);
		return s;
	}
	var ICON = {
		min: "M3 12h10",
		max: "M3 3h10v10H3z",
		restore: "M5 3h8v8M3 5h8v8H3z",
		close: "M4 4l8 8M12 4l-8 8",
		back: "M10 3L5 8l5 5"
	};
	function btn(cls, iconKey, titleLid, titleFb, onClick) {
		var b = el("button", "impwin-btn " + cls);
		b.type = "button";
		b.appendChild(icon(ICON[iconKey]));
		var t = L(titleLid, titleFb);
		b.title = t;
		b.setAttribute("aria-label", t);
		b.addEventListener("click", function (e) { e.stopPropagation(); onClick(); });
		return b;
	}

	//代入 session 的 token:視窗內 FatherPage 的 UserIdx(同源可讀);讀不到再從 sessionStorage 命名空間取
	function tokenOf(w) {
		var t = "";
		try { var cw = w.iframe.contentWindow; if (cw && cw.UserIdx != null && cw.UserIdx !== -1) t = String(cw.UserIdx); } catch (e) { }
		if (!t || t === "undefined" || t === "-1") { try { t = sessionStorage.getItem(NS + w.id + ".UserIdx") || ""; } catch (e) { t = ""; } }
		return (t && t !== "undefined" && t !== "-1" && t !== "-999") ? t : "";
	}
	function logoutToken(token, beacon) {
		if (!token) return;
		var body = "UserIdx=" + encodeURIComponent(token) + "&val=" + Math.random();
		try {
			if (beacon && navigator.sendBeacon) { navigator.sendBeacon("./LogOut", body); return; }
			var x = new XMLHttpRequest();
			x.open("POST", "./LogOut", true);
			x.send(body);
		} catch (e) { }
	}
	function clearNs(id) {
		try {
			var p = NS + id + ".", ks = [];
			for (var i = 0; i < sessionStorage.length; i++) { var k = sessionStorage.key(i); if (k && k.indexOf(p) === 0) ks.push(k); }
			for (var j = 0; j < ks.length; j++) sessionStorage.removeItem(ks[j]);
		} catch (e) { }
	}
	//上一次載入(同分頁重新整理)留下的代入命名空間:撤銷其 session 並清掉
	function pruneStale() {
		try {
			var ids = {};
			for (var i = 0; i < sessionStorage.length; i++) {
				var k = sessionStorage.key(i);
				if (k && k.indexOf(NS) === 0) ids[k.substring(NS.length).split(".")[0]] = 1;
			}
			for (var id in ids) {
				var t = sessionStorage.getItem(NS + id + ".UserIdx");
				if (t) logoutToken(t, false);
				clearNs(id);
			}
		} catch (e) { }
	}

	function find(id) { for (var i = 0; i < wins.length; i++) if (wins[i].id === id) return wins[i]; return null; }
	function findByIdx(idx) { for (var i = 0; i < wins.length; i++) if (wins[i].idx === idx) return wins[i]; return null; }

	function titleText(w) {
		var t = L(39, "代入") + ":" + (w.name || "") + (w.user ? "(" + w.user + ")" : "");
		if (w.ended) t += " · " + L(46, "已結束");
		return t;
	}
	function refreshTitle(w) {
		w.title.textContent = titleText(w);
		w.root.setAttribute("aria-label", titleText(w));
		w.root.classList.toggle("is-ended", !!w.ended);
	}
	function focusWin(w) {
		zTop += 1;
		w.root.style.zIndex = String(zTop);
		for (var i = 0; i < wins.length; i++) wins[i].root.classList.toggle("is-active", wins[i] === w);
	}
	function clampRect(w) {
		var vw = window.innerWidth, vh = window.innerHeight;
		w.rect.w = Math.max(360, Math.min(w.rect.w, vw - 8));
		w.rect.h = Math.max(260, Math.min(w.rect.h, vh - 8));
		//整個視窗留在畫面內(標題列的關閉/最小化鈕一定點得到)
		w.rect.x = Math.max(0, Math.min(w.rect.x, vw - w.rect.w));
		w.rect.y = Math.max(0, Math.min(w.rect.y, vh - w.rect.h));
	}
	function applyRect(w) {
		var s = w.root.style;
		if (w.state === "normal" && !w.phone) {
			clampRect(w);
			s.left = w.rect.x + "px"; s.top = w.rect.y + "px";
			s.width = w.rect.w + "px"; s.height = w.rect.h + "px";
		} else {
			s.left = ""; s.top = ""; s.width = ""; s.height = "";
		}
	}
	function layoutMinimized() {
		var n = 0;
		for (var i = 0; i < wins.length; i++) {
			var w = wins[i];
			if (w.state !== "min") { w.root.style.bottom = ""; continue; }
			w.root.style.bottom = (12 + n * 52) + "px";
			n++;
		}
	}
	function setState(w, st) {
		if (st === "min" && w.state !== "min") w.prevState = w.state;	//最小化前記住原狀態(還原用)
		w.state = st;
		w.root.classList.toggle("is-max", st === "max");
		w.root.classList.toggle("is-min", st === "min");
		w.btnMax.replaceChildren(icon(st === "max" ? ICON.restore : ICON.max));
		var t = st === "max" ? L(42, "還原") : L(41, "最大化");
		w.btnMax.title = t; w.btnMax.setAttribute("aria-label", t);
		applyRect(w);
		layoutMinimized();
		if (st !== "min") focusWin(w);
	}

	//拖曳 / 改大小:Pointer Events + 全頁遮罩(拖過 iframe 時事件才不會被 iframe 吃掉)
	function startDrag(w, e, mode) {
		if (w.phone || w.state !== "normal") return;
		if (e.button != null && e.button !== 0) return;
		e.preventDefault();
		focusWin(w);
		var sx = e.clientX, sy = e.clientY;
		var r0 = { x: w.rect.x, y: w.rect.y, w: w.rect.w, h: w.rect.h };
		var shield = el("div", "impwin-shield" + (mode === "move" ? "" : " is-" + mode));
		document.body.appendChild(shield);
		w.root.classList.add("is-dragging");
		function mv(ev) {
			var dx = ev.clientX - sx, dy = ev.clientY - sy;
			if (mode === "move") { w.rect.x = r0.x + dx; w.rect.y = r0.y + dy; }
			else {
				if (mode.indexOf("e") >= 0) w.rect.w = r0.w + dx;
				if (mode.indexOf("s") >= 0) w.rect.h = r0.h + dy;
				if (mode.indexOf("w") >= 0) { w.rect.w = r0.w - dx; w.rect.x = r0.x + dx; }
				if (mode.indexOf("n") >= 0) { w.rect.h = r0.h - dy; w.rect.y = r0.y + dy; }
			}
			applyRect(w);
		}
		function up() {
			window.removeEventListener("pointermove", mv);
			window.removeEventListener("pointerup", up);
			window.removeEventListener("pointercancel", up);
			if (shield.parentNode) shield.parentNode.removeChild(shield);
			w.root.classList.remove("is-dragging");
		}
		window.addEventListener("pointermove", mv);
		window.addEventListener("pointerup", up);
		window.addEventListener("pointercancel", up);
	}

	function build(w) {
		var root = el("div", "impwin" + (w.phone ? " is-phone" : ""));
		root.setAttribute("role", "dialog");
		root.dataset.impw = w.id;
		var bar = el("div", "impwin-bar");
		var lead = el("div", "impwin-lead");
		if (w.phone) lead.appendChild(btn("impwin-back", "back", 44, "返回", function () { setState(w, "min"); }));
		var badge = el("span", "impwin-badge", L(39, "代入"));
		var title = el("span", "impwin-title");
		lead.appendChild(badge);
		lead.appendChild(title);
		var acts = el("div", "impwin-acts");
		var bMin = btn("impwin-min", "min", 40, "最小化", function () { setState(w, w.state === "min" ? w.prevState || "normal" : "min"); });
		var bMax = btn("impwin-max", "max", 41, "最大化", function () { setState(w, w.state === "max" ? "normal" : "max"); });
		var bClose = btn("impwin-close", "close", 43, "關閉", function () { close(w.id); });
		if (!w.phone) acts.appendChild(bMin);
		if (!w.phone) acts.appendChild(bMax);
		acts.appendChild(bClose);
		bar.appendChild(lead);
		bar.appendChild(acts);
		var body = el("div", "impwin-body");
		var iframe = document.createElement("iframe");
		iframe.className = "impwin-frame";
		iframe.setAttribute("title", L(39, "代入") + " " + (w.name || ""));
		//縱深防禦:代入視窗內任何內容都不能導覽外框(沒有 allow-top-navigation*)。
		//FatherPage 本身要同源才能運作(sessionStorage、SendCmd、與外框 ImpWin 溝通),故保留 allow-same-origin ——
		//同源腳本理論上可自行移除此屬性,所以真正的隔離靠「被代入帳號可控內容」的 iframe 不給 allow-same-origin
		//(平面圖網頁元件 HTML/網址模式、攝影機元件,見 1_2_UserMapView.js impSandboxUserFrame 與 Map_ServeHtmlEmbed 的 CSP sandbox)。
		//allow-popups-to-escape-sandbox:報表/外部連結開的新視窗照常運作;allow-downloads:匯出下載。
		iframe.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox allow-modals allow-downloads");
		iframe.setAttribute("allow", "fullscreen; autoplay; camera; microphone; encrypted-media; display-capture; clipboard-write");
		iframe.src = "FatherPage.html?impw=" + encodeURIComponent(w.id) + "&impticket=" + encodeURIComponent(w.ticket) +
			(w.page > 0 ? "&imppage=" + w.page : "") + (w.phone ? "&isphone=1" : "");
		w.ticket = "";
		body.appendChild(iframe);
		root.appendChild(bar);
		root.appendChild(body);
		if (!w.phone) {
			var dirs = ["n", "s", "e", "w", "ne", "nw", "se", "sw"];
			for (var i = 0; i < dirs.length; i++) {
				(function (d) {
					var h = el("div", "impwin-rs impwin-rs-" + d);
					h.addEventListener("pointerdown", function (e) { startDrag(w, e, d); });
					root.appendChild(h);
				})(dirs[i]);
			}
			bar.addEventListener("pointerdown", function (e) {
				if (e.target.closest && e.target.closest(".impwin-btn")) return;
				if (w.state === "min") return;
				startDrag(w, e, "move");
			});
			bar.addEventListener("dblclick", function (e) {
				if (e.target.closest && e.target.closest(".impwin-btn")) return;
				if (w.state !== "min") setState(w, w.state === "max" ? "normal" : "max");
			});
		}
		//最小化小卡:點標題列還原
		bar.addEventListener("click", function (e) {
			if (w.state !== "min") return;
			if (e.target.closest && e.target.closest(".impwin-btn")) return;
			setState(w, w.prevState || (w.phone ? "max" : "normal"));
		});
		root.addEventListener("pointerdown", function () { if (w.state !== "min") focusWin(w); }, true);
		w.root = root; w.title = title; w.iframe = iframe; w.btnMax = bMax;
		document.body.appendChild(root);
	}

	function open(opt) {
		if (isImpFrame || !opt || !opt.idx) return false;
		var exist = findByIdx(String(opt.idx));
		//同一帳號已開著:叫到最前面(不用新票證;帶來的票證不兌換,60 秒後自然失效)
		if (exist && !exist.ended) { setState(exist, exist.state === "min" ? (exist.prevState || "normal") : exist.state); return true; }
		if (!opt.ticket) return false;
		if (exist) close(exist.id);
		if (wins.length >= MAX) { alert(L(45, "最多同時開啟 4 個代入視窗,請先關閉其中一個。")); return false; }
		seq++;
		var id = "w" + Date.now().toString(36) + seq + Math.floor(Math.random() * 1e6).toString(36);
		var vw = window.innerWidth, vh = window.innerHeight;
		var w = {
			id: id, idx: String(opt.idx), name: opt.name || "", user: opt.user || "", ticket: String(opt.ticket),
			page: parseInt(opt.page, 10) || 0, ended: false, phone: phone(), state: "normal", prevState: "normal",
			rect: { w: Math.min(960, vw - 160), h: Math.min(700, vh - 100), x: 0, y: 0 }
		};
		var off = (wins.length % 4) * 32;
		w.rect.x = Math.max(8, Math.round((vw - w.rect.w) / 2) + off - 48);
		w.rect.y = Math.max(8, Math.round((vh - w.rect.h) / 2) + off - 48);
		wins.push(w);
		build(w);
		refreshTitle(w);
		setState(w, w.phone ? "max" : "normal");
		return true;
	}

	function close(id) {
		var w = find(id);
		if (!w) return;
		logoutToken(tokenOf(w), false);
		try { w.iframe.src = "about:blank"; } catch (e) { }
		if (w.root.parentNode) w.root.parentNode.removeChild(w.root);
		wins.splice(wins.indexOf(w), 1);
		clearNs(id);
		layoutMinimized();
	}
	function closeAll(beacon) {
		var ids = wins.map(function (w) { return w.id; });
		for (var i = 0; i < ids.length; i++) {
			var w = find(ids[i]);
			if (!w) continue;
			if (beacon) { logoutToken(tokenOf(w), true); clearNs(w.id); continue; }
			close(ids[i]);
		}
	}
	//視窗內 FatherPage 回報:ready(登入完成,帶回名稱)/ ended(代入結束)
	function notify(id, kind, data) {
		var w = find(id);
		if (!w) return;
		if (kind === "ready" && data) {
			if (data.name && !w.name) w.name = data.name;
			if (data.user && !w.user) w.user = data.user;
			w.ended = false;
		} else if (kind === "ended") {
			w.ended = true;
		}
		refreshTitle(w);
	}
	function relang() {
		for (var i = 0; i < wins.length; i++) {
			var w = wins[i];
			refreshTitle(w);
			var b = w.root.querySelector(".impwin-badge");
			if (b) b.textContent = L(39, "代入");
			setState(w, w.state);
		}
	}

	if (!isImpFrame) {
		//只有最上層外框清理殘留:內嵌在平台頁面裡的第二層 FatherPage(與外框同一分頁、共用 sessionStorage)
		//若也清,會把外框正開著的代入視窗撤銷掉
		var isTop = false;
		try { isTop = window.top === window; } catch (e) { isTop = false; }
		if (isTop) pruneStale();
		window.addEventListener("pagehide", function () { closeAll(true); });
		window.addEventListener("resize", function () { for (var i = 0; i < wins.length; i++) applyRect(wins[i]); });
	}
	window.ImpWin = {
		open: open, close: close, closeAll: closeAll, notify: notify, relang: relang,
		has: function (idx) { var w = findByIdx(String(idx)); return !!(w && !w.ended); },
		count: function () { return wins.length; },
		max: MAX,
		list: function () { return wins.map(function (w) { return { id: w.id, idx: w.idx, name: w.name, user: w.user, state: w.state, ended: w.ended }; }); }
	};
})();
