import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

// Mock dependencies
vi.mock('fs/promises', () => ({
  access: vi.fn(),
  writeFile: vi.fn(),
}));

vi.mock('../src/index', () => ({
  runLoadTest: vi.fn(),
}));

const runCli = async (args: string[]): Promise<void> => {
  process.argv = ['node', 'tressi', ...args];
  await import('../src/cli');
};

/**
 * Test suite for the main CLI functionality.
 */
describe('CLI', () => {
  beforeEach(() => {
    vi.resetModules(); // Reset module cache before each test
    vi.clearAllMocks(); // Clear mocks
  });

  afterEach(() => {
    // Restore original process.argv
    process.argv = process.env.npm_config_user_agent
      ? ['node', 'vitest', 'test']
      : ['node'];
  });

  /**
   * Test suite for the main command of the CLI.
   */
  describe('main command', () => {
    /**
     * It should correctly parse all the command-line arguments
     * and call the main `runLoadTest` function with the expected options object.
     */
    it('should call runLoadTest with the correct options', async () => {
      const { runLoadTest } = await import('../src/index');
      await runCli([
        '--config',
        './tressi.config.ts',
        '--workers',
        '20',
        '--duration',
        '60',
        '--rps',
        '500',
        '--autoscale',
        '--export',
        'my-report',
      ]);

      expect(runLoadTest).toHaveBeenCalledWith({
        config: './tressi.config.ts',
        workers: 20,
        durationSec: 60,
        rps: 500,
        autoscale: true,
        exportPath: 'my-report',
        rampUpTimeSec: undefined,
        useUI: true,
      });
    });

    /**
     * It should pass `exportPath: true` when the --export flag is used without a value.
     */
    it('should handle the --export flag without a path', async () => {
      const { runLoadTest } = await import('../src/index');
      await runCli(['--config', './tressi.config.ts', '--export']);

      expect(runLoadTest).toHaveBeenCalledWith(
        expect.objectContaining({
          exportPath: true,
        }),
      );
    });

    /**
     * It should validate that when --autoscale is used, --rps is also provided.
     * If not, it should print an error and exit with a non-zero status code.
     */
    it('should exit if --autoscale is used without --rps', async () => {
      const processExitSpy = vi
        .spyOn(process, 'exit')
        .mockImplementation(() => undefined as never);
      const consoleErrorSpy = vi
        .spyOn(console, 'error')
        .mockImplementation(() => {});

      await runCli(['--config', 'tressi.config.ts', '--autoscale']);

      expect(processExitSpy).toHaveBeenCalledWith(1);
      expect(consoleErrorSpy).toHaveBeenCalledWith(
        'Error: --rps is required when --autoscale is enabled.',
      );

      processExitSpy.mockRestore();
      consoleErrorSpy.mockRestore();
    });

    describe('early exit options', () => {
      it('should parse early exit options correctly', async () => {
        const { runLoadTest } = await import('../src/index');
        await runCli([
          '--config',
          './tressi.config.ts',
          '--early-exit-on-error',
          '--error-rate-threshold',
          '0.05',
          '--error-count-threshold',
          '100',
          '--error-status-codes',
          '500,503,404',
        ]);

        expect(runLoadTest).toHaveBeenCalledWith(
          expect.objectContaining({
            earlyExitOnError: true,
            errorRateThreshold: 0.05,
            errorCountThreshold: 100,
            errorStatusCodes: [500, 503, 404],
          }),
        );
      });

      it('should parse single error status code', async () => {
        const { runLoadTest } = await import('../src/index');
        await runCli([
          '--config',
          './tressi.config.ts',
          '--early-exit-on-error',
          '--error-status-codes',
          '500',
        ]);

        expect(runLoadTest).toHaveBeenCalledWith(
          expect.objectContaining({
            earlyExitOnError: true,
            errorStatusCodes: [500],
          }),
        );
      });

      it('should exit with error when early exit is enabled but no thresholds provided', async () => {
        const processExitSpy = vi
          .spyOn(process, 'exit')
          .mockImplementation(() => undefined as never);
        const consoleErrorSpy = vi
          .spyOn(console, 'error')
          .mockImplementation(() => {});

        await runCli([
          '--config',
          './tressi.config.ts',
          '--early-exit-on-error',
        ]);

        expect(processExitSpy).toHaveBeenCalledWith(1);
        expect(consoleErrorSpy).toHaveBeenCalledWith(
          'Error: When --early-exit-on-error is enabled, at least one of --error-rate-threshold, --error-count-threshold, or --error-status-codes must be provided.',
        );

        processExitSpy.mockRestore();
        consoleErrorSpy.mockRestore();
      });

      it('should exit with error when error status codes are invalid', async () => {
        const processExitSpy = vi
          .spyOn(process, 'exit')
          .mockImplementation(() => undefined as never);
        const consoleErrorSpy = vi
          .spyOn(console, 'error')
          .mockImplementation(() => {});

        await runCli([
          '--config',
          './tressi.config.ts',
          '--early-exit-on-error',
          '--error-status-codes',
          'invalid,500',
        ]);

        expect(processExitSpy).toHaveBeenCalledWith(1);
        expect(consoleErrorSpy).toHaveBeenCalled();

        processExitSpy.mockRestore();
        consoleErrorSpy.mockRestore();
      });

      it('should exit with error when error rate threshold is invalid', async () => {
        const { runLoadTest } = await import('../src/index');

        await runCli([
          '--config',
          './tressi.config.ts',
          '--early-exit-on-error',
          '--error-rate-threshold',
          'invalid',
        ]);

        expect(runLoadTest).toHaveBeenCalledWith(
          expect.objectContaining({
            earlyExitOnError: true,
            errorRateThreshold: NaN,
          }),
        );
      });

      it('should exit with error when error count threshold is invalid', async () => {
        const { runLoadTest } = await import('../src/index');

        await runCli([
          '--config',
          './tressi.config.ts',
          '--early-exit-on-error',
          '--error-count-threshold',
          'invalid',
        ]);

        expect(runLoadTest).toHaveBeenCalledWith(
          expect.objectContaining({
            earlyExitOnError: true,
            errorCountThreshold: NaN,
          }),
        );
      });

      it('should handle edge case status codes correctly', async () => {
        const { runLoadTest } = await import('../src/index');
        await runCli([
          '--config',
          './tressi.config.ts',
          '--early-exit-on-error',
          '--error-status-codes',
          '100,200,300,400,500',
        ]);

        expect(runLoadTest).toHaveBeenCalledWith(
          expect.objectContaining({
            earlyExitOnError: true,
            errorStatusCodes: [100, 200, 300, 400, 500],
          }),
        );
      });
    });
  });
});
