use barter::{
    engine::{
        clock::LiveClock,
        state::{
            global::DefaultGlobalData,
            instrument::{data::DefaultInstrumentMarketData, filter::InstrumentFilter},
            trading::TradingState,
        },
    },
    logging::init_logging,
    risk::DefaultRiskManager,
    statistic::time::Daily,
    strategy::DefaultStrategy,
    system::{
        builder::{AuditMode, EngineFeedMode, SystemArgs, SystemBuilder},
        config::SystemConfig,
    },
};
use barter_data::{
    streams::builder::dynamic::indexed::init_indexed_multi_exchange_market_stream,
    subscription::SubKind,
};
use barter_instrument::index::IndexedInstruments;
use barter_integration::Terminal;
use clap::{Parser, ValueEnum};
use futures::StreamExt;
use rust_decimal::Decimal;
use std::{fs::File, io::BufReader, time::Duration};
use tracing::debug;

const DEFAULT_CONFIG_PATH: &str = "assets/examples/config/system_config.json";

#[derive(Debug, Clone, ValueEnum)]
enum FeedMode {
    Iterator,
    Stream,
}

#[derive(Debug, Clone, ValueEnum)]
enum OutputMode {
    Terminal,
    Json,
}

/// Swym paper trading CLI — run a live paper trading session against exchange market feeds.
#[derive(Parser, Debug)]
#[command(version, about)]
struct CliArgs {
    /// Path to SystemConfig JSON file
    #[arg(short, long, default_value = DEFAULT_CONFIG_PATH)]
    config: String,

    /// Session duration in seconds
    #[arg(short, long, default_value_t = 60)]
    duration: u64,

    /// Risk-free return rate for summary statistics
    #[arg(long, default_value = "0.05")]
    risk_free_return: Decimal,

    /// Engine feed mode
    #[arg(long, value_enum, default_value_t = FeedMode::Iterator)]
    feed_mode: FeedMode,

    /// Output format for trading summary
    #[arg(long, value_enum, default_value_t = OutputMode::Terminal)]
    output: OutputMode,

    /// Comma-separated instrument filter (e.g. "BTCUSDT,ETHUSDT")
    #[arg(long, value_delimiter = ',')]
    pairs: Option<Vec<String>>,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    init_logging();

    let args = CliArgs::parse();

    // Load SystemConfig
    let mut config = load_config(&args.config)?;

    // Filter instruments by --pairs if provided
    if let Some(ref pairs) = args.pairs {
        config.instruments.retain(|i| {
            let name: &str = i.name_exchange.as_ref();
            pairs.iter().any(|p| p.eq_ignore_ascii_case(name))
        });
    }

    let SystemConfig {
        instruments,
        executions,
    } = config;

    // Construct IndexedInstruments
    let instruments = IndexedInstruments::new(instruments);

    // Initialise MarketData Stream
    let market_stream = init_indexed_multi_exchange_market_stream(
        &instruments,
        &[SubKind::PublicTrades, SubKind::OrderBooksL1],
    )
    .await?;

    // Determine engine feed mode
    let engine_feed_mode = match args.feed_mode {
        FeedMode::Iterator => EngineFeedMode::Iterator,
        FeedMode::Stream => EngineFeedMode::Stream,
    };

    // Construct System Args
    let system_args = SystemArgs::new(
        &instruments,
        executions,
        LiveClock,
        DefaultStrategy::default(),
        DefaultRiskManager::default(),
        market_stream,
        DefaultGlobalData::default(),
        |_| DefaultInstrumentMarketData::default(),
    );

    // Build & run full system
    let mut system = SystemBuilder::new(system_args)
        .engine_feed_mode(engine_feed_mode)
        .audit_mode(AuditMode::Enabled)
        .trading_state(TradingState::Disabled)
        .build()?
        .init_with_runtime(tokio::runtime::Handle::current())
        .await?;

    // Take ownership of the Engine audit snapshot with updates
    let audit = system.audit.take().unwrap();

    // Run asynchronous AuditStream consumer
    let audit_task = tokio::spawn(async move {
        let mut audit_stream = audit.updates.into_stream();
        while let Some(audit) = audit_stream.next().await {
            debug!(?audit, "AuditStream consumed AuditTick");
            if audit.event.is_terminal() {
                break;
            }
        }
        audit_stream
    });

    // Enable trading
    system.trading_state(TradingState::Enabled);

    // Run for the configured duration
    tokio::time::sleep(Duration::from_secs(args.duration)).await;

    // Before shutting down, CancelOrders and then ClosePositions
    system.cancel_orders(InstrumentFilter::None);
    system.close_positions(InstrumentFilter::None);

    // Shutdown
    let (engine, _shutdown_audit) = system.shutdown().await?;
    let _audit_stream = audit_task.await?;

    // Generate TradingSummary<Daily>
    let trading_summary = engine
        .trading_summary_generator(args.risk_free_return)
        .generate(Daily);

    // Output results
    match args.output {
        OutputMode::Terminal => {
            trading_summary.print_summary();
        }
        OutputMode::Json => {
            let json = serde_json::to_string_pretty(&trading_summary)?;
            println!("{json}");
        }
    }

    Ok(())
}

fn load_config(path: &str) -> Result<SystemConfig, Box<dyn std::error::Error>> {
    let file = File::open(path)?;
    let reader = BufReader::new(file);
    let config = serde_json::from_reader(reader)?;
    Ok(config)
}
