# Standard Library
from unittest.mock import Mock, patch

# Django
from django.test import SimpleTestCase

from incursions.api.helpers.sse import SSEClient, SSEEvent


class TestSSEClient(SimpleTestCase):
    def test_branca_encode_rejects_invalid_key_length(self) -> None:
        client = SSEClient("https://sse.example.com", b"short")

        with self.assertRaisesMessage(ValueError, "SSE_SECRET must be a hex-encoded 32-byte key"):
            client.branca_encode(b"payload")

    @patch("incursions.api.helpers.sse.base62.encodebytes", return_value="encoded-token")
    @patch("incursions.api.helpers.sse.crypto_aead_xchacha20poly1305_ietf_encrypt", return_value=b"ciphertext")
    @patch("incursions.api.helpers.sse.nacl_random", return_value=b"n" * 24)
    def test_branca_encode_builds_token(
        self,
        mock_random: Mock,
        mock_encrypt: Mock,
        mock_encodebytes: Mock,
    ) -> None:
        client = SSEClient("https://sse.example.com", b"k" * 32)

        token = client.branca_encode(b"payload", timestamp=123)

        self.assertEqual(token, "encoded-token")
        mock_random.assert_called_once_with(24)
        expected_header = b"\xBA" + b"\x00\x00\x00{" + (b"n" * 24)
        mock_encrypt.assert_called_once_with(b"payload", expected_header, b"n" * 24, b"k" * 32)
        mock_encodebytes.assert_called_once_with(expected_header + b"ciphertext")

    @patch.object(SSEClient, "branca_encode", return_value="topic-token")
    @patch("incursions.api.helpers.sse.msgpack.packb", return_value=b"packed")
    def test_events_url_packs_topics_and_encodes(self, mock_packb: Mock, mock_branca_encode: Mock) -> None:
        client = SSEClient("https://sse.example.com", "00" * 32)

        result = client.events_url(["announcements", "waitlist"])

        self.assertEqual(result, "https://sse.example.com/events?token=topic-token")
        mock_packb.assert_called_once_with({"topics": ["announcements", "waitlist"]}, use_bin_type=True)
        mock_branca_encode.assert_called_once_with(b"packed")

    @patch.object(SSEClient, "branca_encode", return_value="submission-token")
    @patch("incursions.api.helpers.sse.requests.post")
    @patch("incursions.api.helpers.sse.msgpack.packb", return_value=b"packed-events")
    def test_submit_serializes_events_and_posts(
        self,
        mock_packb: Mock,
        mock_post: Mock,
        mock_branca_encode: Mock,
    ) -> None:
        response = Mock()
        mock_post.return_value = response
        client = SSEClient("https://sse.example.com", "00" * 32)
        events = [SSEEvent("topic", "event", "data")]

        result = client.submit(events)

        self.assertIs(result, response)
        mock_packb.assert_called_once_with(
            {"events": [{"topic": "topic", "event": "event", "data": "data"}]},
            use_bin_type=True,
        )
        mock_branca_encode.assert_called_once_with(b"packed-events")
        mock_post.assert_called_once_with("https://sse.example.com/submit", data="submission-token")


class TestSSEEvent(SimpleTestCase):
    def test_new_returns_event_instance(self) -> None:
        event = SSEEvent.new("topic", "event", "payload")

        self.assertEqual(event.to_dict(), {"topic": "topic", "event": "event", "data": "payload"})

    def test_new_json_serializes_dict_payload(self) -> None:
        event = SSEEvent.new_json("topic", "event", {"hello": "world"})

        self.assertEqual(event.to_dict(), {"topic": "topic", "event": "event", "data": '{"hello": "world"}'})
