"""ITs for the my-sea invite send endpoint + the @mailman Post render.

Covers the real `my_sea_invite` (replaces the "coming soon" stub) and the
prose-only render of the invitee's "Acceptances & rejections" Post. The
explicit accept/decline endpoints were removed 2026-05-29 — acceptance is now
implicit on the bud-page sea-btn visit (see test_sea_visit.py's accept-on-GET
coverage); an un-clicked invite simply lapses after 24h.
"""

from django.test import TestCase
from django.urls import reverse
from django.utils import timezone

from apps.billboard.mail import log_sea_invite
from apps.billboard.models import Brief, Post
from apps.gameboard.models import SeaInvite
from apps.lyric.models import User


class MySeaInviteViewTest(TestCase):
    def setUp(self):
        self.owner = User.objects.create(email="owner@test.io", username="discoman")
        self.bud = User.objects.create(email="bud@test.io", username="budster")
        self.client.force_login(self.owner)
        self.url = reverse("my_sea_invite")

    def _invite(self, recipient):
        return self.client.post(
            self.url, data={"recipient": recipient},
            HTTP_ACCEPT="application/json",
        )

    def test_invite_registered_bud_creates_pending_invite(self):
        resp = self._invite("budster")
        self.assertEqual(resp.status_code, 200)
        invite = SeaInvite.objects.get(owner=self.owner, invitee=self.bud)
        self.assertEqual(invite.status, SeaInvite.PENDING)
        self.assertEqual(invite.invitee_email, self.bud.email)

    def test_invite_logs_mailman_line_and_brief_for_invitee(self):
        self._invite("budster")
        post = Post.objects.get(owner=self.bud, kind=Post.KIND_MAIL_ACCEPTANCE)
        self.assertEqual(post.lines.count(), 1)
        self.assertTrue(
            Brief.objects.filter(
                owner=self.bud, kind=Brief.KIND_MAIL_ACCEPTANCE,
            ).exists()
        )
        invite = SeaInvite.objects.get(owner=self.owner, invitee=self.bud)
        self.assertEqual(invite.line, post.lines.first())

    def test_invite_returns_brief_json(self):
        resp = self._invite("budster")
        data = resp.json()
        self.assertIsNotNone(data["brief"])
        self.assertEqual(data["recipient_display"], "budster")

    def test_invite_by_email_resolves_user(self):
        self._invite("bud@test.io")
        self.assertTrue(
            SeaInvite.objects.filter(owner=self.owner, invitee=self.bud).exists()
        )

    def test_duplicate_pending_invite_does_not_create_second(self):
        self._invite("budster")
        self._invite("budster")
        self.assertEqual(
            SeaInvite.objects.filter(owner=self.owner, invitee=self.bud).count(), 1
        )

    def test_self_invite_creates_nothing(self):
        self._invite("discoman")
        self.assertFalse(SeaInvite.objects.filter(owner=self.owner).exists())

    def test_empty_recipient_creates_nothing(self):
        resp = self._invite("")
        self.assertFalse(SeaInvite.objects.exists())
        self.assertIsNone(resp.json()["brief"])


class MySeaInvitePostRenderTest(TestCase):
    """post.html — the @mailman invite Line carries a post-attribution
    anchor around the owner's handle whose href routes to the owner's
    per-bud landing page. Bud landing page sprint 2026-05-27 ([[project-
    bud-landing-page-sprint]]) migrated the prior inline OK/BYE/Accepted
    block onto bud.html; this class pins the new prose contract."""

    def setUp(self):
        self.owner = User.objects.create(email="owner@test.io", username="discoman")
        self.bud = User.objects.create(email="bud@test.io", username="budster")
        self.invite = SeaInvite.objects.create(
            owner=self.owner, invitee=self.bud, invitee_email=self.bud.email,
        )
        self.post, self.line, _ = log_sea_invite(self.invite)
        self.invite.refresh_from_db()
        self.client.force_login(self.bud)
        self.post_url = reverse("billboard:view_post", args=[self.post.id])

    def test_pending_invite_renders_post_attribution_anchor_not_buttons(self):
        content = self.client.get(self.post_url).content.decode()
        # OK/BYE buttons + the accept/decline form actions are gone entirely.
        self.assertNotIn("invite-ok-btn", content)
        self.assertNotIn("invite-bye-btn", content)
        self.assertNotIn("invite-action-form", content)
        self.assertNotIn("my-sea/invite/accept", content)
        self.assertNotIn("my-sea/invite/decline", content)
        # Anchor wraps the owner's handle, routing to their bud landing page.
        self.assertIn('class="post-attribution"', content)
        self.assertIn(f"/billboard/buds/{self.owner.id}/", content)

    def test_accepted_invite_renders_no_badge(self):
        self.invite.status = SeaInvite.ACCEPTED
        self.invite.accepted_at = timezone.now()
        self.invite.save()
        content = self.client.get(self.post_url).content.decode()
        # No "Accepted <date>" badge — the sweep is unconditional.
        self.assertNotIn("invite-badge--accepted", content)
        self.assertNotIn("invite-action-form", content)

    def test_declined_invite_renders_no_badge(self):
        self.invite.status = SeaInvite.DECLINED
        self.invite.save()
        content = self.client.get(self.post_url).content.decode()
        self.assertNotIn("invite-badge--declined", content)
        self.assertNotIn("invite-action-form", content)

    def test_mailman_line_renders_as_system_with_handles(self):
        content = self.client.get(self.post_url).content.decode()
        self.assertIn("post-line--system", content)
        self.assertIn("@mailman", content)   # author handle column
        self.assertIn("@discoman", content)  # owner handle interpolated in prose
