use super::{
    SwymState,
    audit::AuditLog,
    rule_strategy::RuleStrategy,
    signal::{interval_secs_to_str, parse_interval_secs},
};
use barter::{
    engine::Engine,
    engine::state::instrument::filter::InstrumentFilter,
    strategy::{
        DefaultStrategy,
        algo::AlgoStrategy,
        close_positions::ClosePositionsStrategy,
        on_disconnect::OnDisconnectStrategy,
        on_trading_disabled::OnTradingDisabled,
    },
};
use barter_execution::order::request::{OrderRequestCancel, OrderRequestOpen};
use barter_instrument::{
    asset::AssetIndex,
    exchange::{ExchangeId, ExchangeIndex},
    instrument::InstrumentIndex,
};
use swym_dal::models::strategy_config::{StrategyConfig, collect_timeframes};

/// Enum-dispatched strategy so the monomorphic Engine can use any configured strategy variant.
#[derive(Debug, Clone)]
pub enum SwymStrategy {
    Default(DefaultStrategy<SwymState>),
    RuleBased(RuleStrategy),
}

impl SwymStrategy {
    pub fn from_config(config: &StrategyConfig) -> Self {
        match config {
            StrategyConfig::Default => Self::Default(DefaultStrategy::default()),
            StrategyConfig::RuleBased(params) => {
                let primary_secs = parse_interval_secs(&params.candle_interval).unwrap_or(60);
                // Walk the rule tree to discover additional timeframes referenced by expressions.
                let additional_set = collect_timeframes(params);
                let mut additional: Vec<u64> = additional_set
                    .iter()
                    .filter_map(|tf| parse_interval_secs(tf))
                    .collect();
                additional.sort_unstable();
                Self::RuleBased(RuleStrategy::new(params.rules.clone(), primary_secs, additional))
            }
        }
    }

    /// All (interval_secs, period) EMA pairs this strategy requires.
    pub fn ema_registrations(&self) -> Vec<(u64, usize)> {
        match self {
            Self::RuleBased(s) => s.ema_registrations(),
            _ => vec![],
        }
    }

    /// All candle interval_secs this strategy references (primary + additional).
    pub fn all_interval_secs(&self) -> Vec<u64> {
        match self {
            Self::RuleBased(s) => {
                let mut all = vec![s.interval_secs];
                all.extend_from_slice(&s.additional_interval_secs);
                all
            }
            _ => vec![],
        }
    }

    /// Additional timeframe interval strings needed beyond the primary candle interval.
    /// Used by the backtest runner to load and interleave multi-interval candle data.
    pub fn additional_intervals(&self) -> Vec<&'static str> {
        match self {
            Self::RuleBased(s) => s.additional_interval_secs.iter()
                .filter_map(|&secs| interval_secs_to_str(secs))
                .collect(),
            _ => vec![],
        }
    }

    /// Attach an audit log to a `RuleBased` strategy. No-op for other variants.
    pub fn set_audit_log(&mut self, log: AuditLog) {
        if let Self::RuleBased(s) = self {
            s.audit_log = Some(log);
        }
    }

    /// Candle interval in seconds for live candle-aggregation mode.
    ///
    /// Returns `Some` only for strategies that require candle-based evaluation.
    /// The executor uses this to initialise a [`CandleAggregator`] in live runs.
    pub fn candle_interval_secs(&self) -> Option<u64> {
        match self {
            Self::RuleBased(s) => Some(s.interval_secs),
            _ => None,
        }
    }
}

impl AlgoStrategy for SwymStrategy {
    type State = SwymState;

    fn generate_algo_orders(
        &self,
        state: &Self::State,
    ) -> (
        impl IntoIterator<Item = OrderRequestCancel>,
        impl IntoIterator<Item = OrderRequestOpen>,
    ) {
        match self {
            Self::Default(s) => {
                let (cancels, opens) = s.generate_algo_orders(state);
                (
                    cancels.into_iter().collect::<Vec<_>>(),
                    opens.into_iter().collect::<Vec<_>>(),
                )
            }
            Self::RuleBased(s) => {
                let (cancels, opens) = s.generate_algo_orders(state);
                (
                    cancels.into_iter().collect::<Vec<_>>(),
                    opens.into_iter().collect::<Vec<_>>(),
                )
            }
        }
    }
}

impl ClosePositionsStrategy for SwymStrategy {
    type State = SwymState;

    fn close_positions_requests<'a>(
        &'a self,
        state: &'a Self::State,
        filter: &'a InstrumentFilter,
    ) -> (
        impl IntoIterator<Item = OrderRequestCancel<ExchangeIndex, InstrumentIndex>> + 'a,
        impl IntoIterator<Item = OrderRequestOpen<ExchangeIndex, InstrumentIndex>> + 'a,
    )
    where
        ExchangeIndex: 'a,
        AssetIndex: 'a,
        InstrumentIndex: 'a,
    {
        match self {
            Self::Default(s) => {
                let (cancels, opens) = s.close_positions_requests(state, filter);
                (
                    cancels.into_iter().collect::<Vec<_>>(),
                    opens.into_iter().collect::<Vec<_>>(),
                )
            }
            Self::RuleBased(s) => {
                let (cancels, opens) = s.close_positions_requests(state, filter);
                (
                    cancels.into_iter().collect::<Vec<_>>(),
                    opens.into_iter().collect::<Vec<_>>(),
                )
            }
        }
    }
}

impl<Clock, State, ExecutionTxs, Risk> OnDisconnectStrategy<Clock, State, ExecutionTxs, Risk>
    for SwymStrategy
{
    type OnDisconnect = ();

    fn on_disconnect(
        _engine: &mut Engine<Clock, State, ExecutionTxs, Self, Risk>,
        _exchange: ExchangeId,
    ) {
    }
}

impl<Clock, State, ExecutionTxs, Risk> OnTradingDisabled<Clock, State, ExecutionTxs, Risk>
    for SwymStrategy
{
    type OnTradingDisabled = ();

    fn on_trading_disabled(
        _engine: &mut Engine<Clock, State, ExecutionTxs, Self, Risk>,
    ) {
    }
}
