"""DMS DocumentConsumer — live document presence + version update feed.

WebSocket path: ``ws/dms/documents/{public_id}/``

Channel group: ``dms_doc_{public_id}``

Events sent to the client
--------------------------
``document.updated``
    Fired when the document's title, status, or metadata changes.
    Payload: ``{id, title, status}``

``version.added``
    Fired when a new DocumentVersion is appended.
    Payload: ``{version, status}``

``presence.join`` / ``presence.leave``
    Broadcast when a user connects/disconnects to the document room.
    Payload: ``{user_id}``

The consumer is read-only from the server perspective — client messages are
silently ignored.  Only authenticated users may connect; unauthenticated
requests receive an immediate WebSocket close (4003).
"""

from __future__ import annotations

import structlog
from channels.generic.websocket import AsyncJsonWebsocketConsumer

from simorgh.apps.dms.realtime import document_room

_log = structlog.get_logger("dms.consumers.document")


class DocumentConsumer(AsyncJsonWebsocketConsumer):
    """Streams live document events to subscribed clients."""

    async def connect(self) -> None:
        user = self.scope.get("user")
        if user is None or not getattr(user, "is_authenticated", False):
            _log.info("dms.ws.document.rejected", reason="unauthenticated")
            await self.close(code=4003)
            return

        self.public_id: str = self.scope["url_route"]["kwargs"]["public_id"]
        self.room: str = document_room(self.public_id)
        self.user_pk: int = user.pk

        await self.channel_layer.group_add(self.room, self.channel_name)
        await self.accept()

        # Announce presence to other subscribers in the room.
        await self.channel_layer.group_send(
            self.room,
            {
                "type": "broadcast.message",
                "event": "presence.join",
                "payload": {"user_id": self.user_pk},
            },
        )
        _log.info("dms.ws.document.connected", room=self.room, user_pk=self.user_pk)

    async def disconnect(self, close_code: int) -> None:
        if not hasattr(self, "room"):
            return

        # Announce departure before leaving the group.
        try:
            await self.channel_layer.group_send(
                self.room,
                {
                    "type": "broadcast.message",
                    "event": "presence.leave",
                    "payload": {"user_id": self.user_pk},
                },
            )
        except Exception:
            pass

        await self.channel_layer.group_discard(self.room, self.channel_name)
        _log.info("dms.ws.document.disconnected", room=self.room, code=close_code)

    async def receive_json(self, content: dict, **kwargs: object) -> None:
        # Read-only consumer — client messages are not processed.
        pass

    # ------------------------------------------------------------------
    # Channel layer handler — called when group_send delivers a message.
    # ------------------------------------------------------------------

    async def broadcast_message(self, event: dict) -> None:
        """Forward a channel-layer message to the WebSocket client."""
        await self.send_json(
            {
                "event": event["event"],
                "payload": event.get("payload", {}),
            }
        )
