# api/sse.py
"""Server-Sent Events bus.

One persistent connection per browser tab. Events pushed from background
threads via asyncio.run_coroutine_threadsafe().
Stub until Phase 9 — broadcast() is callable but no clients connect until then.
"""
import asyncio
import json
from fastapi import APIRouter, Request
from fastapi.responses import StreamingResponse

router = APIRouter(tags=["sse"])

_clients: list[asyncio.Queue] = []
_clients_lock = asyncio.Lock()


async def broadcast(event_type: str, data: dict):
    """Push an event to all connected SSE clients."""
    message = f"event: {event_type}\ndata: {json.dumps(data)}\n\n"
    async with _clients_lock:
        dead = []
        for q in _clients:
            try:
                q.put_nowait(message)
            except asyncio.QueueFull:
                dead.append(q)
        for q in dead:
            _clients.remove(q)


@router.get("/api/events")
async def sse_events(request: Request):
    """SSE endpoint. One connection per client tab."""
    queue: asyncio.Queue = asyncio.Queue(maxsize=100)
    async with _clients_lock:
        _clients.append(queue)

    async def event_stream():
        try:
            yield "event: connected\ndata: {}\n\n"
            yield "retry: 3000\n\n"
            while True:
                if await request.is_disconnected():
                    break
                try:
                    message = await asyncio.wait_for(queue.get(), timeout=30.0)
                    yield message
                except asyncio.TimeoutError:
                    yield ": keepalive\n\n"
        finally:
            async with _clients_lock:
                if queue in _clients:
                    _clients.remove(queue)

    return StreamingResponse(
        event_stream(),
        media_type="text/event-stream",
        headers={
            "Cache-Control": "no-cache",
            "Connection": "keep-alive",
            "X-Accel-Buffering": "no",
        },
    )
