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 {
	BREW_CATEGORIES,
	BREW_METHODS,
	BREW_SILHOUETTES,
	findBrewMethod,
} from "/shared/brew-data.js?v=__ASSET_VERSION__";

const SVG_NS = "http://www.w3.org/2000/svg";
let beans = [];
let brews = [];
let editingId = null;
let selectedMethod = null;
let beanFilter = null;
// Owned equipment from /gear: when brewers are configured, the picker shows only those
// (with a show-all escape hatch), and grinders become input suggestions.
let gear = { brewers: [], grinders: [] };
let showAllBrewers = false;

const fmtDate = (v) => (v ? new Date(v).toLocaleDateString() : "—");
const fmtTime = (s) =>
	s == null
		? null
		: `${Math.floor(s / 60)}:${String(s % 60).padStart(2, "0")}`;
function parseTime(str) {
	if (!str || !str.trim()) return null;
	const m = str.trim().match(/^(\d+):(\d{1,2})$/);
	if (m) return Number(m[1]) * 60 + Number(m[2]);
	const n = Number(str);
	return Number.isInteger(n) && n >= 0 ? n : undefined; // undefined = unparseable
}

function silhouetteSvg(methodKey, size = 42) {
	const svg = document.createElementNS(SVG_NS, "svg");
	svg.setAttribute("viewBox", "0 0 64 64");
	svg.setAttribute("width", size);
	svg.setAttribute("height", size);
	svg.setAttribute("aria-hidden", "true");
	for (const d of BREW_SILHOUETTES[methodKey] ?? []) {
		const path = document.createElementNS(SVG_NS, "path");
		path.setAttribute("d", d);
		path.setAttribute("fill", "currentColor");
		svg.append(path);
	}
	return svg;
}

const isEspresso = () => findBrewMethod(selectedMethod)?.category === "espresso";

function visibleMethods() {
	if (!gear.brewers.length || showAllBrewers) return BREW_METHODS;
	// An old brew being edited may use a brewer that's since been removed from the gear list —
	// keep its tile visible so the selection isn't silently unrepresentable.
	return BREW_METHODS.filter(
		(m) => gear.brewers.includes(m.key) || m.key === selectedMethod,
	);
}

function renderBrewerPicker() {
	const mount = document.getElementById("brewer-picker");
	mount.replaceChildren();
	const methods = visibleMethods();
	for (const category of BREW_CATEGORIES) {
		const inCategory = methods.filter((m) => m.category === category.key);
		if (!inCategory.length) continue;
		const title = document.createElement("div");
		title.className = "brewer-cat-title";
		title.textContent = category.name;
		mount.append(title);
		const grid = document.createElement("div");
		grid.className = "brewer-grid";
		for (const method of inCategory) {
			const tile = document.createElement("button");
			tile.type = "button";
			tile.className = "brewer-tile";
			tile.dataset.method = method.key;
			tile.classList.toggle("selected", method.key === selectedMethod);
			tile.setAttribute("aria-pressed", String(method.key === selectedMethod));
			tile.append(silhouetteSvg(method.key));
			const name = document.createElement("span");
			name.className = "brewer-name";
			name.textContent = method.name;
			tile.append(name);
			tile.addEventListener("click", () => selectMethod(method.key));
			grid.append(tile);
		}
		mount.append(grid);
	}
	if (gear.brewers.length) {
		const toggle = document.createElement("button");
		toggle.type = "button";
		toggle.className = "ghost-btn small";
		toggle.style.marginTop = "10px";
		toggle.textContent = showAllBrewers
			? "Show only my brewers"
			: "Show all brewers…";
		toggle.addEventListener("click", () => {
			showAllBrewers = !showAllBrewers;
			renderBrewerPicker();
		});
		mount.append(toggle);
	} else {
		const hint = document.createElement("p");
		hint.className = "field-note";
		hint.style.marginTop = "10px";
		const link = document.createElement("a");
		link.href = "/gear";
		link.textContent = "Set up your equipment";
		hint.append(link, " to only see the brewers you own here.");
		mount.append(hint);
	}
}

function selectMethod(key) {
	selectedMethod = key;
	for (const tile of document.querySelectorAll(".brewer-tile")) {
		const on = tile.dataset.method === key;
		tile.classList.toggle("selected", on);
		tile.setAttribute("aria-pressed", String(on));
	}
	// Espresso-style methods log yield-out instead of brew water and have no bloom.
	const espresso = isEspresso();
	for (const el of document.querySelectorAll("[data-espresso-show]"))
		el.classList.toggle("hidden", !espresso);
	for (const el of document.querySelectorAll("[data-espresso-hide]"))
		el.classList.toggle("hidden", espresso);
	updateRatio();
}

function updateRatio() {
	const form = document.getElementById("brew-form");
	const dose = Number.parseFloat(form.doseG.value);
	const out = Number.parseFloat(isEspresso() ? form.yieldG.value : form.waterG.value);
	document.getElementById("brew-ratio").value =
		Number.isFinite(dose) && dose > 0 && Number.isFinite(out) && out > 0
			? `1:${Math.round((out / dose) * 10) / 10}`
			: "";
}

function updateRatingPill() {
	const value = document.getElementById("brew-rating").value;
	const pill = document.getElementById("rating-value");
	pill.textContent = value;
	pill.classList.toggle("good", Number(value) >= 7.5);
	pill.classList.toggle("poor", Number(value) <= 4);
}

function renderBeanSelect() {
	const select = document.getElementById("brew-bean-select");
	const current = select.value;
	select.replaceChildren(
		Object.assign(document.createElement("option"), {
			value: "",
			textContent: "— no bean selected —",
		}),
		...beans
			.filter((b) => !b.archived || b.id === current)
			.map((bean) => {
				const option = document.createElement("option");
				option.value = bean.id;
				const remaining =
					bean.remainingWeightG == null ? "" : ` (${Math.round(bean.remainingWeightG)} g left)`;
				option.textContent = `${bean.name}${bean.roaster ? ` — ${bean.roaster}` : ""}${remaining}`;
				return option;
			}),
	);
	select.value = current;
}

function ratingPill(rating) {
	const pill = document.createElement("span");
	pill.className = "rating-pill";
	if (rating == null) pill.textContent = "—";
	else {
		pill.textContent = String(rating);
		if (rating >= 7.5) pill.classList.add("good");
		if (rating <= 4) pill.classList.add("poor");
	}
	return pill;
}

function recipeText(brew) {
	const method = findBrewMethod(brew.method);
	const parts = [];
	const out = method?.category === "espresso" ? brew.yieldG : brew.waterG;
	if (brew.doseG != null && out != null)
		parts.push(`${brew.doseG} g → ${out} g (1:${Math.round((out / brew.doseG) * 10) / 10})`);
	else if (brew.doseG != null) parts.push(`${brew.doseG} g`);
	if (brew.waterTempC != null) parts.push(`${brew.waterTempC}°C`);
	if (brew.brewTimeS != null) parts.push(fmtTime(brew.brewTimeS));
	return parts.join(" · ") || "—";
}

function renderBrews() {
	const body = document.getElementById("brews-body");
	const visible = beanFilter ? brews.filter((b) => b.beanId === beanFilter) : brews;
	document
		.getElementById("clear-bean-filter")
		.classList.toggle("hidden", !beanFilter);
	if (!visible.length) {
		body.innerHTML = `<tr><td colspan="6" class="empty-state">No brews logged yet. Pick a brewer above and log your first cup.</td></tr>`;
		return;
	}
	body.replaceChildren(
		...visible.map((brew) => {
			const tr = document.createElement("tr");

			const whenCell = document.createElement("td");
			whenCell.textContent = fmtDate(brew.brewedAt);

			const methodCell = document.createElement("td");
			const chip = document.createElement("span");
			chip.className = "method-chip";
			chip.append(silhouetteSvg(brew.method, 20));
			const methodName = document.createElement("span");
			methodName.textContent = findBrewMethod(brew.method)?.name ?? brew.method;
			chip.append(methodName);
			methodCell.append(chip);

			const beanCell = document.createElement("td");
			beanCell.textContent = brew.beanName ?? "—";

			const recipeCell = document.createElement("td");
			recipeCell.textContent = recipeText(brew);
			if (brew.recipe) {
				const steps = document.createElement("div");
				steps.className = "muted";
				steps.style.fontSize = "11.5px";
				steps.style.maxWidth = "340px";
				steps.textContent = brew.recipe;
				recipeCell.append(steps);
			}
			if (brew.tastingNotes) {
				const notes = document.createElement("div");
				notes.className = "muted";
				notes.style.fontSize = "11.5px";
				notes.style.maxWidth = "340px";
				notes.textContent = brew.tastingNotes;
				recipeCell.append(notes);
			}

			const ratingCell = document.createElement("td");
			ratingCell.append(ratingPill(brew.rating));

			const actions = document.createElement("td");
			actions.className = "data-table-actions";
			const editBtn = document.createElement("button");
			editBtn.className = "ghost-btn small";
			editBtn.type = "button";
			editBtn.textContent = "Edit";
			editBtn.addEventListener("click", () => startEdit(brew));
			const againBtn = document.createElement("button");
			againBtn.className = "ghost-btn small";
			againBtn.type = "button";
			againBtn.textContent = "Brew again";
			againBtn.title = "Copy this brew's recipe into the form as a new brew";
			againBtn.addEventListener("click", () => {
				startEdit(brew);
				editingId = null;
				document.getElementById("brew-form-title").textContent = "Log a brew";
				document.getElementById("brew-form-submit").textContent = "Save brew";
				document.getElementById("brew-form-cancel").classList.add("hidden");
			});
			const deleteBtn = document.createElement("button");
			deleteBtn.className = "ghost-btn small";
			deleteBtn.type = "button";
			deleteBtn.textContent = "Delete";
			deleteBtn.addEventListener("click", async () => {
				if (!confirm("Delete this brew? This cannot be undone.")) return;
				try {
					await api(`/api/brews/${brew.id}`, { method: "DELETE" });
					showToast("Brew deleted.");
					if (editingId === brew.id) cancelEdit();
					await Promise.all([loadBrews(), loadBeans()]);
				} catch (error) {
					showToast(error.message, "fail");
				}
			});
			actions.append(editBtn, againBtn, deleteBtn);

			tr.append(whenCell, methodCell, beanCell, recipeCell, ratingCell, actions);
			return tr;
		}),
	);
}

async function loadBeans() {
	try {
		beans = (await api("/api/beans")).beans;
		renderBeanSelect();
	} catch {
		/* bean select stays empty; brews still work without beans */
	}
}
async function loadBrews() {
	try {
		brews = (await api("/api/brews")).brews;
		renderBrews();
	} catch {
		document.getElementById("brews-body").innerHTML =
			`<tr><td colspan="6" class="empty-state">Could not load brews.</td></tr>`;
	}
}

function startEdit(brew) {
	editingId = brew.id;
	const form = document.getElementById("brew-form");
	form.beanId.value = brew.beanId ?? "";
	selectedMethod = brew.method;
	renderBrewerPicker(); // the edited brew's method may be outside the owned-gear filter
	selectMethod(brew.method);
	form.doseG.value = brew.doseG ?? "";
	form.waterG.value = brew.waterG ?? "";
	form.yieldG.value = brew.yieldG ?? "";
	form.waterTempC.value = brew.waterTempC ?? "";
	form.grinder.value = brew.grinder;
	form.grindSetting.value = brew.grindSetting;
	form.brewTime.value = fmtTime(brew.brewTimeS) ?? "";
	form.bloomTime.value = fmtTime(brew.bloomTimeS) ?? "";
	form.rating.value = brew.rating ?? 5;
	form.recipe.value = brew.recipe;
	form.tastingNotes.value = brew.tastingNotes;
	form.notes.value = brew.notes;
	updateRatio();
	updateRatingPill();
	document.getElementById("brew-form-title").textContent = "Edit brew";
	document.getElementById("brew-form-submit").textContent = "Save changes";
	document.getElementById("brew-form-cancel").classList.remove("hidden");
	document.getElementById("brew-form-card").scrollIntoView({ behavior: "smooth" });
}

function cancelEdit() {
	editingId = null;
	const form = document.getElementById("brew-form");
	form.reset();
	updateRatio();
	updateRatingPill();
	document.getElementById("brew-form-title").textContent = "Log a brew";
	document.getElementById("brew-form-submit").textContent = "Save brew";
	document.getElementById("brew-form-cancel").classList.add("hidden");
}

document.getElementById("brew-form").addEventListener("submit", async (event) => {
	event.preventDefault();
	if (!selectedMethod) {
		showToast("Pick a brewer first.", "fail");
		return;
	}
	const form = event.target;
	const brewTimeS = parseTime(form.brewTime.value);
	const bloomTimeS = parseTime(form.bloomTime.value);
	if (brewTimeS === undefined || bloomTimeS === undefined) {
		showToast("Times must look like m:ss (e.g. 2:45).", "fail");
		return;
	}
	const num = (v) => (v === "" ? null : Number(v));
	const payload = {
		beanId: form.beanId.value || null,
		method: selectedMethod,
		doseG: num(form.doseG.value),
		waterG: num(form.waterG.value),
		yieldG: num(form.yieldG.value),
		waterTempC: num(form.waterTempC.value),
		grinder: form.grinder.value,
		grindSetting: form.grindSetting.value,
		brewTimeS,
		bloomTimeS,
		rating: Number(form.rating.value),
		recipe: form.recipe.value,
		tastingNotes: form.tastingNotes.value,
		notes: form.notes.value,
	};
	const submit = document.getElementById("brew-form-submit");
	submit.disabled = true;
	try {
		if (editingId) {
			await api(`/api/brews/${editingId}`, { method: "PUT", body: JSON.stringify(payload) });
			showToast("Brew updated.");
		} else {
			await api("/api/brews", { method: "POST", body: JSON.stringify(payload) });
			showToast("Brew logged.");
		}
		cancelEdit();
		await Promise.all([loadBrews(), loadBeans()]);
	} catch (error) {
		showToast(error.message, "fail");
	} finally {
		submit.disabled = false;
	}
});

document.getElementById("brew-form-cancel").addEventListener("click", cancelEdit);
document.getElementById("brew-form").addEventListener("input", (event) => {
	if (["doseG", "waterG", "yieldG"].includes(event.target.name)) updateRatio();
	if (event.target.name === "rating") updateRatingPill();
});
document.getElementById("clear-bean-filter").addEventListener("click", () => {
	beanFilter = null;
	renderBrews();
});
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;
	try {
		gear = (await api("/api/gear")).gear;
	} catch {
		/* no gear configured — picker shows everything */
	}
	renderBrewerPicker();
	const grinderOptions = document.getElementById("grinder-options");
	if (grinderOptions)
		grinderOptions.replaceChildren(
			...gear.grinders.map((g) =>
				Object.assign(document.createElement("option"), { value: g }),
			),
		);
	updateRatingPill();
	await Promise.all([loadBeans(), loadBrews()]);
	const params = new URLSearchParams(location.search);
	const requestedBean = params.get("bean");
	if (requestedBean && beans.some((b) => b.id === requestedBean)) {
		if (params.get("new")) {
			document.getElementById("brew-bean-select").value = requestedBean;
		} else {
			beanFilter = requestedBean;
			renderBrews();
		}
	}
}

init();
