import { describe, expect, test } from "vitest";
import {
	BASE_REQUIREMENTS,
	MODEL_CAPABILITY_PROFILES,
	scoreEligibleModels,
	scoreModel,
} from "../model-router.js";

describe("agentic capability axis (ADR-0079)", () => {
	test("execute-task base requirements weight the agentic dimension", () => {
		// If this assertion fails because the weight changed: re-read ADR-0079
		// before adjusting. The whole point of the axis is to outweigh raw
		// coding score for execute-task routing.
		expect(BASE_REQUIREMENTS["execute-task"].agentic).toBeGreaterThanOrEqual(
			0.7,
		);
	});

	test("known agentic-capable models score higher than coding-completion models on execute-task", () => {
		const codestralScore = scoreModel(
			MODEL_CAPABILITY_PROFILES["codestral-latest"],
			BASE_REQUIREMENTS["execute-task"],
		);
		const kimiScore = scoreModel(
			MODEL_CAPABILITY_PROFILES["kimi-k2.6"],
			BASE_REQUIREMENTS["execute-task"],
		);
		const sonnetScore = scoreModel(
			MODEL_CAPABILITY_PROFILES["claude-sonnet-4-6"],
			BASE_REQUIREMENTS["execute-task"],
		);
		// Codestral has high coding (85) but agentic=25 — must not beat agentic models.
		expect(kimiScore).toBeGreaterThan(codestralScore);
		expect(sonnetScore).toBeGreaterThan(codestralScore);
	});

	test("devstral variants score below agentic models on execute-task", () => {
		const devstralScore = scoreModel(
			MODEL_CAPABILITY_PROFILES["devstral-2512"],
			BASE_REQUIREMENTS["execute-task"],
		);
		const kimiScore = scoreModel(
			MODEL_CAPABILITY_PROFILES["kimi-k2.6"],
			BASE_REQUIREMENTS["execute-task"],
		);
		expect(kimiScore).toBeGreaterThan(devstralScore);
	});

	test("scoreEligibleModels ranks agentic models above coding-only models for execute-task", () => {
		const eligible = [
			"mistral/codestral-latest",
			"mistral/devstral-2512",
			"moonshotai/kimi-k2.6",
			"anthropic/claude-sonnet-4-6",
		];
		const ranked = scoreEligibleModels(
			eligible,
			BASE_REQUIREMENTS["execute-task"],
		);
		const top = ranked[0]?.modelId;
		// Either of the two pinned-agentic models must win.
		expect(["moonshotai/kimi-k2.6", "anthropic/claude-sonnet-4-6"]).toContain(
			top,
		);
		// And Codestral specifically must not win.
		expect(top).not.toBe("mistral/codestral-latest");
	});

	test("agentic axis preserves research-* unit-type behavior (no agentic weight there)", () => {
		// Research isn't agentic — those unit types should not gain an agentic
		// dimension. This protects long-context research-tuned models from
		// being penalized.
		expect(BASE_REQUIREMENTS["research-milestone"].agentic).toBeUndefined();
		expect(BASE_REQUIREMENTS["research-slice"].agentic).toBeUndefined();
	});

	test("known coding-only models all have agentic <= 50", () => {
		const codingOnly = [
			"codestral-latest",
			"devstral-2512",
			"devstral-medium-latest",
			"devstral-medium-2507",
			"devstral-small-2505",
			"devstral-small-2507",
			"labs-devstral-small-2512",
			"qwen3-coder:480b",
			"qwen3-coder-next",
		];
		for (const id of codingOnly) {
			const profile = MODEL_CAPABILITY_PROFILES[id];
			expect(
				profile,
				`${id} should be in MODEL_CAPABILITY_PROFILES`,
			).toBeDefined();
			expect(
				profile.agentic,
				`${id} should have agentic <= 50`,
			).toBeLessThanOrEqual(50);
		}
	});

	test("older MiniMax generations score lower than current on agentic", () => {
		// 2026-05-13 incident: minimax/M2.1 stuck in 60+ checkpoint loop on
		// infra repo. Root cause was the router aliasing all minimax-m2.x
		// variants to MiniMax-M2.7's profile, so older models inherited
		// current-gen capability scores and won cost tie-breaks on
		// execute-task. Per-generation profiles + agentic axis fix the
		// underlying routing decision.
		const m21 = MODEL_CAPABILITY_PROFILES["MiniMax-M2.1"];
		const m25 = MODEL_CAPABILITY_PROFILES["MiniMax-M2.5"];
		const m27 = MODEL_CAPABILITY_PROFILES["MiniMax-M2.7"];
		expect(m21, "M2.1 should have its own profile").toBeDefined();
		expect(m25, "M2.5 should have its own profile").toBeDefined();
		expect(m27.agentic).toBeGreaterThan(m25.agentic);
		expect(m25.agentic).toBeGreaterThan(m21.agentic);
		// And on execute-task, the current generation must beat the older one.
		const oldScore = scoreModel(m21, BASE_REQUIREMENTS["execute-task"]);
		const newScore = scoreModel(m27, BASE_REQUIREMENTS["execute-task"]);
		expect(newScore).toBeGreaterThan(oldScore);
	});

	test("every profile has an explicit agentic score (no defaulting to 50)", () => {
		// sf-mp37p9u2-80f2gz: the agentic=50 fallback in scoreModel was
		// silently letting untouched profiles escape penalization for poor
		// tool-use reliability. Every profile must declare a deliberate
		// score so the router can differentiate the full table.
		const offenders = [];
		for (const [id, profile] of Object.entries(MODEL_CAPABILITY_PROFILES)) {
			if (typeof profile.agentic !== "number") offenders.push(id);
		}
		expect(
			offenders,
			`profiles missing explicit agentic: ${offenders.join(", ")}`,
		).toEqual([]);
	});

	test("known agentic-frontier models all have agentic >= 85", () => {
		const agenticFrontier = [
			"claude-opus-4-6",
			"claude-sonnet-4-6",
			"claude-sonnet-4-5-20250514",
			"kimi-k2.6",
			"kimi-k2-thinking",
			"gpt-5",
			"gpt-5.4",
			"gpt-5.5",
			"gemini-3-pro-preview",
			"gemini-3.1-pro-preview",
		];
		for (const id of agenticFrontier) {
			const profile = MODEL_CAPABILITY_PROFILES[id];
			expect(
				profile,
				`${id} should be in MODEL_CAPABILITY_PROFILES`,
			).toBeDefined();
			expect(
				profile.agentic,
				`${id} should have agentic >= 85`,
			).toBeGreaterThanOrEqual(85);
		}
	});
});
