import { beforeEach, describe, expect, type Mock, test, vi } from 'vitest';
import { reportTokenCountTree } from '../../../src/cli/reporters/tokenCountTreeReporter.js';
import type { RepomixConfigMerged } from '../../../src/config/configSchema.js';
import type { ProcessedFile } from '../../../src/core/file/fileTypes.js';
import { logger } from '../../../src/shared/logger.js';

vi.mock('../../../src/shared/logger');
vi.mock('picocolors', () => ({
  default: {
    white: (str: string) => `WHITE:${str}`,
    dim: (str: string) => `DIM:${str}`,
    green: (str: string) => `GREEN:${str}`,
    yellow: (str: string) => `YELLOW:${str}`,
    red: (str: string) => `RED:${str}`,
    cyan: (str: string) => `CYAN:${str}`,
    underline: (str: string) => `UNDERLINE:${str}`,
  },
}));

describe('reportTokenCountTree', () => {
  const mockLogger = logger.log as Mock;

  beforeEach(() => {
    vi.clearAllMocks();
  });

  test('should use existing token counts and display tree', () => {
    const processedFiles: ProcessedFile[] = [
      { path: 'src/file1.js', content: 'const a = 1;' },
      { path: 'src/file2.js', content: 'function test() {}' },
      { path: 'tests/test.js', content: 'test("example", () => {});' },
    ];

    const fileTokenCounts = {
      'src/file1.js': 5,
      'src/file2.js': 7,
      'tests/test.js': 10,
    };

    const config = {
      output: { tokenCountTree: true },
    } as RepomixConfigMerged;

    reportTokenCountTree(processedFiles, fileTokenCounts, config);

    // Verify token count tree is displayed
    const calls = mockLogger.mock.calls.map((call: unknown[]) => call[0] as string);
    expect(calls.some((call) => call.includes('🔢 Token Count Tree:'))).toBe(true);
    expect(calls.some((call) => call.includes('DIM:────────────────────'))).toBe(true);
    expect(calls.some((call) => call.includes('src/'))).toBe(true);
    expect(calls.some((call) => call.includes('file1.js'))).toBe(true);
    expect(calls.some((call) => call.includes('file2.js'))).toBe(true);
  });

  test('should handle empty file list', () => {
    const processedFiles: ProcessedFile[] = [];
    const fileTokenCounts = {};
    const config = {
      output: { tokenCountTree: true },
    } as RepomixConfigMerged;

    reportTokenCountTree(processedFiles, fileTokenCounts, config);

    const calls = mockLogger.mock.calls.map((call: unknown[]) => call[0] as string);
    expect(calls.some((call) => call.includes('🔢 Token Count Tree:'))).toBe(true);
    expect(calls.some((call) => call.includes('DIM:────────────────────'))).toBe(true);
    expect(calls.some((call) => call.includes('No files found.'))).toBe(true);
  });

  test('should pass minimum token count threshold to display function', () => {
    const processedFiles: ProcessedFile[] = [
      { path: 'src/file1.js', content: 'const a = 1;' },
      { path: 'src/file2.js', content: 'function test() {}' },
    ];

    const fileTokenCounts = {
      'src/file1.js': 5,
      'src/file2.js': 15,
    };

    const config = {
      output: { tokenCountTree: 10 },
    } as RepomixConfigMerged;

    reportTokenCountTree(processedFiles, fileTokenCounts, config);

    // Verify threshold message is displayed
    const calls = mockLogger.mock.calls.map((call: unknown[]) => call[0] as string);
    expect(calls.some((call) => call.includes('🔢 Token Count Tree:'))).toBe(true);
    expect(calls.some((call) => call.includes('Showing entries with 10+ tokens:'))).toBe(true);
    expect(calls.some((call) => call.includes('DIM:────────────────────'))).toBe(true);
  });

  test('should skip files without token counts', () => {
    const processedFiles: ProcessedFile[] = [
      { path: 'src/file1.js', content: 'const a = 1;' },
      { path: 'src/file2.js', content: 'function test() {}' },
      { path: 'src/file3.js', content: 'missing tokens' },
    ];

    const fileTokenCounts = {
      'src/file1.js': 5,
      'src/file2.js': 7,
      // 'src/file3.js' is missing
    };

    const config = {
      output: { tokenCountTree: true },
    } as RepomixConfigMerged;

    reportTokenCountTree(processedFiles, fileTokenCounts, config);

    // Verify tree is displayed (files without token counts should be skipped)
    const calls = mockLogger.mock.calls.map((call: unknown[]) => call[0] as string);
    expect(calls.some((call) => call.includes('🔢 Token Count Tree:'))).toBe(true);
    expect(calls.some((call) => call.includes('DIM:────────────────────'))).toBe(true);
    expect(calls.some((call) => call.includes('file1.js'))).toBe(true);
    expect(calls.some((call) => call.includes('file2.js'))).toBe(true);
    expect(calls.some((call) => call.includes('file3.js'))).toBe(false); // Should be skipped
  });
});
