from __future__ import annotations

from io import StringIO
from unittest.mock import Mock, patch

from asgiref.sync import async_to_sync
from django.core.management import call_command
from django.test import TestCase, override_settings

from core.events.ledger import append_event_sync
from core.events.manticore import ManticoreEventLedgerBackend
from core.messaging import history
from core.models import (
    ChatSession,
    ConversationEvent,
    Message,
    Person,
    PersonIdentifier,
    User,
)


@override_settings(
    EVENT_LEDGER_DUAL_WRITE=True,
    MANTICORE_HTTP_URL="http://manticore.test:9308",
    MANTICORE_EVENT_TABLE="gia_events_test",
)
class BehavioralEventPlatformTests(TestCase):
    def setUp(self):
        ManticoreEventLedgerBackend._table_ready_cache.clear()
        self.user = User.objects.create_user(
            username="behavior-user",
            email="behavior@example.com",
            password="x",
        )
        self.person = Person.objects.create(user=self.user, name="Behavior Person")
        self.identifier = PersonIdentifier.objects.create(
            user=self.user,
            person=self.person,
            service="whatsapp",
            identifier="15550001234",
        )
        self.session = ChatSession.objects.create(
            user=self.user,
            identifier=self.identifier,
        )

    @patch("core.events.manticore.requests.post")
    def test_append_event_dual_writes_to_manticore_and_django(self, mocked_post):
        mocked_response = Mock()
        mocked_response.json.side_effect = [{}, {}]
        mocked_response.raise_for_status.return_value = None
        mocked_post.return_value = mocked_response

        row = append_event_sync(
            user=self.user,
            session=self.session,
            ts=1700000000000,
            event_type="delivery_receipt",
            direction="system",
            actor_identifier="15550001234",
            origin_transport="whatsapp",
            origin_message_id="wamid.001",
            payload={"message_ts": 1699999999000},
        )

        self.assertIsNotNone(row)
        self.assertEqual(1, ConversationEvent.objects.count())
        self.assertEqual(2, mocked_post.call_count)
        replace_query = mocked_post.call_args_list[-1].kwargs["data"]["query"]
        self.assertIn("REPLACE INTO gia_events_test", replace_query)
        self.assertIn("message_delivered", replace_query)

    @override_settings(
        EVENT_LEDGER_DUAL_WRITE=False,
        EVENT_PRIMARY_WRITE_PATH=True,
        MANTICORE_HTTP_URL="http://manticore.test:9308",
        MANTICORE_EVENT_TABLE="gia_events_primary",
    )
    @patch("core.events.manticore.requests.post")
    def test_primary_write_path_skips_django_rows(self, mocked_post):
        mocked_response = Mock()
        mocked_response.json.side_effect = [{}, {}]
        mocked_response.raise_for_status.return_value = None
        mocked_post.return_value = mocked_response

        row = append_event_sync(
            user=self.user,
            session=self.session,
            ts=1700000000100,
            event_type="message_created",
            direction="out",
            origin_transport="signal",
            origin_message_id="1700000000100",
        )

        self.assertIsNone(row)
        self.assertEqual(0, ConversationEvent.objects.count())
        self.assertEqual(2, mocked_post.call_count)

    @patch("core.events.manticore.requests.post")
    def test_delivery_receipts_write_delivery_event_type(self, mocked_post):
        mocked_response = Mock()
        mocked_response.json.side_effect = [{}, {}]
        mocked_response.raise_for_status.return_value = None
        mocked_post.return_value = mocked_response

        message = Message.objects.create(
            user=self.user,
            session=self.session,
            ts=1700000000200,
            sender_uuid="BOT",
            text="hello",
            custom_author="BOT",
            source_service="whatsapp",
            source_message_id="wamid.002",
            source_chat_id="15550001234",
        )

        updated = async_to_sync(history.apply_read_receipts)(
            self.user,
            self.identifier,
            [message.ts],
            read_ts=1700000000300,
            source_service="whatsapp",
            read_by_identifier="15550001234",
            payload={"type": "delivered"},
            receipt_event_type="delivery_receipt",
        )

        self.assertEqual(1, updated)
        event = ConversationEvent.objects.get()
        self.assertEqual("delivery_receipt", event.event_type)
        self.assertEqual("delivery_receipt", event.payload.get("receipt_event_type"))
        message.refresh_from_db()
        self.assertEqual(1700000000300, message.delivered_ts)
        self.assertIsNone(message.read_ts)

    @patch("core.management.commands.manticore_backfill.upsert_conversation_event")
    def test_backfill_command_replays_conversation_events(self, mocked_upsert):
        ConversationEvent.objects.create(
            user=self.user,
            session=self.session,
            ts=1700000000400,
            event_type="read_receipt",
            direction="system",
            origin_transport="signal",
            origin_message_id="msg-123",
        )
        out = StringIO()

        call_command(
            "manticore_backfill",
            "--from-conversation-events",
            "--user-id",
            str(self.user.id),
            stdout=out,
        )

        self.assertEqual(1, mocked_upsert.call_count)
        self.assertIn("manticore-backfill scanned=1 indexed=1", out.getvalue())
