import 'reflect-metadata';
import { container } from 'tsyringe';
import { NLPService } from '../../../src/services/nlp/nlp.service';
import { TextParserService } from '../../../src/services/nlp/parsers/text-parser.service';
import { EntityExtractorService } from '../../../src/services/nlp/extractors/entity-extractor.service';
import { TaskClassifierService } from '../../../src/services/nlp/classifiers/task-classifier.service';
import { StructuredDataGeneratorService } from '../../../src/services/nlp/generators/structured-data.service';
import { LanguageDetectorService } from '../../../src/services/nlp/language-detector.service';
import { TranslationService } from '../../../src/services/nlp/translation.service';

// Mock dependencies
jest.mock('../../../src/services/nlp/parsers/text-parser.service');
jest.mock('../../../src/services/nlp/extractors/entity-extractor.service');
jest.mock('../../../src/services/nlp/classifiers/task-classifier.service');
jest.mock('../../../src/services/nlp/generators/structured-data.service');
jest.mock('../../../src/services/nlp/language-detector.service');
jest.mock('../../../src/services/nlp/translation.service');

describe('NLPService', () => {
  let service: NLPService;
  let mockTextParser: jest.Mocked<TextParserService>;
  let mockEntityExtractor: jest.Mocked<EntityExtractorService>;
  let mockTaskClassifier: jest.Mocked<TaskClassifierService>;
  let mockStructuredDataGenerator: jest.Mocked<StructuredDataGeneratorService>;
  let mockLanguageDetector: jest.Mocked<LanguageDetectorService>;
  let mockTranslationService: jest.Mocked<TranslationService>;

  beforeEach(() => {
    // Clear all mocks
    jest.clearAllMocks();
    container.clearInstances();

    // Create mock services
    mockTextParser = {
      parse: jest.fn(),
    } as any;

    mockEntityExtractor = {
      extract: jest.fn(),
    } as any;

    mockTaskClassifier = {
      classify: jest.fn(),
    } as any;

    mockStructuredDataGenerator = {
      generate: jest.fn(),
    } as any;

    mockLanguageDetector = {
      detectLanguage: jest.fn(),
    } as any;

    mockTranslationService = {
      translate: jest.fn(),
    } as any;

    // Register mocks with container
    container.registerInstance(TextParserService, mockTextParser);
    container.registerInstance(EntityExtractorService, mockEntityExtractor);
    container.registerInstance(TaskClassifierService, mockTaskClassifier);
    container.registerInstance(StructuredDataGeneratorService, mockStructuredDataGenerator);
    container.registerInstance(LanguageDetectorService, mockLanguageDetector);
    container.registerInstance(TranslationService, mockTranslationService);

    // Create service
    service = container.resolve(NLPService);
  });

  describe('processText', () => {
    it('should process text through the full NLP pipeline', async () => {
      // Arrange
      const input = {
        text: 'Create a high-priority task to implement user authentication by Friday',
        language: 'en',
      };

      const mockParsedData: any = {
        originalText: input.text,
        sentences: [{ text: input.text, tokens: [] }],
        partsOfSpeech: [],
        syntacticStructure: {},
        language: 'en',
        keywords: ['authentication', 'user'],
      };

      const mockEntities: any = {
        title: 'Implement user authentication',
        priority: 'high' as const,
        deadline: new Date('2025-01-17'),
        estimatedHours: 8,
        confidence: 0.9,
      };

      const mockClassification: any = {
        type: 'feature' as const,
        urgency: 'normal' as const,
        sentiment: 'neutral' as const,
        complexity: 'moderate' as const,
        confidence: 0.85,
      };

      const mockStructuredData: any = {
        title: 'Implement user authentication',
        description: 'High-priority task for user authentication',
        priority: 'high',
        deadline: '2025-01-17',
        taskType: 'feature',
        estimatedHours: 8,
      };

      mockTextParser.parse.mockResolvedValue(mockParsedData);
      mockEntityExtractor.extract.mockResolvedValue(mockEntities);
      mockTaskClassifier.classify.mockResolvedValue(mockClassification);
      mockStructuredDataGenerator.generate.mockResolvedValue(mockStructuredData);

      // Act
      const result = await service.processText(input);

      // Assert
      expect(result.success).toBe(true);
      expect(result.entities).toEqual(mockEntities);
      expect(result.classification).toEqual(mockClassification);
      expect(result.structuredData).toEqual(mockStructuredData);
      expect(result.language).toBe('en');

      expect(mockTextParser.parse).toHaveBeenCalledWith(input.text, 'en');
      expect(mockEntityExtractor.extract).toHaveBeenCalledWith({
        text: input.text,
        parsedData: mockParsedData,
        language: 'en',
      });
      expect(mockTaskClassifier.classify).toHaveBeenCalledWith({
        text: input.text,
        entities: mockEntities,
        language: 'en',
      });
      expect(mockStructuredDataGenerator.generate).toHaveBeenCalledWith({
        entities: mockEntities,
        classification: mockClassification,
        originalText: input.text,
      });
    });

    it('should handle errors gracefully', async () => {
      // Arrange
      const input = {
        text: 'Test text',
        language: 'en',
      };

      mockTextParser.parse.mockRejectedValue(new Error('Parser error'));

      // Act
      const result = await service.processText(input);

      // Assert
      expect(result.success).toBe(false);
      expect(result.errors).toContain('Parser error');
    });

    it('should use cache for repeated requests', async () => {
      // Arrange
      const input = {
        text: 'Cached request',
        language: 'en',
      };

      const mockParsedData: any = { 
        originalText: input.text,
        sentences: [{ text: input.text, tokens: [] }],
        partsOfSpeech: [],
        syntacticStructure: {},
        language: 'en',
        keywords: [],
      };
      const mockEntities: any = { title: 'Cached request' };
      const mockClassification: any = { type: 'task' as const };
      const mockStructuredData: any = { title: 'Cached request' };

      mockTextParser.parse.mockResolvedValue(mockParsedData);
      mockEntityExtractor.extract.mockResolvedValue(mockEntities);
      mockTaskClassifier.classify.mockResolvedValue(mockClassification);
      mockStructuredDataGenerator.generate.mockResolvedValue(mockStructuredData);

      // Act - First call
      await service.processText(input);
      
      // Act - Second call (should use cache)
      const result = await service.processText(input);

      // Assert
      expect(result.success).toBe(true);
      expect(mockTextParser.parse).toHaveBeenCalledTimes(1); // Only called once due to cache
    });
  });

  describe('processTextWithTranslation', () => {
    it('should detect language and translate if needed', async () => {
      // Arrange
      const input = {
        text: 'Créer une tâche urgente pour demain',
      };

      const mockDetectedLanguage = {
        language: 'fr',
        confidence: 0.95,
      };

      const mockTranslation = {
        originalText: input.text,
        translatedText: 'Create an urgent task for tomorrow',
        sourceLanguage: 'fr',
        targetLanguage: 'en',
        confidence: 0.9,
      };

      const mockParsedData: any = { 
        originalText: 'Create an urgent task for tomorrow',
        sentences: [{ text: 'Create an urgent task for tomorrow', tokens: [] }],
        partsOfSpeech: [],
        syntacticStructure: {},
        language: 'en',
        keywords: ['urgent', 'task'],
      };
      const mockEntities: any = { title: 'Urgent task', priority: 'high' as const };
      const mockClassification: any = { type: 'task' as const, urgency: 'urgent' as const };
      const mockStructuredData: any = { title: 'Urgent task' };

      mockLanguageDetector.detectLanguage.mockResolvedValue(mockDetectedLanguage);
      mockTranslationService.translate.mockResolvedValue(mockTranslation);
      mockTextParser.parse.mockResolvedValue(mockParsedData);
      mockEntityExtractor.extract.mockResolvedValue(mockEntities);
      mockTaskClassifier.classify.mockResolvedValue(mockClassification);
      mockStructuredDataGenerator.generate.mockResolvedValue(mockStructuredData);

      // Act
      const result = await service.processTextWithTranslation(input);

      // Assert
      expect(result.success).toBe(true);
      expect(result.language).toBe('fr');
      expect(mockLanguageDetector.detectLanguage).toHaveBeenCalledWith(input.text);
      expect(mockTranslationService.translate).toHaveBeenCalledWith({
        text: input.text,
        sourceLanguage: 'fr',
        targetLanguage: 'en',
        preserveFormatting: true,
        context: 'task management',
      });
    });

    it('should skip translation for English text', async () => {
      // Arrange
      const input = {
        text: 'Create a task',
        language: 'en',
      };

      const mockParsedData: any = { 
        originalText: input.text,
        sentences: [{ text: input.text, tokens: [] }],
        partsOfSpeech: [],
        syntacticStructure: {},
        language: 'en',
        keywords: [],
      };
      const mockEntities: any = { title: 'Create a task' };
      const mockClassification: any = { type: 'task' as const };
      const mockStructuredData: any = { title: 'Create a task' };

      mockTextParser.parse.mockResolvedValue(mockParsedData);
      mockEntityExtractor.extract.mockResolvedValue(mockEntities);
      mockTaskClassifier.classify.mockResolvedValue(mockClassification);
      mockStructuredDataGenerator.generate.mockResolvedValue(mockStructuredData);

      // Act
      const result = await service.processTextWithTranslation(input);

      // Assert
      expect(result.success).toBe(true);
      expect(result.language).toBe('en');
      expect(mockTranslationService.translate).not.toHaveBeenCalled();
    });
  });

  describe('getCacheStats', () => {
    it('should return cache statistics', () => {
      // Act
      const stats = service.getCacheStats();

      // Assert
      expect(stats).toHaveProperty('hits');
      expect(stats).toHaveProperty('misses');
      expect(stats).toHaveProperty('size');
      expect(stats).toHaveProperty('hitRate');
    });
  });

  describe('clearCache', () => {
    it('should clear the cache', async () => {
      // Arrange
      const input = {
        text: 'Test for cache clearing',
        language: 'en',
      };

      const mockParsedData: any = { 
        originalText: input.text,
        sentences: [{ text: input.text, tokens: [] }],
        partsOfSpeech: [],
        syntacticStructure: {},
        language: 'en',
        keywords: [],
      };
      const mockEntities: any = { title: 'Test' };
      const mockClassification: any = { type: 'task' as const };
      const mockStructuredData: any = { title: 'Test' };

      mockTextParser.parse.mockResolvedValue(mockParsedData);
      mockEntityExtractor.extract.mockResolvedValue(mockEntities);
      mockTaskClassifier.classify.mockResolvedValue(mockClassification);
      mockStructuredDataGenerator.generate.mockResolvedValue(mockStructuredData);

      // Act
      await service.processText(input); // First call
      service.clearCache();
      await service.processText(input); // Second call after cache clear

      // Assert
      expect(mockTextParser.parse).toHaveBeenCalledTimes(2); // Called twice due to cache clear
    });
  });
});