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

function makeAlog(title, fcIdx = 14) {
	const timex = [], temp1 = [], temp2 = [];
	for (let i = 0; i <= 20; i++) {
		timex.push(i * 30);
		temp1.push(200 + i);
		temp2.push(i < 3 ? 180 - i * 30 : 90 + (i - 3) * 7);
	}
	return JSON.stringify({ title, mode: "C", weight: [250, 212, "g"], timex, temp1, temp2, timeindex: [1, 8, fcIdx, 0, 0, 0, 20, 0] });
}

const stubEval = async () => ({ summary: "ok", grade: "good", highlights: [], concerns: [], suggestions: [], planComparison: null });

test("profile picture: set, serve, remove, validate", async () => {
	const { agent } = await setup();
	const { csrf } = await signup(agent, "avatar@example.com");
	assert.equal((await agent.get("/api/auth/me")).body.user.hasAvatar, false);
	assert.equal((await agent.get("/api/account/avatar")).status, 404);

	// 1x1 PNG
	const png =
		"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==";
	assert.equal(
		(await agent.put("/api/account/avatar").set("x-csrf-token", csrf).send({ dataUrl: png })).status,
		200,
	);
	assert.equal((await agent.get("/api/auth/me")).body.user.hasAvatar, true);
	const served = await agent.get("/api/account/avatar");
	assert.equal(served.status, 200);
	assert.equal(served.headers["content-type"], "image/png");

	// Junk rejected
	for (const dataUrl of ["not-an-image", "data:image/svg+xml;base64,PHN2Zz4=", 5])
		assert.equal(
			(await agent.put("/api/account/avatar").set("x-csrf-token", csrf).send({ dataUrl })).status,
			400,
		);

	assert.equal(
		(await agent.delete("/api/account/avatar").set("x-csrf-token", csrf)).status,
		200,
	);
	assert.equal((await agent.get("/api/auth/me")).body.user.hasAvatar, false);
	assert.equal((await agent.get("/api/account/avatar")).status, 404);
});

test("roasters: CRUD, default handling, uploads attach and reassign, per-roaster profiles", async () => {
	const { app, agent } = await setup({}, { evaluateRoast: stubEval });
	const { csrf } = await signup(agent, "machines@example.com");

	// First roaster auto-defaults; second doesn't
	const hottop = (
		await agent.post("/api/roasters").set("x-csrf-token", csrf).send({ name: "Hottop", model: "KN-8828B-2K+" })
	).body.roaster;
	assert.equal(hottop.isDefault, true);
	const aillio = (
		await agent.post("/api/roasters").set("x-csrf-token", csrf).send({ name: "Aillio" })
	).body.roaster;
	assert.equal(aillio.isDefault, false);
	assert.equal(
		(await agent.post("/api/roasters").set("x-csrf-token", csrf).send({ name: "" })).status,
		400,
	);

	// Uploads attach to the default roaster unless told otherwise
	const upDefault = await agent
		.post("/api/roasts")
		.set("x-csrf-token", csrf)
		.send({ filename: "d.alog", content: makeAlog("On default") });
	assert.equal(upDefault.body.roast.roasterId, hottop.id);
	const upExplicit = await agent
		.post("/api/roasts")
		.set("x-csrf-token", csrf)
		.send({ filename: "e.alog", content: makeAlog("On aillio", 15), roasterId: aillio.id });
	assert.equal(upExplicit.body.roast.roasterId, aillio.id);
	const list = await agent.get("/api/roasts");
	assert.equal(list.body.roasts.find((r) => r.filename === "d.alog").roasterName, "Hottop");

	// Roaster list counts its roasts
	const roasters = (await agent.get("/api/roasters")).body.roasters;
	assert.equal(roasters.find((r) => r.id === hottop.id).roastCount, 1);
	assert.equal(roasters.find((r) => r.id === aillio.id).roastCount, 1);

	// Per-roaster profile only sees that machine's roasts; default profile = default roaster
	const hottopProfile = (
		await agent.get(`/api/roaster-profile?roaster=${hottop.id}`)
	).body.profile;
	assert.equal(hottopProfile.n, 1);
	assert.equal(hottopProfile.roaster.name, "Hottop");
	const defaultProfile = (await agent.get("/api/roaster-profile")).body.profile;
	assert.equal(defaultProfile.roaster.id, hottop.id);
	assert.equal(defaultProfile.n, 1);

	// Overrides: applied on top of learned medians; bad values rejected
	const put = await agent
		.put(`/api/roasters/${hottop.id}`)
		.set("x-csrf-token", csrf)
		.send({ overrides: { firstCrackTempC: 196, paceFactor: 1.1 } });
	assert.equal(put.status, 200);
	const tweaked = (
		await agent.get(`/api/roaster-profile?roaster=${hottop.id}`)
	).body.profile;
	assert.equal(tweaked.medians.firstCrackTempC, 196);
	assert.notEqual(tweaked.learnedMedians.firstCrackTempC, 196);
	assert.equal(tweaked.paceFactorOverride, 1.1);
	assert.equal(
		(
			await agent
				.put(`/api/roasters/${hottop.id}`)
				.set("x-csrf-token", csrf)
				.send({ overrides: { paceFactor: 9 } })
		).status,
		400,
	);

	// Reassign a roast between machines
	const reassign = await agent
		.put(`/api/roasts/${upDefault.body.roast.id}`)
		.set("x-csrf-token", csrf)
		.send({ roasterId: aillio.id });
	assert.equal(reassign.status, 200);
	assert.equal(
		(await agent.get(`/api/roaster-profile?roaster=${aillio.id}`)).body.profile.n,
		2,
	);

	// Default transfer + delete keeps roasts (detached)
	await agent.put(`/api/roasters/${aillio.id}`).set("x-csrf-token", csrf).send({ isDefault: true });
	const after = (await agent.get("/api/roasters")).body.roasters;
	assert.equal(after.find((r) => r.id === hottop.id).isDefault, false);
	assert.equal(after.find((r) => r.id === aillio.id).isDefault, true);
	assert.equal(
		(await agent.delete(`/api/roasters/${aillio.id}`).set("x-csrf-token", csrf)).status,
		200,
	);
	const survivors = await agent.get("/api/roasts");
	assert.equal(survivors.body.roasts.length, 2);
	assert.equal(
		survivors.body.roasts.every((r) => r.roasterId === null || r.roasterId === hottop.id),
		true,
	);

	// Ownership: a stranger sees nothing and can't touch anything
	const stranger = request.agent(app);
	const { csrf: strangerCsrf } = await signup(stranger, "other@example.com");
	assert.equal((await stranger.get("/api/roasters")).body.roasters.length, 0);
	assert.equal(
		(await stranger.get(`/api/roaster-profile?roaster=${hottop.id}`)).status,
		404,
	);
	assert.equal(
		(
			await stranger
				.put(`/api/roasters/${hottop.id}`)
				.set("x-csrf-token", strangerCsrf)
				.send({ name: "hijack" })
		).status,
		404,
	);
	assert.equal(
		(
			await stranger
				.post("/api/roasts")
				.set("x-csrf-token", strangerCsrf)
				.send({ filename: "x.alog", content: makeAlog("x"), roasterId: hottop.id })
		).status,
		404,
	);
});
