use std::{
    collections::HashMap,
    sync::{atomic::AtomicBool, Arc, RwLock},
};

use async_trait::async_trait;
use once_cell::sync::Lazy;
use serde_json::Value;
use tesser_core::{Candle, Interval, OrderBook, Tick};

use crate::{BrokerError, BrokerResult, ExecutionClient};

/// Configuration passed to market stream factories describing runtime metadata.
#[derive(Clone, Default)]
pub struct ConnectorStreamConfig {
    /// Optional WebSocket endpoint override.
    pub ws_url: Option<String>,
    /// Arbitrary connector-specific metadata (e.g., categories).
    pub metadata: Value,
    /// Connection status flag shared with health monitoring.
    pub connection_status: Option<Arc<AtomicBool>>,
}

/// Trait describing the minimal market stream capabilities required by higher-level tooling.
#[async_trait]
pub trait ConnectorStream: Send {
    /// Subscribe the stream to a set of symbols using the provided candle interval.
    async fn subscribe(&mut self, symbols: &[String], interval: Interval) -> BrokerResult<()>;

    /// Fetch the next tick generated by the subscribed markets.
    async fn next_tick(&mut self) -> BrokerResult<Option<Tick>>;

    /// Fetch the next candle generated by the subscribed markets.
    async fn next_candle(&mut self) -> BrokerResult<Option<Candle>>;

    /// Fetch the next order book snapshot generated by the subscribed markets.
    async fn next_order_book(&mut self) -> BrokerResult<Option<OrderBook>>;
}

/// Factory trait implemented by each connector to expose standardized construction hooks.
#[async_trait]
pub trait ConnectorFactory: Send + Sync {
    /// Unique identifier of the connector (e.g., "bybit", "binance").
    fn name(&self) -> &str;

    /// Construct an execution client using the provided configuration blob.
    async fn create_execution_client(
        &self,
        config: &Value,
    ) -> BrokerResult<Arc<dyn ExecutionClient>>;

    /// Construct a market data stream using the provided configuration blob.
    #[allow(unused_variables)]
    async fn create_market_stream(
        &self,
        config: &Value,
        stream_config: ConnectorStreamConfig,
    ) -> BrokerResult<Box<dyn ConnectorStream>> {
        Err(BrokerError::Other(format!(
            "{} does not implement market stream factory",
            self.name()
        )))
    }
}

static REGISTRY: Lazy<RwLock<HashMap<String, Arc<dyn ConnectorFactory>>>> =
    Lazy::new(|| RwLock::new(HashMap::new()));

/// Register a connector factory so it becomes discoverable by downstream consumers.
pub fn register_connector_factory(factory: Arc<dyn ConnectorFactory>) {
    let mut guard = REGISTRY
        .write()
        .expect("connector factory registry poisoned");
    guard.insert(factory.name().to_string(), factory);
}

/// Retrieve a connector factory by identifier.
pub fn get_connector_factory(name: &str) -> Option<Arc<dyn ConnectorFactory>> {
    REGISTRY
        .read()
        .expect("connector factory registry poisoned")
        .get(name)
        .cloned()
}

/// Enumerate the names of all registered connector factories.
pub fn registered_connectors() -> Vec<String> {
    REGISTRY
        .read()
        .expect("connector factory registry poisoned")
        .keys()
        .cloned()
        .collect()
}
