"""Global Notification WebSocket consumer.

WebSocket path: ``ws/notifications/``

Channel group: ``notifications_user_{user.pk}``

Each authenticated user joins their personal group. The server pushes new
notifications over this channel.  Clients use this to update the
notification badge count without polling.

Events sent to client
---------------------
``notification.new``
    A new notification was delivered to the user.
    Payload: serialised notification dict.

``notification.count``
    Updated unread count.
    Payload: ``{"unread": N}``

Unauthenticated connections are rejected with close code 4003.
"""

from __future__ import annotations

import structlog
from channels.generic.websocket import AsyncJsonWebsocketConsumer

_log = structlog.get_logger("simorgh.notifications.ws")


def notification_group_name(user_pk: int) -> str:
    """Channel layer group name for a user's notification stream."""
    return f"notifications_user_{user_pk}"


class NotificationConsumer(AsyncJsonWebsocketConsumer):
    """Per-user global notification WebSocket stream."""

    async def connect(self) -> None:
        user = self.scope.get("user")
        if user is None or not getattr(user, "is_authenticated", False):
            _log.info("notifications.ws.rejected", reason="unauthenticated")
            await self.close(code=4003)
            return

        self.user_pk: int = user.pk
        self.group_name: str = notification_group_name(self.user_pk)

        await self.channel_layer.group_add(self.group_name, self.channel_name)
        await self.accept()
        _log.info("notifications.ws.connected", group=self.group_name, user_pk=self.user_pk)

    async def disconnect(self, close_code: int) -> None:
        if hasattr(self, "group_name"):
            await self.channel_layer.group_discard(self.group_name, self.channel_name)

    async def receive_json(self, content: dict, **kwargs: object) -> None:
        # Clients don't send messages; ignore.
        pass

    async def notification_new(self, event: dict) -> None:
        """Forward a new-notification event to the WebSocket client."""
        await self.send_json({"event": "notification.new", "payload": event.get("payload", {})})

    async def notification_count(self, event: dict) -> None:
        """Forward an unread-count update to the WebSocket client."""
        await self.send_json({"event": "notification.count", "payload": event.get("payload", {})})
