/* ---- issues engine ---- */
let ACKED = {};
let LAMBDAS = null;
function loadIssueState() {
	ACKED = JSON.parse(wsGet(localStorage, "me-ack") || "{}");
	HIST = JSON.parse(wsGet(localStorage, "me-hist") || "[]");
}
function deriveIssues() {
	const issues = [];
	if (!LIVE?.ok) return issues;
	const s = LIVE.sections;
	const mem = s.memory?.at(-1);
	if (mem && mem.max >= SETTINGS.memCrit) issues.push({ k: "mem90", sev: "p1", tx: `<b>Task memory at ${mem.max}%</b> — OOM kill imminent on ${envCfg().service || "the service"}. Force a new deployment or ship the fix.`, url: awsConsoleUrl("ecs") });
	else if (mem && mem.max >= SETTINGS.memWarn) issues.push({ k: "mem70", sev: "p2", tx: `<b>Task memory at ${mem.max}%</b> — above the 70% alarm line, watch the trend.`, url: awsConsoleUrl("ecs") });
	if (s.ecs && s.ecs.running < s.ecs.desired) issues.push({ k: "tasks", sev: "p1", tx: `<b>${s.ecs.running}/${s.ecs.desired} tasks running</b> — capacity below desired.`, url: awsConsoleUrl("ecs") });
	if (s.ecs && s.ecs.rollout !== "COMPLETED") issues.push({ k: "roll", sev: "p2", tx: `<b>Deployment ${s.ecs.rollout}</b> on ${envCfg().service || "the service"}.`, url: awsConsoleUrl("ecs") });
	if (s.targets && s.targets.healthy < s.targets.total) issues.push({ k: "tg", sev: "p1", tx: `<b>${s.targets.healthy}/${s.targets.total} healthy targets</b> behind the ALB.`, url: awsConsoleUrl("ecs") });
	firingAlarms().forEach((a) => issues.push({ k: "al:" + a.name, sev: "p1", tx: `<b>ALARM ${a.name}</b> — ${a.reason}`, url: awsConsoleUrl("alarm", a.name) }));
	(s.queues || []).forEach((q) => {
		if (/dlq|dead/.test(q.name) && q.visible > 0) issues.push({ k: "dlq:" + q.name, sev: "p1", tx: `<b>${q.visible} message(s) in ${q.name}</b> — inspect and redrive.`, url: awsConsoleUrl("sqs") });
		else if (q.visible > SETTINGS.queueMax) issues.push({ k: "q:" + q.name, sev: "p2", tx: `<b>${q.visible} queued in ${q.name}</b> · oldest ${Math.round(q.oldestSec / 60)}m — consumer keeping up?`, url: awsConsoleUrl("sqs") });
	});
	const p = s.pings || {};
	if (p.api && p.api.status !== 200) issues.push({ k: "api", sev: "p1", tx: `<b>API health check returned ${p.api.status || "no response"}</b> (${ENVS_API()}).` });
	if (p.api && p.api.status === 200 && p.api.ms > 3000) issues.push({ k: "apims", sev: "p2", tx: `<b>API health slow: ${p.api.ms}ms</b>.` });
	const fx = (s.fivexx || []).slice(-3).reduce((a, b) => a + (b.sum ?? 0), 0);
	if (fx > 10) issues.push({ k: "5xx", sev: "p2", tx: `<b>${fx} target 5xx</b> in the last 15 minutes.` });
	(LAMBDAS?.functions || []).forEach((f) => {
		if ((f.errors24h ?? 0) >= 10) issues.push({ k: "lam:" + f.name, sev: "p2", tx: `<b>${f.name}: ${Math.round(f.errors24h)} errors in 24h</b> — check its logs.`, url: awsConsoleUrl("lambda", f.name) });
	});
	if (typeof securityIssues === "function") securityIssues().forEach((i) => issues.push(i));
	return issues;
}
const ENVS_API = () => envCfg().api || "api endpoint";
let lastIssueKeys = new Set();
function renderIssues() {
	const issues = deriveIssues();
	const fresh = issues.filter((i) => !ACKED[i.k]);
	const cnt = fresh.filter((i) => i.sev === "p1").length || fresh.length;
	const bell = $("#bellCnt");
	bell.style.display = fresh.length ? "flex" : "none";
	bell.textContent = fresh.length;
	document.title = fresh.length ? `(${fresh.length}) CloudEye` : "CloudEye";
	const newOnes = issues.filter((i) => !lastIssueKeys.has(i.k));
	const clearedKeys = [...lastIssueKeys].filter((k) => !issues.some((i) => i.k === k));
	newOnes.forEach((i) => pushHist({ t: Date.now(), sev: i.sev, kind: "raised", tx: i.tx.replace(/<[^>]+>/g, "") }));
	clearedKeys.forEach((k) => pushHist({ t: Date.now(), sev: "ok", kind: "cleared", tx: k }));
	if (SETTINGS.notif && "Notification" in window && Notification.permission === "granted") {
		newOnes.filter((i) => i.sev === "p1" && !ACKED[i.k])
			.forEach((i) => new Notification("CloudEye · " + LIVE.env, { body: i.tx.replace(/<[^>]+>/g, "") }));
	}
	if (SETTINGS.sound && newOnes.some((i) => i.sev === "p1" && !ACKED[i.k])) beep();
	lastIssueKeys = new Set(issues.map((i) => i.k));
	renderHist();
	const el = $("#issueList");
	if (!el) return;
	el.innerHTML = issues.length ? issues.map((i) => `
		<div class="issue ${ACKED[i.k] ? "acked" : ""}">
			<span class="sev ${i.sev}">${i.sev.toUpperCase()}</span>
			<span class="tx">${i.tx}</span>
			${extLink(i.url, "open")}
			<button class="btn ack" data-k="${i.k}">${ACKED[i.k] ? "unack" : "ack"}</button>
		</div>`).join("") : `<div class="no-issues">No active issues</div>`;
	$$(".ack", el).forEach((b) => b.onclick = () => {
		if (ACKED[b.dataset.k]) delete ACKED[b.dataset.k]; else ACKED[b.dataset.k] = Date.now();
		localStorage.setItem(wsKey("me-ack"), JSON.stringify(ACKED));
		renderIssues();
	});
}
let HIST = [];
function pushHist(e) { HIST.unshift(e); HIST = HIST.slice(0, 100); localStorage.setItem(wsKey("me-hist"), JSON.stringify(HIST)); }
function renderHist() {
	const el = $("#histList");
	if (!el) return;
	el.innerHTML = HIST.length ? HIST.slice(0, 30).map((h) =>
		`<div class="act-row"><span class="at">${agoTxt(h.t)}</span><span class="sev ${h.sev === "ok" ? "p3" : h.sev}">${h.kind === "cleared" ? "CLEARED" : h.sev.toUpperCase()}</span><span>${h.tx.slice(0, 120)}</span></div>`).join("")
		: `<div class="act-row">no alerts recorded yet</div>`;
}
$("#histClear").onclick = () => { HIST = []; localStorage.setItem(wsKey("me-hist"), "[]"); renderHist(); };
function beep() {
	try {
		const ctx = new (window.AudioContext || window.webkitAudioContext)();
		const o = ctx.createOscillator(), g = ctx.createGain();
		o.connect(g); g.connect(ctx.destination);
		o.frequency.value = 880;
		g.gain.setValueAtTime(0.12, ctx.currentTime);
		g.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.5);
		o.start(); o.stop(ctx.currentTime + 0.5);
	} catch { }
}
$("#bellBtn").onclick = () => {
	if ("Notification" in window && Notification.permission === "default") Notification.requestPermission();
	switchTab("viewAI");
};

