mod binance;
mod coinbase;
mod kraken;

use barter_instrument::exchange::ExchangeId;
use chrono::{DateTime, Utc};
use serde::Serialize;
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Instant;
use tokio::sync::RwLock;

use crate::error::ApiError;

#[derive(Debug, Clone, Serialize)]
pub struct InstrumentPair {
    pub base: String,
    pub quote: String,
}

/// A single OHLCV candle returned from an exchange klines endpoint.
#[derive(Debug, Clone)]
pub struct KlineCandle {
    pub open_time: DateTime<Utc>,
    pub close_time: DateTime<Utc>,
    pub open: f64,
    pub high: f64,
    pub low: f64,
    pub close: f64,
    pub volume: f64,
    pub trade_count: i32,
}

pub struct CachedInstruments {
    pub pairs: Vec<InstrumentPair>,
    pub fetched_at: Instant,
}

impl CachedInstruments {
    const TTL_SECS: u64 = 3600; // 1 hour

    pub fn is_fresh(&self) -> bool {
        self.fetched_at.elapsed().as_secs() < Self::TTL_SECS
    }
}

pub type InstrumentCache = Arc<RwLock<HashMap<ExchangeId, CachedInstruments>>>;

pub fn supports_instrument_lookup(exchange: ExchangeId) -> bool {
    matches!(
        exchange,
        ExchangeId::BinanceSpot
            | ExchangeId::BinanceUs
            | ExchangeId::BinanceFuturesUsd
            | ExchangeId::BinanceFuturesCoin
            | ExchangeId::Coinbase
            | ExchangeId::Kraken
    )
}

pub async fn fetch_instruments(
    client: &reqwest::Client,
    exchange: ExchangeId,
) -> Result<Vec<InstrumentPair>, ApiError> {
    match exchange {
        ExchangeId::BinanceSpot
        | ExchangeId::BinanceUs
        | ExchangeId::BinanceFuturesUsd
        | ExchangeId::BinanceFuturesCoin => binance::fetch(client, exchange).await,
        ExchangeId::Coinbase => coinbase::fetch(client).await,
        ExchangeId::Kraken => kraken::fetch(client).await,
        _ => Err(ApiError::BadRequest(format!(
            "exchange {} does not support instrument lookup",
            exchange.as_str()
        ))),
    }
}

/// Fetch historical OHLCV candles from Binance REST klines API.
///
/// Paginates automatically; Binance returns up to 1000 candles per request.
/// Only Binance exchange variants are supported.
pub async fn fetch_klines(
    client: &reqwest::Client,
    exchange: ExchangeId,
    symbol: &str,
    interval: &str,
    from: DateTime<Utc>,
    to: DateTime<Utc>,
) -> Result<Vec<KlineCandle>, ApiError> {
    match exchange {
        ExchangeId::BinanceSpot
        | ExchangeId::BinanceUs
        | ExchangeId::BinanceFuturesUsd
        | ExchangeId::BinanceFuturesCoin => {
            binance::fetch_klines(client, exchange, symbol, interval, from, to).await
        }
        _ => Err(ApiError::BadRequest(format!(
            "exchange {} does not support kline fetch",
            exchange.as_str()
        ))),
    }
}
