"""Tests for SerieList database loading functionality."""

from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from src.core.entities.series import Serie


@pytest.fixture
def mock_session_factory():
    """Create a mock async session factory."""
    mock_session = AsyncMock()
    mock_session_factory = MagicMock(return_value=mock_session)
    return mock_session_factory, mock_session


@pytest.fixture
def sample_anime_series():
    """Create a sample AnimeSeries DB model for testing."""
    mock = MagicMock()
    mock.key = "attack-on-titan"
    mock.name = "Attack on Titan"
    mock.site = "aniworld.to"
    mock.folder = "Attack on Titan (2013)"
    mock.year = 2013
    mock.episodes = [
        MagicMock(season=1, episode_number=1),
        MagicMock(season=1, episode_number=2),
        MagicMock(season=1, episode_number=3),
        MagicMock(season=2, episode_number=1),
        MagicMock(season=2, episode_number=2),
    ]
    return mock


@pytest.fixture
def sample_serie():
    """Create a sample Serie for testing."""
    return Serie(
        key="attack-on-titan",
        name="Attack on Titan",
        site="aniworld.to",
        folder="Attack on Titan (2013)",
        episodeDict={1: [1, 2, 3], 2: [1, 2]},
        year=2013
    )


class TestLoadAllFromDb:
    """Test load_all_from_db method."""

    @pytest.mark.asyncio
    async def test_load_all_from_db(self, mock_session_factory, sample_anime_series):
        """Verify SerieList loads all series from DB."""
        mock_factory, mock_session = mock_session_factory
        
        with patch(
            "src.server.database.connection.get_async_session_factory",
            return_value=mock_factory
        ):
            with patch(
                "src.server.database.service.AnimeSeriesService.get_all",
                return_value=[sample_anime_series]
            ):
                from src.core.entities.SerieList import SerieList
                
                serie_list = SerieList("/tmp", skip_load=True)
                count = await serie_list.load_all_from_db()
                
                assert count == 1
                assert "attack-on-titan" in serie_list.keyDict
                serie = serie_list.keyDict["attack-on-titan"]
                assert serie.name == "Attack on Titan"
                assert serie.key == "attack-on-titan"
                assert serie.year == 2013

    @pytest.mark.asyncio
    async def test_load_all_from_db_multiple_series(
        self, mock_session_factory, sample_anime_series
    ):
        """Verify SerieList loads multiple series from DB."""
        mock_factory, mock_session = mock_session_factory
        
        mock_series2 = MagicMock()
        mock_series2.key = "one-piece"
        mock_series2.name = "One Piece"
        mock_series2.site = "aniworld.to"
        mock_series2.folder = "One Piece"
        mock_series2.year = 1999
        mock_series2.episodes = []
        
        with patch(
            "src.server.database.connection.get_async_session_factory",
            return_value=mock_factory
        ):
            with patch(
                "src.server.database.service.AnimeSeriesService.get_all",
                return_value=[sample_anime_series, mock_series2]
            ):
                from src.core.entities.SerieList import SerieList
                
                serie_list = SerieList("/tmp", skip_load=True)
                count = await serie_list.load_all_from_db()
                
                assert count == 2
                assert "attack-on-titan" in serie_list.keyDict
                assert "one-piece" in serie_list.keyDict

    @pytest.mark.asyncio
    async def test_load_all_from_db_rebuilds_episode_dict(
        self, mock_session_factory, sample_anime_series
    ):
        """Verify episode_dict is correctly built from Episode records."""
        mock_factory, mock_session = mock_session_factory
        
        with patch(
            "src.server.database.connection.get_async_session_factory",
            return_value=mock_factory
        ):
            with patch(
                "src.server.database.service.AnimeSeriesService.get_all",
                return_value=[sample_anime_series]
            ):
                from src.core.entities.SerieList import SerieList
                
                serie_list = SerieList("/tmp", skip_load=True)
                await serie_list.load_all_from_db()
                
                serie = serie_list.keyDict["attack-on-titan"]
                assert 1 in serie.episodeDict
                assert 2 in serie.episodeDict
                assert sorted(serie.episodeDict[1]) == [1, 2, 3]
                assert sorted(serie.episodeDict[2]) == [1, 2]

    @pytest.mark.asyncio
    async def test_load_all_from_db_no_series(self, mock_session_factory):
        """Verify SerieList handles empty DB."""
        mock_factory, mock_session = mock_session_factory
        
        with patch(
            "src.server.database.connection.get_async_session_factory",
            return_value=mock_factory
        ):
            with patch(
                "src.server.database.service.AnimeSeriesService.get_all",
                return_value=[]
            ):
                from src.core.entities.SerieList import SerieList
                
                serie_list = SerieList("/tmp", skip_load=True)
                count = await serie_list.load_all_from_db()
                
                assert count == 0
                assert len(serie_list.keyDict) == 0

    @pytest.mark.asyncio
    async def test_load_all_from_db_db_not_initialized(self, mock_session_factory):
        """Verify SerieList handles uninitialized DB gracefully."""
        mock_factory, mock_session = mock_session_factory
        
        with patch(
            "src.server.database.connection.get_async_session_factory",
            return_value=mock_factory
        ):
            with patch(
                "src.server.database.service.AnimeSeriesService.get_all",
                side_effect=RuntimeError("Database not initialized")
            ):
                from src.core.entities.SerieList import SerieList
                
                serie_list = SerieList("/tmp", skip_load=True)
                count = await serie_list.load_all_from_db()
                
                assert count == 0
                assert len(serie_list.keyDict) == 0


class TestLoadSingleSeriesFromDb:
    """Test _load_single_series_from_db method."""

    @pytest.mark.asyncio
    async def test_load_single_series_from_db(
        self, mock_session_factory, sample_anime_series
    ):
        """Verify SerieList loads a single series from DB by folder."""
        mock_factory, mock_session = mock_session_factory
        
        with patch(
            "src.server.database.connection.get_async_session_factory",
            return_value=mock_factory
        ):
            with patch(
                "src.server.database.service.AnimeSeriesService.get_by_folder",
                return_value=sample_anime_series
            ):
                from src.core.entities.SerieList import SerieList
                
                serie_list = SerieList("/tmp", skip_load=True)
                serie = await serie_list._load_single_series_from_db("Attack on Titan (2013)")
                
                assert serie is not None
                assert serie.key == "attack-on-titan"
                assert "attack-on-titan" in serie_list.keyDict

    @pytest.mark.asyncio
    async def test_load_single_series_from_db_not_found(
        self, mock_session_factory
    ):
        """Verify SerieList handles series not found in DB."""
        mock_factory, mock_session = mock_session_factory
        
        with patch(
            "src.server.database.connection.get_async_session_factory",
            return_value=mock_factory
        ):
            with patch(
                "src.server.database.service.AnimeSeriesService.get_by_folder",
                return_value=None
            ):
                from src.core.entities.SerieList import SerieList
                
                serie_list = SerieList("/tmp", skip_load=True)
                serie = await serie_list._load_single_series_from_db("Unknown Series")
                
                assert serie is None
                assert len(serie_list.keyDict) == 0

    @pytest.mark.asyncio
    async def test_load_single_series_from_db_db_not_initialized(
        self, mock_session_factory
    ):
        """Verify SerieList handles uninitialized DB gracefully."""
        mock_factory, mock_session = mock_session_factory
        
        with patch(
            "src.server.database.connection.get_async_session_factory",
            return_value=mock_factory
        ):
            with patch(
                "src.server.database.service.AnimeSeriesService.get_by_folder",
                side_effect=RuntimeError("Database not initialized")
            ):
                from src.core.entities.SerieList import SerieList
                
                serie_list = SerieList("/tmp", skip_load=True)
                serie = await serie_list._load_single_series_from_db("Some Folder")
                
                assert serie is None


class TestInvalidateCache:
    """Test invalidate_cache method."""

    def test_invalidate_cache_clears_keydict(self, sample_serie):
        """Verify invalidate_cache clears the in-memory cache."""
        from src.core.entities.SerieList import SerieList
        
        serie_list = SerieList("/tmp", skip_load=True)
        serie_list.keyDict["attack-on-titan"] = sample_serie
        assert len(serie_list.keyDict) == 1
        
        serie_list.invalidate_cache()
        
        assert len(serie_list.keyDict) == 0

    def test_invalidate_cache_allows_reload(self, mock_session_factory, sample_anime_series):
        """Verify cache can be reloaded after invalidation."""
        mock_factory, mock_session = mock_session_factory
        
        with patch(
            "src.server.database.connection.get_async_session_factory",
            return_value=mock_factory
        ):
            with patch(
                "src.server.database.service.AnimeSeriesService.get_all",
                return_value=[sample_anime_series]
            ):
                from src.core.entities.SerieList import SerieList
                
                serie_list = SerieList("/tmp", skip_load=True)
                serie_list.keyDict["some-key"] = MagicMock()
                
                serie_list.invalidate_cache()
                assert len(serie_list.keyDict) == 0
                
                # Reload
                import asyncio
                asyncio.get_event_loop().run_until_complete(serie_list.load_all_from_db())
                
                assert len(serie_list.keyDict) == 1