import { api, protectedFetch } from "./api.js?v=__ASSET_VERSION__";
import { showToast } from "./toast.js?v=__ASSET_VERSION__";

let lots = [];

function findLot(id) {
	return lots.find((l) => l.id === id);
}

// Matches the phrasing the app's own symptom-fix reference table uses ("First crack +0:30",
// "First crack −0:30") — only a note in exactly that shape is safe to offer as a one-click ± Refine
// value. Free text like "Re-weight the blend" or "Smaller batch" isn't a first-crack correction at
// all, so it's shown for context but never auto-applied. The sign class includes the U+2212 minus
// sign the app's own SYMPTOM_FIXES table is written with (see shared/reference-data.js) — an
// ASCII-only class would silently refuse to recognize every slow-down correction it suggests.
const FC_REFINE_RE = /first crack\s*([+‒–—−-]\d{1,2}:\d{2})/i;
// The captured sign may be a non-ASCII dash; normalize to ASCII "-" before it's stored into
// field 1.6, matching the canonical form parseDuration/formatDuration use everywhere else.
const normalizeSign = (value) => value.replace(/^[+‒–—−]/, (c) => (c === "+" ? "+" : "-"));

/** Wires the "From inventory lot" picker (in The Coffee) and the "Draw from lot" action
 * (in Roast Log — Plan vs. Actual). `getRemotePlanId` is a function since the planner's
 * remotePlanId is module-local and can change after a sync. */
export function initLotPicker({
	state,
	recompute,
	flushCurrentPlan,
	getRemotePlanId,
	renderFormFromPlan,
}) {
	const select = document.getElementById("lot-select");
	const note = document.getElementById("lot-picker-note");
	const consumeRow = document.getElementById("inventory-consume");
	const consumeLabel = document.getElementById("inventory-consume-label");
	const consumeBtn = document.getElementById("btn-consume");
	const refineBox = document.getElementById("refine-suggestion");
	const refineText = document.getElementById("refine-suggestion-text");
	const refineApplyBtn = document.getElementById("btn-apply-refine");

	function renderOptions() {
		const current = state.plan.inventory.lotId;
		select.replaceChildren(
			Object.assign(document.createElement("option"), { value: "", textContent: "— none —" }),
			...lots
				.filter((l) => !l.archived)
				.map((l) =>
					Object.assign(document.createElement("option"), {
						value: l.id,
						textContent: `${l.origin}${l.variety ? ` — ${l.variety}` : ""} (${Math.round(l.remainingWeightG)} g remaining)`,
					}),
				),
		);
		if (current && !findLot(current)) {
			// The plan references a lot that's now archived/deleted — keep it selectable so the
			// stored value still displays instead of silently reverting to "— none —".
			select.append(
				Object.assign(document.createElement("option"), {
					value: current,
					textContent: state.plan.inventory.lotLabel || "Unavailable lot",
					disabled: true,
				}),
			);
		}
		select.value = current || "";
	}

	async function loadLots() {
		try {
			const body = await api("/api/inventory");
			lots = body.lots;
		} catch {
			lots = [];
		}
		renderOptions();
		updateConsumeRow();
		loadLastRefine(state.plan.inventory.lotId);
	}

	function updateConsumeRow() {
		const lotId = state.plan.inventory.lotId;
		if (!lotId) {
			consumeRow.classList.add("hidden");
			return;
		}
		consumeRow.classList.remove("hidden");
		const consumed = state.plan.inventory.consumed;
		if (consumed) {
			consumeLabel.textContent = `✓ ${Math.round(consumed.weightG)} g drawn from ${consumed.lotLabel} · ${new Date(consumed.atIso).toLocaleDateString()}`;
			consumeBtn.classList.add("hidden");
			return;
		}
		consumeBtn.classList.remove("hidden");
		const weightG = Number(state.plan.fields["0.4"]);
		const lot = findLot(lotId);
		const lotLabel = lot ? lot.origin : state.plan.inventory.lotLabel || "this lot";
		if (Number.isFinite(weightG) && weightG > 0) {
			consumeLabel.textContent = `Charging will draw ${weightG} g from ${lotLabel}.`;
			consumeBtn.disabled = false;
			consumeBtn.removeAttribute("title");
		} else {
			consumeLabel.textContent = `Set "Green in" (field 0.4) to draw weight from ${lotLabel}.`;
			consumeBtn.disabled = true;
			consumeBtn.title = "Enter a Green in weight on The Coffee first";
		}
	}

	// The auto-refine-carry-forward feature: closes the loop between cupping's "One change next
	// batch" and this plan's ± Refine (1.6) instead of leaving the user to retype their own past
	// conclusion. Never applied silently — only offered, and only when it can be parsed as an
	// unambiguous first-crack correction (see FC_REFINE_RE above).
	async function loadLastRefine(lotId) {
		if (!lotId) {
			refineBox.classList.add("hidden");
			return;
		}
		let refine = null;
		try {
			refine = (await api(`/api/inventory/${lotId}/last-refine`)).refine;
		} catch {
			refine = null;
		}
		if (!refine?.oneChange) {
			refineBox.classList.add("hidden");
			return;
		}
		refineBox.classList.remove("hidden");
		refineText.textContent = `Last time on this lot: "${refine.oneChange}"`;
		const match = refine.oneChange.match(FC_REFINE_RE);
		const value = match ? normalizeSign(match[1]) : null;
		const currentRefine = (state.plan.fields["1.6"] || "").trim();
		const isDefault = currentRefine === "" || currentRefine === "0";
		if (value && isDefault) {
			refineApplyBtn.classList.remove("hidden");
			refineApplyBtn.textContent = `Apply ${value} to ± Refine`;
			refineApplyBtn.onclick = () => {
				state.plan.fields["1.6"] = value;
				renderFormFromPlan?.();
				recompute();
				refineApplyBtn.classList.add("hidden");
				showToast(`± Refine set to ${value}.`);
			};
		} else {
			refineApplyBtn.classList.add("hidden");
		}
	}

	select.addEventListener("change", () => {
		const lotId = select.value;
		state.plan.inventory.lotId = lotId;
		const lot = findLot(lotId);
		state.plan.inventory.lotLabel = lot
			? `${lot.origin}${lot.variety ? ` — ${lot.variety}` : ""}`
			: "";
		if (lot) {
			note.textContent = `${Math.round(lot.remainingWeightG)} g remaining in this lot.`;
			const nameInput = document.querySelector('[name="0.1"]');
			if (nameInput && !nameInput.value) {
				nameInput.value = lot.origin;
				state.plan.fields["0.1"] = lot.origin;
			}
		} else {
			note.textContent =
				"Optional — link a lot and the Roast Log can draw the green weight down when you charge.";
		}
		updateConsumeRow();
		loadLastRefine(lotId);
		recompute();
	});

	consumeBtn.addEventListener("click", async () => {
		consumeBtn.disabled = true;
		try {
			if (!(await flushCurrentPlan())) {
				showToast("Could not sync the plan. Try again.", "fail");
				return;
			}
			const roastPlanId = getRemotePlanId();
			if (!roastPlanId) {
				showToast("Sync the plan first, then draw from the lot.", "fail");
				return;
			}
			const weightG = Number(state.plan.fields["0.4"]);
			const response = await protectedFetch(
				`/api/inventory/${state.plan.inventory.lotId}/consume`,
				{
					method: "POST",
					headers: { "content-type": "application/json" },
					body: JSON.stringify({ weightG, roastPlanId }),
				},
			);
			const body = await response.json().catch(() => ({}));
			if (!response.ok && body.code !== "already_consumed") {
				showToast(body.error || "Could not draw from the lot.", "fail");
				return;
			}
			state.plan.inventory.consumed = {
				lotId: state.plan.inventory.lotId,
				lotLabel: state.plan.inventory.lotLabel,
				weightG,
				atIso: new Date().toISOString(),
			};
			updateConsumeRow();
			recompute();
			showToast(`${weightG} g drawn from lot.`);
			await loadLots();
		} finally {
			consumeBtn.disabled = false;
		}
	});

	loadLots();
	return {
		updateConsumeRow,
		renderOptions,
		refreshRefineSuggestion: () => loadLastRefine(state.plan.inventory.lotId),
	};
}
