import type { LocalizedText } from "./vocabulary";
import type { ModelFact, ProviderFact } from "../facts";

export const MODELS_COPY = {
  metaTitle: { en: "Models and providers · Codewhale", zh: "模型与提供商 · Codewhale" },
  metaDescription: { en: "Connect DeepSeek, Anthropic, OpenAI, Google, xAI or any OpenAI-compatible endpoint, then switch provider and model whenever the work changes.", zh: "连接 DeepSeek、Anthropic、OpenAI、Google、xAI 或任何兼容 OpenAI 的端点,工作变化时随时切换提供商和模型。" },
  title: { en: "Models and providers", zh: "模型与提供商" },
  lead: { en: "Codewhale works with the model providers you already pay for, and you can change the provider or model at any point in a session.", zh: "Codewhale 使用你已有的模型提供商,会话中随时可以更换提供商或模型。" },
  latestTitle: { en: "Recently added", zh: "最近加入" },
  latestLead: { en: "The latest addition to the catalog from each first-party provider. Dates are when Codewhale added the model, not the maker's release date.", zh: "每家一方提供商最近加入目录的模型。日期为 Codewhale 加入该模型的时间,不是厂商的发布日期。" },
  latestSince: { en: "In Codewhale since {date}", zh: "{date} 起已加入 Codewhale" },
  setupTitle: { en: "Connect a model in three steps", zh: "三步连接模型" },
  setupLead: { en: "The same steps work for every provider on this page.", zh: "本页的所有提供商都适用同样的步骤。" },
  steps: [
    {
      title: { en: "Save a key", zh: "保存密钥" },
      detail: { en: "Run `codewhale auth set --provider deepseek`, or the id of another provider from the table below, and paste the key.", zh: "运行 `codewhale auth set --provider deepseek`(或下表中其他提供商的 ID)并粘贴密钥。" },
    },
    {
      title: { en: "Pick a provider and model", zh: "选择提供商和模型" },
      detail: { en: "In a session, choose the provider with `/provider` and the model with `/model`.", zh: "在会话中,用 `/provider` 选择提供商,用 `/model` 选择模型。" },
    },
    {
      title: { en: "Switch any time", zh: "随时切换" },
      detail: { en: "Run `/provider` or `/model` again when the task changes. Your session stays open.", zh: "任务变化时再次运行 `/provider` 或 `/model`,会话保持不变。" },
    },
  ],
  routeDeepseekTitle: { en: "DeepSeek (default)", zh: "DeepSeek(默认)" },
  routeDeepseekBody: { en: "Save a DeepSeek key and start working. The default model is {model}.", zh: "保存 DeepSeek 密钥即可开始工作,默认模型为 {model}。" },
  routeFirstPartyTitle: { en: "Anthropic, OpenAI, Google and more", zh: "Anthropic、OpenAI、Google 等" },
  routeFirstPartyBody: { en: "Each provider reads its own credential setting.", zh: "每家提供商读取各自的凭据设置。" },
  routeCompatTitle: { en: "Any OpenAI-compatible endpoint", zh: "任何兼容 OpenAI 的端点" },
  routeCompatBody: { en: "Point Codewhale at any gateway or self-hosted server that speaks the OpenAI Chat Completions API, such as OrcaRouter or llama.cpp.", zh: "让 Codewhale 指向任何支持 OpenAI Chat Completions API 的网关或自托管服务,例如 OrcaRouter 或 llama.cpp。" },
  routeCompatConfig: { en: "Add a `[providers.<name>]` table to config.toml with `kind = \"openai-compatible\"` and its `base_url`, then select it with `provider = \"<name>\"`.", zh: "在 config.toml 中添加 `[providers.<name>]` 表,设置 `kind = \"openai-compatible\"` 与 `base_url`,再用 `provider = \"<name>\"` 选中它。" },
  providerDocs: { en: "Provider setup guides", zh: "提供商配置指南" },
  modelsTitle: { en: "Full model catalog", zh: "完整模型目录" },
  modelsLead: { en: "{count} models in the catalog of the published release, grouped by maker. A model reached through several providers is listed once.", zh: "已发布版本目录中的 {count} 个模型,按厂商分组。经多家提供商提供的同一模型只列一次。" },
  models: { en: "{count} models", zh: "{count} 个模型" },
  providerCount: { en: "Show all {count} provider routes", zh: "显示全部 {count} 条提供商路由" },
  colModel: { en: "Model", zh: "模型" },
  colContext: { en: "Context", zh: "上下文" },
  colAdded: { en: "In Codewhale since", zh: "加入 Codewhale 时间" },
  colKind: { en: "Type", zh: "类型" },
  reasoning: { en: "reasoning", zh: "推理" },
  listTitle: { en: "Providers and credential settings", zh: "提供商与凭据设置" },
  provider: { en: "Provider", zh: "提供商" },
  id: { en: "Provider ID", zh: "提供商 ID" },
  credential: { en: "Credential setting", zh: "凭据设置" },
  missing: { en: "Missing your provider?", zh: "没有找到你的提供商?" },
  request: { en: "Request support", zh: "申请支持" },
  otherMakers: { en: "Other makers", zh: "其他厂商" },
} satisfies Record<string, LocalizedText | { title: LocalizedText; detail: LocalizedText }[]>;

export const FIRST_PARTY_PROVIDER_IDS = [
  "deepseek", "anthropic", "openai", "openai-codex", "google", "xai",
  "mistral", "moonshot", "zai", "minimax", "meta", "xiaomi-mimo", "stepfun",
];

export const ROUTE_PROVIDERS = [
  { id: "anthropic", label: "Anthropic" },
  { id: "openai", label: "OpenAI" },
  { id: "google", label: "Google" },
  { id: "xai", label: "xAI" },
];

export const MODEL_MAKERS: { key: string; label: string; providers: string[] }[] = [
  { key: "anthropic", label: "Anthropic", providers: ["Anthropic"] },
  { key: "openai", label: "OpenAI", providers: ["OpenAI", "OpenAI Codex"] },
  { key: "google", label: "Google", providers: ["Google"] },
  { key: "deepseek", label: "DeepSeek", providers: ["DeepSeek"] },
  { key: "xai", label: "xAI", providers: ["xAI"] },
  { key: "moonshot", label: "Moonshot/Kimi", providers: ["Moonshot/Kimi"] },
  { key: "qwen", label: "Qwen", providers: ["Qwen"] },
  { key: "zai", label: "Z.ai", providers: ["Z.ai"] },
  { key: "minimax", label: "MiniMax", providers: ["MiniMax"] },
  { key: "mistral", label: "Mistral", providers: ["Mistral"] },
  { key: "meta", label: "Meta", providers: ["Meta"] },
  { key: "xiaomi", label: "Xiaomi", providers: ["Xiaomi MiMo"] },
  { key: "stepfun", label: "StepFun", providers: ["StepFun"] },
];

const FIRST_PARTY_MAKERS = ["anthropic", "openai", "google", "deepseek", "xai"];
const PREVIEW_ID = /(?:^|[-/])(exp|preview)(?:-|$)/;
const SMALL_TIER_ID = /(?:^|[-/])(mini|nano|lite|flash|haiku|small|fast)(?:-|$)/;

const byNewest = (a: ModelFact, b: ModelFact) =>
  (b.addedAt ?? "").localeCompare(a.addedAt ?? "") ||
  b.id.localeCompare(a.id, "en", { numeric: true });

const byNewestFlagship = (a: ModelFact, b: ModelFact) =>
  (b.addedAt ?? "").localeCompare(a.addedAt ?? "") ||
  Number(SMALL_TIER_ID.test(a.id)) - Number(SMALL_TIER_ID.test(b.id)) ||
  b.id.localeCompare(a.id, "en", { numeric: true });

export function makerKeyOf(model: ModelFact): string {
  const maker = MODEL_MAKERS.find((m) => model.provider !== null && m.providers.includes(model.provider));
  return maker?.key ?? "other";
}

export function groupModelsByMaker(models: ModelFact[]) {
  const groups = [...MODEL_MAKERS.map((m) => ({ key: m.key, label: m.label })), { key: "other", label: "" }]
    .map((g) => ({ ...g, models: models.filter((m) => makerKeyOf(m) === g.key).sort(byNewest) }));
  return groups.filter((g) => g.models.length > 0);
}

export function latestFirstPartyModels(models: ModelFact[]) {
  return FIRST_PARTY_MAKERS
    .map((key) => ({
      key,
      label: MODEL_MAKERS.find((m) => m.key === key)?.label ?? key,
      model: models
        .filter((m) => makerKeyOf(m) === key && m.addedAt !== null && !PREVIEW_ID.test(m.id))
        .sort(byNewestFlagship)[0],
    }))
    .flatMap((entry) => (entry.model ? [{ ...entry, model: entry.model }] : []));
}

export function splitCode(text: string): { code: boolean; text: string }[] {
  return text.split("`").map((part, i) => ({ code: i % 2 === 1, text: part })).filter((p) => p.text);
}

export function sortProvidersFirstPartyFirst(providers: ProviderFact[]): ProviderFact[] {
  const rank = (p: ProviderFact) => {
    const i = FIRST_PARTY_PROVIDER_IDS.indexOf(p.id);
    return i === -1 ? FIRST_PARTY_PROVIDER_IDS.length : i;
  };
  return [...providers].sort((a, b) => rank(a) - rank(b) || a.label.localeCompare(b.label));
}

export function formatModelTokens(n: number | null): string {
  if (n === null) return "—";
  if (n >= 1_000_000) {
    const m = n / 1_000_000;
    return `${Number.isInteger(m) ? m : m.toFixed(2)}M`;
  }
  if (n >= 1_000) return `${Math.round(n / 1_000)}K`;
  return String(n);
}

export function formatModelDate(iso: string | null, dateLocale: string): string {
  if (!iso) return "—";
  const d = new Date(`${iso}T00:00:00Z`);
  if (!Number.isFinite(d.getTime())) return "—";
  return new Intl.DateTimeFormat(dateLocale, {
    dateStyle: "medium",
    timeZone: "UTC",
  }).format(d);
}