"""Tests for series filtering functionality."""
import pytest
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine

from src.server.database.models import Base
from src.server.database.service import AnimeSeriesService, EpisodeService


@pytest.fixture
async def db_engine():
    """Create test database engine."""
    engine = create_async_engine(
        "sqlite+aiosqlite:///:memory:",
        echo=False,
    )
    
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)
    
    yield engine
    
    await engine.dispose()


@pytest.fixture
async def session_factory(db_engine):
    """Create session factory for testing."""
    return async_sessionmaker(
        db_engine,
        class_=AsyncSession,
        expire_on_commit=False,
        autoflush=False,
        autocommit=False,
    )


@pytest.fixture
async def async_session(session_factory):
    """Create database session for testing."""
    async with session_factory() as session:
        yield session
        await session.rollback()


@pytest.mark.asyncio
async def test_get_series_with_no_episodes_empty_database(
    async_session: AsyncSession
):
    """Test that empty database returns empty list."""
    result = await AnimeSeriesService.get_series_with_no_episodes(
        async_session
    )
    assert result == []


@pytest.mark.asyncio
async def test_get_series_with_no_episodes_no_downloaded_episodes(
    async_session: AsyncSession
):
    """Test that series with no downloaded episodes are returned.
    
    Episodes in DB represent MISSING episodes, so:
    - Series with episodes in DB (is_downloaded=False) = no files in folder
    - Series with no episodes in DB = all episodes downloaded or no info
    """
    # Create a series with NO episodes in DB
    # (all downloaded or no episodes info)
    series1 = await AnimeSeriesService.create(
        async_session,
        key="test-series-1",
        name="Test Series 1",
        folder="Test Series 1 (2024)",
        site="https://example.com/test1",
    )
    
    # Create a series with undownloaded episodes (MISSING - should appear)
    series2 = await AnimeSeriesService.create(
        async_session,
        key="test-series-2",
        name="Test Series 2",
        folder="Test Series 2 (2024)",
        site="https://example.com/test2",
    )
    await EpisodeService.create(
        async_session,
        series_id=series2.id,
        season=1,
        episode_number=1,
        is_downloaded=False,
    )
    
    # Create a series with downloaded episodes (should not appear)
    series3 = await AnimeSeriesService.create(
        async_session,
        key="test-series-3",
        name="Test Series 3",
        folder="Test Series 3 (2024)",
        site="https://example.com/test3",
    )
    await EpisodeService.create(
        async_session,
        series_id=series3.id,
        season=1,
        episode_number=1,
        is_downloaded=True,
    )
    
    await async_session.commit()
    
    # Query for series with no episodes in folder
    result = await AnimeSeriesService.get_series_with_no_episodes(
        async_session
    )
    
    # Should return only series2 (has missing episodes)
    result_ids = {s.id for s in result}
    assert series1.id not in result_ids  # No episodes in DB
    assert series2.id in result_ids      # Has missing episodes
    assert series3.id not in result_ids  # Has downloaded episodes


@pytest.mark.asyncio
async def test_get_series_with_no_episodes_mixed_downloads(
    async_session: AsyncSession
):
    """Test series with mixed downloaded/undownloaded episodes.

    Series should NOT appear when there is at least one downloaded episode.
    """
    # Create series with some downloaded and some undownloaded episodes
    series = await AnimeSeriesService.create(
        async_session,
        key="test-series-mixed",
        name="Test Series Mixed",
        folder="Test Series Mixed (2024)",
        site="https://example.com/testmixed",
    )

    # Add downloaded episode
    await EpisodeService.create(
        async_session,
        series_id=series.id,
        season=1,
        episode_number=1,
        is_downloaded=True,
    )

    # Add undownloaded episode (MISSING)
    await EpisodeService.create(
        async_session,
        series_id=series.id,
        season=1,
        episode_number=2,
        is_downloaded=False,
    )

    await async_session.commit()

    # Query for series with no episodes in folder
    result = await AnimeSeriesService.get_series_with_no_episodes(
        async_session
    )

    # Should not return the series because it has at least one downloaded episode
    assert len(result) == 0


@pytest.mark.asyncio
async def test_get_series_with_missing_episodes_mixed_downloads(
    async_session: AsyncSession
):
    """Test missing episodes filter includes series with mixed downloads."""
    series = await AnimeSeriesService.create(
        async_session,
        key="test-series-mixed",
        name="Test Series Mixed",
        folder="Test Series Mixed (2024)",
        site="https://example.com/testmixed",
    )

    # Add downloaded episode
    await EpisodeService.create(
        async_session,
        series_id=series.id,
        season=1,
        episode_number=1,
        is_downloaded=True,
    )

    # Add undownloaded episode (MISSING)
    await EpisodeService.create(
        async_session,
        series_id=series.id,
        season=1,
        episode_number=2,
        is_downloaded=False,
    )

    await async_session.commit()

    # Query for series with missing episodes
    result = await AnimeSeriesService.get_series_with_missing_episodes(
        async_session
    )

    assert len(result) == 1
    assert result[0].id == series.id


@pytest.mark.asyncio
async def test_get_series_with_no_episodes_mixed_seasons(
    async_session: AsyncSession
):
    """Test series with some seasons downloaded, some not.

    Series should not appear when any episode is downloaded.
    """
    series = await AnimeSeriesService.create(
        async_session,
        key="test-series",
        name="Test Series",
        folder="Test Series (2024)",
        site="https://example.com/test",
    )

    # Season 1: all episodes downloaded
    await EpisodeService.create(
        async_session,
        series_id=series.id,
        season=1,
        episode_number=1,
        is_downloaded=True,
    )

    # Season 2: has missing episode
    await EpisodeService.create(
        async_session,
        series_id=series.id,
        season=2,
        episode_number=1,
        is_downloaded=False,
    )

    await async_session.commit()

    result = await AnimeSeriesService.get_series_with_no_episodes(
        async_session
    )

    # Should not return the series because it has downloaded episodes
    assert len(result) == 0


@pytest.mark.asyncio
async def test_get_series_with_no_episodes_pagination(
    async_session: AsyncSession
):
    """Test pagination works correctly.
    
    Note: Series with no episodes in DB won't appear.
    """
    # Create multiple series with missing episodes
    for i in range(5):
        series = await AnimeSeriesService.create(
            async_session,
            key=f"test-series-{i}",
            name=f"Test Series {i}",
            folder=f"Test Series {i} (2024)",
            site=f"https://example.com/test{i}",
        )
        # Add missing episode
        await EpisodeService.create(
            async_session,
            series_id=series.id,
            season=1,
            episode_number=1,
            is_downloaded=False,
        )
    
    await async_session.commit()
    
    # Test limit
    result = await AnimeSeriesService.get_series_with_no_episodes(
        async_session, limit=3
    )
    assert len(result) == 3
    
    # Test offset
    result = await AnimeSeriesService.get_series_with_no_episodes(
        async_session, offset=2, limit=2
    )
    assert len(result) == 2
