/**
 * CLI Test Generator
 * Generates Jest tests for CLI commands
 */

import { TestGenerator, GeneratorContext, GenerationResult, GeneratedFile } from '../types';
import * as path from 'path';

export class CliTestGenerator implements TestGenerator {
  id = 'cli-unit-tests';
  name = 'CLI Unit Test Generator';
  description = 'Generates Jest unit tests for CLI commands';
  testFramework = 'cli-jest';

  generate(context: GeneratorContext): GenerationResult {
    const { workflow, outputDirectory } = context;
    const files: GeneratedFile[] = [];

    if (!workflow.testing?.unit_tests) {
      return { files: [] };
    }

    // Filter unit tests for CLI components
    const cliTests = workflow.testing.unit_tests.filter((test: any) => {
      const [componentName] = test.component.split('.');
      const component = workflow.stack[componentName];
      return component?.type === 'cli';
    });

    for (const test of cliTests) {
      const [componentName, commandId] = test.component.split('.');
      const stackComponent = workflow.stack[componentName] as any;

      if (!stackComponent) continue;

      // Find the specific command definition
      const command = stackComponent.commands?.find((c: any) => c.id === commandId);
      if (!command) continue;

      // Generate test file
      files.push(this.generateCommandTest(
        componentName,
        command,
        test.scenarios,
        outputDirectory
      ));
    }

    // Generate Jest config if we have tests
    if (files.length > 0) {
      const cliComponent = Object.entries(workflow.stack).find(([_, c]: [string, any]) => c.type === 'cli');
      if (cliComponent) {
        files.push(this.generateJestConfig(cliComponent[0], outputDirectory));
      }
    }

    return { files };
  }

  private generateCommandTest(
    componentName: string,
    command: any,
    scenarios: string[],
    outputDir: string
  ): GeneratedFile {
    const commandName = command.name || command.id;

    let content = `import { Command } from 'commander';
import { ${this.toCamelCase(commandName)}Command } from '../commands/${commandName}';

describe('${commandName} command', () => {
  let program: Command;
  let consoleLogSpy: jest.SpyInstance;

  beforeEach(() => {
    program = new Command();
    ${this.toCamelCase(commandName)}Command(program);
    consoleLogSpy = jest.spyOn(console, 'log').mockImplementation();
  });

  afterEach(() => {
    consoleLogSpy.mockRestore();
  });

`;

    // Generate test cases for each scenario
    for (const scenario of scenarios) {
      content += this.generateScenarioTest(scenario, command);
    }

    content += `});
`;

    return {
      path: path.join(outputDir, componentName, 'src', 'commands', `${commandName}.test.ts`),
      content,
      overwrite: true,
    };
  }

  private generateScenarioTest(scenario: string, command: any): string {
    const lowerScenario = scenario.toLowerCase();

    if (lowerScenario.includes('default')) {
      return this.generateDefaultTest(command);
    }

    if (lowerScenario.includes('custom') || lowerScenario.includes('provided')) {
      return this.generateCustomArgumentTest(command);
    }

    // Generic test case
    return `  it('${scenario}', async () => {
    // TODO: Implement test for: ${scenario}
    expect(true).toBe(true);
  });

`;
  }

  private generateDefaultTest(command: any): string {
    const commandName = command.name || command.id;
    const args = command.arguments || [];
    const defaultArg = args.find((a: any) => a.default);

    return `  it('displays default greeting message', async () => {
    await program.parseAsync(['node', 'cli', '${commandName}']);

    expect(consoleLogSpy).toHaveBeenCalled();
    ${defaultArg ? `expect(consoleLogSpy.mock.calls[0][0]).toContain('${defaultArg.default}');` : ''}
  });

`;
  }

  private generateCustomArgumentTest(command: any): string {
    const commandName = command.name || command.id;
    const args = command.arguments || [];
    const _firstArg = args[0];

    return `  it('displays custom greeting with provided name', async () => {
    await program.parseAsync(['node', 'cli', '${commandName}', 'Alice']);

    expect(consoleLogSpy).toHaveBeenCalled();
    expect(consoleLogSpy.mock.calls[0][0]).toContain('Alice');
  });

`;
  }

  private generateJestConfig(componentName: string, outputDir: string): GeneratedFile {
    const config = `module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  roots: ['<rootDir>/src'],
  testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
  transform: {
    '^.+\\\\.ts$': 'ts-jest',
  },
  collectCoverageFrom: [
    'src/**/*.ts',
    '!src/**/*.d.ts',
    '!src/**/*.test.ts',
  ],
  coverageDirectory: 'coverage',
  coverageReporters: ['text', 'lcov', 'html'],
};
`;

    return {
      path: path.join(outputDir, componentName, 'jest.config.js'),
      content: config,
      overwrite: false,
    };
  }

  private toCamelCase(str: string): string {
    return str.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
  }
}
