"""Notification multiplexer for fanout to multiple channels (relay + local WS)."""

import asyncio
import logging
from typing import List

from .message_handler import NotificationSender

logger = logging.getLogger("skop-runner")


class NotificationMultiplexer:
    """
    Fans out JSON-RPC notifications to multiple registered sender callbacks.

    Each registered sender is called concurrently via asyncio.gather so that
    a slow or failed sender does not block others. Sender failures are logged
    but do not propagate.

    No rate limiting is applied here — each sender handles its own constraints:
    - Relay sender: WS backpressure + bounded OutputBuffer when disconnected
    - Local WS sender: localhost TCP backpressure via per-client send locks

    Usage:
        mux = NotificationMultiplexer()
        mux.add_sender(relay_client._send_notification)
        mux.add_sender(local_server._broadcast_notification)
        message_handler.set_notification_sender(mux.send)
    """

    def __init__(self):
        self._senders: List[NotificationSender] = []

    def add_sender(self, sender: NotificationSender) -> None:
        """Register a new notification sender."""
        self._senders.append(sender)

    def remove_sender(self, sender: NotificationSender) -> None:
        """Unregister a sender."""
        try:
            self._senders.remove(sender)
        except ValueError:
            pass

    async def send(self, method: str, params: dict) -> None:
        """Fan out the notification to all registered senders concurrently."""
        if not self._senders:
            return

        results = await asyncio.gather(
            *[s(method, params) for s in self._senders],
            return_exceptions=True,
        )

        for i, result in enumerate(results):
            if isinstance(result, Exception):
                logger.warning(
                    f"Notification sender {i} failed for {method}: {result}"
                )
