const test = require("node:test");
const assert = require("node:assert/strict");
const fs = require("node:fs/promises");
const os = require("node:os");
const path = require("node:path");

const {
  parseClaudeLine,
  parseCodexLine,
  parseGeminiLine,
  summarizeRecords,
  scanUsage
} = require("../src/lib/tokenUsage.cjs");

test("parseClaudeLine extracts usage without reading message content", () => {
  const line = JSON.stringify({
    timestamp: "2026-06-11T02:20:00.000Z",
    cwd: "/Users/me/Code/App",
    sessionId: "claude-session",
    message: {
      role: "assistant",
      content: "private conversation text",
      model: "claude-3-5-sonnet",
      usage: {
        input_tokens: 120,
        output_tokens: 30,
        cache_read_input_tokens: 50,
        cache_creation_input_tokens: 10
      }
    }
  });

  const record = parseClaudeLine(line, "/tmp/claude.jsonl");

  assert.equal(record.provider, "Claude");
  assert.equal(record.model, "claude-3-5-sonnet");
  assert.equal(record.inputTokens, 120);
  assert.equal(record.outputTokens, 30);
  assert.equal(record.cachedTokens, 60);
  assert.equal(record.totalTokens, 210);
  assert.equal(record.contentPreview, undefined);
});

test("parseCodexLine uses last_token_usage instead of cumulative total usage", () => {
  const line = JSON.stringify({
    timestamp: "2026-06-11T03:10:00.000Z",
    type: "event_msg",
    payload: {
      info: {
        total_token_usage: {
          input_tokens: 1000,
          output_tokens: 300,
          cached_input_tokens: 200,
          reasoning_output_tokens: 40,
          total_tokens: 1300
        },
        last_token_usage: {
          input_tokens: 100,
          output_tokens: 30,
          cached_input_tokens: 20,
          reasoning_output_tokens: 4,
          total_tokens: 130
        }
      }
    }
  });

  const record = parseCodexLine(line, "/tmp/rollout.jsonl");

  assert.equal(record.provider, "Codex");
  assert.equal(record.inputTokens, 100);
  assert.equal(record.outputTokens, 30);
  assert.equal(record.cachedTokens, 20);
  assert.equal(record.reasoningTokens, 4);
  assert.equal(record.totalTokens, 154);
});

test("parseGeminiLine extracts tokens from Gemini chat jsonl", () => {
  const line = JSON.stringify({
    timestamp: "2026-06-11T04:00:00.000Z",
    type: "response",
    model: "gemini-2.5-pro",
    tokens: {
      input: 70,
      output: 20,
      cached: 5,
      thoughts: 3,
      tool: 2,
      total: 100
    }
  });

  const record = parseGeminiLine(line, "/tmp/session.jsonl");

  assert.equal(record.provider, "Gemini");
  assert.equal(record.model, "gemini-2.5-pro");
  assert.equal(record.inputTokens, 70);
  assert.equal(record.outputTokens, 20);
  assert.equal(record.cachedTokens, 5);
  assert.equal(record.reasoningTokens, 3);
  assert.equal(record.toolTokens, 2);
  assert.equal(record.totalTokens, 100);
});

test("summarizeRecords returns today, month, total, provider, trend and insight data", () => {
  const now = new Date("2026-06-11T12:00:00+08:00");
  const records = [
    record("Claude", "2026-06-11T01:00:00Z", 100, 20, 10),
    record("Codex", "2026-06-10T01:00:00Z", 80, 20, 0),
    record("Gemini", "2026-05-31T01:00:00Z", 50, 10, 0)
  ];

  const summary = summarizeRecords(records, { now, timeZone: "Asia/Shanghai" });

  assert.equal(summary.cards.todayTokens, 130);
  assert.equal(summary.cards.monthTokens, 230);
  assert.equal(summary.cards.totalTokens, 290);
  assert.equal(summary.providerTotals.Claude.totalTokens, 130);
  assert.equal(summary.providerTotals.Codex.totalTokens, 100);
  assert.equal(summary.providerTotals.Gemini.totalTokens, 60);
  assert.ok(summary.dailyTrend.length >= 2);
  assert.ok(summary.sourceRows.length >= 3);
  assert.ok(summary.insights.length >= 1);
});

test("scanUsage reads only known local usage files and aggregates them", async () => {
  const homeDir = await fs.mkdtemp(path.join(os.tmpdir(), "tokenlens-"));
  await fs.mkdir(path.join(homeDir, ".claude/projects/proj"), { recursive: true });
  await fs.mkdir(path.join(homeDir, ".codex/sessions/2026/06/11"), { recursive: true });
  await fs.mkdir(path.join(homeDir, ".gemini/tmp/me/chats"), { recursive: true });

  await fs.writeFile(
    path.join(homeDir, ".claude/projects/proj/a.jsonl"),
    JSON.stringify({
      timestamp: "2026-06-11T02:20:00.000Z",
      message: { model: "claude", usage: { input_tokens: 10, output_tokens: 5 } }
    }) + "\n"
  );
  await fs.writeFile(
    path.join(homeDir, ".codex/sessions/2026/06/11/rollout.jsonl"),
    JSON.stringify({
      timestamp: "2026-06-11T02:30:00.000Z",
      payload: { info: { last_token_usage: { input_tokens: 7, output_tokens: 3 } } }
    }) + "\n"
  );
  await fs.writeFile(
    path.join(homeDir, ".gemini/tmp/me/chats/session.jsonl"),
    JSON.stringify({
      timestamp: "2026-06-11T02:40:00.000Z",
      model: "gemini",
      tokens: { input: 8, output: 2, total: 10 }
    }) + "\n"
  );

  const summary = await scanUsage({
    homeDir,
    now: new Date("2026-06-11T12:00:00+08:00"),
    timeZone: "Asia/Shanghai"
  });

  assert.equal(summary.records.length, 3);
  assert.equal(summary.cards.totalTokens, 35);
  assert.equal(summary.providerTotals.Claude.totalTokens, 15);
  assert.equal(summary.providerTotals.Codex.totalTokens, 10);
  assert.equal(summary.providerTotals.Gemini.totalTokens, 10);
});

function record(provider, timestamp, inputTokens, outputTokens, cachedTokens) {
  return {
    provider,
    source: `${provider} source`,
    sourceFile: `/tmp/${provider}.jsonl`,
    timestamp,
    model: provider,
    inputTokens,
    outputTokens,
    cachedTokens,
    reasoningTokens: 0,
    toolTokens: 0,
    totalTokens: inputTokens + outputTokens + cachedTokens
  };
}
