// 薄 Web 面的 Gateway 客户端。auth/tenant/usage 段与桌面端 lib/api.ts 同构（纯 fetch，
// token 走 localStorage `sdx_token`——localStorage 按 origin 隔离，与桌面端预览不冲突）；
// 成员管理/建组织打租户自助接口（/tenants/current/members、/me/tenants）。
export const GATEWAY: string =
  (import.meta.env.VITE_GATEWAY as string | undefined) ?? "http://localhost:8080";

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

// ---- JWT 令牌存储 ----
const TOKEN_KEY = "sdx_token";
let authToken: string = typeof localStorage !== "undefined" ? localStorage.getItem(TOKEN_KEY) ?? "" : "";

export function setToken(t: string): void {
  authToken = t;
  try {
    localStorage.setItem(TOKEN_KEY, t);
  } catch {
    /* 隐私模式忽略 */
  }
}
export function clearToken(): void {
  authToken = "";
  try {
    localStorage.removeItem(TOKEN_KEY);
  } catch {
    /* ignore */
  }
}

function bearer(): Record<string, string> {
  return authToken ? { Authorization: `Bearer ${authToken}` } : {};
}

// guard401 收到 401 清令牌并广播登出（App 监听后回登录页）。
function guard401(res: Response): Response {
  if (res.status === 401) {
    clearToken();
    if (typeof window !== "undefined") window.dispatchEvent(new Event("sdx:logout"));
  }
  return res;
}

// jsonOrThrow 统一「非 2xx 抛后端 error 文案」的小助手。
async function jsonOrThrow<T>(res: Response, fallback: string): Promise<T> {
  const d = (await res.json().catch(() => ({}))) as T & { error?: string };
  if (!res.ok) throw new Error(d.error ?? `${fallback}: ${res.status}`);
  return d;
}

// ---- 鉴权 ----
export async function authRegister(email: string, password: string, name: string): Promise<AuthUser> {
  const res = await fetch(`${GATEWAY}/api/v1/auth/register`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ email, password, name }),
  });
  const d = await jsonOrThrow<{ token?: string; user?: AuthUser }>(res, "注册失败");
  if (!d.token || !d.user) throw new Error("注册失败");
  setToken(d.token);
  return d.user;
}

export async function authLogin(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 d = await jsonOrThrow<{ token?: string; user?: AuthUser }>(res, "登录失败");
  if (!d.token || !d.user) throw new Error("登录失败");
  setToken(d.token);
  return d.user;
}

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

export function logout(): void {
  clearToken();
}

// ---- 租户上下文 ----
export interface TenantCtx {
  tenant: { id: string; name: string; plan: string; status: string; shared_billing?: boolean } | null;
  role: string;
  credit_balance_micro: number;
  credit_enforce: boolean;
}

export async function tenantCurrent(): Promise<TenantCtx | null> {
  const res = guard401(await fetch(`${GATEWAY}/api/v1/tenants/current`, { headers: bearer() }));
  if (!res.ok) return null;
  const d = (await res.json()) as Partial<TenantCtx>;
  return {
    tenant: d.tenant ?? null,
    role: d.role ?? "",
    credit_balance_micro: d.credit_balance_micro ?? 0,
    credit_enforce: !!d.credit_enforce,
  };
}

export interface MyTenant {
  id: string;
  name: string;
  slug: string;
  plan: string;
  credit_balance_micro: number;
  shared_billing: boolean;
  members: number;
}

export async function myTenants(): Promise<{ tenants: MyTenant[]; active: string }> {
  const res = guard401(await fetch(`${GATEWAY}/api/v1/me/tenants`, { headers: bearer() }));
  if (!res.ok) return { tenants: [], active: "" };
  const d = (await res.json()) as { tenants?: MyTenant[]; active?: string };
  return { tenants: d.tenants ?? [], active: d.active ?? "" };
}

export async function switchTenant(tenantId: string): Promise<void> {
  const res = guard401(
    await fetch(`${GATEWAY}/api/v1/me/tenant`, {
      method: "POST",
      headers: { "Content-Type": "application/json", ...bearer() },
      body: JSON.stringify({ tenant_id: tenantId }),
    }),
  );
  await jsonOrThrow(res, "切换失败");
}

// createTenant 自助建组织：创建者即 owner，后端建完自动切入。
export async function createTenant(name: string): Promise<{ id: string; name: string }> {
  const res = guard401(
    await fetch(`${GATEWAY}/api/v1/me/tenants`, {
      method: "POST",
      headers: { "Content-Type": "application/json", ...bearer() },
      body: JSON.stringify({ name }),
    }),
  );
  const d = await jsonOrThrow<{ tenant?: { id: string; name: string } }>(res, "创建失败");
  if (!d.tenant) throw new Error("创建失败");
  return d.tenant;
}

// ---- 成员自助管理（作用于当前活跃租户；写操作后端要求 ≥admin）----
export interface Member {
  user_id: string;
  email: string;
  name: string;
  role: string;
  status: string;
  joined_at: string;
}

export async function listMembers(): Promise<Member[]> {
  const res = guard401(await fetch(`${GATEWAY}/api/v1/tenants/current/members`, { headers: bearer() }));
  if (!res.ok) return [];
  const d = (await res.json()) as { members?: Member[] };
  return d.members ?? [];
}

export async function addMember(email: string, role: string): Promise<Member> {
  const res = guard401(
    await fetch(`${GATEWAY}/api/v1/tenants/current/members`, {
      method: "POST",
      headers: { "Content-Type": "application/json", ...bearer() },
      body: JSON.stringify({ email, role }),
    }),
  );
  const d = await jsonOrThrow<{ member?: Member }>(res, "邀请失败");
  if (!d.member) throw new Error("邀请失败");
  return d.member;
}

export async function setMemberRole(userId: string, role: string): Promise<void> {
  const res = guard401(
    await fetch(`${GATEWAY}/api/v1/tenants/current/members/${userId}`, {
      method: "PUT",
      headers: { "Content-Type": "application/json", ...bearer() },
      body: JSON.stringify({ role }),
    }),
  );
  await jsonOrThrow(res, "改角色失败");
}

export async function removeMember(userId: string): Promise<void> {
  const res = guard401(
    await fetch(`${GATEWAY}/api/v1/tenants/current/members/${userId}`, {
      method: "DELETE",
      headers: bearer(),
    }),
  );
  await jsonOrThrow(res, "移除失败");
}

// ---- 我的用量（余额 + 按天趋势 + 最近消耗）----
export interface UsageDay {
  day: string; // YYYYMMDD
  total_tok: number;
  credits_micro: number;
  cost_micros: number;
  task_count: number;
}
export interface UsageRecent {
  task_id: string;
  model: string;
  total_tok: number;
  credits_micro: number;
  cost_micros: number;
  currency: string;
  ts: number;
}
export interface MyUsage {
  from: string;
  to: string;
  balance_micro: number;
  credit_enforce: boolean;
  trend: UsageDay[];
  totals: { total_tok: number; credits_micro: number; cost_micros: number; task_count: number };
  recent: UsageRecent[];
}

export async function myUsage(days = 30): Promise<MyUsage> {
  const res = guard401(await fetch(`${GATEWAY}/api/v1/me/usage?days=${days}`, { headers: bearer() }));
  if (!res.ok) throw new Error(`usage failed: ${res.status}`);
  const d = (await res.json()) as Partial<MyUsage>;
  return {
    from: d.from ?? "",
    to: d.to ?? "",
    balance_micro: d.balance_micro ?? 0,
    credit_enforce: !!d.credit_enforce,
    trend: d.trend ?? [],
    totals: d.totals ?? { total_tok: 0, credits_micro: 0, cost_micros: 0, task_count: 0 },
    recent: d.recent ?? [],
  };
}

// ---- 充值（P5.1 兑换码；微信支付 P5.2 上线）----
export interface TopupOrder {
  id: string;
  credits_micro: number;
  amount_fen: number;
  channel: string;
  status: string;
  created_at: string;
}

export interface Pack {
  id: string;
  name: string;
  credits_micro: number;
  price_fen: number;
}

// billingPacks 在售积分包 + 可用渠道（服务端配了微信 env 才会出现 "wechat"）。
export async function billingPacks(): Promise<{ packs: Pack[]; channels: string[] }> {
  const res = guard401(await fetch(`${GATEWAY}/api/v1/billing/packs`, { headers: bearer() }));
  if (!res.ok) return { packs: [], channels: [] };
  const d = (await res.json()) as { packs?: Pack[]; channels?: string[] };
  return { packs: d.packs ?? [], channels: d.channels ?? [] };
}

// createWechatOrder 微信 Native 下单：返回订单号 + code_url（渲染成二维码扫码付）。
export async function createWechatOrder(packId: string): Promise<{ order_id: string; code_url: string; amount_fen: number }> {
  const res = guard401(
    await fetch(`${GATEWAY}/api/v1/billing/orders`, {
      method: "POST",
      headers: { "Content-Type": "application/json", ...bearer() },
      body: JSON.stringify({ pack_id: packId }),
    }),
  );
  return jsonOrThrow(res, "下单失败");
}

// orderStatus 轮询订单态（pending 时服务端顺路主动查单，本地也能确认到账）。
export async function orderStatus(orderId: string): Promise<TopupOrder> {
  const res = guard401(await fetch(`${GATEWAY}/api/v1/billing/orders/${orderId}`, { headers: bearer() }));
  const d = await jsonOrThrow<{ order: TopupOrder }>(res, "查询失败");
  return d.order;
}

// redeemCode 核销兑换码，返回入账后的余额。
export async function redeemCode(code: string): Promise<{ balance_micro: number }> {
  const res = guard401(
    await fetch(`${GATEWAY}/api/v1/billing/redeem`, {
      method: "POST",
      headers: { "Content-Type": "application/json", ...bearer() },
      body: JSON.stringify({ code }),
    }),
  );
  return jsonOrThrow<{ balance_micro: number }>(res, "兑换失败");
}

// billingOrders 计费租户最近充值记录。
export async function billingOrders(): Promise<TopupOrder[]> {
  const res = guard401(await fetch(`${GATEWAY}/api/v1/billing/orders`, { headers: bearer() }));
  if (!res.ok) return [];
  const d = (await res.json()) as { orders?: TopupOrder[] };
  return d.orders ?? [];
}

// 积分显示：micro(1e6) → 人类可读。与桌面端同一约定；小数去尾零（1.50 → 1.5）。
export function fmtCredits(micro: number): string {
  const v = micro / 1e6;
  if (Math.abs(v) >= 1e6) return `${(v / 1e6).toFixed(1)}M`;
  if (Math.abs(v) >= 1e3) return `${(v / 1e3).toFixed(1)}K`;
  return `${parseFloat(v.toFixed(2))}`;
}

// 角色中文名（角色本身英文入库，仅展示层翻译）。
export const ROLE_LABEL: Record<string, string> = {
  owner: "所有者",
  admin: "管理员",
  member: "成员",
  viewer: "只读",
  billing_admin: "财务",
};
// 邀请/改角色可选项（owner 不可授予——须转让，后端也拦）。
export const ASSIGNABLE_ROLES = ["admin", "member", "viewer", "billing_admin"] as const;
