from __future__ import annotations

import asyncio
import json
import websockets
from typing import AsyncIterator
from datetime import datetime

from ..typing import Kline
import os


class BinanceWSProvider:
    def __init__(self, base_url: str | None = None):
        self.base_url = base_url or os.getenv("BINANCE_WS_URL", "wss://stream.binance.com:9443/ws")

    async def stream_klines(self, symbol: str, timeframe: str) -> AsyncIterator[Kline]:
        # Binance expects lowercase, no slash: BTCUSDT -> btcusdt
        stream = f"{symbol.lower()}@kline_{timeframe}"
        url = self.base_url.rstrip("/").replace("/ws", "/stream") + f"?streams={stream}"
        async for msg in self._ws_loop(url):
            try:
                data = json.loads(msg)
                k = data.get("data", {}).get("k", {})
                if not k:
                    continue
                yield Kline(
                    symbol=symbol,
                    timeframe=timeframe,
                    open_time=datetime.fromtimestamp(k["t"] / 1000.0),
                    close_time=datetime.fromtimestamp(k["T"] / 1000.0),
                    open=float(k["o"]),
                    high=float(k["h"]),
                    low=float(k["l"]),
                    close=float(k["c"]),
                    volume=float(k.get("v", 0.0)),
                    is_closed=bool(k.get("x", False)),
                    source="binance",
                )
            except Exception:
                continue

    async def _ws_loop(self, url: str):
        while True:
            try:
                async with websockets.connect(url, ping_interval=20, ping_timeout=20) as ws:
                    async for message in ws:
                        yield message
            except Exception:
                await asyncio.sleep(2)
