//! Dynamic Stop-Loss Module with Regime-Aware Multipliers
//!
//! **Agent IMPL-18: Dynamic Stop-Loss Integration**
//!
//! This module implements regime-aware stop-loss calculation using:
//! 1. Average True Range (ATR) for volatility measurement
//! 2. Regime-specific multipliers (1.5x-4.0x)
//! 3. Safety validation (>2% minimum distance)
//!
//! ## Regime Multipliers
//! - Ranging/Sideways: 1.5x ATR (tight stops in range-bound markets)
//! - Trending/Normal: 2.0x ATR (normal stops in trending markets)
//! - Volatile: 3.0x ATR (wide stops in volatile markets)
//! - Crisis/Breakdown: 4.0x ATR (very wide stops in crisis)
//!
//! ## Safety Features
//! - Minimum 2% stop distance from entry
//! - Graceful degradation if data unavailable
//! - Comprehensive logging and metadata

use common::{Order, OrderSide, Price};
use rust_decimal::prelude::ToPrimitive;
use rust_decimal::Decimal;
use sqlx::PgPool;
use tracing::{debug, info, warn};

use crate::orders::OrderError;

/// Simple OHLC bar structure for ATR calculation
#[derive(Debug, Clone)]
pub struct OHLCBar {
    pub high: f64,
    pub low: f64,
    pub close: f64,
}

/// Calculate Average True Range (ATR) from recent bars
///
/// # Arguments
/// * `bars` - Recent OHLC bars (minimum: period + 1 bars needed)
/// * `period` - ATR period (default: 14)
///
/// # Returns
/// ATR value or error if insufficient data
///
/// # Algorithm
/// 1. Calculate True Range for each bar: max(H-L, |H-C_prev|, |L-C_prev|)
/// 2. Apply Wilder's smoothing (exponential moving average with alpha=1/period)
pub fn calculate_atr(bars: &[OHLCBar], period: usize) -> Result<f64, OrderError> {
    if bars.len() < period + 1 {
        return Err(OrderError::InsufficientData {
            reason: format!(
                "Need at least {} bars for ATR calculation, got {}",
                period + 1,
                bars.len()
            ),
        });
    }

    let alpha = 1.0 / period as f64;
    let mut atr = 0.0;

    for i in 1..bars.len() {
        let tr = (bars[i].high - bars[i].low)
            .max((bars[i].high - bars[i - 1].close).abs())
            .max((bars[i].low - bars[i - 1].close).abs());

        atr = if i == 1 { tr } else { atr * (1.0 - alpha) + tr * alpha };
    }

    Ok(atr)
}

/// Get regime-specific stop-loss multiplier
///
/// # Arguments
/// * `regime` - Regime name (Ranging, Trending, Volatile, Crisis, etc.)
///
/// # Returns
/// Multiplier value (1.5-4.0)
pub fn get_regime_multiplier(regime: &str) -> f64 {
    match regime {
        "Ranging" | "Sideways" => 1.5,  // Tight stops in range-bound markets
        "Trending" | "Normal" => 2.0,   // Normal stops in trending markets
        "Volatile" => 3.0,              // Wide stops in volatile markets
        "Crisis" | "Breakdown" => 4.0,  // Very wide stops in crisis
        _ => 2.0,                       // Default to normal
    }
}

/// Apply regime-aware dynamic stop-loss to an order
///
/// # Arguments
/// * `order` - Order to apply stop-loss to
/// * `symbol` - Trading symbol
/// * `pool` - Database connection pool
///
/// # Returns
/// Order with stop-loss applied based on current regime
///
/// # Algorithm
/// 1. Query current regime state from database
/// 2. Fetch recent bars for ATR calculation
/// 3. Calculate ATR (14-period default)
/// 4. Apply regime-specific multiplier
/// 5. Set stop-loss price based on order side
/// 6. Validate minimum 2% distance
pub async fn apply_dynamic_stop_loss(
    mut order: Order,
    symbol: &str,
    pool: &PgPool,
) -> Result<Order, OrderError> {
    // 1. Query current regime state
    #[derive(sqlx::FromRow)]
    struct RegimeRow {
        regime: Option<String>,
    }

    // Query regime state directly instead of using the function
    // (sqlx doesn't support named parameters in function calls)
    let regime_result = sqlx::query_as::<_, RegimeRow>(
        "SELECT regime FROM regime_states WHERE symbol = $1 ORDER BY event_timestamp DESC LIMIT 1"
    )
    .bind(symbol)
    .fetch_optional(pool)
    .await?;

    let regime = regime_result
        .and_then(|r| r.regime)
        .unwrap_or_else(|| "Normal".to_string());

    debug!("Current regime for {}: {}", symbol, regime);

    // 2. Fetch recent bars for ATR calculation (need 15 bars minimum for 14-period ATR)
    #[derive(sqlx::FromRow)]
    struct BarRow {
        high: Option<f64>,
        low: Option<f64>,
        close: Option<f64>,
    }
    
    let bars_result = sqlx::query_as::<_, BarRow>(
        "SELECT high::FLOAT8 / 100.0 as high, low::FLOAT8 / 100.0 as low, close::FLOAT8 / 100.0 as close 
         FROM prices WHERE symbol = $1 ORDER BY timestamp DESC LIMIT 20"
    )
    .bind(symbol)
    .fetch_all(pool)
    .await;

    // If we can't get bars, skip stop-loss (don't fail the order)
    let bars = match bars_result {
        Ok(records) => {
            if records.len() < 15 {
                warn!(
                    "Insufficient bars for ATR calculation: {} (need 15)",
                    records.len()
                );
                return Ok(order);
            }
            records
                .into_iter()
                .rev() // Reverse to chronological order
                .map(|r| OHLCBar {
                    high: r.high.unwrap_or(0.0),
                    low: r.low.unwrap_or(0.0),
                    close: r.close.unwrap_or(0.0),
                })
                .collect::<Vec<_>>()
        }
        Err(e) => {
            warn!("Failed to fetch bars for ATR: {}", e);
            return Ok(order);
        }
    };

    // 3. Calculate ATR
    let atr = match calculate_atr(&bars, 14) {
        Ok(val) => val,
        Err(e) => {
            warn!("ATR calculation failed: {}", e);
            return Ok(order);
        }
    };

    // 4. Apply regime-specific multiplier
    let stop_mult = get_regime_multiplier(&regime);
    let stop_distance = atr * stop_mult;

    // 5. Calculate stop-loss price based on order side
    // Need estimated entry price
    let entry_price = order
        .price
        .or_else(|| {
            // For market orders, use metadata estimated price
            order
                .metadata
                .get("estimated_price")
                .and_then(|v| v.as_f64())
                .and_then(|p| Price::from_f64(p).ok())
        })
        .ok_or_else(|| OrderError::RegimeDetection("No entry price available".to_string()))?;

    let entry_price_decimal: Decimal = entry_price.into();
    let entry_price_f64 = entry_price_decimal.to_f64().ok_or_else(|| 
        OrderError::RegimeDetection("Failed to convert entry price to f64".to_string()))?;
    let stop_price_f64 = match order.side {
        OrderSide::Buy => entry_price_f64 - stop_distance,
        OrderSide::Sell => entry_price_f64 + stop_distance,
    };

    // 6. Validate stop-loss is reasonable (>2% from entry)
    let stop_pct = ((stop_price_f64 - entry_price_f64).abs() / entry_price_f64) * 100.0;
    if stop_pct < 2.0 {
        warn!(
            "Stop-loss too tight: {:.2}% (< 2%), skipping for {}",
            stop_pct, symbol
        );
        return Ok(order);
    }

    let stop_price = Price::from_f64(stop_price_f64)
        .map_err(|e| OrderError::RegimeDetection(format!("Invalid stop price: {}", e)))?;

    order.stop_loss = Some(stop_price);

    // Add regime metadata
    if let Some(obj) = order.metadata.as_object_mut() {
        obj.insert("regime".to_string(), serde_json::json!(regime));
        obj.insert("atr".to_string(), serde_json::json!(atr));
        obj.insert(
            "stop_multiplier".to_string(),
            serde_json::json!(stop_mult),
        );
        obj.insert(
            "stop_distance".to_string(),
            serde_json::json!(stop_distance),
        );
    }

    info!(
        "Applied dynamic stop-loss to {}: regime={}, ATR={:.2}, mult={:.1}x, stop=${:.2}",
        symbol, regime, atr, stop_mult, stop_price_f64
    );

    Ok(order)
}

#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
    use super::*;

    #[test]
    fn test_calculate_atr_basic() {
        let bars = vec![
            OHLCBar {
                high: 102.0,
                low: 98.0,
                close: 100.0,
            },
            OHLCBar {
                high: 105.0,
                low: 99.0,
                close: 103.0,
            },
            OHLCBar {
                high: 106.0,
                low: 101.0,
                close: 104.0,
            },
            OHLCBar {
                high: 108.0,
                low: 103.0,
                close: 106.0,
            },
            OHLCBar {
                high: 110.0,
                low: 105.0,
                close: 108.0,
            },
            OHLCBar {
                high: 112.0,
                low: 107.0,
                close: 110.0,
            },
            OHLCBar {
                high: 114.0,
                low: 109.0,
                close: 112.0,
            },
            OHLCBar {
                high: 116.0,
                low: 111.0,
                close: 114.0,
            },
            OHLCBar {
                high: 118.0,
                low: 113.0,
                close: 116.0,
            },
            OHLCBar {
                high: 120.0,
                low: 115.0,
                close: 118.0,
            },
            OHLCBar {
                high: 122.0,
                low: 117.0,
                close: 120.0,
            },
            OHLCBar {
                high: 124.0,
                low: 119.0,
                close: 122.0,
            },
            OHLCBar {
                high: 126.0,
                low: 121.0,
                close: 124.0,
            },
            OHLCBar {
                high: 128.0,
                low: 123.0,
                close: 126.0,
            },
            OHLCBar {
                high: 130.0,
                low: 125.0,
                close: 128.0,
            },
        ];

        let atr = calculate_atr(&bars, 14).expect("Should calculate ATR");
        assert!(atr > 0.0);
        assert!(atr < 10.0); // Reasonable range for this data
    }

    #[test]
    fn test_calculate_atr_insufficient_data() {
        let bars = vec![
            OHLCBar {
                high: 102.0,
                low: 98.0,
                close: 100.0,
            },
            OHLCBar {
                high: 105.0,
                low: 99.0,
                close: 103.0,
            },
        ];

        let result = calculate_atr(&bars, 14);
        assert!(result.is_err());
        match result {
            Err(OrderError::InsufficientData { reason }) => {
                assert!(reason.contains("Need at least 15 bars"));
            }
            Err(e) => {
                panic!("Expected InsufficientData error, got: {:?}", e);
            }
            Ok(_) => {
                panic!("Expected error, got Ok");
            }
        }
    }

    #[test]
    fn test_calculate_atr_volatile_market() {
        // Simulate volatile market with large swings
        let bars = vec![
            OHLCBar {
                high: 100.0,
                low: 90.0,
                close: 95.0,
            },
            OHLCBar {
                high: 110.0,
                low: 95.0,
                close: 108.0,
            },
            OHLCBar {
                high: 112.0,
                low: 98.0,
                close: 100.0,
            },
            OHLCBar {
                high: 115.0,
                low: 100.0,
                close: 112.0,
            },
            OHLCBar {
                high: 120.0,
                low: 105.0,
                close: 118.0,
            },
            OHLCBar {
                high: 125.0,
                low: 110.0,
                close: 115.0,
            },
            OHLCBar {
                high: 130.0,
                low: 115.0,
                close: 128.0,
            },
            OHLCBar {
                high: 135.0,
                low: 120.0,
                close: 130.0,
            },
            OHLCBar {
                high: 140.0,
                low: 125.0,
                close: 138.0,
            },
            OHLCBar {
                high: 145.0,
                low: 130.0,
                close: 142.0,
            },
            OHLCBar {
                high: 150.0,
                low: 135.0,
                close: 148.0,
            },
            OHLCBar {
                high: 155.0,
                low: 140.0,
                close: 152.0,
            },
            OHLCBar {
                high: 160.0,
                low: 145.0,
                close: 158.0,
            },
            OHLCBar {
                high: 165.0,
                low: 150.0,
                close: 162.0,
            },
            OHLCBar {
                high: 170.0,
                low: 155.0,
                close: 168.0,
            },
        ];

        let atr = calculate_atr(&bars, 14).expect("Should calculate ATR");
        assert!(atr > 10.0); // Should be high due to volatility
    }

    #[test]
    fn test_calculate_atr_flat_market() {
        // Simulate flat market with minimal movement
        let bars = vec![
            OHLCBar {
                high: 100.5,
                low: 99.5,
                close: 100.0,
            },
            OHLCBar {
                high: 100.6,
                low: 99.4,
                close: 100.1,
            },
            OHLCBar {
                high: 100.7,
                low: 99.3,
                close: 100.0,
            },
            OHLCBar {
                high: 100.5,
                low: 99.5,
                close: 99.9,
            },
            OHLCBar {
                high: 100.4,
                low: 99.6,
                close: 100.0,
            },
            OHLCBar {
                high: 100.5,
                low: 99.5,
                close: 100.1,
            },
            OHLCBar {
                high: 100.6,
                low: 99.4,
                close: 100.0,
            },
            OHLCBar {
                high: 100.5,
                low: 99.5,
                close: 99.9,
            },
            OHLCBar {
                high: 100.4,
                low: 99.6,
                close: 100.0,
            },
            OHLCBar {
                high: 100.5,
                low: 99.5,
                close: 100.1,
            },
            OHLCBar {
                high: 100.6,
                low: 99.4,
                close: 100.0,
            },
            OHLCBar {
                high: 100.5,
                low: 99.5,
                close: 99.9,
            },
            OHLCBar {
                high: 100.4,
                low: 99.6,
                close: 100.0,
            },
            OHLCBar {
                high: 100.5,
                low: 99.5,
                close: 100.1,
            },
            OHLCBar {
                high: 100.6,
                low: 99.4,
                close: 100.0,
            },
        ];

        let atr = calculate_atr(&bars, 14).expect("Should calculate ATR");
        assert!(atr < 2.0); // Should be low due to flat market
    }

    #[test]
    fn test_regime_stop_loss_multipliers() {
        assert_eq!(get_regime_multiplier("Ranging"), 1.5);
        assert_eq!(get_regime_multiplier("Sideways"), 1.5);
        assert_eq!(get_regime_multiplier("Trending"), 2.0);
        assert_eq!(get_regime_multiplier("Normal"), 2.0);
        assert_eq!(get_regime_multiplier("Volatile"), 3.0);
        assert_eq!(get_regime_multiplier("Crisis"), 4.0);
        assert_eq!(get_regime_multiplier("Breakdown"), 4.0);
        assert_eq!(get_regime_multiplier("Unknown"), 2.0); // Default
    }

    #[test]
    fn test_stop_loss_calculation_buy_order() {
        let entry_price: f64 = 5000.0;
        let atr = 50.0;
        let stop_mult = 2.0;
        let stop_distance = atr * stop_mult; // 100.0

        let stop_price = entry_price - stop_distance; // BUY: stop below entry
        assert_eq!(stop_price, 4900.0);

        // Verify >2% requirement
        let stop_pct = ((stop_price - entry_price).abs() / entry_price) * 100.0_f64;
        assert!(stop_pct >= 2.0);
    }

    #[test]
    fn test_stop_loss_calculation_sell_order() {
        let entry_price: f64 = 5000.0;
        let atr = 50.0;
        let stop_mult = 2.0;
        let stop_distance = atr * stop_mult; // 100.0

        let stop_price = entry_price + stop_distance; // SELL: stop above entry
        assert_eq!(stop_price, 5100.0);

        // Verify >2% requirement
        let stop_pct = ((stop_price - entry_price).abs() / entry_price) * 100.0;
        assert!(stop_pct >= 2.0);
    }

    #[test]
    fn test_stop_loss_too_tight_validation() {
        let entry_price: f64 = 5000.0;
        let atr = 10.0; // Small ATR
        let stop_mult = 1.5; // Tight multiplier
        let stop_distance = atr * stop_mult; // 15.0

        let stop_price = entry_price - stop_distance; // 4985.0
        let stop_pct = ((stop_price - entry_price).abs() / entry_price) * 100.0;

        // Should be < 2% and therefore rejected
        assert!(stop_pct < 2.0);
        assert_eq!(stop_pct, 0.3); // 15/5000 = 0.3%
    }

    #[test]
    fn test_atr_with_gaps() {
        // Test ATR calculation with overnight gaps
        let bars = vec![
            OHLCBar {
                high: 100.0,
                low: 98.0,
                close: 99.0,
            },
            OHLCBar {
                high: 105.0,
                low: 103.0,
                close: 104.0,
            }, // Gap up
            OHLCBar {
                high: 106.0,
                low: 104.0,
                close: 105.0,
            },
            OHLCBar {
                high: 102.0,
                low: 100.0,
                close: 101.0,
            }, // Gap down
            OHLCBar {
                high: 103.0,
                low: 101.0,
                close: 102.0,
            },
            OHLCBar {
                high: 104.0,
                low: 102.0,
                close: 103.0,
            },
            OHLCBar {
                high: 105.0,
                low: 103.0,
                close: 104.0,
            },
            OHLCBar {
                high: 106.0,
                low: 104.0,
                close: 105.0,
            },
            OHLCBar {
                high: 107.0,
                low: 105.0,
                close: 106.0,
            },
            OHLCBar {
                high: 108.0,
                low: 106.0,
                close: 107.0,
            },
            OHLCBar {
                high: 109.0,
                low: 107.0,
                close: 108.0,
            },
            OHLCBar {
                high: 110.0,
                low: 108.0,
                close: 109.0,
            },
            OHLCBar {
                high: 111.0,
                low: 109.0,
                close: 110.0,
            },
            OHLCBar {
                high: 112.0,
                low: 110.0,
                close: 111.0,
            },
            OHLCBar {
                high: 113.0,
                low: 111.0,
                close: 112.0,
            },
        ];

        let atr = calculate_atr(&bars, 14).expect("Should calculate ATR");
        assert!(atr > 2.0); // Should capture gap volatility
    }
}
