# [DEF:backend.src.services.notifications.__tests__.test_notification_service:Module]
# @TIER: STANDARD
# @PURPOSE: Unit tests for NotificationService routing and dispatch logic.

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

from src.models.llm import ValidationRecord, ValidationPolicy
from src.models.profile import UserDashboardPreference
from src.models.auth import User
from src.services.notifications.service import NotificationService


@pytest.fixture
def mock_db():
    return MagicMock()


@pytest.fixture
def mock_config_manager():
    cm = MagicMock()
    cm.get_payload.return_value = {
        "notifications": {
            "smtp": {"host": "localhost", "port": 25, "from_email": "test@example.com"},
            "telegram": {"bot_token": "test_token"}
        }
    }
    return cm


@pytest.fixture
def service(mock_db, mock_config_manager):
    return NotificationService(mock_db, mock_config_manager)


@pytest.mark.asyncio
async def test_should_notify_fail_only(service):
    record = ValidationRecord(status="FAIL")
    policy = ValidationPolicy(alert_condition="FAIL_ONLY")
    assert service._should_notify(record, policy) is True

    record.status = "WARN"
    assert service._should_notify(record, policy) is False


@pytest.mark.asyncio
async def test_should_notify_warn_and_fail(service):
    policy = ValidationPolicy(alert_condition="WARN_AND_FAIL")
    
    record = ValidationRecord(status="FAIL")
    assert service._should_notify(record, policy) is True

    record.status = "WARN"
    assert service._should_notify(record, policy) is True

    record.status = "PASS"
    assert service._should_notify(record, policy) is False


@pytest.mark.asyncio
async def test_resolve_targets_owner_routing(service, mock_db):
    record = ValidationRecord(dashboard_id="dash-1", environment_id="env-1")
    
    user = User(email="user@example.com")
    pref = UserDashboardPreference(
        user=user,
        telegram_id="12345",
        notify_on_fail=True,
        superset_username="user1"
    )
    
    mock_db.query.return_value.filter.return_value.all.return_value = [pref]
    
    targets = service._resolve_targets(record, None)
    
    assert ("TELEGRAM", "12345") in targets
    assert ("SMTP", "user@example.com") in targets


@pytest.mark.asyncio
async def test_resolve_targets_custom_channels(service):
    record = ValidationRecord(status="FAIL")
    policy = ValidationPolicy(
        notify_owners=False,
        custom_channels=[{"type": "SLACK", "target": "#alerts"}]
    )
    
    targets = service._resolve_targets(record, policy)
    assert targets == [("SLACK", "#alerts")]


@pytest.mark.asyncio
async def test_dispatch_report_skips_if_no_notify(service):
    record = ValidationRecord(status="PASS")
    policy = ValidationPolicy(alert_condition="FAIL_ONLY")
    
    with patch.object(service, "_resolve_targets") as mock_resolve:
        await service.dispatch_report(record, policy)
        mock_resolve.assert_not_called()


@pytest.mark.asyncio
async def test_dispatch_report_calls_providers(service, mock_db):
    record = ValidationRecord(id="rec-1", status="FAIL", summary="Bad", issues=[])
    
    # Mock providers
    service._initialize_providers()
    service._providers["TELEGRAM"] = AsyncMock()
    service._providers["SMTP"] = AsyncMock()
    
    # Mock targets
    with patch.object(service, "_resolve_targets") as mock_resolve:
        mock_resolve.return_value = [("TELEGRAM", "123"), ("SMTP", "a@b.com")]
        await service.dispatch_report(record, None)
        
        service._providers["TELEGRAM"].send.assert_called_once()
        service._providers["SMTP"].send.assert_called_once()

# [/DEF:backend.src.services.notifications.__tests__.test_notification_service:Module]