/**
 * 本地 CLI 报表（无需服务端）：daily / weekly / monthly / sessions
 *
 * 全部直读 ~/.claude/projects/**\/*.jsonl 解析，跟 ccusage / token-tracker 一样。
 * 实现简单粗暴：扫所有 jsonl + 内存聚合 + 打印 ASCII 表。
 */
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';

interface RawEvent {
  ts: number; // unix ms
  sessionId: string;
  projectPath: string;
  model: string;
  input: number;
  output: number;
  cacheCreate: number;
  cacheRead: number;
}

/** 取 session 的"标题"：用第一条 user message content（截断 60 字）；
 *  如果首条是 system reminder 或工具结果（content 是数组）则跳过取下一条。 */
function deriveTitle(filePath: string): string {
  try {
    const content = fs.readFileSync(filePath, 'utf8');
    for (const line of content.split('\n').slice(0, 60)) {
      if (!line.trim()) continue;
      const o = JSON.parse(line);
      if (o.type !== 'user') continue;
      const c = o.message?.content;
      if (typeof c !== 'string') continue;
      const trimmed = c.trim();
      // 跳过系统 reminder / hook 注入的工具调用
      if (trimmed.startsWith('<') || trimmed.startsWith('[') || trimmed.length < 3) continue;
      return trimmed.slice(0, 60).replace(/\s+/g, ' ');
    }
  } catch {}
  return '';
}

function* walkJsonl(): Generator<RawEvent & { title?: string }> {
  const home = os.homedir();
  const projRoot = path.join(home, '.claude', 'projects');
  if (!fs.existsSync(projRoot)) return;
  for (const projDir of fs.readdirSync(projRoot)) {
    const full = path.join(projRoot, projDir);
    if (!fs.statSync(full).isDirectory()) continue;
    const cwd = '/' + projDir.replace(/^-/, '').replace(/-/g, '/');
    for (const file of fs.readdirSync(full)) {
      if (!file.endsWith('.jsonl')) continue;
      const filePath = path.join(full, file);
      const title = deriveTitle(filePath);
      const content = fs.readFileSync(filePath, 'utf8');
      for (const line of content.split('\n')) {
        if (!line.trim()) continue;
        try {
          const o = JSON.parse(line);
          if (o.type !== 'assistant') continue;
          const u = o.message?.usage;
          if (!u) continue;
          yield {
            ts: o.timestamp ? new Date(o.timestamp).getTime() : Date.now(),
            sessionId: o.sessionId ?? path.basename(file, '.jsonl'),
            projectPath: o.cwd ?? cwd,
            model: o.message?.model ?? 'unknown',
            input: Number(u.input_tokens ?? 0),
            output: Number(u.output_tokens ?? 0),
            cacheCreate: Number(u.cache_creation_input_tokens ?? 0),
            cacheRead: Number(u.cache_read_input_tokens ?? 0),
            title,
          };
        } catch {
          // skip
        }
      }
    }
  }
}

function estimateCost(model: string, e: Pick<RawEvent, 'input' | 'output' | 'cacheCreate' | 'cacheRead'>): number {
  // 简化：opus-4-7 标准价；其他 model 用同样口径（status line 用，不是精确账单）
  const rates: Record<string, { i: number; o: number; cc: number; cr: number }> = {
    'claude-opus-4-7': { i: 15, o: 75, cc: 18.75, cr: 1.5 },
    'claude-opus-4-6': { i: 15, o: 75, cc: 18.75, cr: 1.5 },
    'claude-sonnet-4-6': { i: 3, o: 15, cc: 3.75, cr: 0.3 },
    'claude-haiku-4-5': { i: 1, o: 5, cc: 1.25, cr: 0.1 },
  };
  const r = rates[model] ?? rates['claude-opus-4-7'];
  return (
    (e.input / 1_000_000) * r.i +
    (e.output / 1_000_000) * r.o +
    (e.cacheCreate / 1_000_000) * r.cc +
    (e.cacheRead / 1_000_000) * r.cr
  );
}

function fmtTokens(n: number): string {
  if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;
  if (n >= 1_000) return `${(n / 1_000).toFixed(1)}k`;
  return String(n);
}

function bucketKey(ts: number, mode: 'day' | 'week' | 'month'): string {
  const d = new Date(ts);
  if (mode === 'month') return d.toISOString().slice(0, 7);
  if (mode === 'week') {
    const day = d.getUTCDay() || 7;
    const monday = new Date(d);
    monday.setUTCDate(d.getUTCDate() - day + 1);
    return monday.toISOString().slice(0, 10);
  }
  return d.toISOString().slice(0, 10);
}

interface Aggregated {
  bucket: string;
  tokens: number;
  cost: number;
  events: number;
  models: Set<string>;
  projects: Set<string>;
}

function aggregate(mode: 'day' | 'week' | 'month'): Aggregated[] {
  const buckets = new Map<string, Aggregated>();
  for (const e of walkJsonl()) {
    const key = bucketKey(e.ts, mode);
    let a = buckets.get(key);
    if (!a) {
      a = { bucket: key, tokens: 0, cost: 0, events: 0, models: new Set(), projects: new Set() };
      buckets.set(key, a);
    }
    a.tokens += e.input + e.output + e.cacheCreate + e.cacheRead;
    a.cost += estimateCost(e.model, e);
    a.events += 1;
    a.models.add(e.model);
    a.projects.add(path.basename(e.projectPath));
  }
  return Array.from(buckets.values()).sort((a, b) => (a.bucket < b.bucket ? 1 : -1));
}

export function reportDaily() {
  const rows = aggregate('day').slice(0, 30);
  printTable(['Day', 'Events', 'Tokens', 'Cost USD', 'Projects'], rows.map((r) => [
    r.bucket,
    String(r.events),
    fmtTokens(r.tokens),
    `$${r.cost.toFixed(2)}`,
    String(r.projects.size),
  ]));
}

export function reportWeekly() {
  const rows = aggregate('week').slice(0, 12);
  printTable(['Week Start', 'Events', 'Tokens', 'Cost USD', 'Projects'], rows.map((r) => [
    r.bucket,
    String(r.events),
    fmtTokens(r.tokens),
    `$${r.cost.toFixed(2)}`,
    String(r.projects.size),
  ]));
}

export function reportMonthly() {
  const rows = aggregate('month').slice(0, 12);
  printTable(['Month', 'Events', 'Tokens', 'Cost USD', 'Projects'], rows.map((r) => [
    r.bucket,
    String(r.events),
    fmtTokens(r.tokens),
    `$${r.cost.toFixed(2)}`,
    String(r.projects.size),
  ]));
}

export function reportSessions(limit = 20) {
  const sessions = new Map<string, {
    sessionId: string; project: string; firstTs: number; lastTs: number;
    events: number; tokens: number; cost: number; model: string; title: string;
  }>();
  for (const e of walkJsonl()) {
    let s = sessions.get(e.sessionId);
    if (!s) {
      s = {
        sessionId: e.sessionId,
        project: path.basename(e.projectPath),
        firstTs: e.ts,
        lastTs: e.ts,
        events: 0,
        tokens: 0,
        cost: 0,
        model: e.model,
        title: (e as any).title ?? '',
      };
      sessions.set(e.sessionId, s);
    }
    s.firstTs = Math.min(s.firstTs, e.ts);
    s.lastTs = Math.max(s.lastTs, e.ts);
    s.events += 1;
    s.tokens += e.input + e.output + e.cacheCreate + e.cacheRead;
    s.cost += estimateCost(e.model, e);
    s.model = e.model;
  }
  const rows = Array.from(sessions.values())
    .sort((a, b) => b.lastTs - a.lastTs)
    .slice(0, limit);
  printTable(
    ['Last Activity', 'Project', 'Events', 'Cost USD', 'Title'],
    rows.map((r) => [
      new Date(r.lastTs).toISOString().slice(0, 16).replace('T', ' '),
      r.project,
      String(r.events),
      `$${r.cost.toFixed(2)}`,
      r.title || '(no title)',
    ]),
  );
}

function printTable(headers: string[], rows: string[][]) {
  const widths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
  const fmt = (cells: string[]) => '  ' + cells.map((c, i) => c.padEnd(widths[i])).join('  ');
  console.log(fmt(headers));
  console.log('  ' + widths.map((w) => '─'.repeat(w)).join('  '));
  for (const r of rows) console.log(fmt(r));
  if (rows.length === 0) console.log('  (no data)');
}
