from __future__ import annotations

import asyncio
import json
import logging
import queue
import threading

_log_subscribers: list[queue.Queue] = []
_log_lock = threading.Lock()


class _SSELogHandler(logging.Handler):
    """Copies each log record into the queue of every connected SSE client."""

    def emit(self, record: logging.LogRecord) -> None:
        msg = self.format(record)
        with _log_lock:
            for q in _log_subscribers:
                try:
                    q.put_nowait(msg)
                except queue.Full:
                    pass


_sse_handler = _SSELogHandler()
_sse_handler.setFormatter(logging.Formatter(
    "%(asctime)s [%(levelname)-8s] %(name)s — %(message)s",
    datefmt="%H:%M:%S",
))
logging.getLogger().addHandler(_sse_handler)
logging.getLogger().setLevel(logging.INFO)

_console_handler = logging.StreamHandler()
_console_handler.setFormatter(logging.Formatter(
    "%(asctime)s [%(levelname)-8s] %(name)s: %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
))
logging.getLogger().addHandler(_console_handler)


def subscribe() -> queue.Queue:
    """Register a new SSE client and return its dedicated log queue."""
    q: queue.Queue = queue.Queue(maxsize=500)
    with _log_lock:
        _log_subscribers.append(q)
    return q


def unsubscribe(q: queue.Queue) -> None:
    """Remove an SSE client queue (safe to call even if already removed)."""
    with _log_lock:
        try:
            _log_subscribers.remove(q)
        except ValueError:
            pass


async def sse_generator(client_q: queue.Queue):
    """Async generator yielding SSE-formatted log lines for a connected client."""
    yield f"data: {json.dumps({'connected': True})}\n\n"
    try:
        while True:
            try:
                line = client_q.get_nowait()
                yield f"data: {json.dumps(line)}\n\n"
            except queue.Empty:
                yield ": heartbeat\n\n"
                await asyncio.sleep(0.05)
    except asyncio.CancelledError:
        pass
    finally:
        unsubscribe(client_q)
