/* ---- console links ---- */
const CONSOLE = "https://eu-west-2.console.aws.amazon.com";
const NODE_LINKS = {
	clients: [["Amplify apps", CONSOLE + "/amplify/apps"]],
	alb: [["Load balancers", CONSOLE + "/ec2/home?region=eu-west-2#LoadBalancers:search=fairground-alb-<env>"]],
	cdn: [["CloudFront", "https://console.aws.amazon.com/cloudfront/v4/home#/distributions"]],
	imgx: [["Lambda", CONSOLE + "/lambda/home?region=eu-west-2#/functions/fairground-image-transform-handler-<env>"]],
	ecs: [["ECS service", CONSOLE + "/ecs/v2/clusters/fairground-cluster-<env>/services/fairground-service-<env>?region=eu-west-2"], ["Logs", CONSOLE + "/cloudwatch/home?region=eu-west-2#logsV2:log-groups/log-group/$252Fecs$252Ffairground-<env>"]],
	ecr: [["ECR repo", CONSOLE + "/ecr/repositories/private/<acct>/fairground-backend-<env>?region=eu-west-2"]],
	secrets: [["Secrets Manager", CONSOLE + "/secretsmanager/listsecrets?region=eu-west-2&search=fairground-<env>"]],
	gha: [["Actions", "https://github.com/Fairground-Ltd/fairground-app/actions"]],
	sns: [["SNS topics", CONSOLE + "/sns/v3/home?region=eu-west-2#/topics"]],
	sqsN: [["SQS queues", CONSOLE + "/sqs/v3/home?region=eu-west-2#/queues"]],
	lamN: [["Lambda", CONSOLE + "/lambda/home?region=eu-west-2#/functions/fairground-notifications-handler-<env>"]],
	mc: [["MediaConvert jobs", CONSOLE + "/mediaconvert/home?region=eu-west-2#/jobs/list"]],
	crons: [["Payout lambdas", CONSOLE + "/lambda/home?region=eu-west-2#/functions"]],
	del: [["Deletion lambdas", CONSOLE + "/lambda/home?region=eu-west-2#/functions"]],
	supabase: [["Supabase dashboard", "https://supabase.com/dashboard"]],
	s3: [["S3 buckets", "https://s3.console.aws.amazon.com/s3/buckets?region=eu-west-2&prefix=fairground"]],
	stripe: [["Stripe dashboard", "https://dashboard.stripe.com"]],
	msg: [["Firebase console", "https://console.firebase.google.com"], ["Resend", "https://resend.com/emails"]],
	obs: [["Dashboard", CONSOLE + "/cloudwatch/home?region=eu-west-2#dashboards/dashboard/fairground-monitoring-<env>"], ["Alarms", CONSOLE + "/cloudwatch/home?region=eu-west-2#alarmsV2:"], ["Sentry", "https://sentry.io"], ["PostHog", "https://app.posthog.com"]],
	mercur: [["ECS service", CONSOLE + "/ecs/v2/clusters/fairground-mercur-backend-staging-cluster?region=eu-west-2"]],
	sftp: [["EC2 instance", CONSOLE + "/ec2/home?region=eu-west-2#Instances:search=sftpgo"], ["SFTPGo admin", "https://staging.sftp.fairground.music"]],
	sftpq: [["SQS queues", CONSOLE + "/sqs/v3/home?region=eu-west-2#/queues"]],
};
const QUICK_LINKS = [
	["ECS cluster", "fairground-cluster-<env>", CONSOLE + "/ecs/v2/clusters/fairground-cluster-<env>?region=eu-west-2"],
	["Monitoring dashboard", "fairground-monitoring-<env>", CONSOLE + "/cloudwatch/home?region=eu-west-2#dashboards/dashboard/fairground-monitoring-<env>"],
	["Backend logs", "/ecs/fairground-<env>", CONSOLE + "/cloudwatch/home?region=eu-west-2#logsV2:log-groups/log-group/$252Fecs$252Ffairground-<env>"],
	["Alarms", "CloudWatch", CONSOLE + "/cloudwatch/home?region=eu-west-2#alarmsV2:"],
	["Secrets", "fairground-<env>-*", CONSOLE + "/secretsmanager/listsecrets?region=eu-west-2&search=fairground"],
	["ECR", "backend images", CONSOLE + "/ecr/repositories?region=eu-west-2"],
	["S3", "media buckets", "https://s3.console.aws.amazon.com/s3/buckets?region=eu-west-2"],
	["GitHub Actions", "deploys", "https://github.com/Fairground-Ltd/fairground-app/actions"],
	["Supabase", "db + auth", "https://supabase.com/dashboard"],
	["Stripe", "payments", "https://dashboard.stripe.com"],
	["Amplify", "web hosting", CONSOLE + "/amplify/apps"],
	["Sentry", "errors", "https://sentry.io"],
];
function renderQuickLinks() {
	let links;
	if (wsCurated()) links = QUICK_LINKS;
	else {
		const e = envCfg();
		links = [];
		if (e.cluster) links.push(["ECS cluster", e.cluster, awsConsoleUrl("ecs")]);
		if (e.logGroup) links.push(["Backend logs", e.logGroup, awsConsoleUrl("logGroup")]);
		links.push(["Alarms", "CloudWatch", awsConsoleUrl("alarm")]);
		links.push(["Queues", "SQS", awsConsoleUrl("sqs")]);
		links.push(["Cost Explorer", "month to date", awsConsoleUrl("cost")]);
		if (CFG?.github?.repo) links.push(["GitHub Actions", "deploys", `https://github.com/${CFG.github.repo}/actions`]);
	}
	$("#quickLinks").innerHTML = links.map(([t, s, u]) =>
		`<a class="ql" href="${envName(u)}" target="_blank" rel="noopener">${t}<span>${envName(s)}</span></a>`).join("");
}

/* ---- layer filter ---- */
let activeLayer = "all";
[["all", "All"], ["clients", "Clients"], ["edge", "Edge"], ["core", "Core"], ["async", "Async"], ["ext", "Data"], ["stag", "Staging"]].forEach(([g, label]) => {
	const b = document.createElement("button");
	b.className = "layer-chip" + (g === "all" ? " on" : "");
	b.textContent = label;
	b.onclick = () => {
		activeLayer = g;
		$$(".layer-chip").forEach(x => x.classList.toggle("on", x === b));
		applyLayer();
		if (g === "all") fitAll(); else fitGroup(g);
	};
	$("#layerChips").appendChild(b);
});
function applyLayer() {
	NODES.forEach(n => {
		const el = $("#n-" + n.id);
		if (el) el.classList.toggle("layer-dim", activeLayer !== "all" && n.g !== activeLayer);
	});
}

/* ---- command palette + shortcuts ---- */
function switchTab(v) {
	$$(".tab").forEach(x => x.classList.toggle("active", x.dataset.view === v));
	$$(".view").forEach(x => x.classList.toggle("active", x.id === v));
	if (typeof loadForTab === "function") loadForTab(v);
}
const palette = $("#palette"), palInput = $("#palInput"), palList = $("#palList");
let palIdx = 0, palItems = [];
function buildPalItems() {
	const items = [];
	NODES.forEach(n => items.push({ t: n.t, s: envName(n.s), k: "component", run: () => {
		switchTab("viewMap");
		setTimeout(() => {
			const el = $("#n-" + n.id);
			if (el) { centerOnNode(el, 1); openDrawer(n); }
		}, 40);
	}}));
	[["Overview", "viewOverview"], ["Timeline", "viewTimeline"], ["Architecture", "viewMap"], ["Lambdas", "viewLambdas"], ["Live logs", "viewLive"], ["Issues", "viewAI"], ["Security", "viewSecurity"], ["Actions", "viewActions"], ["Deploys", "viewDeploy"], ["Runbook", "viewLogs"], ["Settings", "viewSettings"]]
		.forEach(([t, v]) => items.push({ t, s: "", k: "tab", run: () => switchTab(v) }));
	(LAMBDAS?.functions || []).forEach((f) => items.push({ t: f.name, s: "lambda logs", k: "logs", run: () => openLogs("/aws/lambda/" + f.name) }));
	if (wsCurated()) Object.keys(FLOWS).forEach(name => items.push({ t: name, s: "", k: "flow", run: () => {
		switchTab("viewMap");
		const btn = [...$$(".flow-btn")].find(b => b.textContent.includes(name));
		if (btn && !btn.classList.contains("active")) btn.click();
	}}));
	items.push({ t: "Daily briefing", s: "last 24h digest", k: "action", run: () => { switchTab("viewOverview"); $("#briefBtn").click(); } });
	items.push({ t: "Search all logs", s: "every service at once", k: "action", run: () => { switchTab("viewLive"); $("#xSearch").focus(); } });
	items.push({ t: "What is running where", s: "releases + drift", k: "action", run: () => switchTab("viewDeploy") });
	items.push({ t: "Fit map to screen", s: "", k: "action", run: () => { switchTab("viewMap"); $("#zoomFit").click(); } });
	items.push({ t: "Reset map layout", s: "", k: "action", run: () => $("#resetLayout").click() });
	items.push({ t: "Toggle theme", s: "", k: "action", run: () => $("#themeBtn").click() });
	return items;
}
function renderPal(q) {
	const all = buildPalItems();
	palItems = q
		? all.filter(i => (i.t + " " + i.s).toLowerCase().includes(q.toLowerCase()))
		: all.slice(0, 12);
	palIdx = 0;
	palList.innerHTML = palItems.length
		? palItems.map((i, idx) => `<div class="pal-item${idx === 0 ? " on" : ""}" data-i="${idx}">${i.t}${i.s ? ` <span style="color:var(--ink-3);font-size:11px">${i.s}</span>` : ""}<span class="k">${i.k}</span></div>`).join("")
		: `<div class="pal-empty">nothing matches</div>`;
	$$(".pal-item", palList).forEach(el => el.onclick = () => { palItems[+el.dataset.i].run(); closePalette(); });
}
function openPalette() { palette.classList.add("open"); palInput.value = ""; renderPal(""); palInput.focus(); }
function closePalette() { palette.classList.remove("open"); }
$("#palBtn").onclick = openPalette;
palInput.addEventListener("input", e => renderPal(e.target.value));
palInput.addEventListener("keydown", e => {
	if (e.key === "Escape") closePalette();
	else if (e.key === "Enter" && palItems[palIdx]) { palItems[palIdx].run(); closePalette(); }
	else if (e.key === "ArrowDown" || e.key === "ArrowUp") {
		e.preventDefault();
		palIdx = (palIdx + (e.key === "ArrowDown" ? 1 : -1) + palItems.length) % palItems.length;
		$$(".pal-item", palList).forEach((el, i) => el.classList.toggle("on", i === palIdx));
	}
});
palette.addEventListener("click", e => { if (e.target === palette) closePalette(); });
document.addEventListener("keydown", e => {
	if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
		e.preventDefault();
		palette.classList.contains("open") ? closePalette() : openPalette();
		return;
	}
	if (e.target.tagName === "INPUT" || e.target.tagName === "TEXTAREA" || palette.classList.contains("open")) return;
	const tabKeys = { "1": "viewOverview", "2": "viewMap", "3": "viewLambdas", "4": "viewLive", "5": "viewAI", "6": "viewActions", "7": "viewDeploy", "8": "viewLogs", "9": "viewSettings" };
	if (tabKeys[e.key]) switchTab(tabKeys[e.key]);
	if ($("#viewMap").classList.contains("active")) {
		if (e.key === "f") fitAll();
		else if (e.key === "ArrowLeft" && !flowActive) { VIEW.x += 110; applyView(); }
		else if (e.key === "ArrowRight" && !flowActive) { VIEW.x -= 110; applyView(); }
		else if (e.key === "ArrowUp") { VIEW.y += 110; applyView(); }
		else if (e.key === "ArrowDown") { VIEW.y -= 110; applyView(); }
		else if (e.key === "=" || e.key === "+") zoomAt(1.15);
		else if (e.key === "-") zoomAt(1 / 1.15);
	}
});

/* ---- pen annotations ---- */
let penMode = false, penColor = "#3987e5";
let inkStrokes = [];
const inkSvg = $("#inkSvg");
function loadInk() { inkStrokes = JSON.parse(wsGet(localStorage, "me-ink") || "[]"); renderInk(); }
function renderInk() {
	inkSvg.innerHTML = inkStrokes.map((st) =>
		`<polyline points="${st.p}" fill="none" stroke="${st.c}" stroke-width="3.4" stroke-linecap="round" stroke-linejoin="round" opacity="0.95"></polyline>`).join("");
}
$("#penBtn").onclick = () => {
	penMode = !penMode;
	$("#penBtn").classList.toggle("on", penMode);
	$("#penColors").style.display = penMode ? "inline-flex" : "none";
	$("#mapWrap").classList.toggle("pen", penMode);
};
$$(".pen-c").forEach((b) => b.onclick = () => {
	penColor = b.dataset.c;
	$$(".pen-c").forEach((x) => x.classList.toggle("on", x === b));
});
$("#penClear").onclick = () => { inkStrokes = []; localStorage.setItem(wsKey("me-ink"), "[]"); renderInk(); };
$("#mapWrap").addEventListener("pointerdown", (ev) => {
	if (!penMode || ev.button !== 0) return;
	ev.preventDefault(); ev.stopPropagation();
	const r = $("#mapWrap").getBoundingClientRect();
	const wpt = (e) => `${((e.clientX - r.left - VIEW.x) / VIEW.z).toFixed(1)},${((e.clientY - r.top - VIEW.y) / VIEW.z).toFixed(1)}`;
	const pts = [wpt(ev)];
	const live = document.createElementNS("http://www.w3.org/2000/svg", "polyline");
	live.setAttribute("fill", "none"); live.setAttribute("stroke", penColor);
	live.setAttribute("stroke-width", "3.4"); live.setAttribute("stroke-linecap", "round"); live.setAttribute("stroke-linejoin", "round");
	inkSvg.appendChild(live);
	const move = (e) => { pts.push(wpt(e)); live.setAttribute("points", pts.join(" ")); };
	const up = () => {
		window.removeEventListener("pointermove", move);
		window.removeEventListener("pointerup", up);
		if (pts.length > 1) { inkStrokes.push({ c: penColor, p: pts.join(" ") }); localStorage.setItem(wsKey("me-ink"), JSON.stringify(inkStrokes)); }
		renderInk();
	};
	window.addEventListener("pointermove", move);
	window.addEventListener("pointerup", up);
}, { capture: true });
renderInk();

/* ---- pan anywhere + wheel ---- */
const wrapEl = $("#mapWrap");
wrapEl.addEventListener("pointerdown", (ev) => {
	if (ev.target !== wrapEl && ev.target !== canvas && !ev.target.classList?.contains("gbox")) return;
	wrapEl.classList.add("panning");
	const sx = ev.clientX, sy = ev.clientY, ox = VIEW.x, oy = VIEW.y;
	const move = (e) => { VIEW.x = ox + (e.clientX - sx); VIEW.y = oy + (e.clientY - sy); applyView(); };
	const up = () => { wrapEl.classList.remove("panning"); window.removeEventListener("pointermove", move); window.removeEventListener("pointerup", up); };
	window.addEventListener("pointermove", move);
	window.addEventListener("pointerup", up);
});
wrapEl.addEventListener("wheel", (e) => {
	e.preventDefault();
	if (e.ctrlKey || e.metaKey) zoomAt(e.deltaY < 0 ? 1.09 : 1 / 1.09, e.clientX, e.clientY);
	else { VIEW.x -= e.deltaX; VIEW.y -= e.deltaY; applyView(); }
}, { passive: false });

