from __future__ import annotations

import asyncio
import contextlib
import json
from typing import List, Callable, Awaitable

from fastapi import APIRouter, HTTPException
from fastapi.responses import StreamingResponse

from app.streaming.binance_hub import hub as binance_hub
from app.streaming.alpha_hub import alpha_hub

router = APIRouter(prefix="/stream", tags=["Stream"]) 


@router.get("/sse")
async def stream_sse(symbols: str = "BTCUSDT,XAUUSD", timeframe: str = "1m"):
    """
    Server-Sent Events (SSE) multiplexer for multiple symbols over a single connection.
    - Supports 1m timeframe (server streams 1m updates; clients can resample locally).
    - symbols: comma-separated list (e.g., BTCUSDT,ETHUSDT,XAUUSD)
    """
    if not symbols:
        raise HTTPException(status_code=400, detail="symbols must not be empty")
    if timeframe != "1m":
        raise HTTPException(status_code=400, detail="Only timeframe=1m is supported")

    syms: List[str] = [s.strip().upper().replace("/", "") for s in symbols.split(",") if s.strip()]
    if not syms:
        raise HTTPException(status_code=400, detail="No valid symbols provided")

    out_queue: asyncio.Queue = asyncio.Queue(maxsize=1000)
    tasks: List[asyncio.Task] = []
    unsubscribers: List[Callable[[], Awaitable[None]]] = []

    async def add_subscription(sym: str):
        if sym.startswith("XAU"):
            q, unsubscribe = await alpha_hub.subscribe(sym, timeframe="1m")
        else:
            q, unsubscribe = await binance_hub.subscribe(sym, timeframe="1m")
        unsubscribers.append(unsubscribe)

        async def worker():
            try:
                while True:
                    evt = await q.get()
                    if evt is None:
                        break
                    try:
                        await out_queue.put(evt)
                    except Exception:
                        await asyncio.sleep(0)
            except asyncio.CancelledError:
                pass
        tasks.append(asyncio.create_task(worker()))

    for s in syms:
        await add_subscription(s)

    async def event_generator():
        try:
            while True:
                try:
                    evt = await asyncio.wait_for(out_queue.get(), timeout=15.0)
                    data = json.dumps(evt, separators=(",", ":"))
                    yield f"event: kline\n".encode("utf-8")
                    yield f"data: {data}\n\n".encode("utf-8")
                except asyncio.TimeoutError:
                    # Keep-alive comment
                    yield b": ping\n\n"
        finally:
            for t in tasks:
                t.cancel()
            for t in tasks:
                with contextlib.suppress(Exception):
                    await t
            for u in unsubscribers:
                with contextlib.suppress(Exception):
                    await u()

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