"""
Delt in-memory WebSocket-kringkasting for sanntidsoppdateringer -- den
offentlige "Følg live"-siden (ADR-027, oppfølging av ADR-025/026) OG
frittstående runder (2026-07-26, oppfølging av ADR-033/036 sin
medspiller-utvidelse).

Egen, RUTEFRI modul -- importerer INGEN router. Grunnen: både
`scoring.py`/`tournaments.py` (som endrer matchstatus) og `messaging.py`
(som eier selve WS-ruten, `/ws/public/tournaments/{id}/live`) trenger denne,
og `registration.py` importerer allerede fra scoring.py/tournaments.py/
matches.py mens `messaging.py` importerer fra `registration.py` -- en
kringkastingsfunksjon plassert i en av routerne ville skapt en sirkulær
import. Denne modulen ligger BAK alle routere i importgrafen i stedet.
`rounds.py` er ikke del av den kjeden, men gjenbruker samme modul for
konsistens (kun ett sted i kodebasen som eier WebSocket-kringkasting).

In-memory PER PROSESS -- samme kjente begrensning som meldinger sin egen
kringkasting (`app/routers/messaging.py`): trygt med dagens ene
`teecup_api`-container, deles IKKE på tvers av flere prosesser/containere
(se ARCHITECTURE_DECISIONS.md sine "Åpne spørsmål").

Bevisst "noe endret seg, hent på nytt"-signal, IKKE selve dataene --
i motsetning til meldinger (der selve meldingen kringkastes direkte),
ville det å bygge/sende hele leaderboard+matchliste+scorekort-formen her
duplisert betydelig beregningslogikk. Klienten reagerer ved å kalle de
samme REST-endepunktene på nytt, akkurat som ved førstegangslasting.
"""

from collections import defaultdict

from fastapi import WebSocket

_tournament_live_sockets: dict[str, set[WebSocket]] = defaultdict(set)
_round_live_sockets: dict[str, set[WebSocket]] = defaultdict(set)


def live_sockets_for(tournament_id: str) -> set[WebSocket]:
    return _tournament_live_sockets[tournament_id]


async def broadcast_live_update(tournament_id: str) -> None:
    await _broadcast(_tournament_live_sockets, tournament_id)


def live_sockets_for_round(round_id: str) -> set[WebSocket]:
    return _round_live_sockets[round_id]


async def broadcast_round_update(round_id: str) -> None:
    await _broadcast(_round_live_sockets, round_id)


async def _broadcast(registry: dict[str, set[WebSocket]], key: str) -> None:
    sockets = registry.get(key)
    if not sockets:
        return
    dead = []
    for ws in list(sockets):
        try:
            await ws.send_json({"type": "update"})
        except Exception:
            dead.append(ws)
    for ws in dead:
        sockets.discard(ws)
