import fs from "node:fs/promises";
import path from "node:path";

const ROOT = "/Users/xieyuanxiang/config-center";
const OUT_ROOT = path.join(ROOT, "字段取值表");
const ZENMUX_FILE = "/tmp/zenmux-models.json";

const TARGET_DIRS = [
  path.join(ROOT, "compute", "providers"),
  path.join(ROOT, "compute", "coding-plans"),
];

const PROVIDER_OWNED_BY = {
  openai: ["openai"],
  anthropic: ["anthropic"],
  deepseek: ["deepseek"],
  google: ["google"],
  moonshot: ["moonshotai"],
  zhipu: ["z-ai"],
  "zhipu-embedding": ["z-ai"],
  dashscope: ["qwen"],
  minimax: ["minimax"],
  baidu: ["baidu"],
  tencent: ["tencent"],
  volcengine: ["bytedance", "volcengine"],
  xai: ["x-ai"],
  mistral: ["mistralai"],
  kwai: ["kuaishou"],
  siliconflow: ["qwen", "baai"],
  cohere: ["cohere"],
  perplexity: ["perplexity"],
};

const OFFICIAL_DOCS = {
  openai: ["https://platform.openai.com/docs/models", "https://platform.openai.com/docs/pricing"],
  anthropic: ["https://docs.anthropic.com/en/docs/about-claude/models/all-models", "https://docs.anthropic.com/en/docs/about-claude/pricing"],
  deepseek: ["https://api-docs.deepseek.com/quick_start/pricing"],
  google: ["https://ai.google.dev/gemini-api/docs/models", "https://ai.google.dev/pricing"],
  moonshot: ["https://platform.moonshot.cn/docs/pricing/chat"],
  zhipu: ["https://docs.bigmodel.cn/cn/guide/models/text/", "https://www.bigmodel.cn/pricing"],
  dashscope: ["https://help.aliyun.com/zh/model-studio/getting-started/models", "https://help.aliyun.com/zh/model-studio/pricing"],
  minimax: ["https://platform.minimax.io/docs/guides/pricing-paygo"],
  baidu: ["https://cloud.baidu.com/doc/qianfan/"],
  tencent: ["https://cloud.tencent.com/document/product/1729"],
  volcengine: ["https://www.volcengine.com/docs/82379"],
  xai: ["https://docs.x.ai/docs/models"],
  mistral: ["https://docs.mistral.ai/getting-started/models", "https://mistral.ai/pricing"],
  cohere: ["https://docs.cohere.com/docs/models", "https://cohere.com/pricing"],
  siliconflow: ["https://www.siliconflow.com/models", "https://siliconflow.cn/pricing"],
  perplexity: ["https://docs.perplexity.ai"],
};

function normalize(s) {
  return String(s || "")
    .toLowerCase()
    .replace(/[._/]/g, "-")
    .replace(/[^a-z0-9-]/g, "")
    .replace(/-+/g, "-")
    .replace(/^-|-$/g, "");
}

function stripTail(s) {
  return s
    .replace(/-(latest|preview|exp|experimental|stable)$/g, "")
    .replace(/-\d{8}$/g, "")
    .replace(/-\d{6}$/g, "")
    .replace(/-\d{4}-\d{2}-\d{2}$/g, "");
}

function parseZen(m) {
  const [vendor, ...rest] = String(m.id).split("/");
  const model = rest.join("/");
  const norm = normalize(model);
  return {
    id: m.id,
    vendor,
    model,
    norm,
    stripped: stripTail(norm),
    context: m.context_length,
    reasoning: m.capabilities?.reasoning,
    prompt: m.pricings?.prompt?.[0],
    completion: m.pricings?.completion?.[0],
  };
}

function jaccard(a, b) {
  const A = new Set(a.split("-").filter(Boolean));
  const B = new Set(b.split("-").filter(Boolean));
  let inter = 0;
  for (const x of A) if (B.has(x)) inter += 1;
  return inter / (new Set([...A, ...B]).size || 1);
}

function matchWithCandidates(provider, modelName, zenModels) {
  const aliases = PROVIDER_OWNED_BY[provider] || [provider];
  const pool = zenModels.filter((z) => aliases.includes(z.vendor));
  const localNorm = normalize(modelName);
  const localStripped = stripTail(localNorm);

  const exact = pool.filter((z) => z.model === modelName);
  if (exact.length === 1) return { matched: exact[0], tier: "exact", candidates: exact };

  const norm = pool.filter((z) => z.norm === localNorm);
  if (norm.length === 1) return { matched: norm[0], tier: "normalized", candidates: norm };

  const stripped = pool.filter((z) => z.stripped === localStripped);
  if (stripped.length === 1) return { matched: stripped[0], tier: "stripped", candidates: stripped };

  const ranked = pool
    .map((z) => ({ z, score: jaccard(localNorm, z.norm) }))
    .filter((x) => x.score >= 0.25)
    .sort((a, b) => b.score - a.score)
    .slice(0, 5)
    .map((x) => x.z);

  if (ranked.length === 1) return { matched: ranked[0], tier: "similar", candidates: ranked };
  return { matched: null, tier: ranked.length ? "ambiguous" : "none", candidates: ranked };
}

function fmt(v) {
  if (v === undefined) return "(缺省)";
  return `\`${JSON.stringify(v)}\``;
}

function quoteCsv(arr) {
  if (!arr.length) return "(none)";
  return arr.map((x) => `\`${x}\``).join("、");
}

function buildRows(doc, model, match) {
  const rows = [];
  const z = match.matched;
  const currency = doc.priceCurrency || "USD";

  rows.push({
    field: "modelName",
    current: model.modelName,
    suggested: z ? z.model : model.modelName,
    decision: z && match.tier === "exact" ? "保持" : "待确认",
    reason: z ? `ZenMux匹配(${match.tier}): ${z.id}` : "ZenMux无稳定匹配",
  });

  rows.push({
    field: "displayName",
    current: model.displayName,
    suggested: model.displayName,
    decision: "保持",
    reason: "展示字段，需按产品命名策略",
  });

  rows.push({
    field: "serviceType",
    current: model.serviceType,
    suggested: model.serviceType,
    decision: "保持",
    reason: "服务路由字段，优先本项目约定",
  });

  const trustedContextMatch = ["exact", "normalized", "stripped"].includes(match.tier);
  if (z && typeof z.context === "number" && trustedContextMatch) {
    const ratio = typeof model.contextWindow === "number" ? Math.abs(model.contextWindow - z.context) / Math.max(model.contextWindow, z.context) : 1;
    const sameScale = ratio <= 0.03;
    rows.push({
      field: "contextWindow",
      current: model.contextWindow,
      suggested: sameScale ? model.contextWindow : z.context,
      decision: sameScale ? "保持" : "建议修改",
      reason: sameScale ? `ZenMux(${z.id})口径近似（≤3%）` : `ZenMux(${z.id})提供明确context_length=${z.context}`,
    });
  } else {
    rows.push({
      field: "contextWindow",
      current: model.contextWindow,
      suggested: model.contextWindow,
      decision: "待确认",
      reason: z && typeof z.context === "number"
        ? `ZenMux命中(${match.tier})但匹配置信不足，不直接覆盖context`
        : "ZenMux无context可用，需官方规格页确认",
    });
  }

  rows.push({
    field: "maxOutputTokens",
    current: model.maxOutputTokens,
    suggested: model.maxOutputTokens,
    decision: "待确认",
    reason: "ZenMux列表未提供统一max output字段，需官方模型详情页",
  });

  const canPriceFromZen = z && z.prompt && z.completion && z.prompt.unit === "perMTokens" && z.prompt.currency === "USD" && z.completion.unit === "perMTokens" && z.completion.currency === "USD";
  if (canPriceFromZen && currency === "USD" && (match.tier === "exact" || match.tier === "normalized")) {
    rows.push({
      field: "inputPrice",
      current: model.inputPrice,
      suggested: z.prompt.value,
      decision: model.inputPrice === z.prompt.value ? "保持" : "建议修改",
      reason: `ZenMux(${z.id}) prompt=${z.prompt.value} USD/MTokens`,
    });
    rows.push({
      field: "outputPrice",
      current: model.outputPrice,
      suggested: z.completion.value,
      decision: model.outputPrice === z.completion.value ? "保持" : "建议修改",
      reason: `ZenMux(${z.id}) completion=${z.completion.value} USD/MTokens`,
    });
  } else {
    rows.push({
      field: "inputPrice",
      current: model.inputPrice,
      suggested: model.inputPrice,
      decision: "待确认",
      reason: canPriceFromZen
        ? `本文件币种为${currency}，ZenMux价格为USD，需官方价格页复核`
        : "ZenMux无稳定价格可用，需官方价格页复核",
    });
    rows.push({
      field: "outputPrice",
      current: model.outputPrice,
      suggested: model.outputPrice,
      decision: "待确认",
      reason: canPriceFromZen
        ? `本文件币种为${currency}，ZenMux价格为USD，需官方价格页复核`
        : "ZenMux无稳定价格可用，需官方价格页复核",
    });
  }

  rows.push({
    field: "capabilities",
    current: model.capabilities,
    suggested: model.capabilities,
    decision: typeof z?.reasoning === "boolean" ? "待确认" : "保持",
    reason: typeof z?.reasoning === "boolean"
      ? `ZenMux给出reasoning=${z.reasoning}，但capabilities是项目语义字段，需官方能力说明复核`
      : "ZenMux无明确能力映射差异",
  });

  rows.push({
    field: "defaultTemperature",
    current: model.defaultTemperature,
    suggested: model.defaultTemperature,
    decision: "待确认",
    reason: "官方通常不提供默认采样参数",
  });

  rows.push({
    field: "defaultTopP",
    current: model.defaultTopP,
    suggested: model.defaultTopP,
    decision: "待确认",
    reason: "官方通常不提供默认采样参数",
  });

  rows.push({
    field: "extra",
    current: model.extra,
    suggested: model.extra,
    decision: "待确认",
    reason: "扩展字段为本地schema，需业务侧定义",
  });

  return rows;
}

async function listFiles() {
  const out = [];
  for (const dir of TARGET_DIRS) {
    for (const f of await fs.readdir(dir)) {
      if (!f.endsWith(".json") || f === "_index.json") continue;
      out.push(path.join(dir, f));
    }
  }
  return out.sort();
}

function folderByJson(relPath) {
  const base = path.basename(relPath, ".json");
  return path.join(OUT_ROOT, base);
}

async function main() {
  const zraw = JSON.parse(await fs.readFile(ZENMUX_FILE, "utf8"));
  const zen = (zraw.data || []).map(parseZen);
  const files = await listFiles();
  const generatedAt = new Date().toISOString();
  await fs.mkdir(OUT_ROOT, { recursive: true });

  const global = [];

  for (const abs of files) {
    const rel = path.relative(ROOT, abs);
    const doc = JSON.parse(await fs.readFile(abs, "utf8"));
    const provider = doc.provider || path.basename(abs, ".json");
    const models = Array.isArray(doc.models) ? doc.models : [];
    const outDir = folderByJson(rel);
    await fs.mkdir(outDir, { recursive: true });

    const detailed = [];
    const unresolved = [];
    detailed.push(`# 详细字段取值表 - ${rel}`);
    detailed.push("");
    detailed.push(`- provider: \`${provider}\``);
    detailed.push(`- priceCurrency: \`${doc.priceCurrency || "USD"}\``);
    detailed.push(`- generatedAt: \`${generatedAt}\``);
    detailed.push("");
    detailed.push("## 来源");
    detailed.push("");
    for (const d of OFFICIAL_DOCS[provider] || ["(待补充provider官方文档)"]) detailed.push(`- ${d}`);
    detailed.push("- https://zenmux.ai/models");
    detailed.push("- https://zenmux.ai/api/v1/models");
    detailed.push("");

    unresolved.push(`# 未确认字段报告 - ${rel}`);
    unresolved.push("");
    unresolved.push(`- provider: \`${provider}\``);
    unresolved.push(`- generatedAt: \`${generatedAt}\``);
    unresolved.push("");

    for (const m of models) {
      const match = matchWithCandidates(provider, m.modelName, zen);
      const rows = buildRows(doc, m, match);
      detailed.push(`## ${m.modelName}`);
      detailed.push("");
      detailed.push(`- ZenMux匹配级别: \`${match.tier}\``);
      if (match.matched) detailed.push(`- ZenMux命中: \`${match.matched.id}\``);
      detailed.push(`- ZenMux候选: ${quoteCsv((match.candidates || []).map((x) => x.id))}`);
      detailed.push("");
      detailed.push("| 字段 | 当前值 | 建议值 | 结论 | 依据/说明 |");
      detailed.push("|---|---|---|---|---|");
      for (const r of rows) {
        detailed.push(`| \`${r.field}\` | ${fmt(r.current)} | ${fmt(r.suggested)} | ${r.decision} | ${r.reason} |`);
      }
      detailed.push("");

      const pending = rows.filter((r) => r.decision === "待确认");
      if (pending.length) {
        unresolved.push(`## ${m.modelName}`);
        unresolved.push("");
        for (const p of pending) unresolved.push(`- \`${p.field}\`: ${p.reason}`);
        unresolved.push("");
      }
    }

    if (unresolved.length === 3) {
      unresolved.push("- (none)");
      unresolved.push("");
    }

    const detailedFile = path.join(outDir, "详细字段取值表.md");
    const unresolvedFile = path.join(outDir, "未确认字段报告.md");
    await fs.writeFile(detailedFile, `${detailed.join("\n")}\n`, "utf8");
    await fs.writeFile(unresolvedFile, `${unresolved.join("\n")}\n`, "utf8");

    global.push({
      file: rel,
      folder: path.relative(ROOT, outDir),
      detailed: path.relative(ROOT, detailedFile),
      unresolved: path.relative(ROOT, unresolvedFile),
      modelCount: models.length,
    });
  }

  await fs.writeFile(
    path.join(OUT_ROOT, "目录索引.json"),
    `${JSON.stringify(global, null, 2)}\n`,
    "utf8"
  );
  console.log(JSON.stringify({ files: global.length, out: OUT_ROOT }, null, 2));
}

await main();
