//! Deterministic order routing based on market microstructure.
//!
//! The DQN selects an exposure level (0-4), and the OrderRouter
//! determines the optimal order type and urgency based on current
//! spread and volatility conditions. This separates the alpha model
//! (what exposure?) from execution (how to route?).

use crate::common::action::{ExposureLevel, FactoredAction, OrderType, Urgency};

/// Deterministic order routing based on market microstructure.
/// DQN selects exposure level (0-4), OrderRouter selects order type + urgency.
#[derive(Debug)]
pub struct OrderRouter;

impl OrderRouter {
    /// Route an exposure-level action to a full FactoredAction.
    ///
    /// Rules:
    /// - Spread < median -> LimitMaker (save costs)
    /// - Spread > 2x median -> Market (ensure fill in wide markets)
    /// - Otherwise -> IoC (aggressive limit)
    /// - Volatility high -> Aggressive, low -> Patient, else Normal
    pub fn route(
        exposure: ExposureLevel,
        spread: f32,
        median_spread: f32,
        volatility: f32,
        median_volatility: f32,
    ) -> FactoredAction {
        let order = if median_spread <= 0.0 {
            OrderType::Market
        } else if spread < median_spread {
            OrderType::LimitMaker
        } else if spread > 2.0 * median_spread {
            OrderType::Market
        } else {
            OrderType::IoC
        };

        let urgency = if median_volatility <= 0.0 {
            Urgency::Normal
        } else if volatility > 1.5 * median_volatility {
            Urgency::Aggressive
        } else if volatility < 0.5 * median_volatility {
            Urgency::Patient
        } else {
            Urgency::Normal
        };

        FactoredAction::new(exposure, order, urgency)
    }

    /// Simple fallback: Market order, Normal urgency.
    pub fn route_default(exposure: ExposureLevel) -> FactoredAction {
        FactoredAction::new(exposure, OrderType::Market, Urgency::Normal)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_tight_spread_uses_limit_maker() {
        let action = OrderRouter::route(
            ExposureLevel::Long100,
            0.5,  // spread
            1.0,  // median_spread (spread < median)
            0.01, // vol
            0.01, // median_vol
        );
        assert_eq!(action.order, OrderType::LimitMaker);
    }

    #[test]
    fn test_wide_spread_uses_market() {
        let action = OrderRouter::route(
            ExposureLevel::Short100,
            2.5,  // spread
            1.0,  // median_spread (spread > 2x median)
            0.01, // vol
            0.01, // median_vol
        );
        assert_eq!(action.order, OrderType::Market);
    }

    #[test]
    fn test_normal_spread_uses_ioc() {
        let action = OrderRouter::route(
            ExposureLevel::Flat,
            1.5,  // spread
            1.0,  // median_spread (between 1x and 2x)
            0.01, // vol
            0.01, // median_vol
        );
        assert_eq!(action.order, OrderType::IoC);
    }

    #[test]
    fn test_high_vol_aggressive() {
        let action = OrderRouter::route(
            ExposureLevel::Long50,
            1.0,  // spread
            1.0,  // median_spread
            0.05, // vol
            0.02, // median_vol (vol > 1.5x median)
        );
        assert_eq!(action.urgency, Urgency::Aggressive);
    }

    #[test]
    fn test_low_vol_patient() {
        let action = OrderRouter::route(
            ExposureLevel::Short50,
            1.0,  // spread
            1.0,  // median_spread
            0.005, // vol
            0.02,  // median_vol (vol < 0.5x median)
        );
        assert_eq!(action.urgency, Urgency::Patient);
    }

    #[test]
    fn test_normal_vol_normal_urgency() {
        let action = OrderRouter::route(
            ExposureLevel::Flat,
            1.0,  // spread
            1.0,  // median_spread
            0.02, // vol
            0.02, // median_vol
        );
        assert_eq!(action.urgency, Urgency::Normal);
    }

    #[test]
    fn test_route_default() {
        let action = OrderRouter::route_default(ExposureLevel::Long100);
        assert_eq!(action.exposure, ExposureLevel::Long100);
        assert_eq!(action.order, OrderType::Market);
        assert_eq!(action.urgency, Urgency::Normal);
    }

    #[test]
    fn test_zero_median_spread_defaults_to_market() {
        let action = OrderRouter::route(
            ExposureLevel::Long100,
            0.5,
            0.0, // zero median
            0.01,
            0.01,
        );
        assert_eq!(action.order, OrderType::Market);
    }

    #[test]
    fn test_zero_median_vol_defaults_to_normal() {
        let action = OrderRouter::route(
            ExposureLevel::Long100,
            1.0,
            1.0,
            0.01,
            0.0, // zero median
        );
        assert_eq!(action.urgency, Urgency::Normal);
    }

    #[test]
    fn test_all_exposure_levels() {
        for idx in 0..5 {
            let exposure = ExposureLevel::from_index(idx).unwrap();
            let action = OrderRouter::route_default(exposure);
            assert_eq!(action.exposure, exposure);
        }
    }
}
