use std::sync::Arc;

use arrow::datatypes::{DataType, Field, Schema, SchemaRef, TimeUnit};
use arrow::record_batch::RecordBatch;
use async_trait::async_trait;
use bt_kernel::{AssetClass, Result, TimeRange};
use serde::{Deserialize, Serialize};

#[async_trait]
pub trait DataProvider: Send + Sync {
    fn name(&self) -> &str;

    fn asset_classes(&self) -> &[AssetClass];

    async fn fetch_trades(&self, symbol: &str, range: TimeRange) -> Result<RecordBatch>;

    async fn fetch_bars(
        &self,
        symbol: &str,
        range: TimeRange,
        interval: BarInterval,
    ) -> Result<Option<RecordBatch>>;

    async fn fetch_funding_rates(
        &self,
        symbol: &str,
        range: TimeRange,
    ) -> Result<Option<RecordBatch>>;

    async fn list_symbols(&self) -> Result<Vec<ProviderSymbol>>;

    fn rate_limits(&self) -> RateLimitInfo;
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum BarInterval {
    Seconds(u32),
    Minutes(u32),
    Hours(u32),
    Days(u32),
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct ProviderSymbol {
    pub symbol: String,
    pub base_asset: String,
    pub quote_asset: String,
    pub status: String,
    pub asset_class: AssetClass,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct RateLimitInfo {
    pub requests_per_minute: Option<u32>,
    pub burst: Option<u32>,
}

pub fn raw_trades_schema() -> SchemaRef {
    Arc::new(Schema::new(vec![
        Field::new(
            "timestamp",
            DataType::Timestamp(TimeUnit::Nanosecond, Some("UTC".into())),
            false,
        ),
        Field::new(
            "received_at",
            DataType::Timestamp(TimeUnit::Nanosecond, Some("UTC".into())),
            false,
        ),
        Field::new("provider", DataType::Utf8, false),
        Field::new("symbol_raw", DataType::Utf8, false),
        Field::new("symbol_id", DataType::UInt32, false),
        Field::new("price", DataType::Float64, false),
        Field::new("quantity", DataType::Float64, false),
        Field::new("side", DataType::UInt8, false),
        Field::new("trade_id", DataType::Utf8, false),
        Field::new("conditions", DataType::Utf8, true),
    ]))
}

pub fn bars_1m_schema() -> SchemaRef {
    Arc::new(Schema::new(vec![
        Field::new(
            "timestamp",
            DataType::Timestamp(TimeUnit::Nanosecond, Some("UTC".into())),
            false,
        ),
        Field::new("symbol_id", DataType::UInt32, false),
        Field::new("open", DataType::Float64, false),
        Field::new("high", DataType::Float64, false),
        Field::new("low", DataType::Float64, false),
        Field::new("close", DataType::Float64, false),
        Field::new("vwap", DataType::Float64, false),
        Field::new("volume", DataType::Float64, false),
        Field::new("buy_volume", DataType::Float64, true),
        Field::new("sell_volume", DataType::Float64, true),
        Field::new("trade_count", DataType::UInt32, false),
        Field::new("bid", DataType::Float64, true),
        Field::new("ask", DataType::Float64, true),
        Field::new("spread", DataType::Float64, true),
        Field::new("is_gap", DataType::Boolean, false),
        Field::new("gap_fill_method", DataType::UInt8, false),
    ]))
}

pub fn funding_rates_schema() -> SchemaRef {
    Arc::new(Schema::new(vec![
        Field::new(
            "timestamp",
            DataType::Timestamp(TimeUnit::Nanosecond, Some("UTC".into())),
            false,
        ),
        Field::new("symbol", DataType::Utf8, false),
        Field::new("funding_rate", DataType::Float64, false),
        Field::new("mark_price", DataType::Float64, true),
    ]))
}
