import { KEYS } from "../data/mock/keys";
import { getSession } from "../data/mock/store";
import { newId } from "./id";
import { readJson, writeJson } from "./storage";
import { nowUnixNano } from "./time";

/** 計費維度（對齊未來付費方案） */
export type UsageMeter = "ai_copy" | "ai_research" | "web_search" | "ai_image";

export type PlanId = "free" | "starter" | "pro";

/** platform = 平台代付扣點；byok = 自備 key 只計次數 */
export type UsageKeyMode = "platform" | "byok";

export type UsageEvent = {
  id: string;
  /** 使用者 uid（舊資料可能缺，會歸到 unknown） */
  uid: string;
  meter: UsageMeter;
  /** 消耗點數（byok 固定 0） */
  credits: number;
  /** 平台 / 自備 key */
  key_mode: UsageKeyMode;
  /** 使用者可讀說明 */
  label: string;
  /** 功能來源，除錯／對帳 */
  source: string;
  /** unix nanoseconds UTC */
  created_at: number;
};

export type PlanDef = {
  id: PlanId;
  name: string;
  price_label: string;
  price_twd: number;
  blurb: string;
  blurb_key: string;
  /** 本月 platform 總點數配給 */
  monthly_credits: number;
  /**
   * 各分項點數參考上限（單位＝點，不是次數）。
   * 加總應等於 monthly_credits。
   */
  soft_caps: Record<UsageMeter, number>;
};

/** 每人方案／是否不擋額度（用量一律計算） */
export type UsageMemberPrefs = {
  plan_id: PlanId;
  /** 達方案上限仍可繼續用；AI／Search／點數照樣記帳 */
  unlimited: boolean;
};

export const METER_META: Record<
  UsageMeter,
  { label: string; hint: string; order: number; group: "ai" | "search" }
> = {
  ai_copy: {
    label: "AI 文案",
    hint: "回覆草稿、靈感聊天、仿寫、結構分析",
    order: 0,
    group: "ai",
  },
  ai_research: {
    label: "AI 研究",
    hint: "海巡周邊知識、上網功課",
    order: 1,
    group: "ai",
  },
  web_search: {
    label: "搜尋",
    hint: "熱點刷新、關鍵字搜、研究檢索",
    order: 2,
    group: "search",
  },
  ai_image: {
    label: "AI 生圖",
    hint: "文生圖／配圖",
    order: 3,
    group: "ai",
  },
};

/**
 * 方案配額（2026-07 · 組合毛利設計）
 *
 * 產品漏斗：
 * - Free：夠用、真能體驗完整流程 → 轉 Starter
 * - Starter：日常付費主力
 * - Pro：重度／多帳
 * - 平台 key 限流或無容量時：僅 BYOK 可用（不扣平台點）
 *
 * 單位成本（platform，USD）：
 * - 文案 ~$0.004–0.008／次 · 搜尋 Exa ~$0.007 · 研究 ~$0.015 · 生圖 ~$0.02–0.04
 * - 安全單價 **$0.012／點**；TWD≈32 → Starter $18.4 / Pro $62.2
 *
 * 組合毛利（目標 ≥50%）：Free 可虧一點，付費扛回。
 * 例 5 Free 滿用 + 2 Starter 滿用 + 1 Pro 滿用：
 *   COGS ≈ 5×$1.44 + 2×$7.2 + $24 = $45.6 · 營收 $99 → 毛利 ~54%
 *
 * soft_caps = 分項點數硬上限，加總 = monthly_credits。
 */
export const PLANS: Record<PlanId, PlanDef> = {
  free: {
    id: "free",
    name: "Free",
    price_label: "NT$0／月",
    price_twd: 0,
    blurb: "夠用試用，體驗完整創作流程",
    blurb_key: "usage.plan.free.blurb",
    // 獲客可虧：滿用 ~$1.44；約 2～3 週輕度日常，用得出價值再升級
    monthly_credits: 120,
    soft_caps: {
      ai_copy: 60, // 60 次文案／回覆
      ai_research: 15, // 5 次研究
      web_search: 30, // 30 次搜尋
      ai_image: 15, // 3 張生圖
    }, // sum = 120
  },
  starter: {
    id: "starter",
    name: "Starter",
    price_label: "NT$590／月",
    price_twd: 590,
    blurb: "小團隊日常發文與海巡",
    blurb_key: "usage.plan.starter.blurb",
    // 滿用 COGS ≤ $7.2 → 單方案毛利 ~61%；轉付費主力
    // 約當：文案 300 · 研究 30 · 搜尋 150 · 生圖 12
    monthly_credits: 600,
    soft_caps: {
      ai_copy: 300,
      ai_research: 90,
      web_search: 150,
      ai_image: 60,
    }, // sum = 600
  },
  pro: {
    id: "pro",
    name: "Pro",
    price_label: "NT$1,990／月",
    price_twd: 1990,
    blurb: "多帳、重度 AI 與研究",
    blurb_key: "usage.plan.pro.blurb",
    // 滿用 COGS ≤ $24 → 單方案毛利 ~61%；重度天花板
    // 約當：文案 1000 · 研究 100 · 搜尋 500 · 生圖 40
    monthly_credits: 2000,
    soft_caps: {
      ai_copy: 1000,
      ai_research: 300,
      web_search: 500,
      ai_image: 200,
    }, // sum = 2000
  },
};

/** 記帳點數／次（platform）；與後端 DefaultCreditCost 對齊 */
export const DEFAULT_CREDIT_COST: Record<UsageMeter, number> = {
  ai_copy: 1,
  ai_research: 3,
  web_search: 1,
  ai_image: 5,
};

export type UsageMonthSummary = {
  month_key: string;
  uid: string;
  plan: PlanDef;
  unlimited: boolean;
  /**
   * 本月已用 platform 點數（不是方案總額）。
   * 方案配給請用 plan.monthly_credits 或 platform.credits_total。
   */
  total_credits: number;
  remaining_credits: number;
  /** 已用 / 方案配給 百分比（platform only） */
  pct: number;
  /** @deprecated 平台 AI 次數；請用 platform / byok 分欄 */
  ai_calls: number;
  /** @deprecated 平台搜尋次數 */
  search_calls: number;
  by_meter: Record<
    UsageMeter,
    { credits: number; count: number; soft_cap: number; pct: number }
  >;
  events: UsageEvent[];
  /** 平台代付：點數 + 次數 */
  platform: {
    credits_used: number;
    credits_remaining: number;
    credits_total: number;
    call_count: number;
  };
  /** BYOK：僅次數，不進點數條 */
  byok: {
    call_count: number;
    by_meter: Record<UsageMeter, { credits: number; count: number }>;
  };
};

export type TenantUsageMemberRow = {
  uid: string;
  email: string;
  display_name: string;
  unlimited: boolean;
  plan_id: PlanId;
  /** 區間內方案配給（購買／額度） */
  purchased_credits: number;
  /** 區間內消耗 */
  total_credits: number;
  ai_calls: number;
  search_calls: number;
  remaining_credits: number;
  pct: number;
};

export type TenantUsageSummary = {
  month_key: string;
  /** @deprecated 等同 consumed_credits；保留相容 */
  total_credits: number;
  purchased_credits: number;
  consumed_credits: number;
  remaining_credits: number;
  ai_calls: number;
  search_calls: number;
  by_meter: Record<UsageMeter, { credits: number; count: number }>;
  members: TenantUsageMemberRow[];
};

/** 圖表時間粒度 */
export type UsageGranularity = "day" | "month" | "year";

export type UsageTimeBucket = {
  /** day: YYYY-MM-DD · month: YYYY-MM · year: YYYY */
  key: string;
  label: string;
  purchased_credits: number;
  consumed_credits: number;
  ai_calls: number;
  search_calls: number;
	byok_call_count: number;
};

export type TenantUsageAnalytics = {
  granularity: UsageGranularity;
  /** 區間起迄（含）YYYY-MM-DD */
  from: string;
  to: string;
  range_label: string;
  purchased_credits: number;
  consumed_credits: number;
  remaining_credits: number;
  pct: number;
  ai_calls: number;
  search_calls: number;
  by_meter: Record<UsageMeter, { credits: number; count: number }>;
	byok: {
		call_count: number;
		by_meter: Record<UsageMeter, { credits: number; count: number }>;
	};
  series: UsageTimeBucket[];
  members: TenantUsageMemberRow[];
};

export type TenantAnalyticsQuery = {
  granularity?: UsageGranularity;
  /** YYYY-MM-DD */
  from?: string;
  /** YYYY-MM-DD */
  to?: string;
};

function monthKeyFromMs(ms: number): string {
  const d = new Date(ms);
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}`;
}

export function currentMonthKey(now = Date.now()): string {
  return monthKeyFromMs(now);
}

function loadEvents(): UsageEvent[] {
  const raw = readJson<(UsageEvent & { uid?: string })[]>(KEYS.usageEvents, []);
  return raw.map((e) => ({
    ...e,
    uid: e.uid || "unknown",
  }));
}

function saveEvents(list: UsageEvent[]): void {
  writeJson(KEYS.usageEvents, list.slice(0, 2000));
}

type PrefsMap = Record<string, UsageMemberPrefs>;

function loadPrefsMap(): PrefsMap {
  return readJson(KEYS.usageMemberPrefs, {} as PrefsMap);
}

function savePrefsMap(map: PrefsMap): void {
  writeJson(KEYS.usageMemberPrefs, map);
}

function normalizePlanId(id: unknown): PlanId {
  if (id === "starter" || id === "pro" || id === "free") return id;
  return "free";
}

export function getMemberPrefs(uid: string): UsageMemberPrefs {
  const map = loadPrefsMap();
  const row = map[uid];
  if (row) {
    return {
      plan_id: normalizePlanId(row.plan_id),
      unlimited: row.unlimited === true,
    };
  }
  // 相容：舊全域方案
  const legacy = readJson<string>(KEYS.usagePlanId, "free");
  return { plan_id: normalizePlanId(legacy), unlimited: false };
}

export function setMemberPrefs(
  uid: string,
  patch: Partial<UsageMemberPrefs>,
): UsageMemberPrefs {
  const map = loadPrefsMap();
  const cur = getMemberPrefs(uid);
  const next: UsageMemberPrefs = {
    plan_id: patch.plan_id != null ? normalizePlanId(patch.plan_id) : cur.plan_id,
    unlimited: patch.unlimited != null ? patch.unlimited === true : cur.unlimited,
  };
  map[uid] = next;
  savePrefsMap(map);
  return next;
}

/** @deprecated 改用 getMemberPrefs(uid) */
export function getPlanId(uid?: string): PlanId {
  if (uid) return getMemberPrefs(uid).plan_id;
  const s = getSession()?.member?.uid;
  return getMemberPrefs(s || "unknown").plan_id;
}

/** @deprecated 改用 setMemberPrefs */
export function setPlanId(id: PlanId, uid?: string): void {
  const s = uid || getSession()?.member?.uid || "unknown";
  setMemberPrefs(s, { plan_id: id });
}

function sessionUid(): string {
  return getSession()?.member?.uid || "unknown";
}

/**
 * 記一筆用量。
 * - 任何人（含「不擋額度」）每次呼叫都寫入，永不跳過計算
 * - unlimited 不影響是否記帳，只表示超過方案仍可繼續用
 */
export function recordUsage(opts: {
  meter: UsageMeter;
  label: string;
  source: string;
  credits?: number;
  uid?: string;
  /** 預設 platform（mock 記點）；byok 時 credits 強制 0 */
  key_mode?: UsageKeyMode;
}): UsageEvent {
  const uid = opts.uid || sessionUid();
  const key_mode: UsageKeyMode = opts.key_mode ?? "platform";
  let credits =
    opts.credits != null && opts.credits > 0
      ? opts.credits
      : DEFAULT_CREDIT_COST[opts.meter];
  if (key_mode === "byok") credits = 0;
  const ev: UsageEvent = {
    id: newId("use"),
    uid,
    meter: opts.meter,
    credits,
    key_mode,
    label: opts.label,
    source: opts.source,
    created_at: nowUnixNano(),
  };
  const list = loadEvents();
  list.unshift(ev);
  saveEvents(list);
  return ev;
}

export function listUsageEvents(opts?: {
  limit?: number;
  uid?: string;
  monthKey?: string;
}): UsageEvent[] {
  const limit = opts?.limit ?? 100;
  const monthKey = opts?.monthKey;
  let list = loadEvents();
  if (opts?.uid) list = list.filter((e) => e.uid === opts.uid);
  if (monthKey) {
    list = list.filter((e) => {
      const ms = Math.floor(e.created_at / 1_000_000);
      return monthKeyFromMs(ms) === monthKey;
    });
  }
  return list.slice(0, limit);
}

function emptyByMeter(plan: PlanDef): UsageMonthSummary["by_meter"] {
  const by_meter = {} as UsageMonthSummary["by_meter"];
  for (const m of Object.keys(METER_META) as UsageMeter[]) {
    by_meter[m] = {
      credits: 0,
      count: 0,
      soft_cap: plan.soft_caps[m],
      pct: 0,
    };
  }
  return by_meter;
}

function aggregateEvents(
  events: UsageEvent[],
  plan: PlanDef,
  _unlimited: boolean,
): Pick<
  UsageMonthSummary,
  | "total_credits"
  | "remaining_credits"
  | "pct"
  | "by_meter"
  | "ai_calls"
  | "search_calls"
  | "platform"
  | "byok"
> {
  // 平台點數只加 key_mode=platform；BYOK 只計次數
  const by_meter = emptyByMeter(plan);
  let platformCredits = 0;
  let platformCalls = 0;
  let byokCalls = 0;
  const byokByMeter = {} as UsageMonthSummary["byok"]["by_meter"];
  for (const m of Object.keys(METER_META) as UsageMeter[]) {
    byokByMeter[m] = { credits: 0, count: 0 };
  }
  for (const e of events) {
    const mode = e.key_mode ?? "platform";
    if (mode === "byok") {
      byokCalls += 1;
      if (byokByMeter[e.meter]) byokByMeter[e.meter].count += 1;
      continue;
    }
    platformCredits += e.credits;
    platformCalls += 1;
    const row = by_meter[e.meter];
    if (row) {
      row.credits += e.credits;
      row.count += 1;
    }
  }
  for (const m of Object.keys(by_meter) as UsageMeter[]) {
    const row = by_meter[m];
    // soft_cap 是點數上限，進度用已用點數
    row.pct =
      row.soft_cap > 0 ? Math.min(999, Math.round((row.credits / row.soft_cap) * 100)) : 0;
  }
  const remaining = Math.max(0, plan.monthly_credits - platformCredits);
  const pct =
    plan.monthly_credits > 0
      ? Math.min(999, Math.round((platformCredits / plan.monthly_credits) * 100))
      : 0;

  // 狀態列用「點數」加總，與方案配給同一單位
  let ai_calls = 0;
  let search_calls = 0;
  for (const m of Object.keys(METER_META) as UsageMeter[]) {
    if (METER_META[m].group === "ai") ai_calls += by_meter[m].credits;
    else search_calls += by_meter[m].credits;
  }

  return {
    // 與 UI／widget 一致：total_credits = 已用
    total_credits: platformCredits,
    remaining_credits: remaining,
    pct,
    by_meter,
    ai_calls,
    search_calls,
    platform: {
      credits_used: platformCredits,
      credits_remaining: remaining,
      credits_total: plan.monthly_credits,
      call_count: platformCalls,
    },
    byok: { call_count: byokCalls, by_meter: byokByMeter },
  };
}

export function summarizeUsage(
  uid?: string,
  monthKey = currentMonthKey(),
): UsageMonthSummary {
  const target = uid || sessionUid();
  const prefs = getMemberPrefs(target);
  const plan = PLANS[prefs.plan_id];
  const events = loadEvents().filter((e) => {
    if (e.uid !== target) return false;
    const ms = Math.floor(e.created_at / 1_000_000);
    return monthKeyFromMs(ms) === monthKey;
  });

  const agg = aggregateEvents(events, plan, prefs.unlimited);
  return {
    month_key: monthKey,
    uid: target,
    plan,
    unlimited: prefs.unlimited,
    ...agg,
    events: events.slice(0, 80),
  };
}

function pad2(n: number): string {
  return String(n).padStart(2, "0");
}

function toDateKey(d: Date): string {
  return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`;
}

function parseDateKey(key: string): Date | null {
  const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec((key || "").trim());
  if (!m) return null;
  const y = Number(m[1]);
  const mo = Number(m[2]);
  const day = Number(m[3]);
  if (!y || mo < 1 || mo > 12 || day < 1 || day > 31) return null;
  const d = new Date(y, mo - 1, day);
  if (d.getFullYear() !== y || d.getMonth() !== mo - 1 || d.getDate() !== day) return null;
  return d;
}

function startOfDay(d: Date): Date {
  return new Date(d.getFullYear(), d.getMonth(), d.getDate());
}

function addDays(d: Date, n: number): Date {
  const x = new Date(d);
  x.setDate(x.getDate() + n);
  return x;
}

function daysInMonth(y: number, monthIndex0: number): number {
  return new Date(y, monthIndex0 + 1, 0).getDate();
}

function eventDayKey(e: UsageEvent): string {
  return toDateKey(new Date(Math.floor(e.created_at / 1_000_000)));
}

function eventMonthKey(e: UsageEvent): string {
  return monthKeyFromMs(Math.floor(e.created_at / 1_000_000));
}

function eventYearKey(e: UsageEvent): string {
  return String(new Date(Math.floor(e.created_at / 1_000_000)).getFullYear());
}

function memberMonthlyQuota(uid: string): number {
  return PLANS[getMemberPrefs(uid).plan_id].monthly_credits;
}

/** 區間內「購買／配給」點數：依每人方案月額度 × 涵蓋月數（日視圖按當月天數比例） */
function purchasedInRange(
  memberUids: string[],
  from: Date,
  to: Date,
  granularity: UsageGranularity,
): number {
  let total = 0;
  for (const uid of memberUids) {
    const monthly = memberMonthlyQuota(uid);
    if (granularity === "year") {
      // 逐年：每年 12 個月 × 月額度（區間裁切到涵蓋月）
      const start = new Date(from.getFullYear(), from.getMonth(), 1);
      const end = new Date(to.getFullYear(), to.getMonth(), 1);
      for (let y = start.getFullYear(); y <= end.getFullYear(); y++) {
        const m0 = y === start.getFullYear() ? start.getMonth() : 0;
        const m1 = y === end.getFullYear() ? end.getMonth() : 11;
        total += monthly * (m1 - m0 + 1);
      }
    } else if (granularity === "month") {
      const start = new Date(from.getFullYear(), from.getMonth(), 1);
      const end = new Date(to.getFullYear(), to.getMonth(), 1);
      let cur = start;
      while (cur <= end) {
        total += monthly;
        cur = new Date(cur.getFullYear(), cur.getMonth() + 1, 1);
      }
    } else {
      // day：每日配給 = 月額度 / 當月天數
      let cur = startOfDay(from);
      const end = startOfDay(to);
      while (cur <= end) {
        const dim = daysInMonth(cur.getFullYear(), cur.getMonth());
        total += monthly / dim;
        cur = addDays(cur, 1);
      }
    }
  }
  return Math.round(total);
}

function defaultRange(granularity: UsageGranularity): { from: Date; to: Date } {
  const to = startOfDay(new Date());
  if (granularity === "day") {
    return { from: addDays(to, -29), to };
  }
  if (granularity === "year") {
    return {
      from: new Date(to.getFullYear() - 2, 0, 1),
      to,
    };
  }
  // month：近 12 個月
  return {
    from: new Date(to.getFullYear(), to.getMonth() - 11, 1),
    to,
  };
}

function bucketKeys(
  from: Date,
  to: Date,
  granularity: UsageGranularity,
): { key: string; label: string; start: Date; end: Date }[] {
  const out: { key: string; label: string; start: Date; end: Date }[] = [];
  if (granularity === "day") {
    let cur = startOfDay(from);
    const end = startOfDay(to);
    while (cur <= end) {
      const key = toDateKey(cur);
      out.push({
        key,
        label: `${cur.getMonth() + 1}/${cur.getDate()}`,
        start: cur,
        end: cur,
      });
      cur = addDays(cur, 1);
    }
    return out;
  }
  if (granularity === "month") {
    let cur = new Date(from.getFullYear(), from.getMonth(), 1);
    const end = new Date(to.getFullYear(), to.getMonth(), 1);
    while (cur <= end) {
      const y = cur.getFullYear();
      const m = cur.getMonth();
      const key = `${y}-${pad2(m + 1)}`;
      const last = new Date(y, m + 1, 0);
      out.push({
        key,
        label: `${y}/${pad2(m + 1)}`,
        start: cur,
        end: last,
      });
      cur = new Date(y, m + 1, 1);
    }
    return out;
  }
  // year
  for (let y = from.getFullYear(); y <= to.getFullYear(); y++) {
    out.push({
      key: String(y),
      label: `${y}`,
      start: new Date(y, 0, 1),
      end: new Date(y, 11, 31),
    });
  }
  return out;
}

function eventInBucket(e: UsageEvent, granularity: UsageGranularity, key: string): boolean {
  if (granularity === "day") return eventDayKey(e) === key;
  if (granularity === "month") return eventMonthKey(e) === key;
  return eventYearKey(e) === key;
}

function purchasedForBucket(
  memberUids: string[],
  bucket: { start: Date; end: Date; key: string },
  granularity: UsageGranularity,
): number {
  return purchasedInRange(memberUids, bucket.start, bucket.end, granularity);
}

/**
 * mock：若歷史事件太少，補近半年示意資料，方便圖表預覽。
 * 只在 events < 12 時寫入一次。
 */
export function ensureDemoUsageHistory(
  members: { uid: string }[],
): void {
  const list = loadEvents();
  if (list.length >= 12) return;
  if (!members.length) return;

  const meters = Object.keys(METER_META) as UsageMeter[];
  const labels: Record<UsageMeter, string> = {
    ai_copy: "示意：文案",
    ai_research: "示意：研究",
    web_search: "示意：搜尋",
    ai_image: "示意：生圖",
  };
  const now = Date.now();
  const seeded: UsageEvent[] = [];
  for (let dayAgo = 160; dayAgo >= 0; dayAgo -= 2 + (dayAgo % 3)) {
    const uid = members[dayAgo % members.length]!.uid;
    const meter = meters[dayAgo % meters.length]!;
    const ms = now - dayAgo * 86_400_000 - (dayAgo % 7) * 3_600_000;
    const key_mode: UsageKeyMode = dayAgo % 5 === 0 ? "byok" : "platform";
    seeded.push({
      id: newId("use"),
      uid,
      meter,
      credits: key_mode === "byok" ? 0 : DEFAULT_CREDIT_COST[meter] * (1 + (dayAgo % 3)),
      key_mode,
      label: labels[meter],
      source: "demo.seed",
      created_at: ms * 1_000_000,
    });
  }
  saveEvents([...seeded, ...list].slice(0, 2000));
}

/** 租戶區間分析：購買 vs 消耗、時間序列、每人 */
export function summarizeTenantAnalytics(
  members: { uid: string; email: string; display_name: string }[],
  query: TenantAnalyticsQuery = {},
): TenantUsageAnalytics {
  const granularity: UsageGranularity =
    query.granularity === "day" || query.granularity === "year" ? query.granularity : "month";

  const defaults = defaultRange(granularity);
  let from = parseDateKey(query.from || "") || defaults.from;
  let to = parseDateKey(query.to || "") || defaults.to;
  if (from > to) {
    const tmp = from;
    from = to;
    to = tmp;
  }
  // 限縮避免爆量桶
  const maxSpanDays =
    granularity === "day" ? 93 : granularity === "month" ? 366 * 3 : 366 * 8;
  if ((startOfDay(to).getTime() - startOfDay(from).getTime()) / 86_400_000 > maxSpanDays) {
    from = addDays(to, -maxSpanDays);
  }

  const fromKey = toDateKey(from);
  const toKey = toDateKey(to);
  const uids = members.map((m) => m.uid);

  const rangeStartMs = startOfDay(from).getTime();
  const rangeEndMs = startOfDay(to).getTime() + 86_400_000 - 1;

  const rangeEvents = loadEvents().filter((e) => {
    const ms = Math.floor(e.created_at / 1_000_000);
    return ms >= rangeStartMs && ms <= rangeEndMs;
  });

  const by_meter = {} as TenantUsageAnalytics["by_meter"];
	const byokByMeter = {} as TenantUsageAnalytics["byok"]["by_meter"];
  for (const m of Object.keys(METER_META) as UsageMeter[]) {
    by_meter[m] = { credits: 0, count: 0 };
		byokByMeter[m] = { credits: 0, count: 0 };
  }
  let consumed_credits = 0;
	let byokCallCount = 0;
  for (const e of rangeEvents) {
		if (e.key_mode === "byok") {
			byokCallCount += 1;
			byokByMeter[e.meter].count += 1;
			continue;
		}
    consumed_credits += e.credits;
    const row = by_meter[e.meter];
    if (row) {
      row.credits += e.credits;
      row.count += 1;
    }
  }
  let ai_calls = 0;
  let search_calls = 0;
  for (const m of Object.keys(METER_META) as UsageMeter[]) {
    if (METER_META[m].group === "ai") ai_calls += by_meter[m].count;
    else search_calls += by_meter[m].count;
  }

  const purchased_credits = purchasedInRange(uids, from, to, granularity);
  const remaining_credits = Math.max(0, Math.round(purchased_credits - consumed_credits));
  const pct =
    purchased_credits > 0
      ? Math.min(999, Math.round((consumed_credits / purchased_credits) * 100))
      : 0;

  const buckets = bucketKeys(from, to, granularity);
  const series: UsageTimeBucket[] = buckets.map((b) => {
    const evs = rangeEvents.filter((e) => eventInBucket(e, granularity, b.key));
    let c = 0;
    let ai = 0;
    let search = 0;
    for (const e of evs) {
			if (e.key_mode === "byok") continue;
      c += e.credits;
      if (METER_META[e.meter]?.group === "ai") ai += 1;
      else search += 1;
    }
    return {
      key: b.key,
      label: b.label,
      purchased_credits: purchasedForBucket(uids, b, granularity),
      consumed_credits: c,
      ai_calls: ai,
      search_calls: search,
			byok_call_count: evs.filter((e) => e.key_mode === "byok").length,
    };
  });

  const memberRows: TenantUsageMemberRow[] = members.map((m) => {
    const prefs = getMemberPrefs(m.uid);
    const plan = PLANS[prefs.plan_id];
    const evs = rangeEvents.filter((e) => e.uid === m.uid);
    let total = 0;
    let ai = 0;
    let search = 0;
    for (const e of evs) {
			if (e.key_mode === "byok") continue;
      total += e.credits;
      if (METER_META[e.meter]?.group === "ai") ai += 1;
      else search += 1;
    }
    const purchased = purchasedInRange([m.uid], from, to, granularity);
    const remaining = Math.max(0, purchased - total);
    const memberPct =
      purchased > 0 ? Math.min(999, Math.round((total / purchased) * 100)) : 0;
    return {
      uid: m.uid,
      email: m.email,
      display_name: m.display_name,
      unlimited: prefs.unlimited,
      plan_id: plan.id,
      purchased_credits: purchased,
      total_credits: total,
      ai_calls: ai,
      search_calls: search,
      remaining_credits: remaining,
      pct: memberPct,
    };
  });
  memberRows.sort((a, b) => b.total_credits - a.total_credits);

  const unit =
    granularity === "day" ? "日" : granularity === "month" ? "月" : "年";
  const range_label = `${fromKey} → ${toKey} · 以${unit}檢視`;

  return {
    granularity,
    from: fromKey,
    to: toKey,
    range_label,
    purchased_credits,
    consumed_credits,
    remaining_credits,
    pct,
    ai_calls,
    search_calls,
    by_meter,
		byok: { call_count: byokCallCount, by_meter: byokByMeter },
    series,
    members: memberRows,
  };
}

/** 租戶本月全體：總體 + 每人（相容舊呼叫） */
export function summarizeTenantUsage(
  members: { uid: string; email: string; display_name: string }[],
  monthKey = currentMonthKey(),
): TenantUsageSummary {
  const [y, mo] = monthKey.split("-").map(Number);
  const from = `${y}-${pad2(mo)}-01`;
  const last = daysInMonth(y, mo - 1);
  const to = `${y}-${pad2(mo)}-${pad2(last)}`;
  const a = summarizeTenantAnalytics(members, {
    granularity: "month",
    from,
    to,
  });
  return {
    month_key: monthKey,
    total_credits: a.consumed_credits,
    purchased_credits: a.purchased_credits,
    consumed_credits: a.consumed_credits,
    remaining_credits: a.remaining_credits,
    ai_calls: a.ai_calls,
    search_calls: a.search_calls,
    by_meter: a.by_meter,
    members: a.members,
  };
}

type WarnTFn = (key: string, params?: Record<string, string | number>) => string;

/** 用量警示（需傳入 t，避免硬編碼語系） */
export function usageWarning(summary: UsageMonthSummary, t?: WarnTFn): string | null {
  const used = summary.platform?.credits_used ?? summary.total_credits;
  // 上限永遠跟方案表，避免舊 API 的 credits_total 造成 120 文案 / 80 上限
  const cap = summary.plan.monthly_credits;
  const pct =
    summary.pct > 0
      ? summary.pct
      : cap > 0
        ? Math.round((used / cap) * 100)
        : 0;
  const tr =
    t ??
    ((key: string, params?: Record<string, string | number>) => {
      // fallback 中文（測試／無 i18n 時）
      const fb: Record<string, string> = {
        "usage.warn.unlimitedOver": `本月已用 ${params?.used} 點（方案 ${params?.cap}，不擋額度，仍可繼續）。`,
        "usage.warn.exhausted": "本月點數已用完。可升級方案或等待下月重置。",
        "usage.warn.high": `本月已用 ${params?.pct}% 點數。`,
        "usage.warn.meterNear": `${params?.label} 接近單項上限（${params?.credits}/${params?.cap} 點）。`,
      };
      return fb[key] ?? key;
    });
  // 不擋額度：只提示已超出，不說「用完」
  if (summary.unlimited) {
    if (used > cap) {
      return tr("usage.warn.unlimitedOver", { used, cap });
    }
    return null;
  }
  if (pct >= 100) {
    return tr("usage.warn.exhausted");
  }
  if (pct >= 80) {
    return tr("usage.warn.high", { pct });
  }
  for (const m of Object.keys(METER_META) as UsageMeter[]) {
    const row = summary.by_meter[m];
    if (row.pct >= 90) {
      return tr("usage.warn.meterNear", {
        label: t ? t(`usage.meter.${m}`) : METER_META[m].label,
        credits: row.credits,
        cap: row.soft_cap,
      });
    }
  }
  return null;
}
