"""WebSocket connection manager for the kommunikation plugin."""

from __future__ import annotations

import json
import logging
from typing import Any

from fastapi import WebSocket

logger = logging.getLogger(__name__)


class WebSocketManager:
    """Manages WebSocket connections per user for real-time messaging."""

    def __init__(self) -> None:
        # user_id (str) → list of WebSocket connections
        self._connections: dict[str, list[WebSocket]] = {}
        # conversation_id (str) → set of user_ids subscribed
        self._subscriptions: dict[str, set[str]] = {}

    async def connect(self, websocket: WebSocket, user_id: str) -> None:
        """Accept and register a new WebSocket connection."""
        await websocket.accept()
        if user_id not in self._connections:
            self._connections[user_id] = []
        self._connections[user_id].append(websocket)
        logger.debug(f"WebSocket connected: user={user_id}, total={len(self._connections[user_id])}")

    async def disconnect(self, websocket: WebSocket, user_id: str) -> None:
        """Remove a WebSocket connection."""
        conns = self._connections.get(user_id, [])
        if websocket in conns:
            conns.remove(websocket)
        if not conns:
            self._connections.pop(user_id, None)
            # Remove from all subscriptions
            for conv_id, users in self._subscriptions.items():
                users.discard(user_id)
        logger.debug(f"WebSocket disconnected: user={user_id}, remaining={len(conns)}")

    def subscribe(self, conversation_id: str, user_id: str) -> None:
        """Subscribe a user to a conversation's updates."""
        if conversation_id not in self._subscriptions:
            self._subscriptions[conversation_id] = set()
        self._subscriptions[conversation_id].add(user_id)

    def unsubscribe(self, conversation_id: str, user_id: str) -> None:
        """Unsubscribe a user from a conversation."""
        if conversation_id in self._subscriptions:
            self._subscriptions[conversation_id].discard(user_id)

    async def send_to_user(self, user_id: str, message: dict[str, Any]) -> None:
        """Send a message to all connections of a specific user."""
        conns = self._connections.get(user_id, [])
        text = json.dumps(message, default=str)
        for ws in conns:
            try:
                await ws.send_text(text)
            except Exception:
                logger.warning(f"Failed to send to user {user_id}, removing connection")
                await self.disconnect(ws, user_id)

    async def send_to_conversation(
        self,
        conversation_id: str,
        message: dict[str, Any],
        exclude_user: str | None = None,
    ) -> None:
        """Send a message to all users subscribed to a conversation."""
        user_ids = self._subscriptions.get(conversation_id, set())
        for user_id in list(user_ids):
            if exclude_user and user_id == exclude_user:
                continue
            await self.send_to_user(user_id, message)

    async def broadcast(self, message: dict[str, Any]) -> None:
        """Broadcast a message to all connected users."""
        for user_id in list(self._connections.keys()):
            await self.send_to_user(user_id, message)

    def get_online_users(self) -> list[str]:
        """Get list of currently connected user IDs."""
        return list(self._connections.keys())

    def is_user_online(self, user_id: str) -> bool:
        """Check if a user has any active connections."""
        return user_id in self._connections and len(self._connections[user_id]) > 0
