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

const MODELS = [
	{ key: "opencode-go:minimax-m3", name: "MiniMax-M3", provider: "opencode-go" },
	{ key: "opencode-go:big-model", name: "Big Model", provider: "opencode-go" },
];

async function bootstrapAdmin(agent) {
	const response = await agent.post("/api/auth/bootstrap").send({
		email: "snowspeeder@gmail.com",
		password,
		setupToken: "a-secure-bootstrap-token",
	});
	assert.equal(response.status, 201);
	return response.body.csrfToken;
}

function makeAlog() {
	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: "T", mode: "C", weight: [250, 212, "g"], timex, temp1, temp2, timeindex: [1, 8, 14, 0, 0, 0, 20, 0] });
}

test("admin can list and change the LLM model; the setting reaches evaluations", async () => {
	const evalCalls = [];
	const { agent } = await setup(
		{},
		{
			listModels: async () => MODELS,
			evaluateRoast: async (parsed, plan, preferredModel) => {
				evalCalls.push(preferredModel);
				return { summary: "ok", grade: "good", highlights: [], concerns: [], suggestions: [], planComparison: null };
			},
		},
	);
	const csrf = await bootstrapAdmin(agent);

	const listing = await agent.get("/api/admin/llm");
	assert.equal(listing.status, 200);
	assert.equal(listing.body.current, "");
	assert.equal(listing.body.models.length, 2);
	assert.equal(listing.body.modelsError, null);

	const set = await agent
		.put("/api/admin/llm")
		.set("x-csrf-token", csrf)
		.send({ model: "opencode-go:big-model" });
	assert.equal(set.status, 200);
	assert.equal((await agent.get("/api/admin/llm")).body.current, "opencode-go:big-model");

	// An unknown model is rejected, a non-string is rejected
	assert.equal(
		(await agent.put("/api/admin/llm").set("x-csrf-token", csrf).send({ model: "nope:x" })).status,
		400,
	);
	assert.equal(
		(await agent.put("/api/admin/llm").set("x-csrf-token", csrf).send({ model: 5 })).status,
		400,
	);

	// The chosen model is what evaluations receive
	const up = await agent
		.post("/api/roasts")
		.set("x-csrf-token", csrf)
		.send({ filename: "r.alog", content: makeAlog() });
	assert.equal(up.status, 201);
	for (let i = 0; i < 100 && !evalCalls.length; i++)
		await new Promise((resolve) => setTimeout(resolve, 10));
	assert.deepEqual(evalCalls, ["opencode-go:big-model"]);

	// Back to auto
	const clear = await agent.put("/api/admin/llm").set("x-csrf-token", csrf).send({ model: "" });
	assert.equal(clear.status, 200);
	assert.equal((await agent.get("/api/admin/llm")).body.current, "");
});

test("LLM setting is admin-only and degrades when no models are configured", async () => {
	const { agent, app } = await setup(
		{},
		{
			listModels: async () => {
				throw new Error("no runtime");
			},
		},
	);
	const { csrf } = await signup(agent, "user@example.com");
	assert.equal((await agent.get("/api/admin/llm")).status, 403);
	assert.equal(
		(await agent.put("/api/admin/llm").set("x-csrf-token", csrf).send({ model: "" })).status,
		403,
	);

	const request = (await import("supertest")).default;
	const adminAgent = request.agent(app);
	const adminCsrf = await bootstrapAdmin(adminAgent);
	const listing = await adminAgent.get("/api/admin/llm");
	assert.equal(listing.status, 200);
	assert.equal(listing.body.modelsError, "llm_unavailable");
	assert.deepEqual(listing.body.models, []);
	// A concrete model can't be validated with no runtime, but clearing to auto still works
	assert.equal(
		(await adminAgent.put("/api/admin/llm").set("x-csrf-token", adminCsrf).send({ model: "a:b" })).status,
		503,
	);
	assert.equal(
		(await adminAgent.put("/api/admin/llm").set("x-csrf-token", adminCsrf).send({ model: "" })).status,
		200,
	);
});
