"""Tests for the NotificationTemplate per-verb override layer.

The resolver should:
  - Override caller-supplied subject/body when an active row exists.
  - Render Django template variables from the email_context.
  - Fall back gracefully when no row exists (preserve caller-supplied copy).
  - Treat is_active=False as "no template".
"""

from __future__ import annotations

from unittest.mock import patch

import pytest
from django.contrib.auth import get_user_model

from apps.core.notifications.models import Notification, NotificationTemplate
from apps.core.notifications.services import send_notification

User = get_user_model()


@pytest.fixture
def user(db):
    return User.objects.create_user(email="recipient@example.com", password="pw", first_name="Asha")


def _capture_dispatch_kwargs():
    """Patch dispatch_notification_channel.delay and return a list to which calls append."""

    captured: list[tuple] = []

    def _fake_delay(notification_id, channel, context):
        captured.append((notification_id, channel, context))

    return captured, _fake_delay


@pytest.mark.django_db
def test_no_template_row_uses_caller_context(user):
    captured, fake = _capture_dispatch_kwargs()
    with patch("apps.core.notifications.services.dispatch_notification_channel.delay", side_effect=fake):
        send_notification(
            user,
            "Hello world",
            verb=Notification.Verb.GRANT_AWARDED,
            email_context={"subject": "Caller subject", "body": "Caller body"},
        )
    assert len(captured) == 1
    _, _, ctx = captured[0]
    assert ctx["subject"] == "Caller subject"
    assert ctx["body"] == "Caller body"


@pytest.mark.django_db
def test_active_template_overrides_subject_and_body(user):
    NotificationTemplate.objects.create(
        verb=Notification.Verb.GRANT_AWARDED,
        name="Award seed",
        subject="Award for {{ recipient_name }} at {{ call_title }}",
        body_text="Hi {{ recipient_name }}, you won {{ call_title }}.",
        body_html="<p>Hi {{ recipient_name }}, you won {{ call_title }}.</p>",
        headline_default="Congratulations",
        action_label_default="Open",
    )
    captured, fake = _capture_dispatch_kwargs()
    with patch("apps.core.notifications.services.dispatch_notification_channel.delay", side_effect=fake):
        send_notification(
            user,
            "Original message",
            verb=Notification.Verb.GRANT_AWARDED,
            email_context={"call_title": "PhD Fellowship 2026"},
        )
    _, _, ctx = captured[0]
    assert ctx["subject"] == "Award for Asha at PhD Fellowship 2026"
    assert ctx["body"] == "Hi Asha, you won PhD Fellowship 2026."
    assert ctx["body_html"] == "<p>Hi Asha, you won PhD Fellowship 2026.</p>"
    assert ctx["headline"] == "Congratulations"
    assert ctx["action_label"] == "Open"


@pytest.mark.django_db
def test_inactive_template_falls_back(user):
    NotificationTemplate.objects.create(
        verb=Notification.Verb.GRANT_AWARDED,
        name="Award seed",
        subject="Should not appear",
        body_text="Should not appear",
        is_active=False,
    )
    captured, fake = _capture_dispatch_kwargs()
    with patch("apps.core.notifications.services.dispatch_notification_channel.delay", side_effect=fake):
        send_notification(
            user,
            "Visible message",
            verb=Notification.Verb.GRANT_AWARDED,
            email_context={"subject": "Caller subject"},
        )
    _, _, ctx = captured[0]
    assert ctx["subject"] == "Caller subject"
    assert "Should not appear" not in ctx.get("body", "")


@pytest.mark.django_db
def test_caller_supplied_action_label_wins_over_default(user):
    NotificationTemplate.objects.create(
        verb=Notification.Verb.DEADLINE_REMINDER,
        name="Deadline",
        subject="Deadline",
        action_label_default="Default label",
    )
    captured, fake = _capture_dispatch_kwargs()
    with patch("apps.core.notifications.services.dispatch_notification_channel.delay", side_effect=fake):
        send_notification(
            user,
            "Tick tock",
            verb=Notification.Verb.DEADLINE_REMINDER,
            email_context={"action_label": "Caller label"},
        )
    _, _, ctx = captured[0]
    assert ctx["action_label"] == "Caller label"


@pytest.mark.django_db
def test_seed_command_idempotent():
    from django.core.management import call_command

    call_command("seed_notification_templates")
    first = NotificationTemplate.objects.count()
    assert first > 0

    # Re-running should not duplicate or change rows when --force is not used.
    call_command("seed_notification_templates")
    second = NotificationTemplate.objects.count()
    assert second == first


# WS2.2 — every REP verb must have a seeded template before WS2.3 wires events to it.


@pytest.mark.django_db
def test_every_rep_verb_has_a_seeded_template():
    """Catches future drift: adding a verb without a default template fails the test."""
    from django.core.management import call_command

    from apps.core.notifications.models import Notification

    call_command("seed_notification_templates")

    rep_verbs = {
        verb_value
        for verb_value, _ in Notification.Verb.choices
        if str(verb_value).startswith("rep_")
    }
    assert rep_verbs, "expected at least one REP verb in the enum"

    seeded = set(
        NotificationTemplate.objects.filter(verb__startswith="rep_").values_list("verb", flat=True)
    )
    unseeded = rep_verbs - seeded
    assert not unseeded, f"REP verbs without a seeded NotificationTemplate: {sorted(unseeded)}"
