"""Unit tests for concrete service implementations."""

import pytest
from pathlib import Path
from typing import Any, Dict, List
from unittest.mock import AsyncMock, MagicMock, patch

from src.services.protocols import (
    YouTubeServiceProtocol,
    MediaServiceProtocol,
    TranscriptionServiceProtocol,
    EnhancementServiceProtocol,
    ExportServiceProtocol,
    BatchProcessorProtocol,
    validate_protocol_implementation,
    get_missing_methods,
)


class TestYouTubeServiceImplementation:
    """Test YouTube service protocol implementation."""

    @pytest.fixture
    def mock_youtube_service(self):
        """Create a mock YouTube service that implements the protocol."""
        service = MagicMock()
        
        # Mock the required methods
        service.extract_metadata = AsyncMock(return_value={
            "title": "Test Video",
            "duration": 120,
            "channel": "Test Channel"
        })
        
        service.batch_extract = AsyncMock(return_value=[
            {"success": True, "data": {"title": "Video 1"}, "url": "http://example.com/1"},
            {"success": True, "data": {"title": "Video 2"}, "url": "http://example.com/2"}
        ])
        
        return service

    def test_youtube_service_protocol_compliance(self, mock_youtube_service):
        """Test that YouTube service implements the protocol correctly."""
        from src.services.protocols import YouTubeServiceProtocol
        
        # Test protocol validation
        assert validate_protocol_implementation(mock_youtube_service, YouTubeServiceProtocol)
        
        # Test that no methods are missing
        missing_methods = get_missing_methods(mock_youtube_service, YouTubeServiceProtocol)
        assert len(missing_methods) == 0

    @pytest.mark.asyncio
    async def test_youtube_service_methods(self, mock_youtube_service):
        """Test YouTube service method calls."""
        # Test extract_metadata
        result = await mock_youtube_service.extract_metadata("http://example.com/video")
        assert isinstance(result, dict)
        assert "title" in result
        
        # Test batch_extract
        results = await mock_youtube_service.batch_extract(["http://example.com/1", "http://example.com/2"])
        assert isinstance(results, list)
        assert len(results) == 2
        assert all("success" in result for result in results)


class TestMediaServiceImplementation:
    """Test Media service protocol implementation."""

    @pytest.fixture
    def mock_media_service(self):
        """Create a mock Media service that implements the protocol."""
        service = MagicMock()
        
        # Mock all required methods
        service.download_media = AsyncMock()
        service.preprocess_audio = AsyncMock(return_value=True)
        service.validate_file_size = AsyncMock(return_value=True)
        service.check_audio_quality = AsyncMock(return_value=True)
        service.get_media_info = AsyncMock(return_value={"duration": 120, "format": "mp4"})
        service.create_media_file_record = AsyncMock()
        service.update_media_file_status = AsyncMock()
        service.get_media_file_by_id = AsyncMock()
        service.get_pending_media_files = AsyncMock(return_value=[])
        service.get_ready_media_files = AsyncMock(return_value=[])
        service.process_media_pipeline = AsyncMock()
        service.get_telemetry_data = MagicMock(return_value=[])
        service.clear_telemetry_data = MagicMock()
        
        return service

    def test_media_service_protocol_compliance(self, mock_media_service):
        """Test that Media service implements the protocol correctly."""
        from src.services.protocols import MediaServiceProtocol
        
        # Test protocol validation
        assert validate_protocol_implementation(mock_media_service, MediaServiceProtocol)
        
        # Test that no methods are missing
        missing_methods = get_missing_methods(mock_media_service, MediaServiceProtocol)
        assert len(missing_methods) == 0

    @pytest.mark.asyncio
    async def test_media_service_methods(self, mock_media_service):
        """Test Media service method calls."""
        # Test download_media
        await mock_media_service.download_media("http://example.com/video", Path("/tmp"))
        mock_media_service.download_media.assert_called_once()
        
        # Test preprocess_audio
        result = await mock_media_service.preprocess_audio(Path("/input.wav"), Path("/output.wav"))
        assert result is True
        
        # Test validate_file_size
        result = await mock_media_service.validate_file_size(Path("/test.mp4"))
        assert result is True
        
        # Test get_media_info
        info = await mock_media_service.get_media_info(Path("/test.mp4"))
        assert isinstance(info, dict)
        assert "duration" in info


class TestTranscriptionServiceImplementation:
    """Test Transcription service protocol implementation."""

    @pytest.fixture
    def mock_transcription_service(self):
        """Create a mock Transcription service that implements the protocol."""
        service = MagicMock()
        
        # Mock all required methods
        service.transcribe_file = AsyncMock()
        service.transcribe_audio = AsyncMock()
        service.create_transcription_job = AsyncMock()
        service.get_job_status = AsyncMock()
        service.cancel_job = AsyncMock(return_value=True)
        
        return service

    def test_transcription_service_protocol_compliance(self, mock_transcription_service):
        """Test that Transcription service implements the protocol correctly."""
        from src.services.protocols import TranscriptionServiceProtocol
        
        # Test protocol validation
        assert validate_protocol_implementation(mock_transcription_service, TranscriptionServiceProtocol)
        
        # Test that no methods are missing
        missing_methods = get_missing_methods(mock_transcription_service, TranscriptionServiceProtocol)
        assert len(missing_methods) == 0

    @pytest.mark.asyncio
    async def test_transcription_service_methods(self, mock_transcription_service):
        """Test Transcription service method calls."""
        # Test transcribe_file
        await mock_transcription_service.transcribe_file(MagicMock())
        mock_transcription_service.transcribe_file.assert_called_once()
        
        # Test transcribe_audio
        await mock_transcription_service.transcribe_audio(Path("/test.wav"))
        mock_transcription_service.transcribe_audio.assert_called_once()
        
        # Test cancel_job
        result = await mock_transcription_service.cancel_job("job-id")
        assert result is True


class TestEnhancementServiceImplementation:
    """Test Enhancement service protocol implementation."""

    @pytest.fixture
    def mock_enhancement_service(self):
        """Create a mock Enhancement service that implements the protocol."""
        service = MagicMock()
        
        # Mock all required methods
        service.initialize = AsyncMock()
        service.enhance_transcript = AsyncMock()
        service.enhance_transcript_batch = AsyncMock()
        service.enhance_transcription_result = AsyncMock()
        
        return service

    def test_enhancement_service_protocol_compliance(self, mock_enhancement_service):
        """Test that Enhancement service implements the protocol correctly."""
        from src.services.protocols import EnhancementServiceProtocol
        
        # Test protocol validation
        assert validate_protocol_implementation(mock_enhancement_service, EnhancementServiceProtocol)
        
        # Test that no methods are missing
        missing_methods = get_missing_methods(mock_enhancement_service, EnhancementServiceProtocol)
        assert len(missing_methods) == 0

    @pytest.mark.asyncio
    async def test_enhancement_service_methods(self, mock_enhancement_service):
        """Test Enhancement service method calls."""
        # Test initialize
        await mock_enhancement_service.initialize()
        mock_enhancement_service.initialize.assert_called_once()
        
        # Test enhance_transcript
        await mock_enhancement_service.enhance_transcript("test transcript")
        mock_enhancement_service.enhance_transcript.assert_called_once()
        
        # Test enhance_transcript_batch
        await mock_enhancement_service.enhance_transcript_batch(["transcript1", "transcript2"])
        mock_enhancement_service.enhance_transcript_batch.assert_called_once()


class TestExportServiceImplementation:
    """Test Export service protocol implementation."""

    @pytest.fixture
    def mock_export_service(self):
        """Create a mock Export service that implements the protocol."""
        service = MagicMock()
        
        # Mock all required methods
        service.export_transcript = AsyncMock()
        service.export_batch = AsyncMock()
        service.get_supported_formats = MagicMock(return_value=["json", "txt", "srt"])
        
        return service

    def test_export_service_protocol_compliance(self, mock_export_service):
        """Test that Export service implements the protocol correctly."""
        from src.services.protocols import ExportServiceProtocol
        
        # Test protocol validation
        assert validate_protocol_implementation(mock_export_service, ExportServiceProtocol)
        
        # Test that no methods are missing
        missing_methods = get_missing_methods(mock_export_service, ExportServiceProtocol)
        assert len(missing_methods) == 0

    def test_export_service_methods(self, mock_export_service):
        """Test Export service method calls."""
        # Test get_supported_formats
        formats = mock_export_service.get_supported_formats()
        assert isinstance(formats, list)
        assert "json" in formats
        
        # Test export_transcript
        mock_export_service.export_transcript(MagicMock(), Path("/output"), "json")
        mock_export_service.export_transcript.assert_called_once()


class TestBatchProcessorImplementation:
    """Test Batch processor protocol implementation."""

    @pytest.fixture
    def mock_batch_processor(self):
        """Create a mock Batch processor that implements the protocol."""
        processor = MagicMock()
        
        # Mock all required methods
        processor.add_task = AsyncMock(return_value="task-id")
        processor.process_tasks = AsyncMock()
        processor.get_progress = AsyncMock()
        processor.cancel_task = AsyncMock(return_value=True)
        processor.get_task_status = AsyncMock()
        processor.get_completed_tasks = AsyncMock(return_value=[])
        
        return processor

    def test_batch_processor_protocol_compliance(self, mock_batch_processor):
        """Test that Batch processor implements the protocol correctly."""
        from src.services.protocols import BatchProcessorProtocol
        
        # Test protocol validation
        assert validate_protocol_implementation(mock_batch_processor, BatchProcessorProtocol)
        
        # Test that no methods are missing
        missing_methods = get_missing_methods(mock_batch_processor, BatchProcessorProtocol)
        assert len(missing_methods) == 0

    @pytest.mark.asyncio
    async def test_batch_processor_methods(self, mock_batch_processor):
        """Test Batch processor method calls."""
        # Test add_task
        task_id = await mock_batch_processor.add_task("transcription", {"url": "test"})
        assert task_id == "task-id"
        
        # Test process_tasks
        await mock_batch_processor.process_tasks(max_workers=4)
        mock_batch_processor.process_tasks.assert_called_once_with(max_workers=4)
        
        # Test cancel_task
        result = await mock_batch_processor.cancel_task("task-id")
        assert result is True


class TestProtocolValidationUtilities:
    """Test protocol validation utility functions."""

    def test_validate_protocol_implementation_with_valid_instance(self):
        """Test protocol validation with a valid instance."""
        class ValidService:
            def extract_metadata(self, url: str) -> Dict[str, Any]:
                return {"title": "Test"}
            
            def batch_extract(self, urls: List[str]) -> List[Dict[str, Any]]:
                return [{"title": "Test"}]
        
        service = ValidService()
        from src.services.protocols import YouTubeServiceProtocol
        
        # This should work at runtime since we have the required methods
        # Note: This is a runtime check, not a static type check
        assert hasattr(service, 'extract_metadata')
        assert hasattr(service, 'batch_extract')

    def test_get_missing_methods_with_incomplete_implementation(self):
        """Test getting missing methods from incomplete implementation."""
        class IncompleteService:
            def extract_metadata(self, url: str) -> Dict[str, Any]:
                return {"title": "Test"}
            # Missing batch_extract method
        
        service = IncompleteService()
        from src.services.protocols import YouTubeServiceProtocol
        
        missing_methods = get_missing_methods(service, YouTubeServiceProtocol)
        assert "batch_extract" in missing_methods

    def test_get_missing_methods_with_complete_implementation(self):
        """Test getting missing methods from complete implementation."""
        class CompleteService:
            def extract_metadata(self, url: str) -> Dict[str, Any]:
                return {"title": "Test"}
            
            def batch_extract(self, urls: List[str]) -> List[Dict[str, Any]]:
                return [{"title": "Test"}]
        
        service = CompleteService()
        from src.services.protocols import YouTubeServiceProtocol
        
        missing_methods = get_missing_methods(service, YouTubeServiceProtocol)
        assert len(missing_methods) == 0


if __name__ == "__main__":
    pytest.main([__file__])
