import { api, protectedFetch } from "./api.js?v=__ASSET_VERSION__";
import { initSideNav, loadNavUser } from "./nav.js?v=__ASSET_VERSION__";
import { showToast } from "./toast.js?v=__ASSET_VERSION__";
import { wireWhyPanels } from "./why-panels.js?v=__ASSET_VERSION__";
import {
	SCORE_ATTRS,
	SCORE_LABELS,
	TICK_ATTRS,
	TICK_LABELS,
	FLAVOR_TAXONOMY,
	MAX_FLAVOR_TAGS,
	MAX_CUP_COUNT,
	computeTotalScore,
} from "/shared/cupping.js?v=__ASSET_VERSION__";

const sessionId = new URLSearchParams(location.search).get("session");

function fmtDate(value) {
	return value ? new Date(value).toLocaleString() : "—";
}

/** Disables `el` for the duration of `run()` so a slow request can't be double-submitted. */
async function guarded(el, run) {
	if (!el || el.disabled) return;
	el.disabled = true;
	try {
		await run();
	} finally {
		el.disabled = false;
	}
}

// ─── List view ───────────────────────────────────────────────────────────

async function loadSessions() {
	const body = document.getElementById("sessions-body");
	try {
		const { sessions } = await api("/api/cupping");
		if (!sessions.length) {
			body.innerHTML = `<tr><td colspan="6" class="empty-state">No cupping sessions yet.</td></tr>`;
			return;
		}
		body.replaceChildren(
			...sessions.map((s) => {
				const tr = document.createElement("tr");
				const coffee = document.createElement("td");
				coffee.textContent = s.planTitle || "Untitled";
				const score = document.createElement("td");
				score.className = "num";
				const badge = document.createElement("span");
				badge.className = "badge badge-current";
				badge.textContent = s.totalScore.toFixed(2);
				score.append(badge);
				const cups = document.createElement("td");
				cups.className = "num";
				cups.textContent = s.cupCount;
				const flavors = document.createElement("td");
				const shown = s.flavorTags.slice(0, 3).map((t) => t.split(".").pop());
				flavors.textContent =
					shown.join(", ") + (s.flavorTags.length > 3 ? ` +${s.flavorTags.length - 3}` : "");
				const updated = document.createElement("td");
				updated.textContent = fmtDate(s.updatedAt);
				const actions = document.createElement("td");
				actions.className = "data-table-actions";
				const open = document.createElement("a");
				open.className = "ghost-btn small";
				open.href = `/cupping?session=${s.id}`;
				open.textContent = "Open";
				const del = document.createElement("button");
				del.className = "ghost-btn small";
				del.type = "button";
				del.textContent = "Delete";
				del.addEventListener("click", (event) => {
					if (!confirm("Delete this cupping session?")) return;
					guarded(event.currentTarget, async () => {
						try {
							await api(`/api/cupping/${s.id}`, { method: "DELETE" });
							showToast("Session deleted.");
							loadSessions();
						} catch (error) {
							showToast(error.message, "fail");
						}
					});
				});
				actions.append(open, del);
				tr.append(coffee, score, cups, flavors, updated, actions);
				return tr;
			}),
		);
	} catch {
		body.innerHTML = `<tr><td colspan="6" class="empty-state">Could not load sessions.</td></tr>`;
	}
}

async function loadPlanOptions() {
	const select = document.getElementById("new-session-plan");
	try {
		const { plans } = await api("/api/plans");
		select.append(
			...plans.map((p) => {
				const option = document.createElement("option");
				option.value = p.id;
				option.textContent = p.plan?.fields?.["0.1"] || "Untitled plan";
				return option;
			}),
		);
	} catch {
		/* the plan-less option still works */
	}
}

function wireNewSessionForm() {
	const form = document.getElementById("new-session-form");
	form.addEventListener("submit", (event) => {
		event.preventDefault();
		const roastPlanId = document.getElementById("new-session-plan").value || undefined;
		const cupCount = Number(document.getElementById("new-session-cups").value) || 5;
		guarded(form.querySelector("button[type=submit]"), async () => {
			try {
				const { session } = await api("/api/cupping", {
					method: "POST",
					body: JSON.stringify({ roastPlanId, cupCount }),
				});
				location.assign(`/cupping?session=${session.id}`);
			} catch (error) {
				showToast(error.message, "fail");
			}
		});
	});
}

// ─── Session view ────────────────────────────────────────────────────────

let data = null; // the coerced session document (snake_case, matches shared/cupping.js)
let saveTimer = null;

function setAutosaveStatus(status) {
	const chip = document.getElementById("cupping-autosave-status");
	const text = chip.querySelector(".autosave-text");
	chip.classList.remove("saving", "saved", "failed");
	chip.classList.add(status);
	text.textContent =
		{ saving: "Saving…", saved: "Synced", failed: "Sync failed" }[status] || "Not saved yet";
}

function scheduleSave() {
	setAutosaveStatus("saving");
	clearTimeout(saveTimer);
	saveTimer = setTimeout(save, 500);
}

async function save() {
	try {
		const body = await api(`/api/cupping/${sessionId}`, {
			method: "PUT",
			body: JSON.stringify({ data }),
		});
		document.getElementById("cup-total").textContent = body.session.totalScore.toFixed(2);
		setAutosaveStatus("saved");
	} catch (error) {
		setAutosaveStatus("failed");
		showToast(error.message, "fail");
	}
}

function liveTotal() {
	return computeTotalScore(
		data.scores,
		data.ticks,
		data.taint_cups,
		data.fault_cups,
		data.cup_count,
	);
}

function renderTotal() {
	document.getElementById("cup-total").textContent = liveTotal().toFixed(2);
}

// ── Score sliders ──
function renderScoreRows() {
	const wrap = document.getElementById("cup-score-rows");
	wrap.replaceChildren(
		...SCORE_ATTRS.map((attr) => {
			const row = document.createElement("div");
			row.className = "cup-score-row";
			row.dataset.attr = attr;
			const unscored = !(data.scores[attr] > 0);
			row.classList.toggle("unscored", unscored);

			const label = document.createElement("span");
			label.className = "cup-score-label";
			label.textContent = SCORE_LABELS[attr];

			const slider = document.createElement("input");
			slider.type = "range";
			slider.min = "6";
			slider.max = "10";
			slider.step = "0.25";
			slider.value = unscored ? "6" : data.scores[attr];
			slider.setAttribute("aria-label", `${SCORE_LABELS[attr]} score`);

			const output = document.createElement("output");
			output.className = "cup-score-value";
			output.textContent = unscored ? "—" : data.scores[attr].toFixed(2);

			const clearBtn = document.createElement("button");
			clearBtn.type = "button";
			clearBtn.className = "ghost-btn small cup-score-clear";
			clearBtn.hidden = unscored;
			clearBtn.textContent = "✕";
			clearBtn.setAttribute("aria-label", `Clear ${SCORE_LABELS[attr]} score`);

			slider.addEventListener("input", () => {
				data.scores[attr] = Number(slider.value);
				row.classList.remove("unscored");
				output.textContent = data.scores[attr].toFixed(2);
				clearBtn.hidden = false;
				renderTotal();
				renderRadar();
				scheduleSave();
			});
			clearBtn.addEventListener("click", () => {
				data.scores[attr] = 0;
				row.classList.add("unscored");
				slider.value = "6";
				output.textContent = "—";
				clearBtn.hidden = true;
				renderTotal();
				renderRadar();
				scheduleSave();
			});

			row.append(label, slider, output, clearBtn);
			return row;
		}),
	);
}

// ── Tick attributes (fill-up-to cup cells) ──
function renderTickRows() {
	const wrap = document.getElementById("cup-tick-rows");
	wrap.replaceChildren(
		...TICK_ATTRS.map((attr) => {
			const row = document.createElement("div");
			row.className = "cup-tick-row";
			row.dataset.tick = attr;

			const label = document.createElement("span");
			label.className = "cup-score-label";
			label.textContent = TICK_LABELS[attr];

			const cells = document.createElement("div");
			cells.className = "cup-tick-cells";
			cells.setAttribute("role", "group");
			cells.setAttribute("aria-label", `${TICK_LABELS[attr]} — cups passed`);

			const output = document.createElement("output");

			function renderCells() {
				const count = data.ticks[attr] || 0;
				cells.replaceChildren(
					...Array.from({ length: data.cup_count }, (_, i) => {
						const btn = document.createElement("button");
						btn.type = "button";
						btn.className = "cup-tick";
						btn.setAttribute("aria-label", `Cup ${i + 1}`);
						const pressed = i < count;
						btn.setAttribute("aria-pressed", String(pressed));
						btn.addEventListener("click", () => {
							data.ticks[attr] = i >= (data.ticks[attr] || 0) ? i + 1 : i;
							renderCells();
							renderTotal();
							scheduleSave();
						});
						return btn;
					}),
				);
				output.textContent = `${count}/${data.cup_count}`;
			}
			renderCells();
			row._renderCells = renderCells;

			row.append(label, cells, output);
			return row;
		}),
	);
}

// ── Defects (taint/fault steppers) ──
function renderDefectRows() {
	const wrap = document.getElementById("cup-defect-rows");
	const specs = [
		{ key: "taint_cups", label: "Taint cups", note: "−2 pts each" },
		{ key: "fault_cups", label: "Fault cups", note: "−4 pts each" },
	];
	wrap.replaceChildren(
		...specs.map(({ key, label, note }) => {
			const row = document.createElement("div");
			row.className = "cup-stepper-row";
			const labelEl = document.createElement("span");
			labelEl.className = "cup-score-label";
			labelEl.textContent = `${label} (${note})`;
			const minus = document.createElement("button");
			minus.type = "button";
			minus.className = "ghost-btn small";
			minus.textContent = "−";
			minus.setAttribute("aria-label", `Decrease ${label}`);
			const count = document.createElement("output");
			const plus = document.createElement("button");
			plus.type = "button";
			plus.className = "ghost-btn small";
			plus.textContent = "+";
			plus.setAttribute("aria-label", `Increase ${label}`);

			function update() {
				count.textContent = data[key];
			}
			minus.addEventListener("click", () => {
				data[key] = Math.max(0, data[key] - 1);
				update();
				renderTotal();
				scheduleSave();
			});
			plus.addEventListener("click", () => {
				data[key] = Math.min(data.cup_count, data[key] + 1);
				update();
				renderTotal();
				scheduleSave();
			});
			update();
			row.append(labelEl, minus, count, plus);
			return row;
		}),
	);
}

function reclampForCupCount() {
	for (const attr of TICK_ATTRS) data.ticks[attr] = Math.min(data.ticks[attr] || 0, data.cup_count);
	data.taint_cups = Math.min(data.taint_cups, data.cup_count);
	data.fault_cups = Math.min(data.fault_cups, data.cup_count);
}

function wireCupCount() {
	const input = document.getElementById("cup-count");
	input.min = "1";
	input.max = String(MAX_CUP_COUNT);
	input.value = data.cup_count;
	input.addEventListener("change", () => {
		const next = Math.max(1, Math.min(MAX_CUP_COUNT, Number(input.value) || 1));
		data.cup_count = next;
		input.value = next;
		reclampForCupCount();
		renderTickRows();
		renderDefectRows();
		renderTotal();
		scheduleSave();
	});
}

// ── Flavor checklist ──
function renderFlavorChips() {
	const chips = document.getElementById("flavor-chips");
	chips.replaceChildren(
		...data.flavor_tags.map((tag) => {
			// A dedicated class, not .chip-opt (a checkbox-option style whose CSS uses a
			// descendant `span` selector — reusing it here with a nested span for the remove
			// button doubled-up borders/padding onto that inner span too).
			const chip = document.createElement("span");
			chip.className = "flavor-chip";
			const text = document.createElement("span");
			text.textContent = tag.split(".").pop().replaceAll("_", " ");
			const remove = document.createElement("button");
			remove.type = "button";
			remove.className = "flavor-chip-remove";
			remove.setAttribute("aria-label", `Remove ${text.textContent}`);
			remove.textContent = "✕";
			remove.addEventListener("click", () => toggleFlavorTag(tag, false));
			chip.append(text, remove);
			return chip;
		}),
	);
	document
		.getElementById("flavor-limit-note")
		.classList.toggle("hidden", data.flavor_tags.length < MAX_FLAVOR_TAGS);
}

function toggleFlavorTag(tag, checked) {
	if (checked) {
		if (data.flavor_tags.length >= MAX_FLAVOR_TAGS || data.flavor_tags.includes(tag)) return;
		data.flavor_tags.push(tag);
	} else {
		data.flavor_tags = data.flavor_tags.filter((t) => t !== tag);
	}
	renderFlavorFamilies();
	renderFlavorChips();
	scheduleSave();
}

function renderFlavorFamilies() {
	const wrap = document.getElementById("flavor-families");
	const atLimit = data.flavor_tags.length >= MAX_FLAVOR_TAGS;
	wrap.replaceChildren(
		...Object.entries(FLAVOR_TAXONOMY).map(([familyId, family]) => {
			const selectedCount = data.flavor_tags.filter((t) => t.startsWith(`${familyId}.`)).length;
			const details = document.createElement("details");
			details.className = "flavor-family";
			const summary = document.createElement("summary");
			summary.textContent = family.label + (selectedCount ? ` (${selectedCount})` : "");
			details.append(summary);
			for (const [subId, descriptors] of Object.entries(family.subgroups)) {
				const subhead = document.createElement("p");
				subhead.className = "subhead";
				subhead.textContent = subId.replaceAll("_", " ");
				const group = document.createElement("div");
				group.className = "chip-group";
				for (const descriptor of descriptors) {
					const tag = `${familyId}.${subId}.${descriptor}`;
					const label = document.createElement("label");
					label.className = "chip-opt";
					const input = document.createElement("input");
					input.type = "checkbox";
					input.checked = data.flavor_tags.includes(tag);
					input.disabled = atLimit && !input.checked;
					input.addEventListener("change", () => toggleFlavorTag(tag, input.checked));
					const span = document.createElement("span");
					span.textContent = descriptor.replaceAll("_", " ");
					label.append(input, span);
					group.append(label);
				}
				details.append(subhead, group);
			}
			return details;
		}),
	);
}

// ── Radar chart (pure SVG, no dependency) ──
const RADAR_CENTER = 120;
const RADAR_RADIUS = 90;

function radarPoints() {
	const n = SCORE_ATTRS.length;
	return SCORE_ATTRS.map((attr, i) => {
		const raw = data.scores[attr] || 0;
		const frac = raw > 0 ? Math.max(0, Math.min(1, (raw - 6) / 4)) : 0;
		const angle = -Math.PI / 2 + (2 * Math.PI * i) / n;
		return {
			x: RADAR_CENTER + frac * RADAR_RADIUS * Math.cos(angle),
			y: RADAR_CENTER + frac * RADAR_RADIUS * Math.sin(angle),
		};
	});
}

function svgEl(tag, attrs) {
	const el = document.createElementNS("http://www.w3.org/2000/svg", tag);
	for (const [k, v] of Object.entries(attrs)) el.setAttribute(k, v);
	return el;
}

function initRadarStatic() {
	const svg = document.getElementById("cup-radar");
	svg.replaceChildren();
	// Rings at scores 7/8/9/10 (6 is the center — an unscored/floor axis point).
	for (const score of [7, 8, 9, 10]) {
		const r = ((score - 6) / 4) * RADAR_RADIUS;
		svg.append(
			svgEl("circle", {
				cx: RADAR_CENTER,
				cy: RADAR_CENTER,
				r,
				fill: "none",
				stroke: "var(--line)",
				"stroke-width": "1",
			}),
		);
	}
	const n = SCORE_ATTRS.length;
	SCORE_ATTRS.forEach((attr, i) => {
		const angle = -Math.PI / 2 + (2 * Math.PI * i) / n;
		const x2 = RADAR_CENTER + RADAR_RADIUS * Math.cos(angle);
		const y2 = RADAR_CENTER + RADAR_RADIUS * Math.sin(angle);
		svg.append(
			svgEl("line", {
				x1: RADAR_CENTER,
				y1: RADAR_CENTER,
				x2,
				y2,
				stroke: "var(--line)",
				"stroke-width": "1",
			}),
		);
		const lx = RADAR_CENTER + (RADAR_RADIUS + 14) * Math.cos(angle);
		const ly = RADAR_CENTER + (RADAR_RADIUS + 14) * Math.sin(angle);
		const label = svgEl("text", {
			x: lx,
			y: ly,
			"text-anchor": "middle",
			"dominant-baseline": "middle",
			"font-size": "9",
			fill: "var(--ink-2)",
		});
		label.textContent = SCORE_LABELS[attr].split("/")[0];
		svg.append(label);
	});
	const shape = svgEl("polygon", {
		id: "radar-shape",
		fill: "var(--ember)",
		"fill-opacity": "0.25",
		stroke: "var(--ember)",
		"stroke-width": "1.5",
	});
	svg.append(shape);
}

function renderRadar() {
	const shape = document.getElementById("radar-shape");
	if (!shape) return;
	shape.setAttribute("points", radarPoints().map((p) => `${p.x},${p.y}`).join(" "));
}

function wireNotes() {
	const textarea = document.getElementById("cup-notes-text");
	textarea.value = data.notes;
	document.getElementById("cup-notes-count").textContent = data.notes.length;
	textarea.addEventListener("input", () => {
		data.notes = textarea.value;
		document.getElementById("cup-notes-count").textContent = data.notes.length;
		scheduleSave();
	});
}

async function initSessionView(user) {
	document.getElementById("cupping-list-view").classList.add("hidden");
	document.getElementById("cupping-session-view").classList.remove("hidden");
	document.getElementById("session-autosave-wrap").classList.remove("hidden");
	setAutosaveStatus("saved");

	let body;
	try {
		body = await api(`/api/cupping/${sessionId}`);
	} catch (error) {
		showToast(error.message || "Could not load this session.", "fail");
		location.assign("/cupping");
		return;
	}
	data = body.session.data;
	document.getElementById("cupping-title").textContent =
		body.session.planTitle || "Cupping session";
	document.getElementById("cupping-subtitle").textContent = `${data.cup_count} cups`;

	wireCupCount();
	renderScoreRows();
	renderTickRows();
	renderDefectRows();
	renderFlavorFamilies();
	renderFlavorChips();
	wireNotes();
	initRadarStatic();
	renderRadar();
	renderTotal();
	wireWhyPanels();
	void user;
}

async function initListView() {
	document.getElementById("cupping-list-view").classList.remove("hidden");
	document.getElementById("cupping-session-view").classList.add("hidden");
	wireNewSessionForm();
	await Promise.all([loadSessions(), loadPlanOptions()]);
}

document.getElementById("btn-logout").addEventListener("click", async () => {
	await protectedFetch("/api/auth/logout", { method: "POST" });
	location.assign("/");
});

async function init() {
	initSideNav();
	const user = await loadNavUser();
	if (!user) return;
	if (sessionId) await initSessionView(user);
	else await initListView();
}

init();
