import { beforeEach, describe, expect, type Mock, type MockedFunction, test, vi } from 'vitest';
import { runDefaultAction } from '../../../src/cli/actions/defaultAction.js';
import * as cliReport from '../../../src/cli/cliReport.js';
import type { CliOptions } from '../../../src/cli/types.js';
import * as configLoad from '../../../src/config/configLoad.js';
import * as packager from '../../../src/core/packager.js';

vi.mock('../../../src/config/configLoad.js');
vi.mock('../../../src/core/packager.js');
vi.mock('../../../src/cli/cliReport.js');
vi.mock('../../../src/cli/actions/migrationAction.js', () => ({
  runMigrationAction: vi.fn(),
}));

const mockSpinner = {
  start: vi.fn() as MockedFunction<() => void>,
  update: vi.fn() as MockedFunction<(message: string) => void>,
  succeed: vi.fn() as MockedFunction<(message: string) => void>,
  fail: vi.fn() as MockedFunction<(message: string) => void>,
};

vi.mock('../../../src/cli/cliSpinner', () => {
  const MockSpinner = class {
    start = mockSpinner.start;
    update = mockSpinner.update;
    succeed = mockSpinner.succeed;
    fail = mockSpinner.fail;
  };
  return { Spinner: MockSpinner };
});

describe('defaultAction with tokenCountTree', () => {
  const mockLoadFileConfig = configLoad.loadFileConfig as Mock;
  const mockMergeConfigs = configLoad.mergeConfigs as Mock;
  const mockPack = packager.pack as Mock;
  const mockReportResults = cliReport.reportResults as Mock;

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

    // Setup default mocks
    mockLoadFileConfig.mockResolvedValue({});
    mockMergeConfigs.mockReturnValue({
      output: {
        filePath: 'output.xml',
        topFilesLength: 5,
        tokenCountTree: false,
      },
      tokenCount: {
        encoding: 'o200k_base',
      },
    });
    mockPack.mockResolvedValue({
      totalFiles: 3,
      totalCharacters: 1000,
      totalTokens: 200,
      fileCharCounts: {},
      fileTokenCounts: {},
      gitDiffTokenCount: 0,
      suspiciousFilesResults: [],
      suspiciousGitDiffResults: [],
      processedFiles: [
        { path: '/test/file1.js', content: 'content1' },
        { path: '/test/file2.js', content: 'content2' },
      ],
      safeFilePaths: ['/test/file1.js', '/test/file2.js'],
    });
  });

  test('should display token count tree when --token-count-tree option is provided', async () => {
    const cliOptions: CliOptions = {
      tokenCountTree: true,
    };

    // Mock config to have tokenCountTree enabled
    mockMergeConfigs.mockReturnValue({
      output: {
        filePath: 'output.xml',
        topFilesLength: 5,
        tokenCountTree: true,
      },
      tokenCount: {
        encoding: 'o200k_base',
      },
    });

    await runDefaultAction(['.'], '/test', cliOptions);

    expect(mockReportResults).toHaveBeenCalledWith(
      '/test',
      expect.objectContaining({
        processedFiles: [
          { path: '/test/file1.js', content: 'content1' },
          { path: '/test/file2.js', content: 'content2' },
        ],
        fileTokenCounts: {},
      }),
      expect.objectContaining({
        output: expect.objectContaining({
          tokenCountTree: true,
        }),
      }),
      expect.any(Object),
    );
  });

  test('should not display token count tree when option is not provided', async () => {
    const cliOptions: CliOptions = {};

    await runDefaultAction(['.'], '/test', cliOptions);

    expect(mockReportResults).toHaveBeenCalledWith(
      '/test',
      expect.any(Object),
      expect.objectContaining({
        output: expect.objectContaining({
          tokenCountTree: false,
        }),
      }),
      expect.any(Object),
    );
  });

  test('should display token count tree for multiple directories', async () => {
    const cliOptions: CliOptions = {
      tokenCountTree: true,
    };

    // Mock config to have tokenCountTree enabled
    mockMergeConfigs.mockReturnValue({
      output: {
        filePath: 'output.xml',
        topFilesLength: 5,
        tokenCountTree: true,
      },
      tokenCount: {
        encoding: 'o200k_base',
      },
    });

    await runDefaultAction(['src', 'tests'], '/project', cliOptions);

    expect(mockReportResults).toHaveBeenCalledWith(
      '/project',
      expect.any(Object),
      expect.objectContaining({
        output: expect.objectContaining({
          tokenCountTree: true,
        }),
      }),
      expect.any(Object),
    );
  });

  test('should pass threshold parameter when provided', async () => {
    const cliOptions: CliOptions = {
      tokenCountTree: 50,
    };

    // Mock config to have tokenCountTree enabled with threshold
    mockMergeConfigs.mockReturnValue({
      output: {
        filePath: 'output.xml',
        topFilesLength: 5,
        tokenCountTree: 50,
      },
      tokenCount: {
        encoding: 'o200k_base',
      },
    });

    await runDefaultAction(['.'], '/test', cliOptions);

    expect(mockReportResults).toHaveBeenCalledWith(
      '/test',
      expect.any(Object),
      expect.objectContaining({
        output: expect.objectContaining({
          tokenCountTree: 50,
        }),
      }),
      expect.any(Object),
    );
  });
});
