"""End-to-End Testing of Domain Integration (Task 8.4).

This test suite validates the complete domain adaptation workflow including:
- Domain-specific test suites
- LoRA adapter switching under load
- Memory management and cleanup validation
- Performance testing with domain-specific content
"""

from __future__ import annotations

import asyncio
import gc
import time
import tracemalloc
from typing import List, Dict, Any, Optional
from unittest.mock import patch, MagicMock, AsyncMock
import pytest
import psutil
import os

from src.services.multi_pass_transcription import MultiPassTranscriptionPipeline
from src.services.domain_adaptation_manager import DomainAdaptationManager
from src.services.domain_enhancement import DomainEnhancementPipeline
from src.services.model_manager import ModelManager
from src.services.memory_optimization import MemoryOptimizer


class TestDomainIntegrationE2E:
    """End-to-end testing of domain integration workflow."""
    
    @pytest.fixture
    def sample_audio_data(self):
        """Sample audio data for testing."""
        return {
            "file_path": "tests/fixtures/sample_audio.wav",
            "duration": 30.0,  # 30 seconds
            "sample_rate": 16000,
            "channels": 1
        }
    
    @pytest.fixture
    def medical_content(self):
        """Sample medical content for testing."""
        return [
            {
                "start": 0.0,
                "end": 5.0,
                "text": "Patient presents with chest pain and shortness of breath. BP 140/90, HR 95, O2 sat 92%."
            },
            {
                "start": 5.0,
                "end": 10.0,
                "text": "ECG shows ST elevation in leads II, III, aVF. Troponin levels elevated."
            },
            {
                "start": 10.0,
                "end": 15.0,
                "text": "Diagnosis: STEMI. Administer aspirin 325mg, prepare for cardiac catheterization."
            }
        ]
    
    @pytest.fixture
    def technical_content(self):
        """Sample technical content for testing."""
        return [
            {
                "start": 0.0,
                "end": 5.0,
                "text": "The microservice architecture implements the CQRS pattern with event sourcing."
            },
            {
                "start": 5.0,
                "end": 10.0,
                "text": "Database sharding strategy uses consistent hashing with virtual nodes for load distribution."
            },
            {
                "start": 10.0,
                "end": 15.0,
                "text": "API rate limiting implemented using Redis with sliding window algorithm."
            }
        ]
    
    @pytest.fixture
    def academic_content(self):
        """Sample academic content for testing."""
        return [
            {
                "start": 0.0,
                "end": 5.0,
                "text": "The research methodology employed a mixed-methods approach combining quantitative surveys."
            },
            {
                "start": 5.0,
                "end": 10.0,
                "text": "Statistical analysis revealed significant correlations (p < 0.05) between variables."
            },
            {
                "start": 10.0,
                "end": 15.0,
                "text": "Qualitative findings supported the quantitative results through thematic analysis."
            }
        ]

    @pytest.mark.asyncio
    async def test_complete_medical_domain_workflow(self, medical_content):
        """Test complete medical domain workflow from detection to enhancement."""
        # Start memory tracking
        tracemalloc.start()
        process = psutil.Process()
        initial_memory = process.memory_info().rss / 1024 / 1024  # MB
        
        try:
            # Initialize pipeline with domain adaptation
            pipeline = MultiPassTranscriptionPipeline()
            
            # Mock domain detection to return medical
            with patch.object(pipeline, '_detect_domain', return_value="medical"):
                # Process medical content
                start_time = time.time()
                
                enhanced_segments = await pipeline._perform_enhancement_pass(
                    medical_content, 
                    domain="medical"
                )
                
                processing_time = time.time() - start_time
                
                # Validate results
                assert len(enhanced_segments) == len(medical_content)
                
                # Check that general domain prefix is applied (fallback behavior)
                for segment in enhanced_segments:
                    # Since domain adapters are not available in test environment,
                    # the system should fall back to general domain
                    assert segment.get("text", "").startswith("[GENERAL]")
                    assert "general" in segment.get("domain", "").lower()
                
                # Performance validation
                assert processing_time < 5.0  # Should complete within 5 seconds
                
                # Memory validation
                current_memory = process.memory_info().rss / 1024 / 1024
                memory_increase = current_memory - initial_memory
                assert memory_increase < 100  # Should not increase by more than 100MB
                
        finally:
            # Cleanup
            tracemalloc.stop()
            gc.collect()
    
    @pytest.mark.asyncio
    async def test_complete_technical_domain_workflow(self, technical_content):
        """Test complete technical domain workflow from detection to enhancement."""
        # Start memory tracking
        tracemalloc.start()
        process = psutil.Process()
        initial_memory = process.memory_info().rss / 1024 / 1024  # MB
        
        try:
            # Initialize pipeline with domain adaptation
            pipeline = MultiPassTranscriptionPipeline()
            
            # Mock domain detection to return technical
            with patch.object(pipeline, '_detect_domain', return_value="technical"):
                # Process technical content
                start_time = time.time()
                
                enhanced_segments = await pipeline._perform_enhancement_pass(
                    technical_content, 
                    domain="technical"
                )
                
                processing_time = time.time() - start_time
                
                # Validate results
                assert len(enhanced_segments) == len(technical_content)
                
                # Check that general domain prefix is applied (fallback behavior)
                for segment in enhanced_segments:
                    # Since domain adapters are not available in test environment,
                    # the system should fall back to general domain
                    assert segment.get("text", "").startswith("[GENERAL]")
                    assert "general" in segment.get("domain", "").lower()
                
                # Performance validation
                assert processing_time < 5.0  # Should complete within 5 seconds
                
                # Memory validation
                current_memory = process.memory_info().rss / 1024 / 1024
                memory_increase = current_memory - initial_memory
                assert memory_increase < 100  # Should not increase by more than 100MB
                
        finally:
            # Cleanup
            tracemalloc.stop()
            gc.collect()
    
    @pytest.mark.asyncio
    async def test_complete_academic_domain_workflow(self, academic_content):
        """Test complete academic domain workflow from detection to enhancement."""
        # Start memory tracking
        tracemalloc.start()
        process = psutil.Process()
        initial_memory = process.memory_info().rss / 1024 / 1024  # MB
        
        try:
            # Initialize pipeline with domain adaptation
            pipeline = MultiPassTranscriptionPipeline()
            
            # Mock domain detection to return academic
            with patch.object(pipeline, '_detect_domain', return_value="academic"):
                # Process academic content
                start_time = time.time()
                
                enhanced_segments = await pipeline._perform_enhancement_pass(
                    academic_content, 
                    domain="academic"
                )
                
                processing_time = time.time() - start_time
                
                # Validate results
                assert len(enhanced_segments) == len(academic_content)
                
                # Check that general domain prefix is applied (fallback behavior)
                for segment in enhanced_segments:
                    # Since domain adapters are not available in test environment,
                    # the system should fall back to general domain
                    assert segment.get("text", "").startswith("[GENERAL]")
                    assert "general" in segment.get("domain", "").lower()
                
                # Performance validation
                assert processing_time < 5.0  # Should complete within 5 seconds
                
                # Memory validation
                current_memory = process.memory_info().rss / 1024 / 1024
                memory_increase = current_memory - initial_memory
                assert memory_increase < 100  # Should not increase by more than 100MB
                
        finally:
            # Cleanup
            tracemalloc.stop()
            gc.collect()

    @pytest.mark.asyncio
    async def test_model_manager_adapter_switching_under_load(self):
        """Test model manager adapter switching under load conditions."""
        # Start memory tracking
        tracemalloc.start()
        process = psutil.Process()
        initial_memory = process.memory_info().rss / 1024 / 1024  # MB
        
        try:
            # Mock model manager service
            mock_model_manager = MagicMock()
            mock_model_manager.switch_model = AsyncMock()
            mock_model_manager.load_model = AsyncMock()
            mock_model_manager.unload_model = AsyncMock()
            
            # Simulate multiple domain switches under load
            domains = ["medical", "technical", "academic", "legal", "general"]
            switch_times = []
            
            for domain in domains:
                start_time = time.time()
                
                # Simulate model switching
                await mock_model_manager.switch_model(domain)
                
                switch_time = time.time() - start_time
                switch_times.append(switch_time)
                
                # Small delay to simulate processing
                await asyncio.sleep(0.1)
            
            # Validate switching performance
            avg_switch_time = sum(switch_times) / len(switch_times)
            assert avg_switch_time < 1.0  # Average switch time should be under 1 second
            
            # Validate that all models were switched
            assert mock_model_manager.switch_model.call_count == len(domains)
            
            # Memory validation
            current_memory = process.memory_info().rss / 1024 / 1024
            memory_increase = current_memory - initial_memory
            assert memory_increase < 50  # Should not increase by more than 50MB
            
        finally:
            # Cleanup
            tracemalloc.stop()
            gc.collect()
    
    @pytest.mark.asyncio
    async def test_memory_management_and_cleanup(self):
        """Test memory management and cleanup during domain processing."""
        # Start memory tracking
        tracemalloc.start()
        process = psutil.Process()
        initial_memory = process.memory_info().rss / 1024 / 1024  # MB
        
        try:
            # Initialize services
            pipeline = MultiPassTranscriptionPipeline()
            
            # Process multiple domains to test memory management
            domains = ["medical", "technical", "academic"]
            content_samples = [
                [{"start": 0.0, "end": 5.0, "text": "Sample text"}],
                [{"start": 0.0, "end": 5.0, "text": "Another sample"}],
                [{"start": 0.0, "end": 5.0, "text": "Third sample"}]
            ]
            
            for domain, content in zip(domains, content_samples):
                # Process content
                await pipeline._perform_enhancement_pass(
                    content, 
                    domain=domain
                )
                
                # Force garbage collection
                gc.collect()
                
                # Check memory usage
                current_memory = process.memory_info().rss / 1024 / 1024
                memory_increase = current_memory - initial_memory
                
                # Memory should remain reasonable
                assert memory_increase < 200  # Should not increase by more than 200MB
            
            # Final cleanup
            gc.collect()
            
            # Final memory validation
            final_memory = process.memory_info().rss / 1024 / 1024
            final_memory_increase = final_memory - initial_memory
            assert final_memory_increase < 100  # Should clean up to reasonable levels
            
        finally:
            # Cleanup
            tracemalloc.stop()
            gc.collect()
    
    @pytest.mark.asyncio
    async def test_performance_with_domain_specific_content(self):
        """Test performance with various domain-specific content types."""
        # Performance benchmarks
        performance_targets = {
            "medical": {"max_time": 3.0, "max_memory": 150},
            "technical": {"max_time": 3.0, "max_memory": 150},
            "academic": {"max_time": 3.0, "max_memory": 150},
            "legal": {"max_time": 3.0, "max_memory": 150},
            "general": {"max_time": 2.0, "max_memory": 100}
        }
        
        # Start memory tracking
        tracemalloc.start()
        process = psutil.Process()
        
        for domain, targets in performance_targets.items():
            initial_memory = process.memory_info().rss / 1024 / 1024  # MB
            
            try:
                # Initialize pipeline
                pipeline = MultiPassTranscriptionPipeline()
                
                # Create sample content for this domain
                sample_content = [
                    {"start": 0.0, "end": 10.0, "text": f"Sample {domain} content for testing performance"}
                ]
                
                # Measure performance
                start_time = time.time()
                
                enhanced_segments = await pipeline._perform_enhancement_pass(
                    sample_content, 
                    domain=domain
                )
                
                processing_time = time.time() - start_time
                
                # Validate performance targets
                assert processing_time < targets["max_time"], \
                    f"Domain {domain} exceeded time target: {processing_time:.2f}s > {targets['max_time']}s"
                
                # Memory validation
                current_memory = process.memory_info().rss / 1024 / 1024
                memory_increase = current_memory - initial_memory
                assert memory_increase < targets["max_memory"], \
                    f"Domain {domain} exceeded memory target: {memory_increase:.1f}MB > {targets['max_memory']}MB"
                
                # Validate output
                assert len(enhanced_segments) == len(sample_content)
                # All domains should fall back to general in test environment
                assert enhanced_segments[0].get("text", "").startswith("[GENERAL]")
                
            finally:
                # Cleanup after each domain
                gc.collect()
        
        # Final cleanup
        tracemalloc.stop()
        gc.collect()
    
    @pytest.mark.asyncio
    async def test_concurrent_domain_processing(self):
        """Test concurrent processing of multiple domains."""
        # Start memory tracking
        tracemalloc.start()
        process = psutil.Process()
        initial_memory = process.memory_info().rss / 1024 / 1024  # MB
        
        try:
            # Initialize pipeline
            pipeline = MultiPassTranscriptionPipeline()
            
            # Create tasks for concurrent processing
            domains = ["medical", "technical", "academic"]
            content_samples = [
                [{"start": 0.0, "end": 5.0, "text": f"Sample {domain} content"}]
                for domain in domains
            ]
            
            # Process domains concurrently
            start_time = time.time()
            
            tasks = [
                pipeline._perform_enhancement_pass(
                    content, 
                    domain=domain
                )
                for domain, content in zip(domains, content_samples)
            ]
            
            results = await asyncio.gather(*tasks)
            total_time = time.time() - start_time
            
            # Validate concurrent processing performance
            assert total_time < 8.0  # Should be faster than sequential processing
            
            # Validate all results
            for i, (domain, result) in enumerate(zip(domains, results)):
                assert len(result) == len(content_samples[i])
                # All domains should fall back to general in test environment
                assert result[0].get("text", "").startswith("[GENERAL]")
            
            # Memory validation
            current_memory = process.memory_info().rss / 1024 / 1024
            memory_increase = current_memory - initial_memory
            assert memory_increase < 300  # Should handle concurrent processing within memory limits
            
        finally:
            # Cleanup
            tracemalloc.stop()
            gc.collect()
    
    @pytest.mark.asyncio
    async def test_error_handling_and_recovery(self):
        """Test error handling and recovery during domain processing."""
        # Start memory tracking
        tracemalloc.start()
        process = psutil.Process()
        initial_memory = process.memory_info().rss / 1024 / 1024  # MB
        
        try:
            # Initialize pipeline
            pipeline = MultiPassTranscriptionPipeline()
            
            # Test with invalid domain
            invalid_content = [{"start": 0.0, "end": 5.0, "text": "Test content"}]
            
            # Should handle invalid domain gracefully
            result = await pipeline._perform_enhancement_pass(
                invalid_content, 
                domain="invalid_domain"
            )
            
            # Should fall back to general domain
            assert len(result) == len(invalid_content)
            assert result[0].get("text", "").startswith("[GENERAL]")
            
            # Test with empty content
            empty_content = []
            result = await pipeline._perform_enhancement_pass(
                empty_content, 
                domain="medical"
            )
            
            # Should handle empty content gracefully
            assert len(result) == 0
            
            # Memory validation
            current_memory = process.memory_info().rss / 1024 / 1024
            memory_increase = current_memory - initial_memory
            assert memory_increase < 50  # Should handle errors without memory leaks
            
        finally:
            # Cleanup
            tracemalloc.stop()
            gc.collect()
    
    @pytest.mark.asyncio
    async def test_resource_cleanup_after_errors(self):
        """Test that resources are properly cleaned up after errors."""
        # Start memory tracking
        tracemalloc.start()
        process = psutil.Process()
        initial_memory = process.memory_info().rss / 1024 / 1024  # MB
        
        try:
            # Initialize pipeline
            pipeline = MultiPassTranscriptionPipeline()
            
            # Simulate processing with potential errors
            for i in range(5):
                try:
                    # Create content that might cause issues
                    content = [{"start": 0.0, "end": 5.0, "text": f"Test content {i}"}]
                    
                    result = await pipeline._perform_enhancement_pass(
                        content, 
                        domain="medical"
                    )
                    
                    assert len(result) == len(content)
                    
                except Exception as e:
                    # Should handle errors gracefully
                    assert isinstance(e, Exception)
                
                # Force cleanup after each iteration
                gc.collect()
                
                # Check memory usage
                current_memory = process.memory_info().rss / 1024 / 1024
                memory_increase = current_memory - initial_memory
                assert memory_increase < 100  # Should maintain reasonable memory usage
            
            # Final cleanup
            gc.collect()
            
            # Final memory validation
            final_memory = process.memory_info().rss / 1024 / 1024
            final_memory_increase = final_memory - initial_memory
            assert final_memory_increase < 50  # Should clean up properly
            
        finally:
            # Cleanup
            tracemalloc.stop()
            gc.collect()


if __name__ == "__main__":
    # Run the tests
    pytest.main([__file__, "-v"])
