import test from "node:test";
import assert from "node:assert/strict";
import request from "supertest";
import { setup, signup } from "./helpers.js";

function planBody(overrides = {}) {
	return {
		fields: { "2.1": "single", "1.4": "7:30", "1.6": "0", "5.6": "" },
		planActual: {
			charge: { actualBt: "" },
			tp: { actualBt: "" },
			yellow: { actualBt: "" },
			fc: { actualTime: "", actualBt: "" },
			drop: { actualBt: "" },
		},
		afterRoast: { oneChange: "" },
		inventory: { lotId: "", lotLabel: "", consumed: null },
		...overrides,
	};
}

test("machine-profile: requires auth", async () => {
	const { app } = await setup();
	const anon = request.agent(app);
	assert.equal((await anon.get("/api/machine-profile")).status, 401);
});

test("machine-profile: a brand-new account gets the reference defaults, not an error", async () => {
	const { app } = await setup();
	const agent = request.agent(app);
	await signup(agent, "fresh@example.com");
	const r = await agent.get("/api/machine-profile");
	assert.equal(r.status, 200);
	assert.equal(r.body.ok, true);
	assert.equal(r.body.profile.pace.value, 1);
	assert.equal(r.body.profile.pace.source, "reference");
	assert.equal(r.body.profile.totalPlans, 0);
});

test("machine-profile: learns a pace factor from this account's own completed roasts, and only this account's", async () => {
	const { app } = await setup();
	const owner = request.agent(app);
	const { csrf } = await signup(owner, "owner@example.com");
	// Every roast here ran 20% slower than its own anchor (7:30=450s -> actual 9:00=540s, ratio 1.2).
	await owner
		.post("/api/plans")
		.set("x-csrf-token", csrf)
		.send({ plan: planBody({ planActual: { ...planBody().planActual, fc: { actualTime: "9:00", actualBt: "" } } }) });
	await owner
		.post("/api/plans")
		.set("x-csrf-token", csrf)
		.send({
			plan: planBody({
				fields: { "2.1": "single", "1.4": "8:00", "1.6": "0", "5.6": "" }, // 480s
				planActual: { ...planBody().planActual, fc: { actualTime: "9:36", actualBt: "" } }, // 576s, ratio 1.2
			}),
		});

	const other = request.agent(app);
	await signup(other, "other@example.com");

	const ownerProfile = (await owner.get("/api/machine-profile")).body.profile;
	assert.equal(ownerProfile.pace.n, 2);
	assert.equal(ownerProfile.pace.source, "learned");
	assert.ok(Math.abs(ownerProfile.pace.value - 1.2) < 0.001, `expected ~1.2, got ${ownerProfile.pace.value}`);

	// The other account's history is empty — it must not see the owner's learned pace.
	const otherProfile = (await other.get("/api/machine-profile")).body.profile;
	assert.equal(otherProfile.pace.source, "reference");
	assert.equal(otherProfile.pace.value, 1);
});

test("last-refine: requires auth and 404s on a lot that doesn't belong to the caller", async () => {
	const { app } = await setup();
	const ownerAgent = request.agent(app);
	const { csrf } = await signup(ownerAgent, "lotowner@example.com");
	const lot = (
		await ownerAgent
			.post("/api/inventory")
			.set("x-csrf-token", csrf)
			.send({ origin: "Huila", initialWeightG: 1000 })
	).body.lot;

	const anon = request.agent(app);
	assert.equal((await anon.get(`/api/inventory/${lot.id}/last-refine`)).status, 401);

	const otherAgent = request.agent(app);
	await signup(otherAgent, "notlotowner@example.com");
	assert.equal((await otherAgent.get(`/api/inventory/${lot.id}/last-refine`)).status, 404);
});

test("last-refine: null when no past roast against the lot has a filled-in one-change note", async () => {
	const { app } = await setup();
	const agent = request.agent(app);
	const { csrf } = await signup(agent, "norefine@example.com");
	const lot = (
		await agent
			.post("/api/inventory")
			.set("x-csrf-token", csrf)
			.send({ origin: "Huila", initialWeightG: 1000 })
	).body.lot;
	await agent
		.post("/api/plans")
		.set("x-csrf-token", csrf)
		.send({ plan: planBody({ inventory: { lotId: lot.id, lotLabel: "Huila", consumed: null } }) });

	const r = await agent.get(`/api/inventory/${lot.id}/last-refine`);
	assert.equal(r.status, 200);
	assert.equal(r.body.refine, null);
});

test("last-refine: returns the most recent filled-in note for that lot, ignoring blank ones and other lots", async () => {
	const { app } = await setup();
	const agent = request.agent(app);
	const { csrf } = await signup(agent, "refine@example.com");
	const lotA = (
		await agent.post("/api/inventory").set("x-csrf-token", csrf).send({ origin: "Lot A", initialWeightG: 1000 })
	).body.lot;
	const lotB = (
		await agent.post("/api/inventory").set("x-csrf-token", csrf).send({ origin: "Lot B", initialWeightG: 1000 })
	).body.lot;

	// Older roast on lot A, with a note.
	await agent.post("/api/plans").set("x-csrf-token", csrf).send({
		plan: planBody({
			inventory: { lotId: lotA.id, lotLabel: "Lot A", consumed: null },
			afterRoast: { oneChange: "First crack +0:30" },
		}),
	});
	// A blank-note roast on lot A (should never win over a real note, regardless of recency).
	await agent.post("/api/plans").set("x-csrf-token", csrf).send({
		plan: planBody({ inventory: { lotId: lotA.id, lotLabel: "Lot A", consumed: null } }),
	});
	// A roast on the OTHER lot with a note — must not leak into lot A's answer.
	await agent.post("/api/plans").set("x-csrf-token", csrf).send({
		plan: planBody({
			inventory: { lotId: lotB.id, lotLabel: "Lot B", consumed: null },
			afterRoast: { oneChange: "Development −0:15" },
		}),
	});

	const r = await agent.get(`/api/inventory/${lotA.id}/last-refine`);
	assert.equal(r.status, 200);
	assert.equal(r.body.refine.oneChange, "First crack +0:30");
});
