import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import * as fs from 'node:fs';
import * as path from 'node:path';
import { createUsageCommand } from '../../src/commands/usage';
import { Command } from 'commander';

function makeSampleJSONL(
  entries: Array<{
    timestamp: string;
    session_id: string;
    input_tokens: number;
    output_tokens: number;
    model?: string;
    cacheCreationTokens?: number;
    cacheReadTokens?: number;
  }>
): string {
  return (
    entries
      .map((e) =>
        JSON.stringify({
          timestamp: e.timestamp,
          session_id: e.session_id,
          token_usage: { input_tokens: e.input_tokens, output_tokens: e.output_tokens },
          ...(e.model != null ? { model: e.model } : {}),
          ...(e.cacheCreationTokens != null
            ? { cache_creation_tokens: e.cacheCreationTokens }
            : {}),
          ...(e.cacheReadTokens != null ? { cache_read_tokens: e.cacheReadTokens } : {}),
        })
      )
      .join('\n') + '\n'
  );
}

describe('harness usage', () => {
  const tmpDir = path.join(__dirname, '__usage-test-tmp__');
  const costsFile = path.join(tmpDir, '.harness', 'metrics', 'costs.jsonl');
  let consoleLogSpy: ReturnType<typeof vi.spyOn>;
  let logOutput: string[];
  let originalCwd: string;

  const sampleData = makeSampleJSONL([
    {
      timestamp: '2026-03-30T10:00:00.000Z',
      session_id: 'sess-aaa-111',
      input_tokens: 1000,
      output_tokens: 500,
    },
    {
      timestamp: '2026-03-30T14:00:00.000Z',
      session_id: 'sess-aaa-111',
      input_tokens: 2000,
      output_tokens: 800,
    },
    {
      timestamp: '2026-03-31T09:00:00.000Z',
      session_id: 'sess-bbb-222',
      input_tokens: 500,
      output_tokens: 200,
      model: 'claude-sonnet-4-20250514',
    },
    {
      timestamp: '2026-03-29T08:00:00.000Z',
      session_id: 'sess-ccc-333',
      input_tokens: 3000,
      output_tokens: 1500,
      cacheReadTokens: 100,
      cacheCreationTokens: 50,
    },
  ]);

  let fetchSpy: ReturnType<typeof vi.spyOn>;

  beforeEach(() => {
    originalCwd = process.cwd();
    fs.mkdirSync(path.dirname(costsFile), { recursive: true });
    fs.writeFileSync(costsFile, sampleData);
    // Seed a fresh pricing cache so loadPricingData skips the network fetch entirely.
    // This avoids flaky timeouts on CI runners where the fetch mock may not fully intercept.
    const pricingCacheDir = path.join(tmpDir, '.harness', 'cache');
    fs.mkdirSync(pricingCacheDir, { recursive: true });
    fs.writeFileSync(
      path.join(pricingCacheDir, 'pricing.json'),
      JSON.stringify({
        fetchedAt: new Date().toISOString(),
        data: {
          'claude-sonnet-4-20250514': {
            input_cost_per_token: 0.000003,
            output_cost_per_token: 0.000015,
            cache_read_input_token_cost: 0.0000003,
            cache_creation_input_token_cost: 0.00000375,
            mode: 'chat',
          },
        },
      })
    );
    process.chdir(tmpDir);
    logOutput = [];
    consoleLogSpy = vi.spyOn(console, 'log').mockImplementation((...args) => {
      logOutput.push(args.join(' '));
    });
    // Suppress logger info (chalk-decorated) from polluting test output
    vi.spyOn(console, 'warn').mockImplementation(() => {});
    // Prevent real network requests to LiteLLM pricing endpoint (avoids timeouts)
    fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue(new Response(null, { status: 503 }));
  });

  afterEach(() => {
    process.chdir(originalCwd);
    consoleLogSpy.mockRestore();
    fetchSpy.mockRestore();
    vi.restoreAllMocks();
    fs.rmSync(tmpDir, { recursive: true, force: true });
  });

  function createProgram(): Command {
    const program = new Command();
    program.option('--json', 'JSON output');
    program.addCommand(createUsageCommand());
    return program;
  }

  describe('daily', () => {
    it('outputs JSON array when --json is passed', async () => {
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'daily', '--json']);

      // Filter for JSON array line to avoid non-JSON logger output on some platforms
      const jsonLine = logOutput.find((line) => line.trimStart().startsWith('['));
      const output = JSON.parse(jsonLine ?? logOutput.join(''));
      expect(Array.isArray(output)).toBe(true);
      expect(output.length).toBeGreaterThanOrEqual(1);
      // Should have date, sessionCount, tokens, models
      expect(output[0]).toHaveProperty('date');
      expect(output[0]).toHaveProperty('sessionCount');
      expect(output[0]).toHaveProperty('tokens');
    });

    it('limits days with --days flag', async () => {
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'daily', '--days', '1', '--json']);

      // Filter for JSON array line to avoid non-JSON logger output on some platforms
      const jsonLine = logOutput.find((line) => line.trimStart().startsWith('['));
      const output = JSON.parse(jsonLine ?? logOutput.join(''));
      expect(output).toHaveLength(1);
    });

    it('outputs empty array when no data exists', async () => {
      fs.unlinkSync(costsFile);
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'daily', '--json']);

      const output = JSON.parse(logOutput.join(''));
      expect(output).toEqual([]);
    });

    it('includes cacheHitRate in daily JSON when cache data exists', async () => {
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'daily', '--json']);

      const output = JSON.parse(logOutput.join(''));
      // 2026-03-29 has cacheReadTokens=100, inputTokens=3000
      const march29 = output.find((d: any) => d.date === '2026-03-29');
      expect(march29).toBeDefined();
      expect(march29.cacheHitRate).toBeCloseTo(0.033, 2);

      // 2026-03-30 has no cache data
      const march30 = output.find((d: any) => d.date === '2026-03-30');
      expect(march30).toBeDefined();
      expect(march30.cacheHitRate).toBeUndefined();
    });
  });

  describe('sessions', () => {
    it('outputs JSON array of sessions when --json is passed', async () => {
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'sessions', '--json']);

      const output = JSON.parse(logOutput.join(''));
      expect(Array.isArray(output)).toBe(true);
      // 3 distinct sessions in sample data
      expect(output).toHaveLength(3);
      expect(output[0]).toHaveProperty('sessionId');
      expect(output[0]).toHaveProperty('tokens');
      expect(output[0]).toHaveProperty('firstTimestamp');
    });

    it('limits sessions with --limit flag', async () => {
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'sessions', '--limit', '2', '--json']);

      const output = JSON.parse(logOutput.join(''));
      expect(output).toHaveLength(2);
    });
  });

  describe('session <id>', () => {
    it('outputs JSON detail for a valid session', async () => {
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'session', 'sess-bbb-222', '--json']);

      const output = JSON.parse(logOutput.join(''));
      expect(output.sessionId).toBe('sess-bbb-222');
      expect(output.tokens.inputTokens).toBe(500);
      expect(output.tokens.outputTokens).toBe(200);
    });

    it('returns error with suggestions for invalid session', async () => {
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'session', 'sess-aaa', '--json']);

      const output = JSON.parse(logOutput.join(''));
      expect(output).toHaveProperty('error');
      expect(output.suggestions).toContain('sess-aaa-111');
    });

    it('returns error with empty suggestions for completely unknown id', async () => {
      const program = createProgram();
      await program.parseAsync([
        'node',
        'harness',
        'usage',
        'session',
        'nonexistent-xyz',
        '--json',
      ]);

      const output = JSON.parse(logOutput.join(''));
      expect(output.error).toContain('not found');
      expect(output.suggestions).toEqual([]);
    });

    it('includes cache tokens in detail view', async () => {
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'session', 'sess-ccc-333', '--json']);

      const output = JSON.parse(logOutput.join(''));
      expect(output.cacheReadTokens).toBe(100);
      expect(output.cacheCreationTokens).toBe(50);
    });
  });

  describe('latest', () => {
    it('outputs JSON for the most recent session', async () => {
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'latest', '--json']);

      const output = JSON.parse(logOutput.join(''));
      // Most recent by timestamp should be sess-bbb-222 (2026-03-31)
      expect(output.sessionId).toBe('sess-bbb-222');
    });

    it('returns error when no data exists', async () => {
      fs.unlinkSync(costsFile);
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'latest', '--json']);

      const output = JSON.parse(logOutput.join(''));
      expect(output).toHaveProperty('error');
    });
  });

  describe('edge cases', () => {
    it('handles malformed JSONL lines without crashing', async () => {
      fs.writeFileSync(
        costsFile,
        'bad line\n' +
          makeSampleJSONL([
            {
              timestamp: '2026-03-31T10:00:00.000Z',
              session_id: 'sess-ok',
              input_tokens: 100,
              output_tokens: 50,
            },
          ])
      );

      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'sessions', '--json']);

      const output = JSON.parse(logOutput.join(''));
      expect(output).toHaveLength(1);
      expect(output[0].sessionId).toBe('sess-ok');
    });

    it('handles legacy entries without model as unknown cost', async () => {
      fs.writeFileSync(
        costsFile,
        makeSampleJSONL([
          {
            timestamp: '2026-03-31T10:00:00.000Z',
            session_id: 'sess-legacy',
            input_tokens: 1000,
            output_tokens: 500,
          },
        ])
      );

      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'session', 'sess-legacy', '--json']);

      const output = JSON.parse(logOutput.join(''));
      expect(output.costMicroUSD).toBeNull();
    });

    it('has --include-claude-sessions option on usage command', () => {
      const cmd = createUsageCommand();
      const opts = cmd.options.map((o) => o.long);
      expect(opts).toContain('--include-claude-sessions');
    });
  });

  describe('daily (text output)', () => {
    it('renders a table with header and rows', async () => {
      const loggerOutput: string[] = [];
      vi.spyOn(console, 'log').mockImplementation((...args) => {
        loggerOutput.push(args.join(' '));
      });
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'daily']);

      // Logger info goes through console.log with chalk prefix
      const allOutput = loggerOutput.join('\n');
      // Should have the header columns
      expect(allOutput).toContain('Date');
      expect(allOutput).toContain('Sessions');
      expect(allOutput).toContain('Input');
      expect(allOutput).toContain('Output');
    });

    it('renders cache column when cache data is present', async () => {
      const loggerOutput: string[] = [];
      vi.spyOn(console, 'log').mockImplementation((...args) => {
        loggerOutput.push(args.join(' '));
      });
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'daily']);

      const allOutput = loggerOutput.join('\n');
      expect(allOutput).toContain('Cache');
    });

    it('renders text message when no data exists', async () => {
      fs.unlinkSync(costsFile);
      const loggerOutput: string[] = [];
      vi.spyOn(console, 'log').mockImplementation((...args) => {
        loggerOutput.push(args.join(' '));
      });
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'daily']);

      const allOutput = loggerOutput.join('\n');
      expect(allOutput).toContain('No usage data found');
    });

    it('clamps --days to minimum of 1', async () => {
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'daily', '--days', '0', '--json']);

      const output = JSON.parse(logOutput.join(''));
      // Clamped to 1, so at most 1 day
      expect(output.length).toBeLessThanOrEqual(3); // We have 3 days of data
      expect(output.length).toBeGreaterThanOrEqual(1);
    });

    it('clamps --days to maximum of 90', async () => {
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'daily', '--days', '200', '--json']);

      const output = JSON.parse(logOutput.join(''));
      // All 3 days should be returned (less than 90)
      expect(output.length).toBe(3);
    });

    it('defaults --days to 7 for non-numeric input', async () => {
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'daily', '--days', 'abc', '--json']);

      const output = JSON.parse(logOutput.join(''));
      // Defaults to 7 which is more than our 3 days of data
      expect(output.length).toBe(3);
    });
  });

  describe('sessions (text output)', () => {
    it('renders a table with header and rows', async () => {
      const loggerOutput: string[] = [];
      vi.spyOn(console, 'log').mockImplementation((...args) => {
        loggerOutput.push(args.join(' '));
      });
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'sessions']);

      const allOutput = loggerOutput.join('\n');
      expect(allOutput).toContain('Session ID');
      expect(allOutput).toContain('Started');
      expect(allOutput).toContain('Duration');
      expect(allOutput).toContain('Cost');
    });

    it('renders text message when no data exists', async () => {
      fs.unlinkSync(costsFile);
      const loggerOutput: string[] = [];
      vi.spyOn(console, 'log').mockImplementation((...args) => {
        loggerOutput.push(args.join(' '));
      });
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'sessions']);

      const allOutput = loggerOutput.join('\n');
      expect(allOutput).toContain('No usage data found');
    });

    it('clamps --limit to minimum of 1', async () => {
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'sessions', '--limit', '1', '--json']);

      const output = JSON.parse(logOutput.join(''));
      expect(output).toHaveLength(1);
    });

    it('defaults --limit to 10 for non-numeric input', async () => {
      const program = createProgram();
      await program.parseAsync([
        'node',
        'harness',
        'usage',
        'sessions',
        '--limit',
        'abc',
        '--json',
      ]);

      const output = JSON.parse(logOutput.join(''));
      // Defaults to 10, but we only have 3 sessions
      expect(output).toHaveLength(3);
    });
  });

  describe('session <id> (text output)', () => {
    it('renders detailed session text view', async () => {
      const loggerOutput: string[] = [];
      vi.spyOn(console, 'log').mockImplementation((...args) => {
        loggerOutput.push(args.join(' '));
      });
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'session', 'sess-bbb-222']);

      const allOutput = loggerOutput.join('\n');
      expect(allOutput).toContain('Session: sess-bbb-222');
      expect(allOutput).toContain('Token Breakdown');
      expect(allOutput).toContain('Input tokens');
      expect(allOutput).toContain('Output tokens');
      expect(allOutput).toContain('Cost:');
    });

    it('renders cache performance section when cache data present', async () => {
      const loggerOutput: string[] = [];
      vi.spyOn(console, 'log').mockImplementation((...args) => {
        loggerOutput.push(args.join(' '));
      });
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'session', 'sess-ccc-333']);

      const allOutput = loggerOutput.join('\n');
      expect(allOutput).toContain('Cache read tokens');
      expect(allOutput).toContain('Cache creation tokens');
      expect(allOutput).toContain('Cache Performance');
      expect(allOutput).toContain('Cache hit rate');
    });

    it('renders error with suggestions in text mode for invalid session', async () => {
      const loggerOutput: string[] = [];
      vi.spyOn(console, 'log').mockImplementation((...args) => {
        loggerOutput.push(args.join(' '));
      });
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'session', 'sess-aaa']);

      const allOutput = loggerOutput.join('\n');
      expect(allOutput).toContain('Did you mean');
      expect(allOutput).toContain('sess-aaa-111');
    });

    it('renders error without suggestions for completely unknown id in text mode', async () => {
      const loggerOutput: string[] = [];
      vi.spyOn(console, 'log').mockImplementation((...args) => {
        loggerOutput.push(args.join(' '));
      });
      vi.spyOn(console, 'error').mockImplementation((...args) => {
        loggerOutput.push(args.join(' '));
      });
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'session', 'nonexistent-xyz']);

      const allOutput = loggerOutput.join('\n');
      expect(allOutput).toContain('not found');
      expect(allOutput).not.toContain('Did you mean');
    });

    it('includes cacheHitRate in JSON when cache data exists', async () => {
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'session', 'sess-ccc-333', '--json']);

      const output = JSON.parse(logOutput.join(''));
      expect(output.cacheHitRate).toBeCloseTo(0.033, 2);
    });

    it('omits cacheHitRate in JSON when no cache data', async () => {
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'session', 'sess-bbb-222', '--json']);

      const output = JSON.parse(logOutput.join(''));
      expect(output.cacheHitRate).toBeUndefined();
    });

    it('omits cacheSavingsMicroUSD when model is unknown', async () => {
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'session', 'sess-ccc-333', '--json']);

      const output = JSON.parse(logOutput.join(''));
      // sess-ccc-333 has cache data but no model, so savings can't be computed
      expect(output.cacheSavingsMicroUSD).toBeUndefined();
    });
  });

  describe('latest (text output)', () => {
    it('renders the most recent session summary in text mode', async () => {
      const loggerOutput: string[] = [];
      vi.spyOn(console, 'log').mockImplementation((...args) => {
        loggerOutput.push(args.join(' '));
      });
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'latest']);

      const allOutput = loggerOutput.join('\n');
      expect(allOutput).toContain('Session:');
      expect(allOutput).toContain('Started:');
      expect(allOutput).toContain('Ended:');
      expect(allOutput).toContain('Model:');
      expect(allOutput).toContain('Tokens:');
      expect(allOutput).toContain('Cost:');
    });

    it('renders text message when no data exists', async () => {
      fs.unlinkSync(costsFile);
      const loggerOutput: string[] = [];
      vi.spyOn(console, 'log').mockImplementation((...args) => {
        loggerOutput.push(args.join(' '));
      });
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'latest']);

      const allOutput = loggerOutput.join('\n');
      expect(allOutput).toContain('No usage data found');
    });
  });

  describe('cache savings', () => {
    const cacheSavingsData = makeSampleJSONL([
      {
        timestamp: '2026-04-01T10:00:00.000Z',
        session_id: 'sess-cached-001',
        input_tokens: 100_000,
        output_tokens: 5_000,
        model: 'claude-sonnet-4-20250514',
        cacheReadTokens: 80_000,
        cacheCreationTokens: 10_000,
      },
    ]);

    it('includes cacheSavingsMicroUSD in session JSON when model and cache data exist', async () => {
      fs.writeFileSync(costsFile, cacheSavingsData);
      const program = createProgram();
      await program.parseAsync([
        'node',
        'harness',
        'usage',
        'session',
        'sess-cached-001',
        '--json',
      ]);

      const output = JSON.parse(logOutput.join(''));
      expect(output.cacheSavingsMicroUSD).toBeDefined();
      expect(output.cacheSavingsMicroUSD).toBeGreaterThan(0);
      // savings = 80000 * (3.0 - 0.3) / 1M * 1M = 80000 * 2.7 / 1M * 1M = 216000
      expect(output.cacheSavingsMicroUSD).toBe(216000);
    });

    it('includes cacheSavingsMicroUSD in daily JSON when model and cache data exist', async () => {
      fs.writeFileSync(costsFile, cacheSavingsData);
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'daily', '--json']);

      const output = JSON.parse(logOutput.join(''));
      const april1 = output.find((d: any) => d.date === '2026-04-01');
      expect(april1).toBeDefined();
      expect(april1.cacheSavingsMicroUSD).toBeDefined();
      expect(april1.cacheSavingsMicroUSD).toBe(216000);
    });

    it('shows Saved column in daily text output with cache data', async () => {
      fs.writeFileSync(costsFile, cacheSavingsData);
      const loggerOutput: string[] = [];
      vi.spyOn(console, 'log').mockImplementation((...args) => {
        loggerOutput.push(args.join(' '));
      });
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'daily']);

      const allOutput = loggerOutput.join('\n');
      expect(allOutput).toContain('Saved');
    });

    it('shows Estimated savings in session text output with cache data', async () => {
      fs.writeFileSync(costsFile, cacheSavingsData);
      const loggerOutput: string[] = [];
      vi.spyOn(console, 'log').mockImplementation((...args) => {
        loggerOutput.push(args.join(' '));
      });
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'session', 'sess-cached-001']);

      const allOutput = loggerOutput.join('\n');
      expect(allOutput).toContain('Estimated savings');
    });
  });

  describe('--include-claude-sessions', () => {
    const ccHome = path.join(tmpDir, '__cc-home__');
    const ccProjectDir = path.join(ccHome, '.claude', 'projects', '-test-project');
    let savedHome: string | undefined;

    function makeCCAssistantLine(opts: {
      sessionId: string;
      model: string;
      inputTokens: number;
      outputTokens: number;
      timestamp: string;
    }): string {
      return JSON.stringify({
        type: 'assistant',
        sessionId: opts.sessionId,
        timestamp: opts.timestamp,
        message: {
          model: opts.model,
          role: 'assistant',
          usage: {
            input_tokens: opts.inputTokens,
            output_tokens: opts.outputTokens,
          },
          stop_reason: 'end_turn',
          content: [{ type: 'text', text: 'response' }],
        },
      });
    }

    beforeEach(() => {
      savedHome = process.env.HOME;
      process.env.HOME = ccHome;
      fs.mkdirSync(ccProjectDir, { recursive: true });
      fs.writeFileSync(
        path.join(ccProjectDir, 'cc-session.jsonl'),
        makeCCAssistantLine({
          sessionId: 'cc-only-sess',
          model: 'claude-opus-4-20250514',
          inputTokens: 5000,
          outputTokens: 2000,
          timestamp: '2026-03-31T12:00:00.000Z',
        }) + '\n'
      );
    });

    afterEach(() => {
      process.env.HOME = savedHome;
    });

    it('merges CC records into sessions output', async () => {
      const program = createProgram();
      await program.parseAsync([
        'node',
        'harness',
        'usage',
        '--include-claude-sessions',
        'sessions',
        '--json',
      ]);

      // Filter for the JSON array line to avoid non-JSON logger output
      const jsonLine = logOutput.find((line) => line.startsWith('['));
      const output = JSON.parse(jsonLine ?? '[]');
      expect(Array.isArray(output)).toBe(true);
      // Should include both harness sessions (3) and CC session (1)
      const ccSession = output.find((s: any) => s.sessionId === 'cc-only-sess');
      expect(ccSession).toBeDefined();
      expect(ccSession.source).toBe('claude-code');
      expect(ccSession.model).toBe('claude-opus-4-20250514');
    });

    it('merges CC data into daily output', async () => {
      const program = createProgram();
      await program.parseAsync([
        'node',
        'harness',
        'usage',
        '--include-claude-sessions',
        'daily',
        '--json',
      ]);

      const jsonLine = logOutput.find((line) => line.startsWith('['));
      const output = JSON.parse(jsonLine ?? '[]');
      // 2026-03-31 should now have 2 sessions (harness sess-bbb-222 + CC cc-only-sess)
      const march31 = output.find((d: any) => d.date === '2026-03-31');
      expect(march31).toBeDefined();
      expect(march31.sessionCount).toBe(2);
    });

    it('does not include CC data when flag is not passed', async () => {
      const program = createProgram();
      await program.parseAsync(['node', 'harness', 'usage', 'sessions', '--json']);

      const output = JSON.parse(logOutput.join(''));
      const ccSession = output.find((s: any) => s.sessionId === 'cc-only-sess');
      expect(ccSession).toBeUndefined();
    });
  });
});
