/**
 * Tests for GeneratorRegistry
 */

import { GeneratorRegistry } from './generator-registry';
import {
  FrontendGenerator,
  BackendGenerator,
  GeneratorContext,
  GenerationResult,
} from './types';

describe('GeneratorRegistry', () => {
  let registry: GeneratorRegistry;

  beforeEach(() => {
    registry = new GeneratorRegistry();
  });

  describe('Frontend generators', () => {
    it('should register and retrieve frontend generators', () => {
      const reactGenerator: FrontendGenerator = {
        id: 'react',
        name: 'React Generator',
        description: 'Generates React components',
        framework: 'react',
        generate: (_context: GeneratorContext): GenerationResult => ({
          files: [],
        }),
      };

      registry.registerFrontendGenerator(reactGenerator);

      const retrieved = registry.getFrontendGenerator('react');
      expect(retrieved).toBe(reactGenerator);
    });

    it('should throw error when registering duplicate framework', () => {
      const generator: FrontendGenerator = {
        id: 'react',
        name: 'React Generator',
        description: 'Generates React components',
        framework: 'react',
        generate: () => ({ files: [] }),
      };

      registry.registerFrontendGenerator(generator);

      expect(() => {
        registry.registerFrontendGenerator(generator);
      }).toThrow('already registered');
    });

    it('should list registered frameworks', () => {
      const reactGen: FrontendGenerator = {
        id: 'react',
        name: 'React',
        description: 'React',
        framework: 'react',
        generate: () => ({ files: [] }),
      };

      const vueGen: FrontendGenerator = {
        id: 'vue',
        name: 'Vue',
        description: 'Vue',
        framework: 'vue',
        generate: () => ({ files: [] }),
      };

      registry.registerFrontendGenerator(reactGen);
      registry.registerFrontendGenerator(vueGen);

      const frameworks = registry.listFrontendFrameworks();
      expect(frameworks).toContain('react');
      expect(frameworks).toContain('vue');
    });
  });

  describe('Backend generators', () => {
    it('should register and retrieve backend generators', () => {
      const tsGenerator: BackendGenerator = {
        id: 'typescript-express',
        name: 'TypeScript/Express Generator',
        description: 'Generates TypeScript Express API',
        language: 'typescript',
        framework: 'express',
        generate: () => ({ files: [] }),
      };

      registry.registerBackendGenerator(tsGenerator);

      const retrieved = registry.getBackendGenerator('typescript', 'express');
      expect(retrieved).toBe(tsGenerator);
    });

    it('should fall back to default framework', () => {
      const defaultGen: BackendGenerator = {
        id: 'typescript-default',
        name: 'TypeScript Default',
        description: 'Default TypeScript generator',
        language: 'typescript',
        generate: () => ({ files: [] }),
      };

      registry.registerBackendGenerator(defaultGen);

      // Request with no framework should get default
      const retrieved = registry.getBackendGenerator('typescript');
      expect(retrieved).toBe(defaultGen);

      // Request with non-existent framework should also get default
      const retrievedFallback = registry.getBackendGenerator('typescript', 'nonexistent');
      expect(retrievedFallback).toBe(defaultGen);
    });

    it('should handle multiple frameworks for same language', () => {
      const expressGen: BackendGenerator = {
        id: 'ts-express',
        name: 'Express',
        description: 'Express',
        language: 'typescript',
        framework: 'express',
        generate: () => ({ files: [] }),
      };

      const fastifyGen: BackendGenerator = {
        id: 'ts-fastify',
        name: 'Fastify',
        description: 'Fastify',
        language: 'typescript',
        framework: 'fastify',
        generate: () => ({ files: [] }),
      };

      registry.registerBackendGenerator(expressGen);
      registry.registerBackendGenerator(fastifyGen);

      expect(registry.getBackendGenerator('typescript', 'express')).toBe(expressGen);
      expect(registry.getBackendGenerator('typescript', 'fastify')).toBe(fastifyGen);
    });

    it('should throw error for duplicate language/framework', () => {
      const gen: BackendGenerator = {
        id: 'ts-express',
        name: 'Express',
        description: 'Express',
        language: 'typescript',
        framework: 'express',
        generate: () => ({ files: [] }),
      };

      registry.registerBackendGenerator(gen);

      expect(() => {
        registry.registerBackendGenerator(gen);
      }).toThrow('already registered');
    });

    it('should list registered languages', () => {
      const tsGen: BackendGenerator = {
        id: 'ts',
        name: 'TS',
        description: 'TS',
        language: 'typescript',
        generate: () => ({ files: [] }),
      };

      const goGen: BackendGenerator = {
        id: 'go',
        name: 'Go',
        description: 'Go',
        language: 'go',
        generate: () => ({ files: [] }),
      };

      registry.registerBackendGenerator(tsGen);
      registry.registerBackendGenerator(goGen);

      const languages = registry.listBackendLanguages();
      expect(languages).toContain('typescript');
      expect(languages).toContain('go');
    });
  });

  describe('Component generator lookup', () => {
    it('should return undefined for empty type', () => {
      const result = registry.getComponentGenerator('', 'react');
      expect(result).toBeUndefined();
    });

    it('should return undefined for empty technology', () => {
      const result = registry.getComponentGenerator('frontend', '');
      expect(result).toBeUndefined();
    });

    it('should return undefined for whitespace-only type', () => {
      const result = registry.getComponentGenerator('   ', 'react');
      expect(result).toBeUndefined();
    });

    it('should return undefined for whitespace-only technology', () => {
      const result = registry.getComponentGenerator('frontend', '   ');
      expect(result).toBeUndefined();
    });
  });

  describe('Generator info', () => {
    it('should return safe default for empty inputs', () => {
      const info = registry.getGeneratorInfo('', '');
      expect(info.exists).toBe(false);
      expect(info.suggestions).toEqual([]);
      expect(info.availableOptions).toEqual([]);
    });

    it('should return safe default for whitespace inputs', () => {
      const info = registry.getGeneratorInfo('   ', '   ');
      expect(info.exists).toBe(false);
      expect(info.suggestions).toEqual([]);
      expect(info.availableOptions).toEqual([]);
    });
  });

  describe('Custom generators', () => {
    it('should register and retrieve custom generators', () => {
      const customGen = {
        id: 'custom',
        name: 'Custom Generator',
        description: 'Custom',
        generate: () => ({ files: [] }),
      };

      registry.registerCustomGenerator(customGen);

      const retrieved = registry.getCustomGenerator('custom');
      expect(retrieved).toBe(customGen);
    });

    it('should throw error for duplicate custom generator IDs', () => {
      const gen = {
        id: 'custom',
        name: 'Custom',
        description: 'Custom',
        generate: () => ({ files: [] }),
      };

      registry.registerCustomGenerator(gen);

      expect(() => {
        registry.registerCustomGenerator(gen);
      }).toThrow('already registered');
    });
  });

  describe('Generator validation', () => {
    // Suppress console.warn for validation tests
    let consoleWarnSpy: jest.SpyInstance;

    beforeEach(() => {
      consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
      registry.clearValidationWarnings();
    });

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

    it('should validate frontend generator has required fields', () => {
      const invalidGen = {
        id: '',
        name: '',
        description: '',
        framework: 'react',
        generate: () => ({ files: [] }),
      } as FrontendGenerator;

      registry.registerFrontendGenerator(invalidGen);

      const warnings = registry.getValidationWarnings();
      expect(warnings.length).toBeGreaterThan(0);
      expect(warnings.some(w => w.includes('missing required field: id'))).toBe(true);
      expect(warnings.some(w => w.includes('missing required field: name'))).toBe(true);
    });

    it('should warn when frontend generator missing generateComponent method', () => {
      const genWithoutMethod: FrontendGenerator = {
        id: 'test-react',
        name: 'Test React',
        description: 'Test',
        framework: 'react',
        generate: () => ({ files: [] }),
      };

      registry.registerFrontendGenerator(genWithoutMethod);

      const warnings = registry.getValidationWarnings();
      expect(warnings.some(w => w.includes('missing recommended method: generateComponent'))).toBe(true);
    });

    it('should warn when backend generator missing generateEndpoint method', () => {
      const genWithoutMethod: BackendGenerator = {
        id: 'test-backend',
        name: 'Test Backend',
        description: 'Test',
        language: 'typescript',
        generate: () => ({ files: [] }),
      };

      registry.registerBackendGenerator(genWithoutMethod);

      const warnings = registry.getValidationWarnings();
      expect(warnings.some(w => w.includes('missing recommended method: generateEndpoint'))).toBe(true);
    });

    it('should validate generator ID format', () => {
      const invalidIdGen: FrontendGenerator = {
        id: 'Invalid_ID_123',
        name: 'Test',
        description: 'Test',
        framework: 'react',
        generate: () => ({ files: [] }),
      };

      registry.registerFrontendGenerator(invalidIdGen);

      const warnings = registry.getValidationWarnings();
      expect(warnings.some(w => w.includes('should be lowercase alphanumeric'))).toBe(true);
    });

    it('should validate backend generator has language', () => {
      const noLangGen = {
        id: 'test',
        name: 'Test',
        description: 'Test',
        language: '',
        generate: () => ({ files: [] }),
      } as BackendGenerator;

      registry.registerBackendGenerator(noLangGen);

      const warnings = registry.getValidationWarnings();
      expect(warnings.some(w => w.includes('missing required field: language'))).toBe(true);
    });

    it('should clear validation warnings', () => {
      const gen: FrontendGenerator = {
        id: '',
        name: 'Test',
        description: 'Test',
        framework: 'react',
        generate: () => ({ files: [] }),
      };

      registry.registerFrontendGenerator(gen);
      expect(registry.getValidationWarnings().length).toBeGreaterThan(0);

      registry.clearValidationWarnings();
      expect(registry.getValidationWarnings().length).toBe(0);
    });

    it('should validate all generator types consistently', () => {
      const testGen = (type: string) => {
        const gen: any = {
          id: '',
          name: '',
          description: '',
          generate: () => ({ files: [] }),
        };

        if (type === 'frontend') {
          gen.framework = 'react';
          registry.registerFrontendGenerator(gen);
        } else if (type === 'backend') {
          gen.language = 'typescript';
          registry.registerBackendGenerator(gen);
        }

        const warnings = registry.getValidationWarnings();
        expect(warnings.some(w => w.includes('missing required field'))).toBe(true);
        registry.clearValidationWarnings();
      };

      testGen('frontend');
      testGen('backend');
    });
  });
});
