// 运维控制台 → Gateway 控制面 API（带 JWT 鉴权；/admin 受 RequireAdmin 保护）。
export const GATEWAY: string =
  (import.meta.env.VITE_GATEWAY as string | undefined) ?? "http://localhost:8080";
const ADMIN = `${GATEWAY}/api/v1/admin`;

// ---- 鉴权（JWT，存 localStorage）----
const TOKEN_KEY = "sdx_admin_token";
let token = typeof localStorage !== "undefined" ? localStorage.getItem(TOKEN_KEY) ?? "" : "";

export function setToken(t: string): void {
  token = t;
  try {
    localStorage.setItem(TOKEN_KEY, t);
  } catch {
    /* ignore */
  }
}
export function clearToken(): void {
  token = "";
  try {
    localStorage.removeItem(TOKEN_KEY);
  } catch {
    /* ignore */
  }
}
export function getToken(): string {
  return token;
}

function authHeaders(json = false): Record<string, string> {
  const h: Record<string, string> = token ? { Authorization: `Bearer ${token}` } : {};
  if (json) h["Content-Type"] = "application/json";
  return h;
}

// guard 在 401(未登录) 时清令牌并广播登出（403=已登录但非管理员，照常抛错）。
function guard(res: Response): Response {
  if (res.status === 401) {
    clearToken();
    if (typeof window !== "undefined") window.dispatchEvent(new Event("sdx:logout"));
  }
  return res;
}

export interface AuthUser {
  id: string;
  email: string;
  name?: string;
}

export async function login(email: string, password: string): Promise<AuthUser> {
  const res = await fetch(`${GATEWAY}/api/v1/auth/login`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ email, password }),
  });
  const data = (await res.json()) as { token?: string; user?: AuthUser; error?: string };
  if (!res.ok || !data.token || !data.user) throw new Error(data.error ?? `登录失败: ${res.status}`);
  setToken(data.token);
  return data.user;
}

export async function me(): Promise<AuthUser | null> {
  if (!token) return null;
  const res = await fetch(`${GATEWAY}/api/v1/auth/me`, { headers: authHeaders() });
  if (!res.ok) {
    clearToken();
    return null;
  }
  return ((await res.json()) as { user?: AuthUser }).user ?? null;
}

// ---- 模型配置（id 为雪花字符串）----
export type Kind = "chat" | "embedding";

export interface Model {
  id: string;
  kind: Kind;
  provider: string;
  base_url: string;
  api_key: string; // 列表里是脱敏值
  model: string;
  active: boolean;
}

export interface ModelInput {
  id?: string;
  kind: Kind;
  provider: string;
  base_url: string;
  api_key: string;
  model: string;
}

export async function listModels(kind: Kind): Promise<Model[]> {
  const res = guard(await fetch(`${ADMIN}/models?kind=${kind}`, { headers: authHeaders() }));
  if (!res.ok) throw new Error(`list failed: ${res.status}`);
  return ((await res.json()) as { models: Model[] }).models;
}

export async function saveModel(m: ModelInput): Promise<string> {
  const res = guard(await fetch(`${ADMIN}/models`, { method: "POST", headers: authHeaders(true), body: JSON.stringify(m) }));
  const data = (await res.json()) as { id?: string; error?: string };
  if (!res.ok) throw new Error(data.error ?? `save failed: ${res.status}`);
  return data.id ?? "";
}

export async function setActive(id: string): Promise<void> {
  const res = guard(await fetch(`${ADMIN}/models/${id}/active`, { method: "POST", headers: authHeaders() }));
  if (!res.ok) throw new Error(`activate failed: ${res.status}`);
}

export async function deleteModel(id: string): Promise<void> {
  const res = guard(await fetch(`${ADMIN}/models/${id}`, { method: "DELETE", headers: authHeaders() }));
  if (!res.ok) throw new Error(`delete failed: ${res.status}`);
}

export async function testModel(m: ModelInput): Promise<{ ok: boolean; message: string }> {
  const res = guard(await fetch(`${ADMIN}/models/test`, { method: "POST", headers: authHeaders(true), body: JSON.stringify(m) }));
  return (await res.json()) as { ok: boolean; message: string };
}

// ---- 计价（token↔真钱，按模型分输入/输出）----
export interface Pricing {
  model_id: string;
  input_per_1k: number;
  output_per_1k: number;
  credit_weight: number; // 每模型积分权重（0/缺省=1.0，不加权）
  currency: string;
}

export async function listPricing(): Promise<Pricing[]> {
  const res = guard(await fetch(`${ADMIN}/pricing`, { headers: authHeaders() }));
  if (!res.ok) throw new Error(`list pricing failed: ${res.status}`);
  return ((await res.json()) as { pricing: Pricing[] }).pricing ?? [];
}

export async function savePricing(p: Pricing): Promise<void> {
  const res = guard(await fetch(`${ADMIN}/pricing`, { method: "PUT", headers: authHeaders(true), body: JSON.stringify(p) }));
  if (!res.ok) {
    const d = (await res.json().catch(() => ({}))) as { error?: string };
    throw new Error(d.error ?? `save pricing failed: ${res.status}`);
  }
}

// —— 全局计费规则（token→积分汇率 + 硬拦截开关）——
export async function getBillingConfig(): Promise<{ tokens_per_credit: number; credit_enforce: boolean }> {
  const res = guard(await fetch(`${ADMIN}/billing-config`, { headers: authHeaders() }));
  if (!res.ok) throw new Error(`billing config failed: ${res.status}`);
  const d = (await res.json()) as { tokens_per_credit?: string; credit_enforce?: boolean };
  return { tokens_per_credit: Number(d.tokens_per_credit) || 1000, credit_enforce: !!d.credit_enforce }; // 空/未设 → 回退默认
}

export async function saveBillingConfig(tokensPerCredit: number, creditEnforce: boolean): Promise<void> {
  const res = guard(
    await fetch(`${ADMIN}/billing-config`, {
      method: "PUT",
      headers: authHeaders(true),
      body: JSON.stringify({ tokens_per_credit: tokensPerCredit, credit_enforce: creditEnforce }),
    }),
  );
  if (!res.ok) {
    const d = (await res.json().catch(() => ({}))) as { error?: string };
    throw new Error(d.error ?? `save billing config failed: ${res.status}`);
  }
}

// —— 充值/发放积分（正=充值，负=校正）——
export async function grantCredits(tenantId: string, credits: number, memo: string): Promise<number> {
  const res = guard(
    await fetch(`${ADMIN}/credits/grant`, { method: "POST", headers: authHeaders(true), body: JSON.stringify({ tenant_id: tenantId, credits, memo }) }),
  );
  const d = (await res.json().catch(() => ({}))) as { balance_micro?: number; error?: string };
  if (!res.ok) throw new Error(d.error ?? `grant failed: ${res.status}`);
  return d.balance_micro ?? 0;
}

// ---- 充值渠道（P5.1）：积分包配置 + 兑换码 ----
export interface CreditPack {
  id: string;
  name: string;
  credits_micro: number;
  price_fen: number;
  active: boolean;
  sort: number;
}

export async function adminPacks(): Promise<CreditPack[]> {
  const res = guard(await fetch(`${ADMIN}/packs`, { headers: authHeaders() }));
  const d = (await res.json().catch(() => ({}))) as { packs?: CreditPack[]; error?: string };
  if (!res.ok) throw new Error(d.error ?? `packs failed: ${res.status}`);
  return d.packs ?? [];
}

// savePack：id 空 = 新建。credits 单位为「积分」（面向人，服务端转 micro）。
export async function savePack(p: { id?: string; name: string; credits: number; price_fen: number; active: boolean; sort: number }): Promise<void> {
  const res = guard(await fetch(`${ADMIN}/packs`, { method: "PUT", headers: authHeaders(true), body: JSON.stringify(p) }));
  const d = (await res.json().catch(() => ({}))) as { error?: string };
  if (!res.ok) throw new Error(d.error ?? `save failed: ${res.status}`);
}

export interface RedeemCodeRow {
  id: string;
  code: string;
  credits_micro: number;
  status: string; // unused / used
  used_tenant: string;
  used_at: string | null;
  memo: string;
  created_at: string;
}

export async function genRedeemCodes(credits: number, count: number, memo: string): Promise<string[]> {
  const res = guard(
    await fetch(`${ADMIN}/redeem-codes`, { method: "POST", headers: authHeaders(true), body: JSON.stringify({ credits, count, memo }) }),
  );
  const d = (await res.json().catch(() => ({}))) as { codes?: string[]; error?: string };
  if (!res.ok) throw new Error(d.error ?? `生成失败: ${res.status}`);
  return d.codes ?? [];
}

export async function listRedeemCodes(): Promise<RedeemCodeRow[]> {
  const res = guard(await fetch(`${ADMIN}/redeem-codes`, { headers: authHeaders() }));
  const d = (await res.json().catch(() => ({}))) as { codes?: RedeemCodeRow[]; error?: string };
  if (!res.ok) throw new Error(d.error ?? `codes failed: ${res.status}`);
  return d.codes ?? [];
}

// ---- 微信支付配置（DB 存储、热生效；APIv3 密钥密文入库、不回显）----
export interface WechatPayConfig {
  mchid: string;
  cert_serial: string;
  private_key_path: string;
  public_key_path: string; // 微信支付公钥（2024 起新商户体系；本商户 2025-09 开户）
  public_key_id: string; // PUB_KEY_ID_ 开头
  appid: string;
  notify_url: string;
  has_apiv3_key: boolean;
}

export async function getWechatPay(): Promise<{ config: WechatPayConfig; enabled: boolean; reason: string }> {
  const res = guard(await fetch(`${ADMIN}/payment/wechat`, { headers: authHeaders() }));
  const d = (await res.json().catch(() => ({}))) as { config?: WechatPayConfig; enabled?: boolean; reason?: string; error?: string };
  if (!res.ok) throw new Error(d.error ?? `load failed: ${res.status}`);
  return { config: d.config!, enabled: !!d.enabled, reason: d.reason ?? "" };
}

// saveWechatPay：apiv3_key 传空串 = 沿用已保存的密钥。返回热重载后的渠道状态。
export async function saveWechatPay(body: {
  mchid: string;
  cert_serial: string;
  private_key_path: string;
  public_key_path: string;
  public_key_id: string;
  apiv3_key: string;
  appid: string;
  notify_url: string;
}): Promise<{ enabled: boolean; reason: string }> {
  const res = guard(await fetch(`${ADMIN}/payment/wechat`, { method: "PUT", headers: authHeaders(true), body: JSON.stringify(body) }));
  const d = (await res.json().catch(() => ({}))) as { enabled?: boolean; reason?: string; error?: string };
  if (!res.ok) throw new Error(d.error ?? `save failed: ${res.status}`);
  return { enabled: !!d.enabled, reason: d.reason ?? "" };
}

// ---- 充值订单流 + 对账（P5.3）----
export interface PayOrder {
  id: string;
  tenant_id: string;
  tenant_name: string;
  amount_fen: number;
  credits_micro: number;
  channel: string;
  status: string;
  created_at: string;
}
export interface OrderStats {
  pending: number;
  paid: number;
  expired: number;
  paid_fen_total: number;
}
export interface ReconcileDiff {
  order_id: string;
  tenant_id: string;
  credits_micro: number;
  issue: string;
}

export async function adminOrders(status = ""): Promise<{ orders: PayOrder[]; stats: OrderStats }> {
  const q = status ? `?status=${status}` : "";
  const res = guard(await fetch(`${ADMIN}/orders${q}`, { headers: authHeaders() }));
  const d = (await res.json().catch(() => ({}))) as { orders?: PayOrder[]; stats?: OrderStats; error?: string };
  if (!res.ok) throw new Error(d.error ?? `orders failed: ${res.status}`);
  return { orders: d.orders ?? [], stats: d.stats ?? { pending: 0, paid: 0, expired: 0, paid_fen_total: 0 } };
}

export async function adminReconcile(): Promise<{ diffs: ReconcileDiff[]; ok: boolean }> {
  const res = guard(await fetch(`${ADMIN}/orders/reconcile`, { headers: authHeaders() }));
  const d = (await res.json().catch(() => ({}))) as { diffs?: ReconcileDiff[]; ok?: boolean; error?: string };
  if (!res.ok) throw new Error(d.error ?? `reconcile failed: ${res.status}`);
  return { diffs: d.diffs ?? [], ok: !!d.ok };
}

// ---- 自动评测观测（真数据，来自 sundynix_eval）----
export interface EvalDay {
  day: string; // YYYYMMDD
  avg_overall: number;
  avg_faithful: number;
  count: number;
  poor_count: number;
}
export interface EvalSummary {
  total: number;
  ok: number;
  warn: number;
  poor: number;
  corrected: number;
  avg_overall: number;
}
export interface PoorEval {
  task_id: string;
  tenant_name: string;
  owner: string;
  overall: number;
  rule: number;
  llm: number;
  faithful: number;
  level: string;
  reason: string;
  sources: number;
  corrected: boolean;
  created_at: string;
}

export async function adminEvals(days = 14): Promise<{ from: string; to: string; trend: EvalDay[]; summary: EvalSummary; poor: PoorEval[] }> {
  const res = guard(await fetch(`${ADMIN}/evals?days=${days}`, { headers: authHeaders() }));
  const d = (await res.json().catch(() => ({}))) as { from?: string; to?: string; trend?: EvalDay[]; summary?: EvalSummary; poor?: PoorEval[]; error?: string };
  if (!res.ok) throw new Error(d.error ?? `evals failed: ${res.status}`);
  return {
    from: d.from ?? "",
    to: d.to ?? "",
    trend: d.trend ?? [],
    summary: d.summary ?? { total: 0, ok: 0, warn: 0, poor: 0, corrected: 0, avg_overall: 0 },
    poor: d.poor ?? [],
  };
}

// ---- 输入护栏安全事件（真数据，来自 guardrail_event）----
export interface GuardrailEvent {
  id: string;
  actor: string;
  kind: string; // blocked（硬拦）/ suspect（灰区放行）
  reason: string;
  signals: string; // 命中软信号 JSON 数组字符串
  method: string;
  path: string;
  ip: string;
  at: string;
}

export async function guardrailEvents(limit = 100): Promise<GuardrailEvent[]> {
  const res = guard(await fetch(`${ADMIN}/guardrail-events?limit=${limit}`, { headers: authHeaders() }));
  const d = (await res.json().catch(() => ({}))) as { events?: GuardrailEvent[]; error?: string };
  if (!res.ok) throw new Error(d.error ?? `guardrail failed: ${res.status}`);
  return d.events ?? [];
}

// ---- 数据源清单（真数据，全平台知识库）----
export interface DatasourceKB {
  id: string;
  name: string;
  kind: string;
  tenant_id: string;
  tenant_name: string;
  owner: string;
  doc_count: number;
  total_words: number;
}

export async function adminDatasources(): Promise<{ counts: { users: number; kbs: number; docs: number }; datasources: DatasourceKB[] }> {
  const res = guard(await fetch(`${ADMIN}/datasources`, { headers: authHeaders() }));
  const d = (await res.json().catch(() => ({}))) as { counts?: { users: number; kbs: number; docs: number }; datasources?: DatasourceKB[]; error?: string };
  if (!res.ok) throw new Error(d.error ?? `datasources failed: ${res.status}`);
  return { counts: d.counts ?? { users: 0, kbs: 0, docs: 0 }, datasources: d.datasources ?? [] };
}

// gatewayOnline 用公开的 /healthz 探活（不受鉴权影响）。
export async function gatewayOnline(): Promise<boolean> {
  try {
    const res = await fetch(`${GATEWAY}/healthz`);
    return res.ok;
  } catch {
    return false;
  }
}

// —— 服务状态：基建 / 应用服务探活 + MCP 工具注册 ——
export interface StatusItem {
  name: string;
  up: boolean;
  detail?: string;
  latency_ms?: number;
}
export interface ToolInfo {
  name: string;
  cn: string;
  desc: string;
}
export interface ToolGroup {
  server: string;
  up: boolean;
  tools: ToolInfo[] | null;
}
export interface SystemStatus {
  checked_at: string;
  infra: StatusItem[];
  services: StatusItem[];
  tools: ToolGroup[];
}

export async function getStatus(): Promise<SystemStatus> {
  const res = guard(await fetch(`${ADMIN}/status`, { headers: authHeaders() }));
  if (!res.ok) throw new Error(`status failed: ${res.status}`);
  return (await res.json()) as SystemStatus;
}

// —— 工作台概览（仪表盘聚合）——
// /api/v1/stats/overview 在 RequireAuth 组下；Task/Eval 表无 owner，故任务/评测口径为全局。
export interface DayCount {
  key: string;
  count: number;
}
export interface RecentRun {
  task_id: string;
  status: string;
  detail: string;
  at: string;
}
export interface Overview {
  tasks_today: number;
  tasks_total: number;
  status_count: DayCount[]; // 近 7 天终态分布
  task_trend: DayCount[]; // 近 7 天每日任务数（仅含有任务的天）
  eval_avg: number;
  faithful_avg: number; // 仅有来源的评测；无来源时为 0
  eval_count: number;
  kb_docs: number;
  kb_count: number;
  tokens_today: number;
  daily_budget: number; // 0 = 不限额
  token_trend: DayCount[];
  recent_runs: RecentRun[];
  services: Record<string, boolean>;
}

export async function statsOverview(): Promise<Overview> {
  const res = guard(await fetch(`${GATEWAY}/api/v1/stats/overview`, { headers: authHeaders() }));
  if (!res.ok) throw new Error(`overview failed: ${res.status}`);
  return (await res.json()) as Overview;
}

// —— 管理端系统级聚合（控制塔口径，RequireAdmin）——
// 区别于 statsOverview（桌面端个人工作台）：这里一律全平台口径——全部用户/任务/评测/模型态/提示词态/健康。
// 单模型运行时健康态（failover 链上主/备各一条）。
export interface ModelHealthItem {
  provider: string;
  model: string;
  role: string; // primary / fallback
  state: string; // closed(在线) / open(熔断中) / half-open(半开探测) / single(无备用链)
  fails: number;
}

export interface AdminOverview {
  users: number;
  kb_count: number; // 全平台知识库数
  kb_docs: number; // 全平台文档数
  tasks_today: number;
  tasks_total: number;
  status_count: DayCount[];
  task_trend: DayCount[];
  eval_avg: number;
  faithful_avg: number;
  eval_count: number;
  models: {
    chat_count: number;
    embedding_count: number;
    active_chat: string;
    active_embedding: string;
    fallbacks: number;
    health: ModelHealthItem[]; // 运行时每模型 failover/熔断态（来自 dispatcher）
  };
  prompts: { managed: number; overrides: number };
  services: Record<string, boolean>;
  checked_at: string;
}

export async function adminOverview(): Promise<AdminOverview> {
  const res = guard(await fetch(`${ADMIN}/overview`, { headers: authHeaders() }));
  if (!res.ok) throw new Error(`admin overview failed: ${res.status}`);
  return (await res.json()) as AdminOverview;
}

// —— 审计 / 安全事件（敏感操作留痕 + 护栏命中）——
export interface AuditEntry {
  id: string;
  actor: string; // 操作者 uid
  action: string; // POST / PUT / DELETE / PATCH
  route: string;
  path: string;
  status: number;
  ip: string;
  detail: string;
  at: string;
}
export interface GuardrailEventItem {
  id: string;
  actor: string;
  kind: string; // blocked / suspect
  reason: string;
  signals: string; // JSON 数组字符串
  method: string;
  path: string;
  ip: string;
  at: string;
}

export async function listAudit(limit = 50, offset = 0): Promise<AuditEntry[]> {
  const res = guard(await fetch(`${ADMIN}/audit?limit=${limit}&offset=${offset}`, { headers: authHeaders() }));
  if (!res.ok) throw new Error(`audit failed: ${res.status}`);
  return ((await res.json()) as { logs?: AuditEntry[] }).logs ?? [];
}

export async function listGuardrailEvents(limit = 50, offset = 0): Promise<GuardrailEventItem[]> {
  const res = guard(await fetch(`${ADMIN}/guardrail-events?limit=${limit}&offset=${offset}`, { headers: authHeaders() }));
  if (!res.ok) throw new Error(`guardrail events failed: ${res.status}`);
  return ((await res.json()) as { events?: GuardrailEventItem[] }).events ?? [];
}

// —— Prompt 控制面（建版本 → 激活 → 控制面热下发各服务，不重启即生效）——
// 注意：prompt 路由在 RequireAuth 组下（/api/v1/prompts），不在 /admin 前缀内。
const PROMPTS = `${GATEWAY}/api/v1/prompts`;

export interface PromptVersion {
  key: string;
  version: number;
  active: boolean;
  note: string;
  content: string;
}

export interface PromptListResp {
  keys: string[]; // 平台受管的全部可配 key（即便还没建过版本）
  versions: PromptVersion[];
}

// 某 key 的聚合视图：版本按号倒序、当前激活版（无则 null = 回退代码内置默认）。
export interface PromptGroup {
  key: string;
  versions: PromptVersion[];
  active: PromptVersion | null;
}

// groupPrompts 把扁平的 {keys, versions} 归并成按 key 的分组（控制面页面的事实源）。
// 纯函数：保持 keys 的注册顺序，每组版本号倒序，挑出激活版。
export function groupPrompts(resp: PromptListResp): PromptGroup[] {
  const byKey = new Map<string, PromptVersion[]>();
  for (const k of resp.keys) byKey.set(k, []);
  for (const v of resp.versions) {
    if (!byKey.has(v.key)) byKey.set(v.key, []); // 容错：DB 有但 Known 未列的 key 也展示
    byKey.get(v.key)!.push(v);
  }
  return Array.from(byKey.entries()).map(([key, vs]) => {
    const versions = [...vs].sort((a, b) => b.version - a.version);
    return { key, versions, active: versions.find((v) => v.active) ?? null };
  });
}

export async function listPrompts(): Promise<PromptListResp> {
  const res = guard(await fetch(PROMPTS, { headers: authHeaders() }));
  if (!res.ok) throw new Error(`list prompts failed: ${res.status}`);
  const data = (await res.json()) as Partial<PromptListResp>;
  return { keys: data.keys ?? [], versions: data.versions ?? [] };
}

export async function createPromptVersion(key: string, content: string, note: string): Promise<number> {
  const res = guard(
    await fetch(`${PROMPTS}/version`, { method: "POST", headers: authHeaders(true), body: JSON.stringify({ key, content, note }) }),
  );
  const data = (await res.json()) as { version?: number; error?: string };
  if (!res.ok) throw new Error(data.error ?? `create version failed: ${res.status}`);
  return data.version ?? 0;
}

export async function activatePrompt(key: string, version: number): Promise<void> {
  const res = guard(
    await fetch(`${PROMPTS}/activate`, { method: "POST", headers: authHeaders(true), body: JSON.stringify({ key, version }) }),
  );
  const data = (await res.json()) as { error?: string; warn?: string };
  if (!res.ok) throw new Error(data.error ?? `activate failed: ${res.status}`);
  if (data.warn) throw new Error(data.warn); // 已激活但广播失败 → 当作错误提示运维
}

export async function deactivatePrompt(key: string): Promise<void> {
  const res = guard(await fetch(`${PROMPTS}/deactivate`, { method: "POST", headers: authHeaders(true), body: JSON.stringify({ key }) }));
  if (!res.ok) {
    const d = (await res.json().catch(() => ({}))) as { error?: string };
    throw new Error(d.error ?? `deactivate failed: ${res.status}`);
  }
}

// —— 用量 / 计费（SaaS P2 计量：token / 积分 / 成本，按租户）——
// 金额与积分均为「微单位」（×10⁻⁶）：展示时 ÷1e6。走 /admin/usage（系统级，跨租户）。
export interface UsageDay {
  day: string; // YYYYMMDD
  total_tok: number;
  credits_micro: number;
  cost_micros: number;
  task_count: number;
}
export interface UsageTenantSum {
  tenant_id: string;
  name: string;
  total_tok: number;
  credits_micro: number;
  cost_micros: number;
  task_count: number;
  balance_micro: number;
}
export interface UsageReport {
  from: string;
  to: string;
  tenant: string; // 空 = 全平台口径
  trend: UsageDay[];
  totals: { total_tok: number; credits_micro: number; cost_micros: number; task_count: number };
  balance_micro?: number; // 仅单租户口径
  tenants?: UsageTenantSum[]; // 仅全平台口径：各租户排行
}

export async function adminUsage(params?: { tenant?: string; from?: string; to?: string }): Promise<UsageReport> {
  const q = new URLSearchParams();
  if (params?.tenant) q.set("tenant", params.tenant);
  if (params?.from) q.set("from", params.from);
  if (params?.to) q.set("to", params.to);
  const qs = q.toString();
  const res = guard(await fetch(`${ADMIN}/usage${qs ? "?" + qs : ""}`, { headers: authHeaders() }));
  if (!res.ok) throw new Error(`usage failed: ${res.status}`);
  const d = (await res.json()) as UsageReport;
  return { ...d, trend: d.trend ?? [], tenants: d.tenants ?? [] }; // Go 空切片可能序列化为 null
}

// —— 多成员租户：租户目录 + 成员管理 ——
export interface TenantRow {
  id: string;
  name: string;
  slug: string;
  plan: string;
  status: string;
  credit_balance_micro: number;
  shared_billing: boolean;
  members: number;
}
export interface Member {
  user_id: string;
  email: string;
  name: string;
  role: string; // owner / admin / member / viewer / billing_admin
  status: string;
  joined_at: string;
}

export async function listTenants(): Promise<TenantRow[]> {
  const res = guard(await fetch(`${ADMIN}/tenants`, { headers: authHeaders() }));
  if (!res.ok) throw new Error(`list tenants failed: ${res.status}`);
  return ((await res.json()) as { tenants?: TenantRow[] }).tenants ?? [];
}

export async function createTenant(name: string, slug: string, ownerEmail?: string): Promise<void> {
  const res = guard(await fetch(`${ADMIN}/tenants`, { method: "POST", headers: authHeaders(true), body: JSON.stringify({ name, slug, owner_email: ownerEmail ?? "" }) }));
  const d = (await res.json().catch(() => ({}))) as { error?: string; warn?: string };
  if (!res.ok) throw new Error(d.error ?? `create tenant failed: ${res.status}`);
  if (d.warn) throw new Error(d.warn); // 租户已建但指定 owner 失败 → 当提示
}

export async function listMembers(tenantId: string): Promise<Member[]> {
  const res = guard(await fetch(`${ADMIN}/tenants/${tenantId}/members`, { headers: authHeaders() }));
  if (!res.ok) throw new Error(`list members failed: ${res.status}`);
  return ((await res.json()) as { members?: Member[] }).members ?? [];
}

export async function addMember(tenantId: string, email: string, role: string): Promise<void> {
  const res = guard(await fetch(`${ADMIN}/tenants/${tenantId}/members`, { method: "POST", headers: authHeaders(true), body: JSON.stringify({ email, role }) }));
  if (!res.ok) {
    const d = (await res.json().catch(() => ({}))) as { error?: string };
    throw new Error(d.error ?? `add member failed: ${res.status}`);
  }
}

export async function setMemberRole(tenantId: string, uid: string, role: string): Promise<void> {
  const res = guard(await fetch(`${ADMIN}/tenants/${tenantId}/members/${uid}`, { method: "PUT", headers: authHeaders(true), body: JSON.stringify({ role }) }));
  if (!res.ok) {
    const d = (await res.json().catch(() => ({}))) as { error?: string };
    throw new Error(d.error ?? `set role failed: ${res.status}`);
  }
}

export async function removeMember(tenantId: string, uid: string): Promise<void> {
  const res = guard(await fetch(`${ADMIN}/tenants/${tenantId}/members/${uid}`, { method: "DELETE", headers: authHeaders() }));
  if (!res.ok) {
    const d = (await res.json().catch(() => ({}))) as { error?: string };
    throw new Error(d.error ?? `remove member failed: ${res.status}`);
  }
}

export async function setSharedBilling(tenantId: string, on: boolean): Promise<void> {
  const res = guard(await fetch(`${ADMIN}/tenants/${tenantId}/shared-billing`, { method: "PUT", headers: authHeaders(true), body: JSON.stringify({ shared_billing: on }) }));
  if (!res.ok) {
    const d = (await res.json().catch(() => ({}))) as { error?: string };
    throw new Error(d.error ?? `set shared billing failed: ${res.status}`);
  }
}
