import { codeModelProviderSourceContract } from "../src/components/microservices/code-queue/src/code-agent/common";
import { codexSubmitModelRegistryForTest, codexSubmitRoutingRecommendationForTest } from "./src/code-queue";

type JsonRecord = Record<string, unknown>;

function assertCondition(condition: unknown, message: string, detail: unknown = {}): void {
  if (!condition) throw new Error(`${message}: ${JSON.stringify(detail)}`);
}

function asRecord(value: unknown): JsonRecord {
  assertCondition(typeof value === "object" && value !== null && !Array.isArray(value), "expected JSON object", { value });
  return value as JsonRecord;
}

const lowRiskPrompt = `
目标：更新 docs/reference/code-queue-supervision.md 中的 MiniMax 派单规则。
范围：只改中文长期文档和一个轻量 dry-run contract test，不触碰 runtime 调度核心。
禁止：不要重启服务，不要读取密钥，不要写数据库，不要部署 prod。
验证：运行 bun scripts/code-queue-submit-routing-contract-test.ts，并在 final response 给出验证证据、commit 和风险。
背景：本 prompt 是完整需求来源，GitHub issue 只能作为辅助引用，不能作为唯一来源。需要 dry-run/preflight 输出帮助指挥官判断 runner/model。请保持改动低风险、可审阅、可回滚，并让指挥官完成后审阅未读任务。
`;

const runtimePrompt = `
目标：修复 Code Queue runtime scheduler 的 active run 状态机。
范围：src/components/microservices/code-queue/src/index.ts 和 runtime-preflight。
禁止：不要部署 prod。
验证：需要证明 scheduler heartbeat、active run、OpenCode session recovery 都正确。
`;

const mediumPrompt = `
目标：实现一个前端 React 控制台组件的小功能，给 Code Queue 任务列表增加可折叠的验证证据摘要。
范围：只改用户界面模块中的一个 TSX 组件和一个相邻的轻量 contract guard，不触碰 backend-core、Code Queue runtime、provider-gateway、k3sctl-adapter、部署配置或数据库 schema。
禁止：不要部署 prod，不要重启服务，不要读取密钥，不要写数据库，不要修改 release/v1，不要跑 heavy check/e2e/Playwright。
验证：运行针对该组件或 contract guard 的轻量脚本，final response 必须报告修改文件、验证命令、输出摘要、commit 和遗留风险。
背景：本 prompt 是完整需求来源，GitHub issue 只能作为辅助引用，不能作为唯一来源。这个任务有真实代码变更和 UI 状态判断，复杂度高于只读文档，但写入边界局部、可审阅、可用轻量测试复核。
`;

const commanderOnlyPrompt = `
目标：在 production 上 deploy apply 并 restart code-queue，必要时读取 secret token 和写 PostgreSQL 修复任务状态。
验证：live health。
`;

export function runCodeQueueSubmitRoutingContract(): JsonRecord {
  const lowRisk = codexSubmitRoutingRecommendationForTest(lowRiskPrompt);
  assertCondition(lowRisk.route === "minimax-opencode", "low-risk self-contained prompt should be a MiniMax candidate", lowRisk);
  assertCondition(lowRisk.recommendedRunner === "opencode", "MiniMax candidate should recommend OpenCode", lowRisk);
  assertCondition(lowRisk.recommendedModel === "minimax-m3", "MiniMax candidate should recommend minimax-m3 as new default", lowRisk);
  assertCondition(asRecord(lowRisk.riskControls).promptSelfContained === true, "low-risk prompt should be self-contained", lowRisk);
  assertCondition(asRecord(lowRisk.riskControls).issueIsNotOnlySource === true, "issue must not be the only source", lowRisk);

  const runtime = codexSubmitRoutingRecommendationForTest(runtimePrompt);
  assertCondition(runtime.route === "gpt-5.5-codex", "runtime/core work should stay on GPT-5.5", runtime);
  assertCondition(runtime.recommendedRunner === "codex", "runtime/core work should recommend Codex runner", runtime);
  assertCondition(runtime.recommendedModel === "gpt-5.5", "runtime/core work should recommend GPT-5.5", runtime);

  const medium = codexSubmitRoutingRecommendationForTest(mediumPrompt, "deepseek");
  assertCondition(medium.route === "deepseek-opencode", "medium bounded frontend work should recommend DeepSeek/OpenCode", medium);
  assertCondition(medium.recommendedRunner === "opencode", "DeepSeek work should use OpenCode runner", medium);
  assertCondition(medium.recommendedModel === "deepseek-chat", "DeepSeek candidate should recommend deepseek-chat", medium);
  assertCondition(asRecord(medium.riskControls).mediumComplexityCandidate === true, "medium prompt should satisfy medium complexity controls", medium);
  assertCondition(asRecord(medium.explicitRequest).model === "deepseek-chat", "explicit deepseek alias should normalize to deepseek-chat", medium);
  assertCondition(asRecord(medium.explicitRequest).runner === "opencode", "explicit deepseek alias should route to OpenCode", medium);
  const policyContract = asRecord(medium.policyContract);
  assertCondition(asRecord(policyContract.concurrency).gpt55Routine === 5, "policy contract should expose GPT-5.5 routine concurrency", policyContract);
  assertCondition(asRecord(policyContract.concurrency).gpt55BurstMax === 10, "policy contract should expose GPT-5.5 burst concurrency", policyContract);
  assertCondition(asRecord(policyContract.concurrency).minimaxSimpleMax === 10, "policy contract should expose MiniMax simple concurrency", policyContract);
  assertCondition(asRecord(policyContract.concurrency).deepseekMediumDefault === 5, "policy contract should expose DeepSeek medium default concurrency", policyContract);
  const modelTiers = asRecord(policyContract).modelTiers as unknown[];
  assertCondition(Array.isArray(modelTiers), "policy contract should expose model tiers", policyContract);
  const deepseekTier = modelTiers.map(asRecord).find((tier) => tier.model === "deepseek-chat");
  assertCondition(deepseekTier?.runner === "opencode", "DeepSeek policy tier should use OpenCode", policyContract);
  const externalProvider429 = asRecord(policyContract.externalProvider429);
  assertCondition(externalProvider429.commanderAction === "wait-while-exponential-backoff-is-healthy", "429 policy should tell commander to wait on healthy backoff", policyContract);
  assertCondition(Array.isArray(externalProvider429.interveneWhen), "429 policy should expose intervention conditions", policyContract);

  const registry = codexSubmitModelRegistryForTest(["gpt-5.5", "deepseek", "minimax-m3", "minimax-m2.7"]);
  const modelPorts = asRecord(registry.modelPorts);
  assertCondition(modelPorts["deepseek-chat"] === "opencode", "modelPorts should route deepseek-chat to OpenCode", registry);
  assertCondition(modelPorts["minimax-m3"] === "opencode", "modelPorts should keep minimax-m3 on OpenCode", registry);
  assertCondition(modelPorts["minimax-m2.7"] === "opencode", "modelPorts should keep minimax-m2.7 on OpenCode", registry);
  assertCondition(modelPorts["gpt-5.5"] === "codex", "modelPorts should keep default GPT on Codex", registry);
  assertCondition(registry.opencodeModels.includes("deepseek-chat"), "opencodeModels should include deepseek-chat", registry);
  assertCondition(registry.opencodeModels.includes("minimax-m3"), "opencodeModels should include minimax-m3", registry);
  assertCondition(registry.opencodeModels.includes("minimax-m2.7"), "opencodeModels should include minimax-m2.7", registry);
  assertCondition(registry.codexModels.includes("gpt-5.5"), "codexModels should include default GPT", registry);
  const providerSource = codeModelProviderSourceContract({
    codeModels: ["gpt-5.5", "deepseek", "minimax-m3", "minimax-m2.7"],
    deepseekApiBase: "https://api.deepseek.example",
    deepseekApiKey: "ds-secret-must-not-print",
    deepseekModel: "deepseek-chat",
    minimaxApiBase: "https://api.minimax.example",
    minimaxApiKey: "",
    minimaxModel: "MiniMax-M2.7",
    minimaxM3Model: "MiniMax-M3",
  }, {
    DEEPSEEK_API_KEY: "ds-secret-must-not-print",
    DEEPSEEK_API_BASE: "https://api.deepseek.example",
    DEEPSEEK_MODEL: "deepseek-chat",
  });
  const providerSourceJson = JSON.stringify(providerSource);
  assertCondition(!providerSourceJson.includes("ds-secret-must-not-print"), "provider source contract must not print API key values", providerSource);
  assertCondition(!providerSourceJson.includes("https://api.deepseek.example"), "provider source contract must not print baseURL values", providerSource);
  assertCondition(asRecord(providerSource).valuesPrinted === false, "provider source contract must declare valuesPrinted=false", providerSource);
  const providers = asRecord(providerSource.providers);
  const deepseekProvider = asRecord(providers.deepseek);
  const deepseekCredentialSource = asRecord(deepseekProvider.credentialSource);
  assertCondition(deepseekProvider.runner === "opencode", "DeepSeek provider source should use OpenCode", providerSource);
  assertCondition(deepseekProvider.publicModel === "deepseek-chat", "DeepSeek provider source should expose public model alias", providerSource);
  assertCondition(asRecord(deepseekCredentialSource.apiKey).ref === "env:DEEPSEEK_API_KEY", "DeepSeek apiKey source should expose env ref only", providerSource);
  assertCondition(asRecord(deepseekCredentialSource.apiKey).present === true, "DeepSeek apiKey presence should be true when configured", providerSource);
  assertCondition(asRecord(deepseekCredentialSource.baseURL).ref === "env:DEEPSEEK_API_BASE", "DeepSeek baseURL source should expose env ref only", providerSource);

  const commanderOnly = codexSubmitRoutingRecommendationForTest(commanderOnlyPrompt);
  assertCondition(commanderOnly.route === "commander-human-only", "prod restart/secrets/DB work should be commander-only", commanderOnly);
  assertCondition(commanderOnly.recommendedRunner === "commander", "commander-only work should not recommend a runner", commanderOnly);
  assertCondition(commanderOnly.recommendedModel === null, "commander-only work should not recommend a model", commanderOnly);

  const explicitGpt = codexSubmitRoutingRecommendationForTest(lowRiskPrompt, "gpt-5.5");
  const explicitRequest = asRecord(explicitGpt.explicitRequest);
  assertCondition(explicitRequest.runner === "codex", "explicit gpt model should map to Codex", explicitGpt);
  assertCondition(String(explicitRequest.note ?? "").includes("differs"), "explicit model mismatch should be visible", explicitGpt);
  assertCondition(asRecord(explicitGpt.routingPolicy).doesNotChangeSubmittedPayload === true, "dry-run recommendation must not rewrite payload", explicitGpt);

  return {
    ok: true,
    checks: [
      "low-risk self-contained prompts recommend minimax-m3/OpenCode (new default)",
      "runtime/core work recommends GPT-5.5/Codex",
      "medium bounded frontend work recommends deepseek-chat/OpenCode",
      "model registry maps deepseek-chat, minimax-m3, and minimax-m2.7 to OpenCode and GPT-5.5 to Codex",
      "model provider source contract exposes DeepSeek refs/presence without secret values",
      "dry-run policy contract exposes model-tier concurrency",
      "prod/restart/secret/DB work is commander-only",
      "explicit --model mismatch is visible and payload is unchanged",
    ],
  };
}

if (import.meta.main) {
  process.stdout.write(`${JSON.stringify(runCodeQueueSubmitRoutingContract(), null, 2)}\n`);
}
