"""G17 — Output ready-to-close cascade signal + notification."""
from __future__ import annotations

from datetime import date

import pytest
from django.contrib.auth import get_user_model

from apps.mel.indicators.models import (
    LogFrame,
    LogFrameLevel,
    LogFrameRow,
)
from apps.mel.tracking.models import (
    Activity,
    ActivityStatus,
    OutputDeliverable,
)
from apps.mel.tracking.services import record_activity_progress
from apps.mel.tracking.signals import mel_output_ready_to_close

pytestmark = pytest.mark.django_db(transaction=True)

User = get_user_model()

_LF = {"n": 0}


def _next(prefix: str) -> str:
    _LF["n"] += 1
    return f"{prefix}-{_LF['n']}"


def _seed_output_with_activities(*, num_activities: int = 2):
    owner = User.objects.create_user(email=f"owner-g17-{_LF['n'] + 1}@example.com", password="x")
    slug = _next("g17")
    lf = LogFrame.objects.create(name=f"G17 LF {slug}", slug=slug, owner=owner)
    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="OUT", parent=outcome)
    deliverable = OutputDeliverable.objects.create(
        logframe_row=output, title="The deliverable",
        target_quantity=10, unit="count",
    )
    activities = []
    for i in range(num_activities):
        row = LogFrameRow.objects.create(
            logframe=lf, level=LogFrameLevel.ACTIVITY, title=f"A{i}", parent=output,
        )
        activities.append(
            Activity.objects.create(
                logframe_row=row, name=f"Activity {i}",
                scheduled_start=date(2026, 4, 1), scheduled_end=date(2026, 4, 30),
            )
        )
    return owner, output, deliverable, activities


def test_signal_fires_when_last_activity_completes():
    owner, output, deliverable, activities = _seed_output_with_activities(num_activities=2)
    captured: list[int] = []

    def handler(sender, output_row, deliverables, **kwargs):
        captured.append(output_row.pk)

    mel_output_ready_to_close.connect(handler, dispatch_uid="test.g17.last")
    try:
        # Complete the first activity — signal must NOT fire yet.
        record_activity_progress(activities[0].pk, new_status=ActivityStatus.IN_PROGRESS)
        record_activity_progress(activities[0].pk, new_status=ActivityStatus.COMPLETED)
        assert captured == []

        # Complete the second (last) activity — signal fires once.
        record_activity_progress(activities[1].pk, new_status=ActivityStatus.IN_PROGRESS)
        record_activity_progress(activities[1].pk, new_status=ActivityStatus.COMPLETED)
        assert captured == [output.pk]
    finally:
        mel_output_ready_to_close.disconnect(dispatch_uid="test.g17.last")


def test_notification_sent_to_logframe_owner():
    from apps.core.notifications.models import Notification

    owner, output, deliverable, activities = _seed_output_with_activities(num_activities=1)
    record_activity_progress(activities[0].pk, new_status=ActivityStatus.IN_PROGRESS)
    record_activity_progress(activities[0].pk, new_status=ActivityStatus.COMPLETED)

    notif = Notification.objects.filter(
        recipient=owner, verb=Notification.Verb.MEL_OUTPUT_READY_TO_CLOSE,
    ).first()
    assert notif is not None
    assert deliverable.title in notif.message


def test_signal_does_not_fire_when_cancelled_siblings_present():
    """Cancelled activities count as terminal; the cascade should still fire."""
    owner, output, deliverable, activities = _seed_output_with_activities(num_activities=2)
    captured: list[int] = []

    def handler(sender, output_row, **kwargs):
        captured.append(output_row.pk)

    mel_output_ready_to_close.connect(handler, dispatch_uid="test.g17.cancel")
    try:
        # Cancel one activity, complete the other — both terminal → signal fires.
        record_activity_progress(activities[0].pk, new_status=ActivityStatus.CANCELLED)
        record_activity_progress(activities[1].pk, new_status=ActivityStatus.IN_PROGRESS)
        record_activity_progress(activities[1].pk, new_status=ActivityStatus.COMPLETED)
        assert captured == [output.pk]
    finally:
        mel_output_ready_to_close.disconnect(dispatch_uid="test.g17.cancel")
