/* ---- github release intelligence ---- */
let GH = null;
const envForBranch = (b) => CFG?.envs?.find((e) => e.branch === b)?.key || null;
const ghRepoSet = () => !!CFG?.github?.repo;
async function fetchGithub() {
	if (CFG && !ghRepoSet()) { renderGithub(); return; }
	if (!GH) {
		const cached = wsGet(localStorage, "me-gh");
		let usedCache = false;
		if (cached) {
			try {
				const c = JSON.parse(cached);
				if (!CFG || c.repo === CFG.github?.repo) { GH = c; usedCache = true; renderGithub(); }
			} catch { }
		}
		if (!usedCache && $("#relRuns")) $("#relRuns").innerHTML = ["70%", "85%", "60%"].map((w) => `<div class="act-row"><div class="skl" style="height:13px;width:${w}"></div></div>`).join("");
	}
	try {
		const r = await fetch("/api/github");
		const fresh = await r.json();
		if (fresh?.ok) { GH = fresh; localStorage.setItem(wsKey("me-gh"), JSON.stringify(fresh)); }
	} catch { }
	renderGithub();
	if (typeof renderCharts === "function" && LIVE?.ok) renderCharts();
}
function renderGithub() {
	if (!$("#relNow")) return;
	if (CFG && !ghRepoSet()) {
		$("#ghMeta").textContent = "";
		$("#relNow").innerHTML = `<div class="env-card"><h3>No repository connected</h3><p class="cfg-note" style="margin-top:8px">Connect a GitHub repository in Settings to see deploy intelligence.</p><button class="btn" id="ghToSettings" style="margin-top:12px">Open Settings</button></div>`;
		$("#relDrift").innerHTML = "";
		$("#relRuns").innerHTML = "";
		$("#ghToSettings").onclick = () => switchTab("viewSettings");
		return;
	}
	if (!GH?.ok) { $("#relNow").innerHTML = `<div class="env-card"><p class="cfg-note">GitHub data unavailable (is gh authenticated?)</p></div>`; return; }
	$("#ghMeta").textContent = GH.repo + " · " + agoTxt(GH.fetchedAt);
	$("#relNow").innerHTML = (CFG?.envs || []).map((e) => {
		const v = GH.envs?.[e.key];
		return `<div class="env-card"><h3><span class="dot" style="background:${v ? "var(--good)" : "var(--ink-3)"}"></span>${e.name}</h3>` +
			(v ? `<dl>
				<div class="kv"><dt>Commit</dt><dd><a href="https://github.com/${GH.repo}/commit/${v.sha}" target="_blank" rel="noopener" style="color:var(--blue);text-decoration:none">${v.short} ↗</a></dd></div>
				<div class="kv"><dt>Change</dt><dd style="max-width:230px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="${v.message.replace(/"/g, "&quot;")}">${v.message}</dd></div>
				<div class="kv"><dt>Author</dt><dd>${v.author || ""}</dd></div>
				${v.pr ? `<div class="kv"><dt>Pull request</dt><dd><a href="${v.pr.url}" target="_blank" rel="noopener" style="color:var(--blue);text-decoration:none">#${v.pr.number} ↗</a></dd></div>` : ""}
				${v.deployedAt ? `<div class="kv"><dt>Deployed</dt><dd>${v.runUrl ? `<a href="${v.runUrl}" target="_blank" rel="noopener" style="color:var(--blue);text-decoration:none">${agoTxt(v.deployedAt)} ↗</a>` : agoTxt(v.deployedAt)}</dd></div>` : ""}
			</dl>` : `<p class="cfg-note">running image not mapped to a commit yet</p>`) + `</div>`;
	}).join("");
	const d = GH.drift;
	$("#relDrift").innerHTML = !d ? "" : d.ahead > 0
		? `<div class="callout"><b>${d.ahead} commit${d.ahead === 1 ? "" : "s"} on ${d.to} waiting to ship to ${d.from}</b>
			<div style="margin-top:8px">${d.commits.slice(-8).map((c) => `<div class="act-row"><a class="at" href="https://github.com/${GH.repo}/commit/${c.sha}" target="_blank" rel="noopener" style="color:var(--blue);text-decoration:none">${c.sha}</a><span>${c.message.slice(0, 84)}</span><span class="who">${c.author}</span></div>`).join("")}
			${d.ahead > 8 ? `<div class="act-row">… and ${d.ahead - 8} earlier commits</div>` : ""}</div></div>`
		: `<div class="no-issues">both environments run the same commit</div>`;
	$("#relRuns").innerHTML = (GH.runs || []).slice(0, 12).map((r) => {
		const envKey = envForBranch(r.headBranch);
		const st = r.status !== "completed" ? "running" : (r.conclusion || "unknown");
		const cls = st === "success" ? "env-ok" : st === "failure" ? "env-bad" : "";
		return `<div class="act-row"><span class="at">${agoTxt(r.createdAt)}</span><span><b>${r.workflowName}</b> · ${r.headBranch}${envKey ? " → " + envKey : ""} · ${r.displayTitle.slice(0, 50)}</span><span class="${cls}">${st}</span><a class="who" href="${r.url}" target="_blank" rel="noopener" style="color:var(--blue);text-decoration:none">open ↗</a></div>`;
	}).join("");
}
$("#ghRefresh").onclick = fetchGithub;
$("#depTabIns").onclick = () => {
	$("#depTabIns").classList.add("active"); $("#depTabPlay").classList.remove("active");
	$("#depInsights").style.display = "block"; $("#depPlaybooks").style.display = "none";
};
$("#depTabPlay").onclick = () => {
	$("#depTabPlay").classList.add("active"); $("#depTabIns").classList.remove("active");
	$("#depPlaybooks").style.display = "block"; $("#depInsights").style.display = "none";
};

/* ---- cross-service log search ---- */
async function searchEverything() {
	const q = $("#xSearch").value.trim();
	if (q.length < 2) return;
	const v = $("#logView");
	v.innerHTML = `<div class="logline meta">searching every log group for "${q}"… (takes a few seconds)</div>`;
	$("#logMeta").textContent = "searching…";
	try {
		const r = await fetch(`/api/search?env=${ENV}&q=${encodeURIComponent(q)}&minutes=${LOG.minutes * 4}`);
		const d = await r.json();
		if (!d.ok) { v.innerHTML = `<div class="logline meta">${d.detail || d.reason}</div>`; return; }
		$("#logMeta").textContent = `${d.rows.length} hits across ${d.groupsSearched} log groups`;
		v.innerHTML = d.rows.length ? d.rows.map((row) => {
			const cls = /error|exception|fatal|failed/i.test(row.m) ? "err" : /warn/i.test(row.m) ? "warn" : "";
			return `<div class="logline ${cls}"><span class="ts">${(row.t || "").slice(5, 19)}</span><span style="color:var(--violet)">[${row.g}]</span> ${row.m.replace(/&/g, "&amp;").replace(/</g, "&lt;")}</div>`;
		}).join("") : `<div class="logline meta">no matches for "${q}"</div>`;
	} catch { v.innerHTML = `<div class="logline meta">search failed</div>`; }
}
$("#xSearchBtn").onclick = searchEverything;
$("#xSearch").addEventListener("keydown", (e) => { if (e.key === "Enter") searchEverything(); });

/* ---- daily briefing ---- */
async function runBriefing(fresh) {
	const box = $("#briefBox");
	box.style.display = "block";
	box.className = "ai-box thinking";
	box.textContent = "Preparing the daily briefing — deploys, alerts, errors and cost from the last 24 hours…";
	try {
		const r = await fetch(`/api/ai/briefing?env=${ENV}${fresh ? "&fresh=1" : ""}`);
		const d = await r.json();
		box.className = "ai-box";
		box.innerHTML = d.ok ? md(d.analysis) : (d.detail || "briefing unavailable");
		bindCopies(box);
		if (d.ok) {
			$("#briefRegen").style.display = "";
			$("#briefMeta").textContent = `generated ${agoTxt(d.fetchedAt)} · ${d.model.split(".").pop().split("-").slice(1, 3).join(" ")}`;
		}
	} catch { box.className = "ai-box"; box.textContent = "briefing unavailable"; }
}
$("#briefBtn").onclick = () => runBriefing(false);
$("#briefRegen").onclick = () => runBriefing(true);

/* ---- AI-generated map ---- */
const CURATED_NODES = NODES.slice(), CURATED_EDGES = EDGES.slice();
const GROUP_COLOR = { clients: "--aqua", edge: "--blue", core: "--violet", async: "--yellow", ext: "--red", stag: "--orange" };
let AUTO_LAYOUT = {};
function computeAutoLayout(nodes) {
	const colX = { clients: 46, edge: 330, core: 616, async: 902, ext: 1204 };
	const yNext = { clients: 130, edge: 130, core: 130, async: 100, ext: 100 };
	const layout = {};
	const stag = [];
	nodes.forEach((n) => {
		if (n.g === "stag") { stag.push(n); return; }
		const g = colX[n.g] != null ? n.g : "core";
		layout[n.id] = [colX[g], yNext[g]];
		yNext[g] += (detailsOn ? 118 : 82);
	});
	const maxY = Math.max(...Object.values(yNext), 700);
	stag.forEach((n, i) => { layout[n.id] = [330 + i * 276, maxY + 84]; });
	$("#canvas").style.height = Math.max(1120, maxY + (stag.length ? 240 : 80)) + "px";
	return layout;
}
function applyGraph(g) {
	NODES = g.nodes.map((n) => ({
		id: String(n.id), g: n.g, c: GROUP_COLOR[n.g] || "--blue",
		t: n.t || n.id, s: n.s || "", env: "both",
		d: { desc: n.desc || "", facts: Array.isArray(n.facts) ? n.facts : [] },
	}));
	EDGES = g.edges.filter((e) => Array.isArray(e) && e.length >= 2).map((e) => [String(e[0]), String(e[1]), e[2] || ""]);
	mapSource = "ai";
	AUTO_LAYOUT = computeAutoLayout(NODES);
	POSKEY = "fg-pos-ai";
	loadPositions();
	renderNodes(); drawEdges(); applyLayer(); applyNodeStatus();
	$("#mapReset").style.display = wsCurated() ? "" : "none";
	$("#mapMeta").textContent = "generated from live inventory · " + agoTxt(g.generatedAt || Date.now());
	renderFlowBtns();
	syncMapEmpty();
}
function restoreCurated() {
	NODES = wsCurated() ? CURATED_NODES.slice() : [];
	EDGES = wsCurated() ? CURATED_EDGES.slice() : [];
	mapSource = "curated";
	$("#canvas").style.height = "1120px";
	POSKEY = "fg-pos2";
	loadPositions();
	renderNodes(); drawEdges(); applyLayer(); applyNodeStatus();
	$("#mapReset").style.display = "none";
	$("#mapMeta").textContent = "";
	renderFlowBtns();
	syncMapEmpty();
}
$("#remapBtn").onclick = async () => {
	const b = $("#remapBtn");
	b.disabled = true; b.textContent = "remapping… ~1 min";
	try {
		const r = await fetch(`/api/ai/remap?env=${ENV}`);
		const d = await r.json();
		if (d.ok) { localStorage.setItem(wsKey("me-map"), "ai"); applyGraph(d.graph); }
		else $("#mapMeta").textContent = d.detail || "remap failed";
	} catch { $("#mapMeta").textContent = "remap failed"; }
	b.disabled = false; b.textContent = "Remap from live infra";
};
$("#mapReset").onclick = async () => {
	try { await fetch("/api/map", { method: "DELETE" }); } catch { }
	localStorage.setItem(wsKey("me-map"), "curated");
	restoreCurated();
};
async function loadSavedMap() {
	if (wsCurated() && wsGet(localStorage, "me-map") === "curated") return;
	try {
		const r = await fetch("/api/map");
		const d = await r.json();
		if (d.graph) applyGraph(d.graph);
	} catch { }
}
$("#mapEmptyRemap").onclick = async () => {
	const b = $("#mapEmptyRemap");
	b.disabled = true;
	b.textContent = "generating from live inventory… ~1 min";
	await $("#remapBtn").onclick();
	b.disabled = false;
	b.textContent = "Remap from live infra";
};

/* ---- auto refresh ---- */
let autoTimer = null;
function refreshActive() {
	const v = $$(".view").find((x) => x.classList.contains("active"))?.id;
	fetchPulse();
	if (v === "viewOverview") { fetchLive(); fetchCost(); fetchActivity(); }
	else if (v === "viewMap" || v === "viewAI") fetchLive();
	else if (v === "viewLambdas") fetchLambdas();
	else if (v === "viewLive") refreshLogs();
	else if (v === "viewDeploy") fetchGithub();
}
function applyAuto() {
	clearInterval(autoTimer);
	const on = (SETTINGS.autoSec || 0) > 0;
	$("#autoBtn").classList.toggle("on", on);
	$("#autoBtn").textContent = on ? `auto ${SETTINGS.autoSec}s` : "auto off";
	if (on) autoTimer = setInterval(refreshActive, SETTINGS.autoSec * 1000);
}
$("#autoSec").addEventListener("change", (e) => {
	const v = Math.max(5, Math.min(600, +e.target.value || 30));
	e.target.value = v;
	if (SETTINGS.autoSec > 0) SETTINGS.autoSec = v;
	SETTINGS.autoSecLast = v;
	saveSettings(); applyAuto();
});
$("#autoBtn").onclick = () => {
	SETTINGS.autoSec = SETTINGS.autoSec > 0 ? 0 : (+$("#autoSec").value || 30);
	saveSettings(); applyAuto();
};

