import test from "node:test";
import assert from "node:assert/strict";
import { createServer } from "node:http";
import type { Server } from "node:http";

import {
  buildOmniRouteOpenCodeConfig,
  createOmniRouteAgentBlock,
  createOmniRouteComboConfig,
  createOmniRouteMCPEntry,
  createOmniRouteModesBlock,
  createOmniRouteProvider,
  fetchLiveModels,
  listCombos,
  mergeIntoExistingConfig,
  normalizeBaseURL,
  OMNIROUTE_DEFAULT_MODEL_CAPABILITIES,
  OMNIROUTE_DEFAULT_MODEL_CONTEXT_LENGTHS,
  OMNIROUTE_DEFAULT_OPENCODE_MODELS,
  OMNIROUTE_MCP_DEFAULT_SCOPES,
  OMNIROUTE_PROVIDER_NPM,
  OPENCODE_CONFIG_SCHEMA,
} from "../src/index.ts";

test("normalizeBaseURL preserves a bare host:port", () => {
  assert.equal(normalizeBaseURL("http://localhost:20128"), "http://localhost:20128/v1");
});

test("normalizeBaseURL strips trailing slashes", () => {
  assert.equal(normalizeBaseURL("http://localhost:20128////"), "http://localhost:20128/v1");
});

test("normalizeBaseURL deduplicates an existing /v1 suffix", () => {
  assert.equal(normalizeBaseURL("http://localhost:20128/v1"), "http://localhost:20128/v1");
  assert.equal(normalizeBaseURL("http://localhost:20128/v1/"), "http://localhost:20128/v1");
});

test("normalizeBaseURL rejects empty input", () => {
  assert.throws(() => normalizeBaseURL("   "), /baseURL is required/);
});

test("normalizeBaseURL rejects malformed URLs", () => {
  assert.throws(() => normalizeBaseURL("not a url"), /not a valid URL/);
});

test("createOmniRouteProvider validates required fields", () => {
  assert.throws(
    () => createOmniRouteProvider({ baseURL: "", apiKey: "x" } as never),
    /baseURL is required/
  );
  assert.throws(
    () => createOmniRouteProvider({ baseURL: "http://x", apiKey: "" } as never),
    /apiKey is required/
  );
});

test("createOmniRouteProvider produces the OpenCode-compatible shape", () => {
  const provider = createOmniRouteProvider({
    baseURL: "http://localhost:20128",
    apiKey: "sk_omniroute",
  });

  assert.equal(provider.npm, OMNIROUTE_PROVIDER_NPM);
  assert.equal(provider.name, "OmniRoute");
  assert.equal(provider.options.baseURL, "http://localhost:20128/v1");
  assert.equal(provider.options.apiKey, "sk_omniroute");
  assert.equal(typeof provider.models, "object");
});

test("createOmniRouteProvider seeds the default model catalog", () => {
  const provider = createOmniRouteProvider({
    baseURL: "http://localhost:20128",
    apiKey: "sk_omniroute",
  });

  const modelIds = Object.keys(provider.models).sort();
  const defaultIds = [...OMNIROUTE_DEFAULT_OPENCODE_MODELS].sort();
  assert.deepEqual(modelIds, defaultIds);
  for (const id of defaultIds) {
    assert.equal(provider.models[id]?.name, id);
    assert.equal(provider.models[id]?.attachment, true);
  }
});

test("createOmniRouteProvider honours a custom models list and labels", () => {
  const provider = createOmniRouteProvider({
    baseURL: "http://localhost:20128",
    apiKey: "sk_omniroute",
    models: ["auto", "claude-opus-4-7"],
    modelLabels: { auto: "Auto-Combo", "claude-opus-4-7": "Opus 4.7" },
  });

  assert.deepEqual(Object.keys(provider.models), ["auto", "claude-opus-4-7"]);
  assert.equal(provider.models.auto.name, "Auto-Combo");
  assert.equal(provider.models["claude-opus-4-7"].name, "Opus 4.7");
});

test("createOmniRouteProvider deduplicates and trims model ids", () => {
  const provider = createOmniRouteProvider({
    baseURL: "http://localhost:20128",
    apiKey: "sk_omniroute",
    models: ["  auto  ", "auto", "", "claude-opus-4-7"],
  });
  assert.deepEqual(Object.keys(provider.models), ["auto", "claude-opus-4-7"]);
});

test("createOmniRouteProvider honours displayName override", () => {
  const provider = createOmniRouteProvider({
    baseURL: "http://localhost:20128",
    apiKey: "sk_omniroute",
    displayName: "Local OmniRoute",
  });
  assert.equal(provider.name, "Local OmniRoute");
});

test("buildOmniRouteOpenCodeConfig wraps the provider with the OpenCode schema", () => {
  const doc = buildOmniRouteOpenCodeConfig({
    baseURL: "http://localhost:20128/v1",
    apiKey: "sk_omniroute",
  });

  assert.equal(doc.$schema, OPENCODE_CONFIG_SCHEMA);
  assert.equal(typeof doc.provider.omniroute, "object");
  assert.equal(doc.provider.omniroute.options.baseURL, "http://localhost:20128/v1");
});

test("config document is JSON-serialisable", () => {
  const doc = buildOmniRouteOpenCodeConfig({
    baseURL: "http://localhost:20128",
    apiKey: "sk_omniroute",
  });
  const round = JSON.parse(JSON.stringify(doc));
  assert.deepEqual(round, doc);
});

test("buildOmniRouteOpenCodeConfig emits model and small_model prefixed with provider key", () => {
  const doc = buildOmniRouteOpenCodeConfig({
    baseURL: "http://localhost:20128",
    apiKey: "sk_omniroute",
    model: "claude-sonnet-4-5-thinking",
    smallModel: "gemini-3-flash",
  });
  assert.equal(doc.model, "omniroute/claude-sonnet-4-5-thinking");
  assert.equal(doc.small_model, "omniroute/gemini-3-flash");
});

test("buildOmniRouteOpenCodeConfig omits model and small_model when not supplied", () => {
  const doc = buildOmniRouteOpenCodeConfig({
    baseURL: "http://localhost:20128",
    apiKey: "sk_omniroute",
  });
  assert.equal(doc.model, undefined);
  assert.equal(doc.small_model, undefined);
  assert.ok(!("model" in doc));
  assert.ok(!("small_model" in doc));
});

test("buildOmniRouteOpenCodeConfig ignores blank model strings", () => {
  const doc = buildOmniRouteOpenCodeConfig({
    baseURL: "http://localhost:20128",
    apiKey: "sk_omniroute",
    model: "   ",
    smallModel: "",
  });
  assert.ok(!("model" in doc));
  assert.ok(!("small_model" in doc));
});

test("mergeIntoExistingConfig preserves existing provider entries", () => {
  const existing = {
    $schema: OPENCODE_CONFIG_SCHEMA,
    provider: {
      anthropic: { npm: "@ai-sdk/anthropic", name: "Anthropic", options: {}, models: {} },
    },
    keybinds: { submit: "enter" },
  };
  const result = mergeIntoExistingConfig(existing, {
    baseURL: "http://localhost:20128",
    apiKey: "sk_omniroute",
  });
  assert.ok("anthropic" in (result.provider as Record<string, unknown>));
  assert.ok("omniroute" in (result.provider as Record<string, unknown>));
  assert.deepEqual((result as Record<string, unknown>).keybinds, { submit: "enter" });
});

test("mergeIntoExistingConfig overwrites existing omniroute entry", () => {
  const existing = {
    provider: {
      omniroute: {
        npm: "@ai-sdk/openai-compatible",
        name: "OLD",
        options: { baseURL: "http://old/v1", apiKey: "old" },
        models: {},
      },
    },
  };
  const result = mergeIntoExistingConfig(existing, {
    baseURL: "http://new",
    apiKey: "new-key",
    displayName: "NEW",
  });
  const omniroute = (result.provider as Record<string, unknown>).omniroute as { name: string };
  assert.equal(omniroute.name, "NEW");
});

test("mergeIntoExistingConfig writes model and small_model when supplied", () => {
  const result = mergeIntoExistingConfig(
    {},
    {
      baseURL: "http://localhost:20128",
      apiKey: "sk_omniroute",
      model: "claude-sonnet-4-5-thinking",
      smallModel: "gemini-3-flash",
    }
  );
  assert.equal(result.model, "omniroute/claude-sonnet-4-5-thinking");
  assert.equal(result.small_model, "omniroute/gemini-3-flash");
});

test("mergeIntoExistingConfig does not add model keys when not supplied", () => {
  const result = mergeIntoExistingConfig(
    {},
    { baseURL: "http://localhost:20128", apiKey: "sk_omniroute" }
  );
  assert.ok(!("model" in result));
  assert.ok(!("small_model" in result));
});

test("OMNIROUTE_MCP_DEFAULT_SCOPES contains 7 read-only scopes", () => {
  assert.equal(OMNIROUTE_MCP_DEFAULT_SCOPES.length, 7);
  assert.ok(OMNIROUTE_MCP_DEFAULT_SCOPES.every((s) => s.startsWith("read:")));
});

test("createOmniRouteMCPEntry defaults to tsx runtime", () => {
  const entry = createOmniRouteMCPEntry({
    serverPath: "/path/to/server.ts",
    apiKey: "sk_omniroute",
  });
  assert.equal(entry.command, "npx");
  assert.deepEqual(entry.args, ["tsx", "/path/to/server.ts"]);
  assert.equal(entry.env.OMNIROUTE_API_KEY, "sk_omniroute");
  assert.ok(!("OMNIROUTE_MCP_ENFORCE_SCOPES" in entry.env));
  assert.ok(!("OMNIROUTE_MANAGEMENT_API_KEY" in entry.env));
});

test("createOmniRouteMCPEntry uses node runtime when specified", () => {
  const entry = createOmniRouteMCPEntry({
    serverPath: "/path/to/server.js",
    apiKey: "sk_omniroute",
    runtime: "node",
  });
  assert.equal(entry.command, "node");
  assert.deepEqual(entry.args, ["/path/to/server.js"]);
});

test("createOmniRouteMCPEntry sets management key and scopes when supplied", () => {
  const entry = createOmniRouteMCPEntry({
    serverPath: "/path/to/server.ts",
    apiKey: "sk_omniroute",
    managementApiKey: "sk_manage",
    scopes: ["read:health", "read:combos", "execute:completions"],
  });
  assert.equal(entry.env.OMNIROUTE_MANAGEMENT_API_KEY, "sk_manage");
  assert.equal(entry.env.OMNIROUTE_MCP_ENFORCE_SCOPES, "true");
  assert.equal(entry.env.OMNIROUTE_MCP_SCOPES, "read:health,read:combos,execute:completions");
});

test("createOmniRouteMCPEntry rejects missing required fields", () => {
  assert.throws(
    () => createOmniRouteMCPEntry({ serverPath: "", apiKey: "x" }),
    /serverPath is required/
  );
  assert.throws(
    () => createOmniRouteMCPEntry({ serverPath: "/p", apiKey: "" }),
    /apiKey is required/
  );
});

function startMockServer(
  handler: (path: string) => unknown
): Promise<{ url: string; close: () => void }> {
  return new Promise((resolve) => {
    const server: Server = createServer((req, res) => {
      const body = JSON.stringify(handler(req.url ?? ""));
      res.writeHead(200, { "Content-Type": "application/json" });
      res.end(body);
    });
    server.listen(0, "127.0.0.1", () => {
      const addr = server.address() as { port: number };
      resolve({ url: `http://127.0.0.1:${addr.port}`, close: () => server.close() });
    });
  });
}

test("fetchLiveModels handles array envelope", async () => {
  const { url, close } = await startMockServer(() => [
    { id: "claude-sonnet", name: "Claude Sonnet" },
    { id: "gemini-flash", displayName: "Gemini Flash" },
  ]);
  try {
    const models = await fetchLiveModels(url, "sk_test");
    assert.equal(models.length, 2);
    assert.equal(models[0].id, "claude-sonnet");
    assert.equal(models[0].name, "Claude Sonnet");
    assert.equal(models[1].id, "gemini-flash");
    assert.equal(models[1].name, "Gemini Flash");
  } finally {
    close();
  }
});

test("fetchLiveModels handles data-envelope and snake_case fields", async () => {
  const { url, close } = await startMockServer(() => ({
    data: [{ model_id: "gpt-4o", display_name: "GPT-4o" }],
  }));
  try {
    const models = await fetchLiveModels(url, "sk_test");
    assert.equal(models.length, 1);
    assert.equal(models[0].id, "gpt-4o");
    assert.equal(models[0].name, "GPT-4o");
  } finally {
    close();
  }
});

test("fetchLiveModels falls back to id as name when no name field", async () => {
  const { url, close } = await startMockServer(() => [{ id: "auto" }]);
  try {
    const models = await fetchLiveModels(url, "sk_test");
    assert.equal(models[0].name, "auto");
  } finally {
    close();
  }
});

test("listCombos normalises compressionOverride", async () => {
  const { url, close } = await startMockServer(() => ({
    combos: [
      {
        id: "c1",
        name: "Primary",
        strategy: "priority",
        active: true,
        compressionOverride: "standard",
      },
      {
        id: "c2",
        name: "Cheap",
        strategy: "weighted",
        active: false,
        compressionOverride: "unknown-value",
      },
      { id: "c3", name: "Off", strategy: "round-robin", active: true, compressionOverride: "" },
    ],
  }));
  try {
    const combos = await listCombos(url, "sk_manage");
    assert.equal(combos.length, 3);
    assert.equal(combos[0].compressionOverride, "standard");
    assert.equal(combos[1].compressionOverride, "");
    assert.equal(combos[2].compressionOverride, "");
  } finally {
    close();
  }
});

test("createOmniRouteComboConfig builds minimal payload", () => {
  const payload = createOmniRouteComboConfig({ name: "my-combo", strategy: "priority" });
  assert.equal(payload.name, "my-combo");
  assert.equal(payload.strategy, "priority");
  assert.equal(payload.active, true);
  assert.ok(!("compressionOverride" in payload));
  assert.ok(!("providers" in payload));
});

test("createOmniRouteComboConfig includes optional fields when supplied", () => {
  const payload = createOmniRouteComboConfig({
    name: "full",
    strategy: "weighted",
    compressionOverride: "aggressive",
    active: false,
    providers: ["provider-a", "provider-b"],
  });
  assert.equal(payload.compressionOverride, "aggressive");
  assert.equal(payload.active, false);
  assert.deepEqual(payload.providers, ["provider-a", "provider-b"]);
});

test("OMNIROUTE_DEFAULT_OPENCODE_MODELS includes cc/ prefixed models", () => {
  const defaults = [...OMNIROUTE_DEFAULT_OPENCODE_MODELS];
  assert.ok(defaults.includes("cc/claude-opus-4-8"));
  assert.ok(
    defaults.some((m) => m.startsWith("cc/")),
    "should have cc/ prefixed models"
  );
  assert.ok(defaults.length >= 7, "should have at least 7 models");
});

test("OMNIROUTE_DEFAULT_MODEL_CONTEXT_LENGTHS covers every default model id", () => {
  for (const id of OMNIROUTE_DEFAULT_OPENCODE_MODELS) {
    const ctx = OMNIROUTE_DEFAULT_MODEL_CONTEXT_LENGTHS[id];
    assert.ok(
      typeof ctx === "number" && ctx > 0,
      `default context_length for ${id} missing — should be a positive number`
    );
    // Sanity: context should be at least 8K, at most 2M tokens
    assert.ok(ctx >= 8_000, `${id} context_length ${ctx} seems too low`);
    assert.ok(ctx <= 2_000_000, `${id} context_length ${ctx} seems too high`);
  }
});

test("createOmniRouteProvider emits limit.context on default model entries", () => {
  const provider = createOmniRouteProvider({
    baseURL: "http://localhost:20128",
    apiKey: "sk_omniroute",
  });
  const entry = provider.models["cc/claude-opus-4-8"];
  assert.ok(entry.limit, "model entry should have a limit field");
  assert.equal(entry.limit!.context, 1_000_000);
  assert.equal(provider.models["cc/claude-opus-4-7"].limit!.context, 1_000_000);
});

test("createOmniRouteProvider omits limit.context for unknown model ids", () => {
  const provider = createOmniRouteProvider({
    baseURL: "http://localhost:20128",
    apiKey: "sk_omniroute",
    models: ["completely-unknown-model"],
  });
  const entry = provider.models["completely-unknown-model"];
  assert.equal(entry.limit, undefined);
});

test("createOmniRouteProvider reads contextLength from a live model entry for ids absent from the static map", () => {
  // #3298 regression guard: the static OMNIROUTE_DEFAULT_MODEL_CONTEXT_LENGTHS
  // map only covers the legacy 8 Claude/Gemini ids. Before this change, any
  // other model got `undefined` context (see the test above, string form) and
  // OpenCode silently fell back to its 128K internal default. A live model
  // entry carrying `contextLength` must now surface as `limit.context`.
  const provider = createOmniRouteProvider({
    baseURL: "http://localhost:20128",
    apiKey: "sk_omniroute",
    models: [{ id: "completely-unknown-model", contextLength: 262_144 }],
  });
  const entry = provider.models["completely-unknown-model"];
  assert.ok(entry.limit, "a live contextLength should produce a limit field even for ids absent from the static map");
  assert.equal(entry.limit!.context, 262_144);
});

test("createOmniRouteProvider: a live model contextLength wins over the static default map", () => {
  // `cc/claude-opus-4-8` has a static default (1_000_000). A live entry carrying
  // a different contextLength must take precedence (live > modelContextLengths >
  // static defaults).
  const provider = createOmniRouteProvider({
    baseURL: "http://localhost:20128",
    apiKey: "sk_omniroute",
    models: [{ id: "cc/claude-opus-4-8", contextLength: 524_288 }],
  });
  assert.equal(provider.models["cc/claude-opus-4-8"].limit!.context, 524_288);
});

test("createOmniRouteProvider serialises limit.context to JSON", () => {
  const provider = createOmniRouteProvider({
    baseURL: "http://localhost:20128",
    apiKey: "sk_omniroute",
  });
  const round = JSON.parse(JSON.stringify(provider));
  for (const id of OMNIROUTE_DEFAULT_OPENCODE_MODELS) {
    const expectedContext = OMNIROUTE_DEFAULT_MODEL_CONTEXT_LENGTHS[id];
    assert.equal(
      round.models[id].limit?.context,
      expectedContext,
      `${id} should serialise limit.context=${expectedContext}`
    );
  }
});

test("fetchLiveModels extracts context_length from snake_case field", async () => {
  const { url, close } = await startMockServer(() => ({
    data: [
      { id: "cc/claude-opus-4-7", name: "Claude Opus 4.7", context_length: 200_000 },
      { id: "gemini-3.1-pro-high", name: "Gemini 3.1 Pro", context_length: 1_000_000 },
      { id: "no-context", name: "No Context" },
    ],
  }));
  try {
    const models = await fetchLiveModels(url, "sk_test");
    const claude = models.find((m) => m.id === "cc/claude-opus-4-7");
    assert.ok(claude, "claude model should be present");
    assert.equal(claude!.contextLength, 200_000);
    const gemini = models.find((m) => m.id === "gemini-3.1-pro-high");
    assert.equal(gemini!.contextLength, 1_000_000);
    const noCtx = models.find((m) => m.id === "no-context");
    assert.equal(noCtx!.contextLength, undefined);
  } finally {
    close();
  }
});

test("OMNIROUTE_DEFAULT_MODEL_CAPABILITIES covers every default model id", () => {
  for (const id of OMNIROUTE_DEFAULT_OPENCODE_MODELS) {
    const caps = OMNIROUTE_DEFAULT_MODEL_CAPABILITIES[id];
    assert.ok(caps, `default capabilities for ${id} missing`);
    assert.equal(caps.attachment, true, `${id} should default to attachment=true`);
    assert.equal(caps.tool_call, true, `${id} should default to tool_call=true`);
  }
});

test("createOmniRouteProvider emits default capability flags inline with the model entry", () => {
  const provider = createOmniRouteProvider({
    baseURL: "http://localhost:20128",
    apiKey: "sk_omniroute",
  });
  const entry = provider.models["cc/claude-opus-4-8"];
  assert.equal(entry.name, "cc/claude-opus-4-8");
  assert.equal(entry.attachment, true);
  assert.equal(entry.reasoning, true);
  assert.equal(entry.temperature, true);
  assert.equal(entry.tool_call, true);
});

test("createOmniRouteProvider modelCapabilities overrides defaults and merges per id", () => {
  const provider = createOmniRouteProvider({
    baseURL: "http://localhost:20128",
    apiKey: "sk_omniroute",
    modelCapabilities: {
      "cc/claude-opus-4-7": { reasoning: false, label: "Opus (no thinking)" },
    },
  });
  const entry = provider.models["cc/claude-opus-4-7"];
  assert.equal(entry.name, "Opus (no thinking)");
  assert.equal(entry.reasoning, false);
  assert.equal(entry.attachment, true);
  assert.equal(entry.tool_call, true);
});

test("createOmniRouteProvider applies capability overrides to non-default model ids", () => {
  const provider = createOmniRouteProvider({
    baseURL: "http://localhost:20128",
    apiKey: "sk_omniroute",
    models: ["custom-model"],
    modelCapabilities: {
      "custom-model": { attachment: false, tool_call: true, label: "Custom" },
    },
  });
  const entry = provider.models["custom-model"];
  assert.equal(entry.name, "Custom");
  assert.equal(entry.attachment, false);
  assert.equal(entry.tool_call, true);
  assert.equal(entry.reasoning, undefined);
  assert.equal(entry.temperature, undefined);
});

test("createOmniRouteProvider modelLabels still works when modelCapabilities omits label", () => {
  const provider = createOmniRouteProvider({
    baseURL: "http://localhost:20128",
    apiKey: "sk_omniroute",
    models: ["claude-opus-4-5-thinking"],
    modelLabels: { "claude-opus-4-5-thinking": "Opus 4.5 (legacy label)" },
  });
  assert.equal(provider.models["claude-opus-4-5-thinking"].name, "Opus 4.5 (legacy label)");
});

test("createOmniRouteAgentBlock builds provider-prefixed entries per role", () => {
  const block = createOmniRouteAgentBlock({
    roles: {
      build: { modelId: "claude-sonnet-4-5-thinking", temperature: 0.2 },
      plan: { modelId: "claude-opus-4-5-thinking", top_p: 0.95 },
      review: { modelId: "gemini-3-flash", temperature: 0.0 },
    },
  });
  assert.equal(block.build.model, "omniroute/claude-sonnet-4-5-thinking");
  assert.equal(block.build.temperature, 0.2);
  assert.equal(block.plan.model, "omniroute/claude-opus-4-5-thinking");
  assert.equal(block.plan.top_p, 0.95);
  assert.equal(block.review.model, "omniroute/gemini-3-flash");
  assert.equal(block.review.temperature, 0.0);
});

test("createOmniRouteAgentBlock omits optional fields when not supplied", () => {
  const block = createOmniRouteAgentBlock({
    roles: { build: { modelId: "claude-sonnet-4-5-thinking" } },
  });
  assert.equal(block.build.model, "omniroute/claude-sonnet-4-5-thinking");
  assert.ok(!("temperature" in block.build));
  assert.ok(!("top_p" in block.build));
  assert.ok(!("tools" in block.build));
  assert.ok(!("prompt" in block.build));
});

test("createOmniRouteAgentBlock skips roles with empty modelId", () => {
  const block = createOmniRouteAgentBlock({
    roles: {
      build: { modelId: "claude-sonnet-4-5-thinking" },
      plan: { modelId: "   " },
      review: { modelId: "" },
    },
  });
  assert.deepEqual(Object.keys(block), ["build"]);
});

test("createOmniRouteAgentBlock emits tools as Record<string, boolean> per OC schema", () => {
  const block = createOmniRouteAgentBlock({
    roles: {
      build: {
        modelId: "claude-sonnet-4-5-thinking",
        tools: { edit: true, bash: true, web: false },
        prompt: "Edit files carefully.",
      },
    },
  });
  assert.deepEqual(block.build.tools, { edit: true, bash: true, web: false });
  assert.equal(block.build.prompt, "Edit files carefully.");
});

test("createOmniRouteAgentBlock filters invalid tool entries and omits empty maps", () => {
  const block = createOmniRouteAgentBlock({
    roles: {
      build: {
        modelId: "claude-sonnet-4-5-thinking",
        // @ts-expect-error — exercising runtime guard against bad input
        tools: { edit: true, bash: "yes", "": true, web: null },
      },
      plan: {
        modelId: "claude-opus-4-5-thinking",
        tools: {},
      },
    },
  });
  assert.deepEqual(block.build.tools, { edit: true });
  assert.ok(!("tools" in block.plan));
});

test("createOmniRouteModesBlock builds provider-prefixed mode entries", () => {
  const block = createOmniRouteModesBlock({
    modes: {
      build: { modelId: "claude-sonnet-4-5-thinking", tools: { edit: true, bash: true } },
      plan: { modelId: "claude-opus-4-5-thinking", prompt: "Plan first, code later." },
      review: { modelId: "gemini-3-flash" },
    },
  });
  assert.equal(block.build.model, "omniroute/claude-sonnet-4-5-thinking");
  assert.deepEqual(block.build.tools, { edit: true, bash: true });
  assert.equal(block.plan.prompt, "Plan first, code later.");
  assert.equal(block.review.model, "omniroute/gemini-3-flash");
});

test("createOmniRouteModesBlock skips modes with empty modelId", () => {
  const block = createOmniRouteModesBlock({
    modes: {
      build: { modelId: "claude-sonnet-4-5-thinking" },
      plan: { modelId: "" },
    },
  });
  assert.deepEqual(Object.keys(block), ["build"]);
});

test("createOmniRouteModesBlock honours numeric overrides limited to OC schema", () => {
  const block = createOmniRouteModesBlock({
    modes: {
      build: {
        modelId: "claude-sonnet-4-5-thinking",
        temperature: 0.7,
        top_p: 0.9,
      },
    },
  });
  assert.equal(block.build.temperature, 0.7);
  assert.equal(block.build.top_p, 0.9);
});
