# Standard Library
from unittest.mock import Mock, patch

# Django
from django.test import RequestFactory, SimpleTestCase

from incursions.api import sse


class FakeAPI:
    def __init__(self) -> None:
        self.routes: dict[str, object] = {}

    def get(self, path: str, tags: list[str] | None = None):
        def decorator(func):
            self.routes[path] = func
            return func

        return decorator


class TestSSEAPIEndpoints(SimpleTestCase):
    def setUp(self) -> None:
        self.factory = RequestFactory()

    def _build_route(self):
        api = FakeAPI()
        sse.setup(api)
        return api.routes["/sse/stream"]

    def test_sse_stream_denies_user_without_permission(self) -> None:
        view = self._build_route()
        request = self.factory.get("/sse/stream")
        request.user = Mock(has_perm=Mock(return_value=False))

        response = view(request)

        self.assertEqual(response, {"error": "Permission denied"})

    @patch("incursions.api.sse.get_main_character_from_user", side_effect=RuntimeError("boom"))
    def test_sse_stream_handles_main_character_lookup_failure(self, mock_main: Mock) -> None:
        view = self._build_route()
        request = self.factory.get("/sse/stream")
        request.user = Mock(has_perm=Mock(return_value=True))

        response = view(request)

        self.assertEqual(response, {"error": "Failed to resolve main character"})
        mock_main.assert_called_once_with(request.user)

    @patch("incursions.api.sse.app_settings.SSE_SITE_URL", "https://sse.example.com")
    @patch("incursions.api.sse.app_settings.SSE_SECRET", "00" * 32)
    @patch("incursions.api.sse.SSEClient")
    @patch("incursions.api.sse.msgpack.packb", return_value=b"packed-topics")
    @patch("incursions.api.sse.get_main_character_from_user")
    def test_sse_stream_redirects_with_expected_topics(
        self,
        mock_main_character: Mock,
        mock_packb: Mock,
        mock_sse_client: Mock,
    ) -> None:
        mock_main_character.return_value = Mock(character_id=42)
        mock_sse_client.return_value.branca_encode.return_value = "signed-token"
        view = self._build_route()
        request = self.factory.get("/sse/stream")
        request.user = Mock(
            is_authenticated=True,
            has_perm=Mock(side_effect=lambda perm: perm in {"incursions.basic_waitlist", "waitlist_fleet_view"}),
        )

        response = view(request)

        self.assertEqual(response.status_code, 302)
        self.assertEqual(response.url, "https://sse.example.com/events?token=signed-token")
        mock_packb.assert_called_once_with(
            {
                "topics": ["announcements", "waitlist", "account;42", "fleet_comp"],
            },
            use_bin_type=True,
        )
        mock_sse_client.assert_called_once_with("https://sse.example.com", "00" * 32)
        mock_sse_client.return_value.branca_encode.assert_called_once_with(payload=b"packed-topics")
