//! Binance WebSocket price feed.
//!
//! Connects to `wss://stream.binance.com:9443/ws/{symbol}@trade` and
//! streams real-time trade prices into a shared FeedSnapshot.

use crate::feeds::FeedSnapshot;
use dashmap::DashMap;
use futures_util::StreamExt;
use serde::Deserialize;
use std::sync::Arc;
use tokio_tungstenite::connect_async;
use tracing::warn;

/// Typed Binance trade message — avoids dynamic serde_json::Value allocation.
#[derive(Deserialize)]
struct BinanceTrade {
    /// Price as string
    p: String,
    /// Trade time in milliseconds
    #[serde(rename = "T")]
    trade_time: Option<f64>,
}

/// Run the Binance WebSocket feed until shutdown is signaled.
pub async fn run_binance_ws(
    name: String,
    symbol: String,
    snapshots: Arc<DashMap<String, FeedSnapshot>>,
    shutdown: Arc<tokio::sync::Notify>,
) {
    let symbol_lower = symbol.to_lowercase();
    let url = format!(
        "wss://stream.binance.com:9443/ws/{}@trade",
        symbol_lower
    );
    // Pre-compute source string to avoid allocation per message
    let source = format!("binance:{}", symbol_lower);

    let mut reconnect_delay_ms: u64 = 1000;

    loop {
        let connect_result = tokio::select! {
            _ = shutdown.notified() => return,
            result = connect_async(&url) => result,
        };

        match connect_result {
            Ok((ws_stream, _)) => {
                reconnect_delay_ms = 1000;
                let (_, mut read) = ws_stream.split();

                loop {
                    let msg = tokio::select! {
                        _ = shutdown.notified() => return,
                        msg = read.next() => msg,
                    };

                    match msg {
                        Some(Ok(tokio_tungstenite::tungstenite::Message::Text(text))) => {
                            if let Ok(trade) = serde_json::from_str::<BinanceTrade>(&text) {
                                if let Ok(price) = trade.p.parse::<f64>() {
                                    let timestamp =
                                        trade.trade_time.unwrap_or(0.0) / 1000.0;

                                    let snap = FeedSnapshot {
                                        price,
                                        timestamp,
                                        source: source.clone(),
                                        bid: price,
                                        ask: price,
                                        volume_24h: 0.0,
                                    };

                                    snapshots.insert(name.clone(), snap);
                                }
                            }
                        }
                        Some(Ok(tokio_tungstenite::tungstenite::Message::Close(_))) => break,
                        Some(Err(e)) => {
                            warn!(symbol = %symbol_lower, error = %e, "binance feed: connection error");
                            break;
                        }
                        None => break,
                        _ => {}
                    }
                }
            }
            Err(e) => {
                warn!(symbol = %symbol_lower, error = %e, "binance feed: connection failed");
            }
        }

        warn!(symbol = %symbol_lower, delay_ms = reconnect_delay_ms, "binance feed: reconnecting");
        tokio::select! {
            _ = shutdown.notified() => return,
            _ = tokio::time::sleep(tokio::time::Duration::from_millis(reconnect_delay_ms)) => {},
        }
        reconnect_delay_ms = (reconnect_delay_ms * 2).min(30_000);
    }
}
