use debot_db::{CandlePattern, PricePoint, SampleTerm, TradingStrategy, TrendType};
use debot_ml::RandomForest;
use debot_utils::{num_seconds_from_midnight, random_bool};
use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
use rust_decimal::Decimal;
use smartcore_proba::linalg::basic::arrays::Array2;
use smartcore_proba::linalg::basic::matrix::DenseMatrix;

use crate::candle::Candle;
use crate::pid_controller::{AdaptivePidController, PidThresholds, SetpointMode};
use crate::{trade_action::TradeDetail, trend_metrics::TrendMetrics, TradeAction};
use std::collections::VecDeque;
use std::env;
use std::str::FromStr;

#[derive(Debug, Clone)]
struct MarketDataConfig {
    short_period: usize,
    long_period: usize,
    trading_period: usize,
    flash_crash_period: usize,
    max_size: usize,
    only_read_price: bool,
    min_expected_score: f64,
}

#[derive(Debug, Clone)]
struct MarketDataMetrics {
    price: TrendMetrics,
    rsi: TrendMetrics,
    atr: TrendMetrics,
    adx: TrendMetrics,
    stochastic: TrendMetrics,
}

impl MarketDataMetrics {
    fn new(short_period: usize, long_period: usize, macd_period: usize) -> Self {
        Self {
            price: TrendMetrics::new("price", short_period, long_period, macd_period),
            rsi: TrendMetrics::new("rsi", short_period, long_period, macd_period),
            atr: TrendMetrics::new("atr", short_period, long_period, macd_period),
            adx: TrendMetrics::new("adx", short_period, long_period, macd_period),
            stochastic: TrendMetrics::new("stochastic", short_period, long_period, macd_period),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VolumeBarMode {
    BarOnThreshold(Decimal),
    BarOnRecovery(Decimal),
    Always,
}

#[derive(Debug, Clone)]
pub struct InagoThresholds {
    pub min_score: i32,
    pub min_direction_diff: i32,
    pub adx_strength: Decimal,
    pub rsi_momentum: Decimal,
    pub rsi_upper_bound: Decimal,
    pub rsi_lower_bound: Decimal,
    pub atr_ratio: Decimal,
    pub volume_ratio: Decimal,
}

impl Default for InagoThresholds {
    fn default() -> Self {
        Self {
            min_score: 2,
            min_direction_diff: 1,
            adx_strength: Decimal::new(25, 0),
            rsi_momentum: Decimal::new(15, 0),
            rsi_upper_bound: Decimal::new(70, 0),
            rsi_lower_bound: Decimal::new(30, 0),
            atr_ratio: Decimal::new(15, 1), // 1.5
            volume_ratio: Decimal::new(2, 0),
        }
    }
}

pub struct MarketData {
    name: String,
    prices: VecDeque<PricePoint>,
    config: MarketDataConfig,
    metrics: MarketDataMetrics,
    random_forest: Option<RandomForest>,
    candles_5m: Vec<Candle>,
    candles_1h: Vec<Candle>,
    candles_4h: Vec<Candle>,
    current_candle_5m: Option<Candle>,
    current_candle_1h: Option<Candle>,
    current_candle_4h: Option<Candle>,
    score_history: VecDeque<f64>,
    volume_bar_mode: VolumeBarMode,
    pending_volume: Decimal,
    last_bar_updated: bool,
    time_divider: Decimal,
    pid_controller: Option<AdaptivePidController>,
}

impl MarketData {
    pub fn new(
        name: String,
        short_period: usize,
        long_period: usize,
        trading_period: usize,
        flash_crash_period: usize,
        max_size: usize,
        random_forest: Option<RandomForest>,
        only_read_price: bool,
        min_expected_score: f64,
        volume_bar_mode: VolumeBarMode,
        time_divider: Decimal,
    ) -> MarketData {
        debug_assert!(time_divider > Decimal::ZERO, "time_divider must be > 0");

        let config = MarketDataConfig {
            short_period,
            long_period,
            trading_period,
            flash_crash_period,
            max_size: if only_read_price {
                long_period * 3
            } else {
                max_size
            },
            only_read_price,
            min_expected_score,
        };
        let metrics = MarketDataMetrics::new(short_period, long_period, trading_period);

        log::warn!("volume_bar_mode = {:?}", volume_bar_mode);

        MarketData {
            name,
            prices: VecDeque::with_capacity(max_size),
            config,
            metrics,
            random_forest,
            candles_5m: Vec::new(),
            candles_1h: Vec::new(),
            candles_4h: Vec::new(),
            current_candle_5m: None,
            current_candle_1h: None,
            current_candle_4h: None,
            score_history: VecDeque::with_capacity(trading_period),
            volume_bar_mode,
            pending_volume: Decimal::ZERO,
            last_bar_updated: false,
            time_divider,
            pid_controller: None,
        }
    }

    pub fn is_signaled(
        &mut self,
        strategy: TradingStrategy,
        _index: usize,
        take_profit_ratio: Decimal,
        atr_spread: Decimal,
        atr_term: &SampleTerm,
        _seed: Option<u64>,
        tick_spread: i64,
        risk_reward: Decimal,
        min_tick: Option<Decimal>,
        bias_ticks: i64,
        min_profit_ratio: Decimal,
        inago_thresholds: Option<InagoThresholds>,
        pid_thresholds: Option<PidThresholds>,
    ) -> Vec<TradeAction> {
        let mut actions: Vec<TradeAction> = vec![];

        if self.is_metrics_none() {
            log::debug!("is_signaled skipped: No metrics - ADX: {:?}, ATR: {:?}, Price: {:?}, RSI: {:?}, Stochastic: {:?}",
                self.metrics.adx.get().is_none(),
                self.metrics.atr.get().is_none(),
                self.metrics.price.get().is_none(),
                self.metrics.rsi.get().is_none(),
                self.metrics.stochastic.get().is_none());
            return actions;
        }

        if !self.last_bar_updated {
            log::debug!(
                "is_signaled skipped: waiting next volume bar(pending_volume = {:2})",
                self.pending_volume
            );
            return Vec::new();
        }

        // Handle PID controller strategies (can be combined with other strategies)
        if let Some(ref pid_thresholds) = pid_thresholds {
            if let Some(signal) = self.evaluate_pid_signal(pid_thresholds) {
                log::info!("PID signal detected: {:?}", signal);
                let action = self.create_pid_action(
                    &strategy,
                    signal,
                    min_tick,
                    take_profit_ratio,
                    bias_ticks,
                    min_profit_ratio,
                );
                if !matches!(action, TradeAction::None) {
                    actions.push(action);
                }
            }
        }

        match strategy {
            TradingStrategy::RandomPidController(trend_type) => {
                log::debug!(
                    "RandomPidController strategy called for trend_type: {:?}",
                    trend_type
                );

                if let Some(ref pid_thresholds) = pid_thresholds {
                    if let Some(signal) = self.evaluate_pid_signal(pid_thresholds) {
                        if signal == trend_type || matches!(trend_type, TrendType::Any) {
                            if random_bool(0.5, None) {
                                // 50% chance to execute
                                log::debug!("Random PID signal executed: {:?}", signal);
                                let action = self.create_pid_action(
                                    &strategy,
                                    signal,
                                    min_tick,
                                    take_profit_ratio,
                                    bias_ticks,
                                    min_profit_ratio,
                                );
                                if !matches!(action, TradeAction::None) {
                                    actions.push(action);
                                }
                            }
                        }
                    }
                }
                return actions;
            }
            TradingStrategy::RandomInago(trend_type) => {
                log::debug!(
                    "RandomInago strategy called for trend_type: {:?}",
                    trend_type
                );
                let thresholds = inago_thresholds.unwrap_or_default();
                log::debug!("Using thresholds: {:?}", thresholds);

                // Debug current market state
                let rsi = self.rsi();
                let atr = self.atr();
                let adx = self.adx();
                let price = self.last_price();
                log::debug!(
                    "Market state - RSI: {:?}, ATR: {:?}, ADX: {:?}, Price: {}",
                    rsi,
                    atr,
                    adx,
                    price
                );

                if let Some(detected) = self.detect_inago_opportunity(&thresholds) {
                    if random_bool(0.5, None) {
                        // 50% chance to execute
                        log::debug!(
                            "Inago opportunity detected: {:?}, target: {:?}",
                            detected,
                            trend_type
                        );
                        if detected == trend_type {
                            log::debug!(
                                "Trend matches! Creating trade action for {:?}",
                                trend_type
                            );
                            actions.push(self.create_open_action(
                                &strategy,
                                trend_type,
                                None,
                                None,
                                None,
                                min_tick,
                                take_profit_ratio,
                                bias_ticks,
                                min_profit_ratio,
                            ));
                        } else {
                            log::debug!(
                                "Trend mismatch: detected {:?} but target is {:?}",
                                detected,
                                trend_type
                            );
                        }
                    }
                } else {
                    log::debug!("No Inago opportunity detected with current thresholds");
                }
                return actions;
            }
            TradingStrategy::RandomMeanReversion(trend_type) => {
                if random_bool(0.01, None) {
                    let action = self.create_open_action(
                        &strategy,
                        trend_type,
                        None,
                        None,
                        None,
                        min_tick,
                        take_profit_ratio,
                        bias_ticks,
                        min_profit_ratio,
                    );
                    actions.push(action.clone());
                }
                return actions;
            }
            TradingStrategy::RandomMarketMake => {
                if self.is_low_volatility() {
                    actions.push(self.create_open_action(
                        &strategy,
                        TrendType::Up,
                        None,
                        None,
                        None,
                        min_tick,
                        take_profit_ratio,
                        bias_ticks,
                        min_profit_ratio,
                    ));
                    actions.push(self.create_open_action(
                        &strategy,
                        TrendType::Down,
                        None,
                        None,
                        None,
                        min_tick,
                        take_profit_ratio,
                        bias_ticks,
                        min_profit_ratio,
                    ));
                }
                return actions;
            }
            TradingStrategy::Inago(_)
            | TradingStrategy::MeanReversion(_)
            | TradingStrategy::PidController(_)
            | TradingStrategy::MarketMake => {}
            TradingStrategy::Hybrid | TradingStrategy::Rebalance => {
                panic!("Not supported");
            }
        }

        let include_one_hot = !matches!(strategy, TradingStrategy::MarketMake);
        let feats = self.extract_signal_features(
            include_one_hot,
            take_profit_ratio,
            atr_spread,
            atr_term,
            tick_spread,
            risk_reward,
            bias_ticks,
        );

        let x = DenseMatrix::from_2d_array(&[&feats]);
        log::trace!("num of dense matrix x = {:?}", x);

        let trend_type = strategy.trend_type();

        if let Some(rf) = &self.random_forest {
            match x {
                Ok(ref x_matrix) => {
                    #[cfg(feature = "classification")]
                    {
                        let (proba, expected_score) = rf.predict_proba(x_matrix);
                        let p_row = proba.get_row(0);
                        let p: Vec<f64> = p_row.iterator(0).copied().collect();
                        let p_loss = p[0];
                        let p_expired = p[1];
                        let p_take = p[2];

                        if p_loss >= p_expired && p_loss >= p_take {
                            log::debug!(
                                "[{:?}]Skip: Loss is most probable (p0 = {:.3})",
                                strategy,
                                p_loss
                            );
                            return actions;
                        }

                        // Dynamic threshold based on market volatility
                        let dynamic_threshold = self.calculate_dynamic_threshold();
                        if p_take < dynamic_threshold {
                            log::debug!(
                                "[{:?}]Skip: TakeProfit prob too low ({:.3}) - requiring >= {:.3} (dynamic)",
                                strategy,
                                p_take,
                                dynamic_threshold
                            );
                            return actions;
                        }

                        let (w_loss, w_expired, w_take) = rf.weights();
                        let risk = w_loss.abs() * p_loss;
                        let reward = w_expired * p_expired + w_take * p_take;
                        let rr = reward / (risk + 1e-8);

                        let rr_dec = Decimal::from_f64(rr).unwrap();
                        // Require higher risk-reward ratio for safer trades
                        let min_rr = risk_reward.max(Decimal::new(15, 1)); // At least 1.5:1
                        if rr_dec < min_rr {
                            log::debug!(
                                "[{:?}]Skip: RiskReward -> reward: {:.5}, risk: {:.5}, R/R: {:.3} < {:.1}",
                                strategy,
                                reward,
                                risk,
                                rr,
                                min_rr.to_f64().unwrap()
                            );
                            return actions;
                        }

                        if expected_score > self.config.min_expected_score {
                            // Additional safety check: avoid trading against strong trends
                            if self.is_strong_trend_continuation(trend_type) {
                                log::debug!(
                                    "[{:?}]Skip: Strong trend continuation detected, avoiding counter-trend trade",
                                    strategy
                                );
                                return actions;
                            }

                            log::info!(
                                "[{:?}]PredictProba -> Loss: {:.3}, ExpiredProfit: {:.3}, TakeProfit: {:.3}, ExpectedScore: {:.3}",
                                strategy, p_loss, p_expired, p_take, expected_score
                            );

                            let label = [0, 1, 2]
                                .iter()
                                .enumerate()
                                .max_by(|a, b| p[a.0].partial_cmp(&p[b.0]).unwrap())
                                .map(|(_, &label)| label)
                                .unwrap_or(1);

                            let (pred1, pred2) = rf.predict_timeout(x_matrix);
                            let (pred1_u32, pred2_u32): (u32, u32) = (pred1 as u32, pred2 as u32);

                            if pred1_u32 == 0 || pred1_u32 == 0 {
                                log::warn!(
                                    "Wrong predictions: pred1 = {}, pred = {}",
                                    pred1_u32,
                                    pred1_u32
                                );
                            }

                            if *trend_type == TrendType::Any {
                                actions.push(self.create_open_action(
                                    &strategy,
                                    TrendType::Up,
                                    Some(pred1_u32),
                                    Some(pred2_u32),
                                    Some(label),
                                    min_tick,
                                    take_profit_ratio,
                                    bias_ticks,
                                    min_profit_ratio,
                                ));
                                actions.push(self.create_open_action(
                                    &strategy,
                                    TrendType::Down,
                                    Some(pred1_u32),
                                    Some(pred2_u32),
                                    Some(label),
                                    min_tick,
                                    take_profit_ratio,
                                    bias_ticks,
                                    min_profit_ratio,
                                ));
                            } else {
                                actions.push(self.create_open_action(
                                    &strategy,
                                    *trend_type,
                                    Some(pred1_u32),
                                    Some(pred2_u32),
                                    Some(label),
                                    min_tick,
                                    take_profit_ratio,
                                    bias_ticks,
                                    min_profit_ratio,
                                ));
                            }
                        }
                    }

                    #[cfg(feature = "regression")]
                    {
                        let pred = rf.predict(x_matrix);
                        let expected_score = pred[0];
                        if expected_score > self.config.min_expected_score {
                            log::info!(
                                "[{:?}]Execute: ExpectedScore (return rate) = {:.3}",
                                strategy,
                                expected_score
                            );
                            let (pred1, pred2) = rf.predict_timeout(x_matrix);
                            let (pred1_u32, pred2_u32): (u32, u32) = (pred1 as u32, pred2 as u32);

                            actions.push(Self::create_open_action(
                                *trend_type,
                                Some(pred1_u32),
                                Some(pred2_u32),
                                None,
                            ));
                        } else {
                            log::debug!(
                                "[{:?}]Skip: ExpectedScore (return rate) = {:.3}",
                                strategy,
                                expected_score
                            );
                        }
                    }
                }
                Err(e) => {
                    log::error!("Failed to build DenseMatrix: {:?}", e);
                }
            }
        }

        actions
    }

    fn create_open_action(
        &self,
        strategy: &TradingStrategy,
        trend: TrendType,
        entry_timeout_sec: Option<u32>,
        max_holding_sec: Option<u32>,
        label: Option<i32>,
        min_tick: Option<Decimal>,
        take_profit_ratio: Decimal,
        bias_ticks: i64,
        min_profit_ratio: Decimal,
    ) -> TradeAction {
        let mut confidence = Decimal::from(label.unwrap_or(1)) / Decimal::new(2, 0);

        let spread = match strategy {
            TradingStrategy::PidController(_) => {
                // For PidController, use PID-based confidence and position sizing
                if let Some(ref controller) = self.pid_controller {
                    let (proportional, integral, derivative) = controller.get_components();
                    let total_magnitude = proportional.abs() + integral.abs() + derivative.abs();

                    // Override confidence with PID-based calculation
                    let pid_confidence = {
                        let raw_confidence = total_magnitude / (total_magnitude + Decimal::ONE);
                        raw_confidence.clamp(Decimal::new(1, 1), Decimal::ONE) // 0.1 to 1.0
                    };

                    // Combine RandomForest confidence with PID confidence
                    confidence = (confidence + pid_confidence) / Decimal::from(2);

                    log::debug!(
                        "PidController RandomForest action - Original confidence: {}, PID confidence: {}, Combined: {}",
                        Decimal::from(label.unwrap_or(1)) / Decimal::new(2, 0),
                        pid_confidence,
                        confidence
                    );
                }
                None // No spread for PID controller
            }
            TradingStrategy::MarketMake | TradingStrategy::RandomMarketMake => {
                let tick = match min_tick {
                    Some(v) => v,
                    None => return TradeAction::None,
                };

                let price = self.last_price();
                if price.is_zero() {
                    return TradeAction::None;
                }

                if take_profit_ratio < min_profit_ratio {
                    log::debug!(
                        "skip MarketMake: take_profit_ratio={:.5} <= min_profit_ratio={:.5}",
                        take_profit_ratio,
                        min_profit_ratio
                    );
                    return TradeAction::None;
                }

                let side_spread = price * take_profit_ratio / Decimal::new(2, 0);

                let raw_ticks = (side_spread / tick).ceil();
                let tick_count: i64 = raw_ticks.try_into().unwrap_or_default();
                if tick_count <= 0 {
                    return TradeAction::None;
                }

                let biased_tick_count = match trend {
                    TrendType::Up => tick_count.saturating_sub(bias_ticks),
                    TrendType::Down => tick_count.saturating_add(bias_ticks),
                    _ => tick_count,
                };
                if biased_tick_count <= 0 {
                    return TradeAction::None;
                }

                log::debug!(
                    "MarketMake spread calc → price={:.4}, profit_ratio={:.4}, raw_ticks={}, bias_ticks={}, final_tick_count={}",
                    price,
                    take_profit_ratio,
                    tick_count,
                    bias_ticks,
                    biased_tick_count
                );

                Some(biased_tick_count)
            }
            _ => None,
        };

        match trend {
            TrendType::Up => TradeAction::BuyOpen(TradeDetail::new(
                None,
                None,
                confidence,
                entry_timeout_sec,
                max_holding_sec,
                spread,
            )),
            TrendType::Down => TradeAction::SellOpen(TradeDetail::new(
                None,
                None,
                confidence,
                entry_timeout_sec,
                max_holding_sec,
                spread,
            )),
            _ => TradeAction::None,
        }
    }

    pub fn add_price(
        &mut self,
        price: Option<Decimal>,
        timestamp: Option<i64>,
        volume: Option<Decimal>,
        num_trades: Option<u64>,
        funding_rate: Option<Decimal>,
        open_interest: Option<Decimal>,
        oracle_price: Option<Decimal>,
    ) -> PricePoint {
        let tick_vol = volume.unwrap_or_default();
        self.last_bar_updated = false;

        let early_price_point = PricePoint::new(
            price.unwrap_or_else(|| self.prices.back().map_or(Decimal::ZERO, |pp| pp.price)),
            timestamp,
            volume,
            num_trades,
            funding_rate,
            open_interest,
            oracle_price,
            None,
        );

        match self.volume_bar_mode {
            VolumeBarMode::Always => {}
            VolumeBarMode::BarOnThreshold(threshold) => {
                self.pending_volume += tick_vol;
                if self.pending_volume < threshold {
                    return early_price_point;
                }
                self.pending_volume -= threshold;
            }
            VolumeBarMode::BarOnRecovery(threshold) => {
                self.pending_volume += tick_vol;
                if self.pending_volume > threshold {
                    self.pending_volume -= threshold;
                    return early_price_point;
                }
            }
        }

        self.last_bar_updated = true;

        if self.prices.len() == self.config.max_size {
            self.prices.pop_front();
        }

        let price =
            price.unwrap_or_else(|| self.prices.back().map_or(Decimal::ZERO, |pp| pp.price));

        self.update_ema(price);
        self.update_rsi();
        self.update_adx();
        self.update_stochastic();
        self.update_atr();

        let adjusted_volume = volume.map(|v| {
            if self.time_divider.is_zero() {
                log::error!("time_divider is zero; skipping volume normalization");
                v
            } else {
                v / self.time_divider
            }
        });
        let price_point = PricePoint::new(
            price,
            timestamp,
            adjusted_volume,
            num_trades,
            funding_rate,
            open_interest,
            oracle_price,
            None,
        );

        self.prices.push_back(price_point.clone());

        if self.config.only_read_price {
            return price_point;
        }

        let mut candles_5m = std::mem::take(&mut self.candles_5m);
        let mut current_candle_5m = self.current_candle_5m.take();
        self.update_candle(
            price,
            price_point.timestamp,
            300,
            &mut candles_5m,
            &mut current_candle_5m,
        );
        self.candles_5m = candles_5m;
        self.current_candle_5m = current_candle_5m;

        let mut candles_1h = std::mem::take(&mut self.candles_1h);
        let mut current_candle_1h = self.current_candle_1h.take();
        self.update_candle(
            price,
            price_point.timestamp,
            3600,
            &mut candles_1h,
            &mut current_candle_1h,
        );
        self.candles_1h = candles_1h;
        self.current_candle_1h = current_candle_1h;

        let mut candles_4h = std::mem::take(&mut self.candles_4h);
        let mut current_candle_4h = self.current_candle_4h.take();
        self.update_candle(
            price,
            price_point.timestamp,
            14400,
            &mut candles_4h,
            &mut current_candle_4h,
        );
        self.candles_4h = candles_4h;
        self.current_candle_4h = current_candle_4h;

        let score = self.compute_signal_score();
        self.score_history.push_back(score);
        if self.score_history.len() > self.config.trading_period {
            self.score_history.pop_front();
        }

        price_point
    }

    pub fn name(&self) -> &str {
        &self.name
    }

    pub fn last_price(&self) -> Decimal {
        self.metrics.price.val.current_value().unwrap_or_default()
    }

    pub fn last_volume(&self) -> Option<Decimal> {
        self.prices.iter().last().and_then(|p| p.volume)
    }

    pub fn last_num_trades(&self) -> Option<u64> {
        self.prices.iter().last().and_then(|p| p.num_trades)
    }

    pub fn last_funding_rate(&self) -> Option<Decimal> {
        self.prices.iter().last().and_then(|p| p.funding_rate)
    }

    pub fn last_open_interest(&self) -> Option<Decimal> {
        self.prices.iter().last().and_then(|p| p.open_interest)
    }

    pub fn last_oracle_price(&self) -> Option<Decimal> {
        self.prices.iter().last().and_then(|p| p.oracle_price)
    }

    pub fn atr(&self) -> (Decimal, Decimal, Decimal, Decimal, Decimal, Decimal) {
        match self.metrics.atr.get() {
            Some(v) => v,
            None => (
                Decimal::ZERO,
                Decimal::ZERO,
                Decimal::ZERO,
                Decimal::ZERO,
                Decimal::ZERO,
                Decimal::ZERO,
            ),
        }
    }

    pub fn atr_by_term(&self, term: &SampleTerm) -> Decimal {
        let atrs = self.atr();
        match term {
            SampleTerm::TradingTerm => atrs.0,
            SampleTerm::ShortTerm => atrs.1,
            SampleTerm::LongTerm => atrs.2,
        }
    }

    pub fn adx(&self) -> (Decimal, Decimal, Decimal, Decimal, Decimal, Decimal) {
        match self.metrics.adx.get() {
            Some(v) => v,
            None => (
                Decimal::ZERO,
                Decimal::ZERO,
                Decimal::ZERO,
                Decimal::ZERO,
                Decimal::ZERO,
                Decimal::ZERO,
            ),
        }
    }

    pub fn rsi(&self) -> (Decimal, Decimal, Decimal, Decimal, Decimal, Decimal) {
        match self.metrics.rsi.get() {
            Some(v) => v,
            None => (
                Decimal::ZERO,
                Decimal::ZERO,
                Decimal::ZERO,
                Decimal::ZERO,
                Decimal::ZERO,
                Decimal::ZERO,
            ),
        }
    }

    pub fn price(&self) -> (Decimal, Decimal, Decimal, Decimal, Decimal, Decimal) {
        match self.metrics.price.get() {
            Some(v) => v,
            None => (
                Decimal::ZERO,
                Decimal::ZERO,
                Decimal::ZERO,
                Decimal::ZERO,
                Decimal::ZERO,
                Decimal::ZERO,
            ),
        }
    }

    pub fn stochastic(&self) -> (Decimal, Decimal, Decimal, Decimal, Decimal, Decimal) {
        match self.metrics.stochastic.get() {
            Some(v) => v,
            None => (
                Decimal::ZERO,
                Decimal::ZERO,
                Decimal::ZERO,
                Decimal::ZERO,
                Decimal::ZERO,
                Decimal::ZERO,
            ),
        }
    }

    pub fn candle_pattern(
        &self,
    ) -> (
        CandlePattern,
        CandlePattern,
        CandlePattern,
        CandlePattern,
        CandlePattern,
        CandlePattern,
    ) {
        // Current and previous patterns for 5m candles
        let candle_pattern_5m = Candle::detect_pattern(&self.candles_5m);
        let prev_candle_pattern_5m = if self.candles_5m.len() > 1 {
            Candle::detect_pattern(&self.candles_5m[..self.candles_5m.len() - 1])
        } else {
            CandlePattern::None
        };

        // Current and previous patterns for 1h candles
        let candle_pattern_1h = Candle::detect_pattern(&self.candles_1h);
        let prev_candle_pattern_1h = if self.candles_1h.len() > 1 {
            Candle::detect_pattern(&self.candles_1h[..self.candles_1h.len() - 1])
        } else {
            CandlePattern::None
        };

        // Current and previous patterns for 4h candles
        let candle_pattern_4h = Candle::detect_pattern(&self.candles_4h);
        let prev_candle_pattern_4h = if self.candles_4h.len() > 1 {
            Candle::detect_pattern(&self.candles_4h[..self.candles_4h.len() - 1])
        } else {
            CandlePattern::None
        };

        (
            candle_pattern_5m,
            candle_pattern_1h,
            candle_pattern_4h,
            prev_candle_pattern_5m,
            prev_candle_pattern_1h,
            prev_candle_pattern_4h,
        )
    }

    pub fn get_min_max_price(&self, period: usize) -> Option<(Decimal, Decimal)> {
        let prices_len = self.prices.len();
        if prices_len <= period {
            return None;
        }

        let range_start = prices_len.saturating_sub(period);
        if range_start >= prices_len {
            return None;
        }

        let first_price = self.prices[range_start].price;
        let mut min = first_price;
        let mut max = first_price;

        for i in range_start..prices_len {
            let price = self.prices[i].price;
            min = Decimal::min(min, price);
            max = Decimal::max(max, price);
        }

        Some((min, max))
    }

    fn update_candle(
        &mut self,
        price: Decimal,
        timestamp: i64,
        interval: i64,
        candles: &mut Vec<Candle>,
        current_candle: &mut Option<Candle>,
    ) {
        let rounded_timestamp = (timestamp / interval) * interval;

        let one_day_ago = timestamp - 86400;
        candles.retain(|candle| candle.timestamp >= one_day_ago);

        if let Some(ref mut candle) = current_candle {
            if candle.timestamp == rounded_timestamp {
                candle.update(price);
            } else {
                candles.push(candle.clone());
                *current_candle = Some(Candle::new(price, price, price, price, rounded_timestamp));
            }
        } else {
            *current_candle = Some(Candle::new(price, price, price, price, rounded_timestamp));
        }
    }

    fn extract_signal_features(
        &self,
        include_one_hot: bool,
        take_profit_ratio: Decimal,
        atr_spread: Decimal,
        atr_term: &SampleTerm,
        tick_spread: i64,
        risk_reward: Decimal,
        bias_ticks: i64,
    ) -> Vec<f64> {
        // 1–8: price short/long/macd
        let price = self.price();
        let mut feat = vec![
            price.3.round_dp(4).to_f64().unwrap(),
            price.4.round_dp(4).to_f64().unwrap(),
            price.5.round_dp(4).to_f64().unwrap(),
        ];
        // 4–8: ATR
        let atr = self.atr();
        feat.push(atr.1.round_dp(4).to_f64().unwrap());
        feat.push(atr.2.round_dp(4).to_f64().unwrap());
        feat.push(atr.3.round_dp(4).to_f64().unwrap());
        feat.push(atr.4.round_dp(4).to_f64().unwrap());
        feat.push(atr.5.round_dp(4).to_f64().unwrap());
        // 9–13: RSI
        let rsi = self.rsi();
        feat.push(rsi.1.round_dp(4).to_f64().unwrap());
        feat.push(rsi.2.round_dp(4).to_f64().unwrap());
        feat.push(rsi.3.round_dp(4).to_f64().unwrap());
        feat.push(rsi.4.round_dp(4).to_f64().unwrap());
        feat.push(rsi.5.round_dp(4).to_f64().unwrap());
        // 14–18: last_volume, last_num_trades, last_funding_rate, last_open_interest, oracle_price_diff
        feat.push(
            self.last_volume()
                .unwrap_or_default()
                .round_dp(4)
                .to_f64()
                .unwrap(),
        );
        feat.push(self.last_num_trades().unwrap_or_default().to_f64().unwrap());
        // 16: volume change ratio (critical for Inago detection) - using available DebugLog input_16
        feat.push(self.calculate_volume_change_ratio().to_f64().unwrap());
        feat.push(
            self.last_funding_rate()
                .unwrap_or_default()
                .to_f64()
                .unwrap(),
        );
        feat.push(
            self.last_open_interest()
                .unwrap_or_default()
                .to_f64()
                .unwrap(),
        );
        let oracle_diff = (self.last_oracle_price().unwrap_or_default().round_dp(4)
            - price.0.round_dp(4))
        .to_f64()
        .unwrap();
        feat.push(oracle_diff);
        // 19–20: tick_spread, atr_spread
        feat.push(tick_spread.to_f64().unwrap());
        feat.push(atr_spread.to_f64().unwrap());
        // 21–23
        feat.push(risk_reward.to_f64().unwrap());
        feat.push(if take_profit_ratio.is_zero() {
            0.0
        } else {
            1.0
        });
        feat.push(atr_term.to_numeric().round_dp(4).to_f64().unwrap());
        // 24: bias_ticks
        feat.push(bias_ticks.to_f64().unwrap());
        // 25: timestamp(sec)
        let num_seconds = num_seconds_from_midnight().to_f64().unwrap();
        feat.push(num_seconds);
        // 26–29: PID controller features (if available)
        if let Some(ref controller) = self.pid_controller {
            let (proportional, integral, derivative) = controller.get_components();
            let (error_mean, _error_variance, _error_std) = controller.get_error_metrics();

            feat.push(proportional.round_dp(4).to_f64().unwrap_or(0.0));
            feat.push(integral.round_dp(4).to_f64().unwrap_or(0.0));
            feat.push(derivative.round_dp(4).to_f64().unwrap_or(0.0));
            feat.push(error_mean.round_dp(4).to_f64().unwrap_or(0.0));
        } else {
            // Zero placeholders when PID controller is not available
            feat.extend(std::iter::repeat(0.0).take(4));
        }

        // 30: volume_change_ratio (input_40 in DebugLog) - critical for Inago detection
        feat.push(self.calculate_volume_change_ratio().to_f64().unwrap());

        if include_one_hot {
            let (p0, p1, p2, p3, p4, p5) = self.candle_pattern();
            for pat in &[p0, p1, p2, p3, p4, p5] {
                feat.extend(pat.to_one_hot().into_iter().map(|x| x.to_f64().unwrap()));
            }
        }

        feat
    }

    fn is_metrics_none(&self) -> bool {
        self.metrics.adx.get().is_none()
            || self.metrics.atr.get().is_none()
            || self.metrics.price.get().is_none()
            || self.metrics.rsi.get().is_none()
            || self.metrics.stochastic.get().is_none()
    }

    fn update_rsi(&mut self) {
        let periods = [
            self.config.trading_period,
            self.config.short_period,
            self.config.long_period,
        ];
        let values: Vec<_> = periods
            .iter()
            .filter_map(|&period| self.calculate_rsi(period))
            .collect();

        if values.len() == periods.len() {
            self.metrics.rsi.update(values[0], values[1], values[2]);
        }
    }

    fn update_adx(&mut self) {
        let periods = [
            self.config.trading_period,
            self.config.short_period,
            self.config.long_period,
        ];
        let values: Vec<_> = periods
            .iter()
            .filter_map(|&period| self.calculate_adx(period))
            .collect();

        if values.len() == periods.len() {
            self.metrics.adx.update(values[0], values[1], values[2]);
        }
    }

    fn update_stochastic(&mut self) {
        let periods = [
            self.config.trading_period,
            self.config.short_period,
            self.config.long_period,
        ];
        let values: Vec<_> = periods
            .iter()
            .filter_map(|&period| self.calculate_stochastic(period))
            .collect();

        if values.len() == periods.len() {
            self.metrics
                .stochastic
                .update(values[0], values[1], values[2]);
        }
    }

    fn update_ema(&mut self, price: Decimal) {
        self.metrics.price.update(price, price, price);
    }

    fn calculate_rsi(&self, period: usize) -> Option<Decimal> {
        let window_size = 10;

        if self.prices.len() < period {
            return None;
        }

        let mut gains = Decimal::ZERO;
        let mut losses = Decimal::ZERO;

        let recent_prices = self
            .prices
            .iter()
            .skip(self.prices.len().saturating_sub(period))
            .cloned()
            .collect::<Vec<_>>();

        for window in recent_prices.windows(window_size) {
            if let (Some(first), Some(last)) = (window.first(), window.last()) {
                let change = last.price - first.price;
                if change.is_sign_positive() {
                    gains += change;
                } else {
                    losses += change.abs();
                }
            }
        }

        let rs = if losses.is_zero() {
            Decimal::new(100, 0)
        } else {
            gains / losses
        };

        let one = Decimal::ONE;
        Some(one - (one / (one + rs)))
    }

    fn calculate_adx(&mut self, period: usize) -> Option<Decimal> {
        if self.prices.len() < period {
            return None;
        }

        let (atr, high, low) = match self.calculate_atr(period) {
            Some((atr, high, low)) => (atr, high, low),
            None => return None,
        };

        if atr.is_zero() {
            return None;
        }

        let mut plus_dm = Decimal::ZERO;
        let mut minus_dm = Decimal::ZERO;

        for price_point in &self
            .prices
            .iter()
            .skip(self.prices.len() - period)
            .collect::<Vec<_>>()
        {
            let up_move = high - price_point.price;
            let down_move = price_point.price - low;
            if up_move > down_move && up_move.is_sign_positive() {
                plus_dm += up_move;
            }
            if down_move > up_move && down_move.is_sign_positive() {
                minus_dm += down_move;
            }
        }

        let plus_di = plus_dm / atr;
        let minus_di = minus_dm / atr;

        let denominator = plus_di + minus_di;
        if denominator.is_zero() {
            return None;
        }

        Some((plus_di - minus_di).abs() / denominator)
    }

    fn calculate_stochastic(&self, period: usize) -> Option<Decimal> {
        if self.prices.len() < period {
            log::debug!(
                "Stochastic: insufficient data {} < {}",
                self.prices.len(),
                period
            );
            return None;
        }

        let (high, low) = self.extract_high_low(period);
        let current_close = match self.prices.iter().last() {
            Some(v) => v.price,
            None => {
                log::debug!("Stochastic: no current close price");
                return None;
            }
        };

        log::trace!(
            "Stochastic: high={}, low={}, close={}",
            high,
            low,
            current_close
        );

        if high == low {
            log::debug!("Stochastic: high equals low, returning None");
            None
        } else {
            let result = (current_close - low) / (high - low);
            log::trace!("Stochastic result: {}", result);
            Some(result)
        }
    }

    fn calculate_atr(&self, period: usize) -> Option<(Decimal, Decimal, Decimal)> {
        if self.prices.len() <= period {
            log::debug!("ATR: insufficient data {} <= {}", self.prices.len(), period);
            return None;
        }

        let (current_high, current_low) = self.extract_high_low(period);
        let previous_close = self.prices[self.prices.len() - period - 1].price;

        let tr = [
            current_high - current_low,
            (current_high - previous_close).abs(),
            (current_low - previous_close).abs(),
        ]
        .iter()
        .fold(Decimal::ZERO, |acc, &val| Decimal::max(acc, val));

        log::trace!(
            "ATR: period={}, high={}, low={}, prev_close={}, tr={}",
            period,
            current_high,
            current_low,
            previous_close,
            tr
        );

        Some((tr, current_high, current_low))
    }

    fn update_atr(&mut self) {
        let periods = [
            self.config.trading_period,
            self.config.short_period,
            self.config.long_period,
        ];
        let values: Vec<_> = periods
            .iter()
            .filter_map(|&period| self.calculate_atr(period))
            .map(|x| x.0)
            .collect();

        if values.len() == periods.len() {
            self.metrics.atr.update(values[0], values[1], values[2]);
        }
    }

    fn extract_high_low(&self, period: usize) -> (Decimal, Decimal) {
        self.prices
            .iter()
            .skip(self.prices.len() - period)
            .fold((Decimal::MIN, Decimal::MAX), |(high, low), pp| {
                (high.max(pp.price), low.min(pp.price))
            })
    }

    pub fn detect_flash_crash(
        &self,
        atr_multiplier: Decimal,
        min_percentage: Option<Decimal>,
    ) -> Option<Decimal> {
        let current_price = self.last_price();
        let atr = self.atr().0;

        if atr.is_zero() || current_price.is_zero() {
            return None;
        }

        if let Some((min_price, max_price)) = self.get_min_max_price(self.config.flash_crash_period)
        {
            if min_price.is_zero()
                || max_price.is_zero()
                || min_price.is_sign_negative()
                || max_price.is_sign_negative()
            {
                log::warn!(
                    "Invalid min/max price detected: min_price={} max_price={}",
                    min_price,
                    max_price
                );
                return None;
            }

            let atr_threshold = atr * atr_multiplier;
            let percentage_threshold = current_price * min_percentage.unwrap_or_default();
            let threshold = Decimal::max(atr_threshold, percentage_threshold);

            for (label, ref_price) in [("dropped", min_price), ("surged", max_price)] {
                let price_change = (current_price - ref_price).round_dp(5);
                log::debug!(
                    "atr_threshold: {:.2}, threshold: {:.2}, price_change: {:.2}",
                    atr_threshold,
                    threshold,
                    price_change
                );

                if price_change.abs() > threshold {
                    log::warn!(
                        "Flash crash detected: Price {} rapidly! Change: {}({:.2} - {:.2}) (Threshold: {:.2})",
                        label,
                        price_change,
                        current_price,
                        ref_price,
                        threshold
                    );
                    return Some(price_change);
                }
            }
        }
        None
    }

    /// Detects short-term trend (Up, Down, or None) based on ensemble of RSI, ATR, and price change.
    pub fn detect_trade_term_trend(
        &self,
        rsi_threshold: Decimal,
        price_lookback: usize,
    ) -> Option<TrendType> {
        if self.prices.len() < price_lookback + 1 {
            log::debug!("Not enough data to detect short-term trend");
            return None;
        }

        let current_price = self.last_price();
        let past_price = self.prices[self.prices.len() - 1 - price_lookback].price;
        let price_change = current_price - past_price;
        let price_return = price_change / past_price;

        let rsi = self.rsi().0;
        let atr = self.atr().0;
        let adx = self.adx().0;

        // Voting system with thresholds
        let mut up_votes = 0;
        let mut down_votes = 0;

        // RSI signal
        if rsi > rsi_threshold {
            up_votes += 1;
        } else if rsi < Decimal::ONE - rsi_threshold {
            down_votes += 1;
        }

        // Price return signal
        if price_return > Decimal::new(1, 2) {
            up_votes += 1;
        } else if price_return < Decimal::new(-1, 2) {
            down_votes += 1;
        }

        // ATR signal: volatility breakout
        if atr > Decimal::ZERO && price_change.abs() > atr {
            if price_change.is_sign_positive() {
                up_votes += 1;
            } else {
                down_votes += 1;
            }
        }

        // ADX signal: trend strength confirmation
        if adx > Decimal::new(2, 1) {
            // ADX > 20
            if price_change.is_sign_positive() {
                up_votes += 1;
            } else {
                down_votes += 1;
            }
        }

        // Final decision based on majority
        match (up_votes, down_votes) {
            (u, d) if u > d && u >= 2 => Some(TrendType::Up),
            (u, d) if d > u && d >= 2 => Some(TrendType::Down),
            _ => None,
        }
    }

    fn detect_inago_opportunity(&self, thresholds: &InagoThresholds) -> Option<TrendType> {
        self.detect_enhanced_inago_trend(thresholds)
    }

    #[allow(dead_code)]
    fn detect_inago_reversion_opportunity(&self) -> Option<TrendType> {
        self.detect_dynamic_trend().map(|dir| match dir {
            TrendType::Up => TrendType::Down,
            TrendType::Down => TrendType::Up,
            x => x,
        })
    }

    fn compute_signal_score(&self) -> f64 {
        let (_ma_s, _ma_ll, macd, adx_val, rsi_val) = {
            let p = self.price();
            let a = self.adx();
            let r = self.rsi();
            (p.1, p.2, p.3, a.1, r.1)
        };

        let norm_adx = adx_val.to_f64().unwrap_or(0.0).clamp(0.0, 1.0);
        let atr = self.atr().0.to_f64().unwrap_or(1.0);
        let norm_macd = (macd.to_f64().unwrap_or(0.0) / atr).clamp(-1.0, 1.0);
        let norm_rsi = ((rsi_val.to_f64().unwrap_or(50.0) - 50.0) / 50.0).clamp(-1.0, 1.0);

        norm_adx + norm_macd + norm_rsi
    }

    fn detect_dynamic_trend(&self) -> Option<TrendType> {
        let n = self.score_history.len();
        if n < 2 {
            return None;
        }
        let sum: f64 = self.score_history.iter().sum();
        let mean = sum / n as f64;
        let var = self
            .score_history
            .iter()
            .map(|&s| (s - mean).powi(2))
            .sum::<f64>()
            / n as f64;
        let std = var.sqrt();
        let latest = *self.score_history.back().unwrap();

        let trend = if latest > mean + std {
            Some(TrendType::Up)
        } else if latest < mean - std {
            Some(TrendType::Down)
        } else {
            None
        };

        log::debug!("trend: {:?}, {:.2}/{:.2}/{:.2}", trend, latest, mean, std);

        trend
    }

    fn is_low_volatility(&self) -> bool {
        let atr = self.atr().0;
        let adx = self.adx().0;
        let price = self.last_price();

        let atr = if price.is_zero() {
            Decimal::ZERO
        } else {
            atr / price
        };

        let atr_thresh = env::var("MM_ATR_THRESHOLD").unwrap_or_else(|_| "0.10".to_string());
        let adx_thresh = env::var("MM_ADX_THRESHOLD").unwrap_or_else(|_| "0.15".to_string());
        let atr_thresh = Decimal::from_str(&atr_thresh).unwrap_or(Decimal::ZERO);
        let adx_thresh = Decimal::from_str(&adx_thresh).unwrap_or(Decimal::ZERO);

        if atr > atr_thresh {
            log::info!("Volatility is high → ATR: {:.3} > {:.3}", atr, atr_thresh,);
        }

        if adx > adx_thresh {
            log::info!("Volatility is high → ADX: {:.3} > {:.3}", adx, adx_thresh);
        }

        atr <= atr_thresh && adx <= adx_thresh
    }

    fn detect_enhanced_inago_trend(&self, thresholds: &InagoThresholds) -> Option<TrendType> {
        log::debug!("=== Inago Detection Start ===");

        if self.prices.len() < 10 {
            log::debug!("Insufficient price data: {} < 10", self.prices.len());
            return None;
        }

        let current_price = self.last_price();
        let _rsi = self.rsi();
        let _atr = self.atr();
        let adx = self.adx();

        log::debug!("Current price: {}, ADX: {:?}", current_price, adx);

        // Detect momentum using RSI
        let rsi_signal = self.detect_rsi_momentum(thresholds);
        log::debug!("RSI signal: {:?}", rsi_signal);

        // Detect volatility surge using ATR
        let volatility_surge = self.detect_volatility_surge(thresholds);
        log::debug!("Volatility surge: {}", volatility_surge);

        // Multi-timeframe confirmation
        let multi_timeframe_signal = self.detect_multi_timeframe_inago();
        log::debug!("Multi-timeframe signal: {:?}", multi_timeframe_signal);

        // Volume surge confirmation
        let volume_signal = self.detect_volume_surge(thresholds);
        log::debug!("Volume signal: {}", volume_signal);

        // Trend strength confirmation using ADX
        let trend_strength = adx.0 > thresholds.adx_strength;
        log::debug!(
            "Trend strength check: ADX {} > threshold {} = {}",
            adx.0,
            thresholds.adx_strength,
            trend_strength
        );

        // Scoring system
        let mut inago_score = 0i32;
        let mut direction_up = 0i32;
        let mut direction_down = 0i32;

        // RSI signal
        match rsi_signal {
            Some(TrendType::Up) => {
                inago_score += 2;
                direction_up += 2;
            }
            Some(TrendType::Down) => {
                inago_score += 2;
                direction_down += 2;
            }
            _ => {}
        }

        // Volatility surge
        if volatility_surge {
            inago_score += 1;
            // Check price movement direction
            if let Some(prev_price) = self.prices.get(self.prices.len().saturating_sub(5)) {
                if current_price > prev_price.price {
                    direction_up += 1;
                } else {
                    direction_down += 1;
                }
            }
        }

        // Multi-timeframe confirmation
        match multi_timeframe_signal {
            Some(TrendType::Up) => {
                inago_score += 1;
                direction_up += 1;
            }
            Some(TrendType::Down) => {
                inago_score += 1;
                direction_down += 1;
            }
            _ => {}
        }

        // Volume confirmation
        if volume_signal {
            inago_score += 1;
            // Check price movement direction
            if let Some(prev_price) = self.prices.get(self.prices.len().saturating_sub(3)) {
                if current_price > prev_price.price {
                    direction_up += 1;
                } else {
                    direction_down += 1;
                }
            }
        }

        // Trend strength confirmation
        if trend_strength {
            inago_score += 1;
        }

        log::debug!(
            "Final scoring - Score: {}, Up: {}, Down: {}",
            inago_score,
            direction_up,
            direction_down
        );
        log::debug!(
            "Thresholds - min_score: {}, min_direction_diff: {}",
            thresholds.min_score,
            thresholds.min_direction_diff
        );

        // Generate signal only when minimum score and direction thresholds are met
        if inago_score >= thresholds.min_score
            && (direction_up - direction_down).abs() >= thresholds.min_direction_diff
        {
            if direction_up > direction_down {
                log::info!(
                    "Enhanced Inago UP detected - Score: {}, Direction: {}/{}",
                    inago_score,
                    direction_up,
                    direction_down
                );
                log::debug!("=== Inago Detection End: UP SIGNAL ===");
                Some(TrendType::Up)
            } else {
                log::info!(
                    "Enhanced Inago DOWN detected - Score: {}, Direction: {}/{}",
                    inago_score,
                    direction_up,
                    direction_down
                );
                log::debug!("=== Inago Detection End: DOWN SIGNAL ===");
                Some(TrendType::Down)
            }
        } else {
            log::debug!(
                "Inago threshold not met - Score: {} < {}, Direction: {}/{}, abs_diff: {} < {}",
                inago_score,
                thresholds.min_score,
                direction_up,
                direction_down,
                (direction_up - direction_down).abs(),
                thresholds.min_direction_diff
            );
            log::debug!("=== Inago Detection End: NO SIGNAL ===");
            None
        }
    }

    fn detect_rsi_momentum(&self, thresholds: &InagoThresholds) -> Option<TrendType> {
        let rsi = self.rsi();
        let rsi_current = rsi.0;
        let rsi_short = rsi.1;

        // Detect rapid RSI changes
        let rsi_change = rsi_current - rsi_short;

        log::debug!(
            "RSI momentum check: current={}, short={}, change={}, threshold={}",
            rsi_current,
            rsi_short,
            rsi_change,
            thresholds.rsi_momentum
        );
        log::debug!(
            "RSI bounds: upper={}, lower={}",
            thresholds.rsi_upper_bound,
            thresholds.rsi_lower_bound
        );

        if rsi_change > thresholds.rsi_momentum && rsi_current < thresholds.rsi_upper_bound {
            log::debug!(
                "RSI momentum UP: change {} > {} and current {} < {}",
                rsi_change,
                thresholds.rsi_momentum,
                rsi_current,
                thresholds.rsi_upper_bound
            );
            Some(TrendType::Up)
        } else if rsi_change < -thresholds.rsi_momentum && rsi_current > thresholds.rsi_lower_bound
        {
            log::debug!(
                "RSI momentum DOWN: change {} < -{} and current {} > {}",
                rsi_change,
                thresholds.rsi_momentum,
                rsi_current,
                thresholds.rsi_lower_bound
            );
            Some(TrendType::Down)
        } else {
            log::debug!("No RSI momentum signal");
            None
        }
    }

    fn detect_volatility_surge(&self, thresholds: &InagoThresholds) -> bool {
        let atr = self.atr();
        let atr_current = atr.0;
        let _atr_short = atr.1;
        let atr_long = atr.2;

        log::debug!(
            "Volatility surge check: current={}, long={}",
            atr_current,
            atr_long
        );

        // Detect rapid ATR increase (short-term 50% larger than long-term)
        if atr_long.is_zero() {
            log::debug!("ATR long is zero, no volatility surge");
            return false;
        }

        let atr_ratio = atr_current / atr_long;
        let result = atr_ratio > thresholds.atr_ratio;
        log::debug!(
            "ATR ratio: {} > threshold {} = {}",
            atr_ratio,
            thresholds.atr_ratio,
            result
        );
        result
    }

    fn detect_multi_timeframe_inago(&self) -> Option<TrendType> {
        let (pattern_5m, pattern_1h, pattern_4h, _, _, _) = self.candle_pattern();

        log::debug!(
            "Multi-timeframe patterns: 5m={:?}, 1h={:?}, 4h={:?}",
            pattern_5m,
            pattern_1h,
            pattern_4h
        );

        // Check if breakout patterns in same direction are confirmed across multiple timeframes
        let bullish_patterns = [
            CandlePattern::BullishEngulfing,
            CandlePattern::Hammer,
            CandlePattern::InvertedHammer,
        ];

        let bearish_patterns = [
            CandlePattern::BearishEngulfing,
            CandlePattern::EveningStar,
            CandlePattern::DarkCloudCover,
        ];

        let mut bullish_count = 0;
        let mut bearish_count = 0;

        for pattern in [pattern_5m, pattern_1h, pattern_4h] {
            if bullish_patterns.contains(&pattern) {
                bullish_count += 1;
                log::debug!("Found bullish pattern: {:?}", pattern);
            } else if bearish_patterns.contains(&pattern) {
                bearish_count += 1;
                log::debug!("Found bearish pattern: {:?}", pattern);
            }
        }

        log::debug!(
            "Pattern counts: bullish={}, bearish={}",
            bullish_count,
            bearish_count
        );

        if bullish_count >= 2 {
            log::debug!("Multi-timeframe bullish signal (count >= 2)");
            Some(TrendType::Up)
        } else if bearish_count >= 2 {
            log::debug!("Multi-timeframe bearish signal (count >= 2)");
            Some(TrendType::Down)
        } else {
            log::debug!("No multi-timeframe signal");
            None
        }
    }

    fn detect_volume_surge(&self, thresholds: &InagoThresholds) -> bool {
        if self.prices.len() < 5 {
            log::debug!(
                "Insufficient price data for volume surge: {} < 5",
                self.prices.len()
            );
            return false;
        }

        let recent_volumes: Vec<Decimal> = self
            .prices
            .iter()
            .rev()
            .take(5)
            .filter_map(|p| p.volume)
            .collect();

        if recent_volumes.len() < 3 {
            log::debug!("Insufficient volume data: {} < 3", recent_volumes.len());
            return false;
        }

        let current_volume = recent_volumes[0];
        let avg_volume: Decimal =
            recent_volumes[1..].iter().sum::<Decimal>() / Decimal::from(recent_volumes.len() - 1);

        log::debug!(
            "Volume surge check: current={}, avg={}",
            current_volume,
            avg_volume
        );

        if avg_volume.is_zero() {
            log::debug!("Average volume is zero, no volume surge");
            return false;
        }

        // Current volume is above threshold ratio compared to average
        let volume_ratio = current_volume / avg_volume;
        let result = volume_ratio > thresholds.volume_ratio;
        log::debug!(
            "Volume ratio: {} > threshold {} = {}",
            volume_ratio,
            thresholds.volume_ratio,
            result
        );
        result
    }

    pub fn initialize_pid_controller(&mut self, pid_thresholds: &PidThresholds) {
        let setpoint = self.calculate_setpoint(&pid_thresholds.setpoint_mode);

        let controller = AdaptivePidController::new(
            pid_thresholds.kp,
            pid_thresholds.ki,
            pid_thresholds.kd,
            setpoint,
            self.config.long_period,
            self.config.short_period,
        );

        self.pid_controller = Some(controller);
        log::info!("PID controller initialized with setpoint: {}", setpoint);
    }

    fn calculate_setpoint(&self, mode: &SetpointMode) -> Decimal {
        match mode {
            SetpointMode::FixedPrice(price) => *price,
            SetpointMode::MovingAverage(period) => {
                if self.prices.len() < *period {
                    return self.last_price();
                }
                let sum: Decimal = self
                    .prices
                    .iter()
                    .rev()
                    .take(*period)
                    .map(|p| p.price)
                    .sum();
                sum / Decimal::from(*period)
            }
            SetpointMode::ATRBased(multiplier) => {
                let current_price = self.last_price();
                let atr = self.atr().1; // Short-term ATR
                current_price + (atr * *multiplier)
            }
            SetpointMode::RSIBaseline(target_rsi) => {
                // More sophisticated RSI-based setpoint calculation
                let current_price = self.last_price();
                let rsi_values = self.rsi();
                let rsi_current = rsi_values.0;
                let rsi_short = rsi_values.1;

                if rsi_current.is_zero() || rsi_short.is_zero() {
                    return current_price;
                }

                // Use RSI momentum to estimate direction and magnitude
                let rsi_momentum = rsi_current - rsi_short;
                let rsi_deviation = *target_rsi - rsi_current;

                // Conservative approach: Use ATR to scale price adjustments
                let atr_short = self.atr().1;
                let max_adjustment = atr_short * Decimal::new(2, 0); // Limit to 2x ATR

                // Calculate price adjustment based on RSI deviation
                // Scale by RSI range (0-100) and apply momentum consideration
                let base_adjustment =
                    (rsi_deviation / Decimal::from(100)) * current_price * Decimal::new(1, 1); // 10% max
                let momentum_factor =
                    if rsi_momentum.is_sign_positive() && rsi_deviation.is_sign_positive() {
                        Decimal::new(12, 1) // 1.2x when momentum and target align
                    } else if rsi_momentum.is_sign_negative() && rsi_deviation.is_sign_negative() {
                        Decimal::new(12, 1)
                    } else {
                        Decimal::new(8, 1) // 0.8x when momentum and target oppose
                    };

                let adjusted_adjustment = base_adjustment * momentum_factor;
                let clamped_adjustment = adjusted_adjustment.clamp(-max_adjustment, max_adjustment);

                log::debug!(
                    "RSI setpoint calc - Current RSI: {}, Target: {}, Momentum: {}, Adjustment: {}",
                    rsi_current,
                    target_rsi,
                    rsi_momentum,
                    clamped_adjustment
                );

                current_price + clamped_adjustment
            }
        }
    }

    fn evaluate_pid_signal(&mut self, pid_thresholds: &PidThresholds) -> Option<TrendType> {
        if self.pid_controller.is_none() {
            self.initialize_pid_controller(pid_thresholds);
        }

        let current_price = self.last_price();
        let timestamp = self.prices.back().map(|p| p.timestamp);

        if let Some(ref mut controller) = self.pid_controller {
            let output = controller.update_with_timestamp(current_price, timestamp);
            let (mean_error, _variance, std_dev) = controller.get_error_metrics();

            log::debug!(
                "PID evaluation - Current: {}, Output: {}, Error: {}, StdDev: {}",
                current_price,
                output,
                mean_error,
                std_dev
            );

            // Check if error is within acceptable range (convert to percentage)
            let error_percentage = if current_price.is_zero() {
                Decimal::ZERO
            } else {
                (mean_error.abs() / current_price) * Decimal::from(100)
            };

            if error_percentage > pid_thresholds.max_error_threshold {
                log::debug!(
                    "PID error too high: {:.2}% > {:.2}% (absolute error: {} vs price: {})",
                    error_percentage,
                    pid_thresholds.max_error_threshold,
                    mean_error.abs(),
                    current_price
                );
                if pid_thresholds.adaptive {
                    controller.reset();
                    log::info!("PID controller reset due to high error");
                }
                return None;
            }

            // Generate signal based on PID output
            if output.abs() > pid_thresholds.signal_threshold {
                let signal = if output.is_sign_positive() {
                    TrendType::Up
                } else {
                    TrendType::Down
                };

                log::info!(
                    "PID signal generated: {:?} (output: {}, threshold: {})",
                    signal,
                    output,
                    pid_thresholds.signal_threshold
                );

                Some(signal)
            } else {
                log::debug!(
                    "PID output below threshold: {} <= {}",
                    output.abs(),
                    pid_thresholds.signal_threshold
                );
                None
            }
        } else {
            log::error!("PID controller not initialized");
            None
        }
    }

    fn create_pid_action(
        &self,
        _strategy: &TradingStrategy,
        trend: TrendType,
        _min_tick: Option<Decimal>,
        _take_profit_ratio: Decimal,
        _bias_ticks: i64,
        _min_profit_ratio: Decimal,
    ) -> TradeAction {
        // Use PID-specific confidence calculation like RandomInago
        let confidence = if let Some(ref controller) = self.pid_controller {
            let (proportional, integral, derivative) = controller.get_components();
            let total_magnitude = proportional.abs() + integral.abs() + derivative.abs();

            // Normalize confidence between 0.1 and 1.0
            let raw_confidence = total_magnitude / (total_magnitude + Decimal::ONE);
            raw_confidence.clamp(Decimal::new(1, 1), Decimal::ONE) // 0.1 to 1.0
        } else {
            Decimal::new(5, 1) // Default 0.5 confidence
        };

        log::debug!(
            "PID action - Confidence: {}, Position Size: None (calculated from trading_amount)",
            confidence
        );

        match trend {
            TrendType::Up => TradeAction::BuyOpen(TradeDetail::new(
                None, None, // Use None like RandomInago to calculate from trading_amount
                confidence, None, // entry_timeout_sec
                None, // max_holding_sec
                None, // spread
            )),
            TrendType::Down => TradeAction::SellOpen(TradeDetail::new(
                None, None, // Use None like RandomInago to calculate from trading_amount
                confidence, None, None, None,
            )),
            _ => TradeAction::None,
        }
    }

    pub fn update_pid_setpoint(&mut self, new_mode: SetpointMode) {
        let new_setpoint = self.calculate_setpoint(&new_mode);
        if let Some(ref mut controller) = self.pid_controller {
            controller.set_setpoint(new_setpoint);
            log::info!("PID setpoint updated to: {}", new_setpoint);
        }
    }

    pub fn get_pid_status(&self) -> Option<(Decimal, Decimal, Decimal)> {
        self.pid_controller.as_ref().map(|c| c.get_gains())
    }

    pub fn get_pid_error_mean(&self) -> Decimal {
        self.pid_controller
            .as_ref()
            .map(|c| c.get_error_metrics().0) // error_mean is the first component
            .unwrap_or(Decimal::ZERO)
    }

    fn is_strong_trend_continuation(&self, trend_type: &TrendType) -> bool {
        if self.prices.len() < 5 {
            return false;
        }

        let rsi = self.rsi();
        let adx = self.adx();
        let price_change = {
            let recent = self.prices[self.prices.len() - 1].price;
            let older = self.prices[self.prices.len() - 5].price;
            recent - older
        };

        // Strong uptrend indicators
        let strong_uptrend = price_change.is_sign_positive()
            && adx.0 > Decimal::new(30, 0)  // Strong trend strength
            && rsi.0 > Decimal::new(60, 0)  // Bullish momentum
            && rsi.0 < Decimal::new(80, 0); // Not yet overbought

        // Strong downtrend indicators
        let strong_downtrend = price_change.is_sign_negative()
            && adx.0 > Decimal::new(30, 0)  // Strong trend strength
            && rsi.0 < Decimal::new(40, 0)  // Bearish momentum
            && rsi.0 > Decimal::new(20, 0); // Not yet oversold

        // Check if we're trying to trade against the strong trend
        let is_counter_trend = match trend_type {
            TrendType::Up => strong_downtrend, // Trying to buy in strong downtrend
            TrendType::Down => strong_uptrend, // Trying to sell in strong uptrend
            TrendType::Any => false,           // No specific direction
        };

        if is_counter_trend {
            log::debug!(
                "Strong trend continuation: price_change={}, ADX={}, RSI={}, trend_type={:?}",
                price_change,
                adx.0,
                rsi.0,
                trend_type
            );
        }

        is_counter_trend
    }

    fn calculate_dynamic_threshold(&self) -> f64 {
        let base_threshold = 0.2;

        if self.prices.len() < 5 {
            return base_threshold;
        }

        let atr = self.atr();
        let current_price = self.last_price();

        // Calculate volatility as percentage
        let volatility_pct = if current_price.is_zero() {
            0.0
        } else {
            (atr.0 / current_price * Decimal::from(100))
                .to_f64()
                .unwrap_or(0.0)
        };

        // In high volatility (>2%), require higher confidence (up to 0.6)
        // In low volatility (<0.5%), can use lower threshold (down to 0.35)
        let volatility_adjustment = if volatility_pct > 2.0 {
            0.2 * ((volatility_pct - 2.0) / 3.0).min(1.0) // Max +0.2
        } else if volatility_pct < 0.5 {
            -0.05 * ((0.5 - volatility_pct) / 0.5).min(1.0) // Max -0.05
        } else {
            0.0
        };

        let dynamic_threshold = base_threshold + volatility_adjustment;

        log::debug!(
            "Dynamic threshold: base={:.3}, volatility={:.2}%, adjustment={:.3}, final={:.3}",
            base_threshold,
            volatility_pct,
            volatility_adjustment,
            dynamic_threshold
        );

        dynamic_threshold.clamp(0.35, 0.6)
    }

    pub fn calculate_volume_change_ratio(&self) -> Decimal {
        if self.prices.len() < 5 {
            return Decimal::ONE; // Default to 1.0 (no change)
        }

        let recent_volumes: Vec<Decimal> = self
            .prices
            .iter()
            .rev()
            .take(5)
            .filter_map(|p| p.volume)
            .collect();

        if recent_volumes.len() < 3 {
            return Decimal::ONE;
        }

        let current_volume = recent_volumes[0];
        let avg_volume: Decimal = if recent_volumes.len() > 1 {
            recent_volumes[1..].iter().sum::<Decimal>() / Decimal::from(recent_volumes.len() - 1)
        } else {
            current_volume
        };

        if avg_volume.is_zero() {
            Decimal::ONE
        } else {
            // Cap at reasonable range to avoid outliers in training
            (current_volume / avg_volume).clamp(Decimal::new(1, 1), Decimal::new(10, 0))
            // 0.1 to 10.0
        }
    }
}
