//! Multiple strategy modules in one runtime.
//!
//! This example wires an empty scheduler-driven strategy together with a Binance
//! public websocket strategy. It demonstrates how multiple modules share one
//! `EnvBuilder`, receive independent callbacks, and send websocket commands
//! through task handles.
//!
//! Run it with:
//!
//! ```text
//! cargo run --example multi_strategy_example --features binance
//! ```

use std::{sync::Arc, time::Duration};
use tokio::sync::oneshot;
use tracing::{error, info, warn};

use extrema_infra::{arch::market_assets::exchange::prelude::*, prelude::*};

///---------------------------------------------------------
/// Empty Strategy
///---------------------------------------------------------
/// This is a placeholder strategy that:
/// - Initializes but does not trade
/// - Logs incoming events (candles, schedules)
///
/// Useful for testing system wiring without executing orders.
#[derive(Clone)]
struct EmptyStrategy;

impl Strategy for EmptyStrategy {
    async fn initialize(&mut self) {
        info!("[EmptyStrategy] Executing init strategy...");
    }

    fn strategy_name(&self) -> &'static str {
        "EmptyStrategy"
    }
}

impl CommandEmitter for EmptyStrategy {
    /// Register command channel (not used in EmptyStrategy).
    fn command_init(&mut self, _registry: Arc<CommandRegistry>) {
        info!(
            "[EmptyStrategy] Command channel registered: {:?}",
            _registry
        );
    }

    /// No commands in this strategy.
    fn command_registry(&self) -> Arc<CommandRegistry> {
        Arc::new(CommandRegistry::default())
    }
}

impl EventHandler for EmptyStrategy {
    /// Called periodically if scheduled tasks are configured.
    async fn on_schedule(&mut self, msg: InfraMsg<AltScheduleEvent>) {
        info!("[EmptyStrategy] AltEventHandler: {:?}", msg);
    }

    /// Called when new candles are broadcasted.
    async fn on_candle(&mut self, msg: InfraMsg<Vec<WsCandle>>) {
        info!("[EmptyStrategy] Candle event: {:?}", msg);
    }
}

///---------------------------------------------------------
/// Binance Strategy
///---------------------------------------------------------
/// This strategy demonstrates:
/// - Connecting to Binance UM Futures public WebSocket (candles, trades, etc.)
/// - Subscribing to BTC/USDT perpetual candles
/// - Receiving and logging events
///
/// Note: This strategy only listens/logs data, does not trade.
#[derive(Clone)]
struct BinanceStrategy {
    command_registry: Arc<CommandRegistry>,
    binance_um_cli: BinanceUmCli, // Public Binance UM Futures client (no API keys)
}

impl BinanceStrategy {
    fn new() -> Self {
        Self {
            command_registry: Arc::new(CommandRegistry::default()),
            binance_um_cli: BinanceUmCli::default(),
        }
    }

    /// Connect to Binance WebSocket channel and send subscription.
    /// This runs only when a LOB event is received that signals the channel is ready.
    async fn connect_channel(&self, channel: &WsChannel) -> InfraResult<()> {
        if let Some(handle) = self.find_ws_handle(channel, 1) {
            info!("[BinanceStrategy] Sending connect to {:?}", handle);

            // Step 1: Request connection URL
            let ws_url = self.binance_um_cli.get_public_connect_msg(channel).await?;
            let (tx, rx) = oneshot::channel();
            let cmd = TaskCommand::WsConnect {
                msg: ws_url,
                ack: AckHandle::new(tx),
            };
            handle
                .send_command(cmd, Some((AckStatus::WsConnect, rx)))
                .await?;

            // Step 2: Subscribe to BTC/USDT perpetual candle updates
            let ws_msg = self
                .binance_um_cli
                .get_public_sub_msg(channel, Some(&["BTC_USDT_PERP".into()]))
                .await?;

            let cmd = TaskCommand::WsMessage {
                msg: ws_msg,
                ack: AckHandle::none(), // no need to wait for ack
            };
            handle.send_command(cmd, None).await?;
        } else {
            warn!(
                "[BinanceStrategy] No handle found for channel {:?}",
                channel
            );
        }

        Ok(())
    }
}

impl Strategy for BinanceStrategy {
    async fn initialize(&mut self) {
        info!("[BinanceStrategy] Starting strategy");
    }
}

impl CommandEmitter for BinanceStrategy {
    fn command_init(&mut self, registry: Arc<CommandRegistry>) {
        info!(
            "[BinanceStrategy] Command channel registered: {:?}",
            registry
        );
        self.command_registry = registry;
    }

    fn command_registry(&self) -> Arc<CommandRegistry> {
        self.command_registry.clone()
    }
}

impl EventHandler for BinanceStrategy {
    async fn on_schedule(&mut self, msg: InfraMsg<AltScheduleEvent>) {
        info!("[BinanceStrategy] AltEventHandler: {:?}", msg);
    }

    /// Triggered when a new WebSocket task is ready.
    /// Example: After creating WsTaskInfo for Binance Candle, this event will be fired
    /// and connect_channel() will be executed.
    async fn on_ws_event(&mut self, msg: InfraMsg<WsTaskInfo>) {
        info!(
            "[BinanceStrategy] Triggering connect for channel: {:?}",
            msg.data.ws_channel
        );
        if let Err(e) = self.connect_channel(&msg.data.ws_channel).await {
            error!("[BinanceStrategy] connect failed: {:?}", e);
        }
    }

    /// Handle incoming Binance candle data (1-minute candles here).
    async fn on_candle(&mut self, msg: InfraMsg<Vec<WsCandle>>) {
        info!("[BinanceStrategy] Candle event: {:?}", msg);
    }
}

///---------------------------------------------------------
/// Main Entry Point
///---------------------------------------------------------
/// - Initializes logging
/// - Creates strategies (EmptyStrategy + BinanceStrategy)
/// - Creates tasks (Binance candle WebSocket, Alt scheduler)
/// - Wires everything into EnvBuilder (pub/sub channels, strategies, tasks)
/// - Executes mediator event loop
#[tokio::main]
async fn main() {
    rustls::crypto::aws_lc_rs::default_provider()
        .install_default()
        .expect("failed to install rustls crypto provider");

    tracing_subscriber::fmt::init();
    info!("Logger initialized");

    // WebSocket Task: Binance Candle (1-minute)
    let binance_ws_candle = WsTaskInfo {
        market: Market::BinanceUmFutures,
        ws_channel: WsChannel::Candles(Some(CandleParam::OneMinute)),
        filter_channels: false, // false for debug msg
        chunk: 1,               // number of websocket connections for this task
        task_base_id: None,
    };

    // Alt Task: Time Scheduler (fires every 5 seconds)
    let alt_task = AltTaskInfo {
        alt_task_type: AltTaskType::TimeScheduler(Duration::from_secs(5)),
        chunk: 1,
        task_base_id: None,
    };

    // EnvBuilder builds the full runtime:
    // - Register broadcast channels (pub/sub message passing)
    // - Register strategy modules
    // - Register tasks
    let env = EnvBuilder::new()
        .with_board_cast_channel(BoardCastChannel::default_alt_event())
        .with_board_cast_channel(BoardCastChannel::default_ws_event())
        .with_board_cast_channel(BoardCastChannel::default_candle())
        .with_board_cast_channel(BoardCastChannel::default_candle()) // duplicated skip (can be removed)
        .with_board_cast_channel(BoardCastChannel::default_scheduler())
        .with_task(TaskInfo::WsTask(Arc::new(binance_ws_candle)))
        .with_task(TaskInfo::AltTask(Arc::new(alt_task)))
        .with_strategy_module(EmptyStrategy)
        .with_strategy_module(BinanceStrategy::new())
        .build();

    // Start event loop (spawns all tasks, connects strategies, begins message flow)
    env.execute().await;
}
