"""G7 — Silent-indicator notifications.

Tests :func:`scan_silent_responsibles`: idempotency, period-rollover semantics,
and skip conditions (no responsible, fresh data).
"""
from __future__ import annotations

from datetime import date
from decimal import Decimal

import pytest
from django.contrib.auth import get_user_model

from apps.core.notifications.models import Notification
from apps.core.permissions.roles import UserRole
from apps.mel.indicators.models import (
    DataPoint,
    Indicator,
    IndicatorFrequency,
    IndicatorSilentAlert,
    LogFrame,
    LogFrameLevel,
    LogFrameRow,
)
from apps.mel.indicators.services import (
    current_period_label,
    scan_silent_responsibles,
)

User = get_user_model()


pytestmark = pytest.mark.django_db


def _build_indicator(*, code: str, responsible=None, frequency=IndicatorFrequency.MONTHLY):
    lf = LogFrame.objects.create(name=f"LF {code}", slug=f"lf-{code}")
    impact = LogFrameRow.objects.create(logframe=lf, level=LogFrameLevel.IMPACT, title="I")
    outcome = LogFrameRow.objects.create(logframe=lf, level=LogFrameLevel.OUTCOME, title="O", parent=impact)
    output = LogFrameRow.objects.create(logframe=lf, level=LogFrameLevel.OUTPUT, title="P", parent=outcome)
    return Indicator.objects.create(
        logframe_row=output,
        code=code,
        name=code.upper(),
        unit="count",
        calculation_method="count",
        data_source="manual",
        frequency=frequency,
        responsible=responsible,
    )


@pytest.fixture
def officer():
    return User.objects.create_user(
        email="g7-officer@example.com", password="x", role=UserRole.MEL_OFFICER,
    )


def test_silent_responsible_fires_one_notification(officer):
    _build_indicator(code="g7-a", responsible=officer)
    notified = scan_silent_responsibles()
    assert notified == 1
    n = Notification.objects.filter(
        recipient=officer, verb=Notification.Verb.MEL_INDICATOR_SILENT
    ).count()
    assert n == 1
    assert IndicatorSilentAlert.objects.count() == 1


def test_silent_alert_is_idempotent_until_period_rolls(officer):
    _build_indicator(code="g7-b", responsible=officer)
    first = scan_silent_responsibles()
    second = scan_silent_responsibles()
    assert first == 1
    assert second == 0  # dedupe row blocks a second alert in the same period.
    assert Notification.objects.filter(
        recipient=officer, verb=Notification.Verb.MEL_INDICATOR_SILENT
    ).count() == 1


def test_silent_alert_skipped_when_no_responsible():
    _build_indicator(code="g7-c", responsible=None)
    notified = scan_silent_responsibles()
    assert notified == 0
    assert not IndicatorSilentAlert.objects.exists()


def test_silent_alert_skipped_when_data_exists_for_period(officer):
    ind = _build_indicator(code="g7-d", responsible=officer)
    period = current_period_label(ind.frequency)
    DataPoint.objects.create(
        indicator=ind, value=Decimal("3"), period_label=period, reported_at=date.today(),
    )
    notified = scan_silent_responsibles()
    assert notified == 0
    assert not IndicatorSilentAlert.objects.filter(indicator=ind, period_label=period).exists()


def test_inactive_indicator_skipped(officer):
    ind = _build_indicator(code="g7-e", responsible=officer)
    ind.is_active = False
    ind.save(update_fields=["is_active"])
    assert scan_silent_responsibles() == 0
