"""Tests for app.services.daily_digest"""

import pytest
from unittest.mock import AsyncMock, MagicMock, patch


@pytest.mark.asyncio
async def test_send_daily_digest_to_user_no_feed():
    """Empty query result returns 0."""
    from app.services.daily_digest import send_daily_digest_to_user

    mock_db = AsyncMock()
    mock_result = MagicMock()
    mock_result.all.return_value = []
    mock_db.execute.return_value = mock_result

    result = await send_daily_digest_to_user(
        mock_db, "00000000-0000-0000-0000-000000000000", "test@test.com", "Test"
    )

    assert result == 0


@pytest.mark.asyncio
async def test_send_daily_digest_to_user_has_feed():
    """2 rows, patches build_daily_digest and send_email, verifies feed_items payload and subject line, returns 2."""
    from app.services.daily_digest import send_daily_digest_to_user

    mock_feed1 = MagicMock()
    mock_feed1.priority = "must_read"
    mock_lit1 = MagicMock()
    mock_lit1.title = "Article 1"
    mock_lit1.journal = "Journal A"
    mock_lit1.authors = [{"family": "Smith"}]

    mock_feed2 = MagicMock()
    mock_feed2.priority = "recommended"
    mock_lit2 = MagicMock()
    mock_lit2.title = "Article 2"
    mock_lit2.journal = "Journal B"
    mock_lit2.authors = [{"family": "Jones"}]

    mock_db = AsyncMock()
    mock_result = MagicMock()
    mock_result.all.return_value = [(mock_feed1, mock_lit1), (mock_feed2, mock_lit2)]
    mock_db.execute.return_value = mock_result

    with patch("app.services.daily_digest.build_daily_digest") as mock_build, \
         patch("app.services.daily_digest.send_email", return_value=True) as mock_send:
        mock_build.return_value = "<html>body</html>"
        result = await send_daily_digest_to_user(
            mock_db, "00000000-0000-0000-0000-000000000000", "test@test.com", "Test"
        )

    assert result == 2

    # Verify feed_items payload passed to build_daily_digest
    mock_build.assert_called_once()
    items = mock_build.call_args[0][0]
    assert len(items) == 2
    assert items[0]["title"] == "Article 1"
    assert items[0]["first_author"] == "Smith"
    assert items[0]["journal"] == "Journal A"
    assert items[0]["priority"] == "must_read"
    assert items[1]["title"] == "Article 2"
    assert items[1]["first_author"] == "Jones"
    assert items[1]["journal"] == "Journal B"
    assert items[1]["priority"] == "recommended"

    # Verify subject line passed to send_email
    mock_send.assert_called_once()
    subject = mock_send.call_args[0][1]
    assert "2篇" in subject


@pytest.mark.asyncio
async def test_send_daily_digest_to_user_email_fails():
    """send_email returns False, returns 0 despite feed items."""
    from app.services.daily_digest import send_daily_digest_to_user

    mock_feed = MagicMock()
    mock_feed.priority = "must_read"
    mock_lit = MagicMock()
    mock_lit.title = "Test"
    mock_lit.journal = "J"
    mock_lit.authors = []

    mock_db = AsyncMock()
    mock_result = MagicMock()
    mock_result.all.return_value = [(mock_feed, mock_lit)]
    mock_db.execute.return_value = mock_result

    with patch("app.services.daily_digest.build_daily_digest", return_value="<html>"), \
         patch("app.services.daily_digest.send_email", return_value=False):
        result = await send_daily_digest_to_user(
            mock_db, "00000000-0000-0000-0000-000000000000", "test@test.com", "Test"
        )

    assert result == 0


@pytest.mark.asyncio
async def test_send_daily_digest_to_user_first_author():
    """Article with authors=[{"family":"Smith"}], verifies first_author="Smith" in the feed_items passed to build."""
    from app.services.daily_digest import send_daily_digest_to_user

    mock_feed = MagicMock()
    mock_feed.priority = "must_read"
    mock_lit = MagicMock()
    mock_lit.title = "Test Article"
    mock_lit.journal = "Test Journal"
    mock_lit.authors = [{"family": "Smith"}, {"family": "Johnson"}]

    mock_db = AsyncMock()
    mock_result = MagicMock()
    mock_result.all.return_value = [(mock_feed, mock_lit)]
    mock_db.execute.return_value = mock_result

    with patch("app.services.daily_digest.build_daily_digest") as mock_build, \
         patch("app.services.daily_digest.send_email", return_value=True):
        await send_daily_digest_to_user(
            mock_db, "00000000-0000-0000-0000-000000000000", "test@test.com", "Test"
        )

    items = mock_build.call_args[0][0]
    assert len(items) == 1
    assert items[0]["first_author"] == "Smith"


@pytest.mark.asyncio
async def test_send_all_daily_digests():
    """Patches async_session context manager and send_daily_digest_to_user, 1 user returns 5, stats={"users":1,"sent":1,"total_feed_items":5}."""
    from app.services.daily_digest import send_all_daily_digests

    mock_db = AsyncMock()
    mock_result = MagicMock()
    mock_result.all.return_value = [
        ("00000000-0000-0000-0000-000000000001", "a@b.com", "A"),
    ]
    mock_db.execute.return_value = mock_result

    with patch("app.services.daily_digest.async_session") as mock_session, \
         patch("app.services.daily_digest.send_daily_digest_to_user", return_value=5):
        mock_cm = AsyncMock()
        mock_cm.__aenter__.return_value = mock_db
        mock_session.return_value = mock_cm
        stats = await send_all_daily_digests()

    assert stats == {"users": 1, "sent": 1, "total_feed_items": 5}


@pytest.mark.asyncio
async def test_send_all_daily_digests_empty():
    """No users found, all stats zero."""
    from app.services.daily_digest import send_all_daily_digests

    mock_db = AsyncMock()
    mock_result = MagicMock()
    mock_result.all.return_value = []
    mock_db.execute.return_value = mock_result

    with patch("app.services.daily_digest.async_session") as mock_session:
        mock_cm = AsyncMock()
        mock_cm.__aenter__.return_value = mock_db
        mock_session.return_value = mock_cm
        stats = await send_all_daily_digests()

    assert stats == {"users": 0, "sent": 0, "total_feed_items": 0}


@pytest.mark.asyncio
async def test_run_daily_digest():
    """Patches send_all_daily_digests, verifies it's called and its return value is passed through."""
    from app.services.daily_digest import run_daily_digest

    expected = {"users": 1, "sent": 1, "total_feed_items": 3}
    with patch("app.services.daily_digest.send_all_daily_digests", return_value=expected) as mock_send:
        result = await run_daily_digest()

    mock_send.assert_awaited_once()
    assert result == expected
