"""
Download System Stress Testing.

This module tests the download queue and management system under
heavy load and stress conditions.
"""

import asyncio
from unittest.mock import AsyncMock, MagicMock

import pytest

from src.server.models.download import DownloadPriority, EpisodeIdentifier
from src.server.services.anime_service import AnimeService
from src.server.services.download_service import DownloadService


@pytest.mark.performance
class TestDownloadQueueStress:
    """Stress testing for download queue."""

    @pytest.fixture
    def mock_anime_service(self):
        """Create mock AnimeService."""
        service = MagicMock(spec=AnimeService)
        service._directory = "/tmp/test_anime"
        service.download = AsyncMock(return_value=True)
        return service

    @pytest.fixture
    def download_service(self, mock_anime_service, tmp_path):
        """Create download service with mock repository."""
        from tests.unit.test_download_service import MockQueueRepository
        mock_repo = MockQueueRepository()
        service = DownloadService(
            anime_service=mock_anime_service,
            max_retries=3,
            queue_repository=mock_repo,
        )
        return service

    @pytest.mark.asyncio
    async def test_concurrent_download_additions(
        self, download_service
    ):
        """Test adding many downloads concurrently."""
        num_downloads = 100

        # Add downloads concurrently
        tasks = [
            download_service.add_to_queue(
                serie_id=f"series-{i}",
                serie_folder=f"series_{i}",
                serie_name=f"Test Series {i}",
                episodes=[EpisodeIdentifier(season=1, episode=1)],
                priority=DownloadPriority.NORMAL,
            )
            for i in range(num_downloads)
        ]

        results = await asyncio.gather(*tasks, return_exceptions=True)

        # Count successful additions
        successful = sum(
            1 for r in results if not isinstance(r, Exception)
        )

        # Should handle at least 90% successfully
        success_rate = (successful / num_downloads) * 100
        assert (
            success_rate >= 90.0
        ), f"Queue addition success rate too low: {success_rate}%"

    @pytest.mark.asyncio
    async def test_queue_capacity(self, download_service):
        """Test queue behavior at capacity."""
        # Fill queue beyond reasonable capacity
        num_downloads = 1000

        for i in range(num_downloads):
            try:
                await download_service.add_to_queue(
                    serie_id=f"series-{i}",
                serie_folder=f"series_folder",
                serie_name=f"Test Series {i}",
                    episodes=[EpisodeIdentifier(season=1, episode=1)],
                    priority=DownloadPriority.NORMAL,
                )
            except Exception:
                # Queue might have limits
                pass

        # Queue should still be functional
        status = await download_service.get_queue_status()
        assert status is not None, "Queue became non-functional"

    @pytest.mark.asyncio
    async def test_rapid_queue_operations(self, download_service):
        """Test rapid add/remove operations."""
        num_operations = 200

        operations = []
        for i in range(num_operations):
            if i % 2 == 0:
                # Add operation
                operations.append(
                    download_service.add_to_queue(
                        serie_id=f"series-{i}",
                serie_folder=f"series_folder",
                serie_name=f"Test Series {i}",
                        episodes=[EpisodeIdentifier(season=1, episode=1)],
                        priority=DownloadPriority.NORMAL,
                    )
                )
            else:
                # Remove operation - get item IDs from pending queue
                item_ids = list(
                    download_service._pending_items_by_id.keys()
                )
                if item_ids:
                    operations.append(
                        download_service.remove_from_queue([item_ids[0]])
                    )

        results = await asyncio.gather(
            *operations, return_exceptions=True
        )

        # Most operations should succeed
        successful = sum(
            1 for r in results if not isinstance(r, Exception)
        )
        success_rate = (successful / len(results)) * 100 if results else 0

        assert success_rate >= 80.0, "Operation success rate too low"

    @pytest.mark.asyncio
    async def test_concurrent_queue_reads(self, download_service):
        """Test concurrent queue status reads."""
        # Add some items to queue
        for i in range(10):
            await download_service.add_to_queue(
                serie_id=f"series-{i}",
                serie_folder=f"series_folder",
                serie_name=f"Test Series {i}",
                episodes=[EpisodeIdentifier(season=1, episode=1)],
                priority=DownloadPriority.NORMAL,
            )

        # Perform many concurrent reads
        num_reads = 100
        tasks = [
            download_service.get_queue_status() for _ in range(num_reads)
        ]

        results = await asyncio.gather(*tasks, return_exceptions=True)

        # All reads should succeed
        successful = sum(
            1 for r in results if not isinstance(r, Exception)
        )

        assert (
            successful == num_reads
        ), "Some queue reads failed"


@pytest.mark.performance
class TestDownloadMemoryUsage:
    """Test memory usage under load."""

    @pytest.fixture
    def mock_anime_service(self):
        """Create mock AnimeService."""
        service = MagicMock(spec=AnimeService)
        service._directory = "/tmp/test_anime"
        service.download = AsyncMock(return_value=True)
        return service

    @pytest.fixture
    def download_service(self, mock_anime_service, tmp_path):
        """Create download service with mock repository."""
        from tests.unit.test_download_service import MockQueueRepository
        mock_repo = MockQueueRepository()
        mock_anime_service._directory = "/tmp/test_anime"
        service = DownloadService(
            anime_service=mock_anime_service,
            max_retries=3,
            queue_repository=mock_repo,
        )
        return service

    @pytest.mark.asyncio
    async def test_queue_memory_leak(self, download_service):
        """Test for memory leaks in queue operations."""
        # This is a placeholder for memory profiling
        # In real implementation, would use memory_profiler
        # or similar tools

        # Perform many operations
        for i in range(1000):
            await download_service.add_to_queue(
                serie_id=f"series-{i}",
                serie_folder=f"series_folder",
                serie_name=f"Test Series {i}",
                episodes=[EpisodeIdentifier(season=1, episode=1)],
                priority=DownloadPriority.NORMAL,
            )

            if i % 100 == 0:
                # Clear some items periodically
                item_ids = list(download_service._pending_items_by_id.keys())
                if item_ids:
                    await download_service.remove_from_queue([item_ids[0]])

        # Service should still be functional
        status = await download_service.get_queue_status()
        assert status is not None


@pytest.mark.performance
class TestDownloadConcurrency:
    """Test concurrent download handling."""

    @pytest.fixture
    def mock_anime_service(self):
        """Create mock AnimeService with slow downloads."""
        service = MagicMock(spec=AnimeService)
        service._directory = "/tmp/test_anime"
        
        async def slow_download(*args, **kwargs):
            # Simulate slow download
            await asyncio.sleep(0.1)
            return True

        service.download = slow_download
        return service

    @pytest.fixture
    def download_service(self, mock_anime_service, tmp_path):
        """Create download service with mock repository."""
        from tests.unit.test_download_service import MockQueueRepository
        mock_repo = MockQueueRepository()
        service = DownloadService(
            anime_service=mock_anime_service,
            max_retries=3,
            queue_repository=mock_repo,
        )
        return service

    @pytest.mark.asyncio
    async def test_concurrent_download_execution(
        self, download_service
    ):
        """Test executing multiple downloads concurrently."""
        # Start multiple downloads
        num_downloads = 20
        tasks = [
            download_service.add_to_queue(
                serie_id=f"series-{i}",
                serie_folder=f"series_folder",
                serie_name=f"Test Series {i}",
                episodes=[EpisodeIdentifier(season=1, episode=1)],
                priority=DownloadPriority.NORMAL,
            )
            for i in range(num_downloads)
        ]

        await asyncio.gather(*tasks)

        # All downloads should be queued
        status = await download_service.get_queue_status()
        total = (
            len(status.pending_queue) +
            len(status.active_downloads) +
            len(status.completed_downloads)
        )
        assert total <= num_downloads

    @pytest.mark.asyncio
    async def test_download_priority_under_load(
        self, download_service
    ):
        """Test that priority is respected under load."""
        # Add downloads with different priorities
        await download_service.add_to_queue(
            serie_id="series-1",
            serie_folder=f"series_folder",
                serie_name="Test Series 1",
            episodes=[EpisodeIdentifier(season=1, episode=1)],
            priority=DownloadPriority.LOW,
        )
        await download_service.add_to_queue(
            serie_id="series-2",
            serie_folder=f"series_folder",
                serie_name="Test Series 2",
            episodes=[EpisodeIdentifier(season=1, episode=1)],
            priority=DownloadPriority.HIGH,
        )
        await download_service.add_to_queue(
            serie_id="series-3",
            serie_folder=f"series_folder",
                serie_name="Test Series 3",
            episodes=[EpisodeIdentifier(season=1, episode=1)],
            priority=DownloadPriority.NORMAL,
        )

        # High priority should be processed first
        status = await download_service.get_queue_status()
        assert status is not None


@pytest.mark.performance
class TestDownloadErrorHandling:
    """Test error handling under stress."""

    @pytest.fixture
    def mock_failing_anime_service(self):
        """Create mock AnimeService that fails downloads."""
        service = MagicMock(spec=AnimeService)
        service._directory = "/tmp/test_anime"
        service.download = AsyncMock(
            side_effect=Exception("Download failed")
        )
        return service

    @pytest.fixture
    def download_service_failing(
        self, mock_failing_anime_service, tmp_path
    ):
        """Create download service with failing mock."""
        from tests.unit.test_download_service import MockQueueRepository
        mock_repo = MockQueueRepository()
        service = DownloadService(
            anime_service=mock_failing_anime_service,
            max_retries=3,
            queue_repository=mock_repo,
        )
        return service

    @pytest.fixture
    def mock_anime_service(self):
        """Create mock AnimeService."""
        service = MagicMock(spec=AnimeService)
        service._directory = "/tmp/test_anime"
        service.download = AsyncMock(return_value=True)
        return service

    @pytest.fixture
    def download_service(self, mock_anime_service, tmp_path):
        """Create download service with mock repository."""
        from tests.unit.test_download_service import MockQueueRepository
        mock_repo = MockQueueRepository()
        mock_anime_service._directory = "/tmp/test_anime"
        service = DownloadService(
            anime_service=mock_anime_service,
            max_retries=3,
            queue_repository=mock_repo,
        )
        return service

    @pytest.mark.asyncio
    async def test_multiple_failed_downloads(
        self, download_service_failing
    ):
        """Test handling of many failed downloads."""
        # Add multiple downloads
        for i in range(50):
            await download_service_failing.add_to_queue(
                serie_id=f"series-{i}",
                serie_folder=f"series_folder",
                serie_name=f"Test Series {i}",
                episodes=[EpisodeIdentifier(season=1, episode=1)],
                priority=DownloadPriority.NORMAL,
            )

        # Service should remain stable despite failures
        status = await download_service_failing.get_queue_status()
        assert status is not None

    @pytest.mark.asyncio
    async def test_recovery_from_errors(self, download_service):
        """Test system recovery after errors."""
        # Cause some errors
        try:
            await download_service.remove_from_queue(["nonexistent-id"])
        except Exception:
            pass

        # System should still work
        await download_service.add_to_queue(
            serie_id="series-1",
            serie_folder=f"series_folder",
                serie_name="Test Series 1",
            episodes=[EpisodeIdentifier(season=1, episode=1)],
            priority=DownloadPriority.NORMAL,
        )

        status = await download_service.get_queue_status()
        assert status is not None
