import assert from "node:assert/strict";
import { describe, test } from "vitest";

import {
	isSameRootVendor,
	ModelRolePolicyValidationError,
	normalizeRolePolicies,
	normalizeRolePolicy,
	rootVendorFor,
	SUPPORTED_MODEL_ROLE_CONSTRAINTS,
	SUPPORTED_MODEL_ROLES,
	validateRolePolicy,
} from "../uok/model-role-policy.js";

describe("model role policy", () => {
	test("normalizeRolePolicy_when_role_has_no_override_returns_auto_policy_with_symbolic_constraints", () => {
		assert.deepEqual(normalizeRolePolicy("worker"), {
			role: "worker",
			mode: "auto",
			constraints: ["coding"],
		});
	});

	test("normalizeRolePolicy_when_constraints_use_alias_casing_returns_canonical_constraint_names", () => {
		assert.deepEqual(
			normalizeRolePolicy("validator", {
				mode: "auto",
				constraints: ["STRICT_JSON", "review", "strict-json"],
			}),
			{
				role: "validator",
				mode: "auto",
				constraints: ["strict-json", "review"],
			},
		);
	});

	test("validateRolePolicy_when_constraint_unknown_returns_clear_validation_error", () => {
		const result = validateRolePolicy("reviewer", {
			mode: "auto",
			constraints: ["review", "vision-model"],
		});

		assert.equal(result.ok, false);
		assert.match(result.errors[0], /unsupported constraint "vision-model"/);
		assert.match(result.errors[0], /coding, review, cheap, long-context/);
		assert.equal(result.policy, null);
	});

	test("normalizeRolePolicy_when_policy_contains_concrete_model_id_rejects_durable_route_config", () => {
		assert.throws(
			() =>
				normalizeRolePolicy("orchestrator", {
					mode: "auto",
					modelId: "concrete-route",
				}),
			(error) => {
				assert.equal(error instanceof ModelRolePolicyValidationError, true);
				assert.match(error.message, /durable policy cannot include "modelId"/);
				return true;
			},
		);
	});

	test("normalizeRolePolicy_when_mode_is_concrete_model_rejects_non_auto_intent", () => {
		assert.throws(
			() => normalizeRolePolicy("worker", { model: "concrete-route" }),
			/must use mode "auto"/,
		);
	});

	test("normalizeRolePolicies_when_partial_policy_supplied_projects_all_supported_roles", () => {
		assert.deepEqual(
			normalizeRolePolicies({
				Reviewer: { mode: "auto", constraints: ["cheap", "review"] },
			}),
			{
				orchestrator: {
					role: "orchestrator",
					mode: "auto",
					constraints: ["long-context", "strict-json"],
				},
				worker: {
					role: "worker",
					mode: "auto",
					constraints: ["coding"],
				},
				validator: {
					role: "validator",
					mode: "auto",
					constraints: ["strict-json", "review"],
				},
				reviewer: {
					role: "reviewer",
					mode: "auto",
					constraints: ["cheap", "review"],
				},
				adversary: {
					role: "adversary",
					mode: "auto",
					constraints: ["review", "lineage-diverse-from-worker"],
				},
			},
		);
	});

	test("normalizeRolePolicies_when_role_unknown_rejects_projection", () => {
		assert.throws(
			() => normalizeRolePolicies({ planner: { mode: "auto" } }),
			/unsupported model role "planner"/,
		);
	});

	// ── adversary role ──────────────────────────────────────────────────────────

	test("adversary_is_a_supported_role", () => {
		assert.ok(SUPPORTED_MODEL_ROLES.includes("adversary"));
	});

	test("lineage_diverse_from_worker_is_a_supported_constraint", () => {
		assert.ok(
			SUPPORTED_MODEL_ROLE_CONSTRAINTS.includes("lineage-diverse-from-worker"),
		);
	});

	test("adversary_default_constraints_include_review_and_lineage_diverse_from_worker", () => {
		const policy = normalizeRolePolicy("adversary");
		assert.deepEqual(policy, {
			role: "adversary",
			mode: "auto",
			constraints: ["review", "lineage-diverse-from-worker"],
		});
	});

	test("reviewer_default_constraints_include_lineage_diverse_from_worker", () => {
		const policy = normalizeRolePolicy("reviewer");
		assert.ok(
			policy.constraints.includes("lineage-diverse-from-worker"),
			`expected lineage-diverse-from-worker in reviewer defaults, got: ${policy.constraints.join(", ")}`,
		);
	});

	test("validateRolePolicy_adversary_with_lineage_diverse_from_worker_is_valid", () => {
		const result = validateRolePolicy("adversary", {
			constraints: ["lineage-diverse-from-worker"],
		});
		assert.equal(result.ok, true);
		assert.ok(
			result.policy.constraints.includes("lineage-diverse-from-worker"),
		);
	});

	test("normalizeRolePolicies_empty_input_includes_adversary_role", () => {
		const policies = normalizeRolePolicies({});
		assert.ok(
			"adversary" in policies,
			"adversary key missing from full policy map",
		);
		assert.deepEqual(policies.adversary, {
			role: "adversary",
			mode: "auto",
			constraints: ["review", "lineage-diverse-from-worker"],
		});
	});

	// ── rootVendorFor / isSameRootVendor ────────────────────────────────────────

	test("rootVendorFor_maps_claude_prefix_to_anthropic", () => {
		assert.equal(rootVendorFor("claude-3-7-sonnet"), "anthropic");
		assert.equal(rootVendorFor("claude-opus-4"), "anthropic");
	});

	test("rootVendorFor_maps_gpt_and_o_series_to_openai", () => {
		assert.equal(rootVendorFor("gpt-4o"), "openai");
		assert.equal(rootVendorFor("o1-preview"), "openai");
		assert.equal(rootVendorFor("o3-mini"), "openai");
	});

	test("rootVendorFor_maps_gemini_prefix_to_google", () => {
		assert.equal(rootVendorFor("gemini-2.5-pro"), "google");
		assert.equal(rootVendorFor("gemma-3"), "google");
	});

	test("rootVendorFor_maps_kimi_prefix_to_moonshot", () => {
		assert.equal(rootVendorFor("kimi-for-coding"), "moonshot");
		assert.equal(rootVendorFor("kimi-k2.6"), "moonshot");
	});

	test("rootVendorFor_maps_minimax_prefix_to_minimax", () => {
		assert.equal(rootVendorFor("minimax-text-01"), "minimax");
	});

	test("rootVendorFor_maps_glm_and_zai_prefix_to_zhipu", () => {
		assert.equal(rootVendorFor("glm-4"), "zhipu");
		assert.equal(rootVendorFor("zai-something"), "zhipu");
	});

	test("rootVendorFor_maps_longcat_prefix_to_meituan", () => {
		assert.equal(rootVendorFor("longcat-v1"), "meituan");
	});

	test("rootVendorFor_returns_unknown_for_unrecognised_model", () => {
		assert.equal(rootVendorFor("mystery-model-x"), "unknown");
		assert.equal(rootVendorFor(""), "unknown");
	});

	test("isSameRootVendor_true_when_same_family", () => {
		assert.equal(isSameRootVendor("claude-3-5-sonnet", "claude-opus-4"), true);
		assert.equal(isSameRootVendor("gpt-4o", "o1-preview"), true);
	});

	test("isSameRootVendor_false_when_different_family", () => {
		assert.equal(isSameRootVendor("claude-3-5-sonnet", "gpt-4o"), false);
		assert.equal(isSameRootVendor("gemini-2.5-pro", "kimi-for-coding"), false);
	});

	test("isSameRootVendor_false_when_either_model_is_unknown", () => {
		assert.equal(isSameRootVendor("mystery-model", "claude-3-5-sonnet"), false);
		assert.equal(isSameRootVendor("claude-3-5-sonnet", "mystery-model"), false);
		assert.equal(isSameRootVendor("mystery-a", "mystery-b"), false);
	});
});
