from __future__ import annotations

from asgiref.sync import async_to_sync
from django.test import TestCase

from core.messaging import history
from core.models import ChatSession, Message, Person, PersonIdentifier, User
from core.views.compose import _serialize_message


class ReactionNormalizationTests(TestCase):
    def setUp(self):
        self.user = User.objects.create_user(
            username="react-normalize",
            email="react-normalize@example.com",
            password="pw",
        )
        self.person = Person.objects.create(user=self.user, name="Reaction Person")
        self.identifier = PersonIdentifier.objects.create(
            user=self.user,
            person=self.person,
            service="signal",
            identifier="+15551239999",
        )
        self.session = ChatSession.objects.create(
            user=self.user,
            identifier=self.identifier,
        )

    def test_apply_reaction_prefers_exact_source_timestamp_match(self):
        near_message = Message.objects.create(
            user=self.user,
            session=self.session,
            ts=1700000000100,
            sender_uuid="author-near",
            text="near",
            source_service="signal",
            source_message_id="1700000000100",
        )
        exact_message = Message.objects.create(
            user=self.user,
            session=self.session,
            ts=1700000000000,
            sender_uuid="author-exact",
            text="exact",
            source_service="signal",
            source_message_id="1700000000000",
        )

        updated = async_to_sync(history.apply_reaction)(
            self.user,
            self.identifier,
            target_ts=1700000000000,
            emoji="❤️",
            source_service="signal",
            actor="reactor-1",
            target_author="author-exact",
            remove=False,
            payload={"origin": "test"},
        )

        self.assertEqual(str(exact_message.id), str(updated.id))
        exact_message.refresh_from_db()
        near_message.refresh_from_db()
        self.assertEqual(
            1, len((exact_message.receipt_payload or {}).get("reactions") or [])
        )
        self.assertEqual(
            "exact_source_message_id_ts",
            str(
                (exact_message.receipt_payload or {}).get(
                    "reaction_last_match_strategy"
                )
                or ""
            ),
        )
        self.assertEqual(
            0, len((near_message.receipt_payload or {}).get("reactions") or [])
        )

    def test_remove_without_emoji_is_audited_not_active(self):
        message = Message.objects.create(
            user=self.user,
            session=self.session,
            ts=1700000001000,
            sender_uuid="author-1",
            text="msg",
            source_service="signal",
            source_message_id="1700000001000",
        )

        async_to_sync(history.apply_reaction)(
            self.user,
            self.identifier,
            target_ts=1700000001000,
            emoji="",
            source_service="whatsapp",
            actor="actor-1",
            remove=True,
            payload={"origin": "test"},
        )

        message.refresh_from_db()
        payload = dict(message.receipt_payload or {})
        self.assertEqual([], list(payload.get("reactions") or []))
        self.assertEqual(1, len(list(payload.get("reaction_events") or [])))

    def test_emoji_only_reply_text_is_not_reaction(self):
        anchor = Message.objects.create(
            user=self.user,
            session=self.session,
            ts=1700000002000,
            sender_uuid="author-1",
            text="anchor",
            source_service="signal",
            source_message_id="1700000002000",
        )
        heart_reply = Message.objects.create(
            user=self.user,
            session=self.session,
            ts=1700000003000,
            sender_uuid="author-2",
            text="❤️",
            source_service="signal",
            source_message_id="1700000003000",
            reply_to=anchor,
            reply_source_service="signal",
            reply_source_message_id="1700000002000",
            receipt_payload={},
        )

        serialized = _serialize_message(heart_reply)
        self.assertEqual("❤️", serialized["text"])
        self.assertEqual([], list(serialized.get("reactions") or []))
        self.assertEqual(str(anchor.id), serialized["reply_to_id"])

    def test_apply_message_edit_tracks_history_and_updates_text(self):
        message = Message.objects.create(
            user=self.user,
            session=self.session,
            ts=1700000004000,
            sender_uuid="author-3",
            text="before",
            source_service="signal",
            source_message_id="1700000004000",
        )

        updated = async_to_sync(history.apply_message_edit)(
            self.user,
            self.identifier,
            target_ts=1700000004000,
            new_text="after",
            source_service="signal",
            actor="+15550000000",
            payload={"origin": "test"},
        )

        self.assertIsNotNone(updated)
        message.refresh_from_db()
        self.assertEqual("after", str(message.text or ""))
        edit_history = list((message.receipt_payload or {}).get("edit_history") or [])
        self.assertEqual(1, len(edit_history))
        self.assertEqual("before", str(edit_history[0].get("previous_text") or ""))
        self.assertEqual("after", str(edit_history[0].get("new_text") or ""))

    def test_serialize_message_marks_deleted_and_preserves_edit_history(self):
        message = Message.objects.create(
            user=self.user,
            session=self.session,
            ts=1700000005000,
            sender_uuid="author-4",
            text="keep me",
            source_service="signal",
            source_message_id="1700000005000",
            receipt_payload={
                "edit_history": [
                    {
                        "edited_ts": 1700000005100,
                        "source_service": "signal",
                        "actor": "+15550000000",
                        "previous_text": "old",
                        "new_text": "keep me",
                    }
                ],
                "is_deleted": True,
                "deleted": {
                    "deleted_ts": 1700000005200,
                    "source_service": "signal",
                    "actor": "+15550000000",
                },
            },
        )

        serialized = _serialize_message(message)
        self.assertTrue(bool(serialized.get("is_deleted")))
        self.assertEqual("(message deleted)", str(serialized.get("display_text") or ""))
        self.assertEqual(1, int(serialized.get("edit_count") or 0))
        self.assertEqual(1, len(list(serialized.get("edit_history") or [])))
