import pytest
from unittest.mock import AsyncMock, patch, MagicMock
from datetime import datetime, timezone, timedelta
from src.services.notification_service import check_and_send_all_groups
from src.group_settings.models import GroupConfig
from src.events.models import NotificationHistory
from sqlalchemy import select

@pytest.mark.anyio
async def test_notification_sent_for_upcoming_event(session):
    # 1. Setup mock data
    group = GroupConfig(
        group_email="test-group@example.com",
        auto_mail_recipient="recipient@example.com",
        campflow_api_token_enc="some-token"
    )
    session.add(group)
    await session.commit()
    await session.refresh(group)

    # 2. Mock external services
    mock_event = {
        "list_id": "event_123",
        "title": "Upcoming Camp",
        "start_date": (datetime.now(timezone.utc) + timedelta(days=3)).isoformat(),
        "published": True
    }
    
    mock_client = AsyncMock()
    mock_client.get_events.return_value = [mock_event]
    mock_client.get_event_details.return_value = mock_event

    with patch("src.services.notification_service.get_campflow_client", return_value=mock_client), \
         patch("src.services.notification_service.mail_service.send_auto_mail", new_callable=AsyncMock) as mock_send_mail, \
         patch("src.services.notification_service.fetch_event_tn_data", new_callable=AsyncMock) as mock_fetch_tn, \
         patch("src.services.notification_service.generate_email_stats", return_value=("<html>test</html>", [])):
        
        mock_fetch_tn.return_value = ([], [], [])
        mock_send_mail.return_value = True

        # 3. Run the service
        await check_and_send_all_groups(session_override=session)

        # 4. Verify results
        # Check if mail was sent
        mock_send_mail.assert_called_once()
        assert mock_send_mail.call_args.kwargs["to_address"] == "recipient@example.com"
        assert "Upcoming Camp" in mock_send_mail.call_args.kwargs["event_title"]

        # Check if history was recorded
        stmt = select(NotificationHistory).where(NotificationHistory.list_id == "event_123")
        result = await session.execute(stmt)
        history = result.scalar_one_or_none()
        assert history is not None

@pytest.mark.anyio
async def test_notification_not_sent_if_already_sent(session):
    # 1. Setup mock data
    group = GroupConfig(
        group_email="test-group@example.com",
        auto_mail_recipient="recipient@example.com",
        campflow_api_token_enc="some-token"
    )
    session.add(group)
    
    # Pre-record history
    history = NotificationHistory(list_id="event_123")
    session.add(history)
    await session.commit()

    # 2. Mock external services
    mock_event = {
        "list_id": "event_123",
        "title": "Upcoming Camp",
        "start_date": (datetime.now(timezone.utc) + timedelta(days=3)).isoformat(),
        "published": True
    }
    
    mock_client = AsyncMock()
    mock_client.get_events.return_value = [mock_event]

    with patch("src.services.notification_service.get_campflow_client", return_value=mock_client), \
         patch("src.services.notification_service.mail_service.send_auto_mail", new_callable=AsyncMock) as mock_send_mail:
        
        # 3. Run the service
        await check_and_send_all_groups(session_override=session)

        # 4. Verify results
        mock_send_mail.assert_not_called()

@pytest.mark.anyio
async def test_notification_not_sent_if_too_far_away(session):
    # 1. Setup mock data
    group = GroupConfig(
        group_email="test-group@example.com",
        auto_mail_recipient="recipient@example.com",
        campflow_api_token_enc="some-token"
    )
    session.add(group)
    await session.commit()

    # 2. Mock external services
    mock_event = {
        "list_id": "event_999",
        "title": "Future Camp",
        "start_date": (datetime.now(timezone.utc) + timedelta(days=30)).isoformat(),
        "published": True
    }
    
    mock_client = AsyncMock()
    mock_client.get_events.return_value = [mock_event]

    with patch("src.services.notification_service.get_campflow_client", return_value=mock_client), \
         patch("src.services.notification_service.mail_service.send_auto_mail", new_callable=AsyncMock) as mock_send_mail:
        
        # 3. Run the service
        await check_and_send_all_groups(session_override=session)

        # 4. Verify results
        mock_send_mail.assert_not_called()
@pytest.mark.anyio
async def test_notification_sent_even_with_zero_participants(session):
    # 1. Setup mock data
    group = GroupConfig(
        group_email="test-group@example.com",
        auto_mail_recipient="recipient@example.com",
        campflow_api_token_enc="some-token"
    )
    session.add(group)
    await session.commit()

    # 2. Mock external services
    mock_event = {
        "list_id": "event_empty",
        "title": "Empty Camp",
        "start_date": (datetime.now(timezone.utc) + timedelta(days=3)).isoformat(),
        "published": True
    }
    
    mock_client = AsyncMock()
    mock_client.get_events.return_value = [mock_event]
    mock_client.get_event_details.return_value = mock_event

    with patch("src.services.notification_service.get_campflow_client", return_value=mock_client), \
         patch("src.services.notification_service.mail_service.send_auto_mail", new_callable=AsyncMock) as mock_send_mail, \
         patch("src.services.notification_service.fetch_event_tn_data", new_callable=AsyncMock) as mock_fetch_tn, \
         patch("src.services.notification_service.generate_email_stats", return_value=("<html>test</html>", [])):
        
        # Simulate empty data
        mock_fetch_tn.return_value = ([], [], mock_event)
        mock_send_mail.return_value = True

        # 3. Run the service
        await check_and_send_all_groups(session_override=session)

        # 4. Verify results
        mock_send_mail.assert_called_once()
        assert mock_send_mail.call_args.kwargs["to_address"] == "recipient@example.com"
