pub use swym_dal::models::run_config::RunConfig;

use barter::system::config::{ExecutionConfig, InstrumentConfig, SystemConfig};
use barter_execution::{
    AccountSnapshot, InstrumentAccountSnapshot,
    balance::{AssetBalance as BarterAssetBalance, Balance as BarterBalance},
    client::mock::MockExecutionConfig as BarterMockExecutionConfig,
    order::UnindexedOrderSnapshot,
};
use barter_instrument::{
    asset::name::AssetNameExchange,
    exchange::ExchangeId,
    instrument::name::InstrumentNameExchange,
};
use serde_json::Value;
use std::error::Error;
use swym_dal::models::strategy_config::StrategyConfig;

/// Convert a swym `RunConfig` into barter's `SystemConfig` plus the strategy config.
pub fn into_system_config(
    config: RunConfig,
) -> Result<(SystemConfig, StrategyConfig), Box<dyn Error + Send + Sync>> {
    // Deserialize instrument JSON as barter InstrumentConfig
    let instrument: InstrumentConfig = serde_json::from_value(config.instrument)?;

    // Parse ExchangeId from string via serde
    let exchange_id: ExchangeId =
        serde_json::from_value(Value::String(config.execution.mocked_exchange.clone()))?;

    let state_exchange_id: ExchangeId =
        serde_json::from_value(Value::String(config.execution.initial_state.exchange.clone()))?;

    // Map balances
    let balances: Vec<BarterAssetBalance<AssetNameExchange>> = config
        .execution
        .initial_state
        .balances
        .into_iter()
        .map(|b| BarterAssetBalance {
            asset: AssetNameExchange::from(b.asset.as_str()),
            balance: BarterBalance {
                total: b.balance.total,
                free: b.balance.free,
            },
            time_exchange: b.time_exchange,
        })
        .collect();

    // Map singular instrument into a vec of one
    let inst = &config.execution.initial_state.instrument;
    let orders: Vec<UnindexedOrderSnapshot> = inst
        .orders
        .iter()
        .map(|v| serde_json::from_value(v.clone()))
        .collect::<Result<_, _>>()?;

    let instrument_snapshot = InstrumentAccountSnapshot {
        instrument: InstrumentNameExchange::from(inst.instrument_name.as_str()),
        orders,
    };

    let initial_state = AccountSnapshot {
        exchange: state_exchange_id,
        balances,
        instruments: vec![instrument_snapshot],
    };

    let mock_config = BarterMockExecutionConfig {
        mocked_exchange: exchange_id,
        initial_state,
        latency_ms: config.execution.latency_ms,
        fees_percent: config.execution.fees_percent,
    };

    let system = SystemConfig {
        instruments: vec![instrument],
        executions: vec![ExecutionConfig::Mock(mock_config)],
    };

    Ok((system, config.strategy))
}
