//! Transaction Cost Module for PPO
//!
//! Ported from DQN `action_space.rs` to provide consistent transaction cost
//! calculations across both DQN and PPO training.
//!
//! # Fee Structure (Wave 2.5 Calibration)
//! - **Market**: 0.15% (0.0015) - Immediate execution, high cost
//! - **`LimitMaker`**: 0.05% (0.0005) - Passive order, maker rebate
//! - **`IoC`**: 0.10% (0.0010) - Immediate-or-cancel, medium cost

pub use ml_core::action_space::OrderType;

/// Calculate total transaction cost for a trade
///
/// # Arguments
///
/// * `order_type` - The type of order (Market, `LimitMaker`, `IoC`)
/// * `trade_value` - Absolute value of the trade in dollars (price × quantity)
///
/// # Returns
///
/// Total cost in dollars based on order type fee percentage
///
/// # Example
///
/// ```rust
/// use ml::ppo::transaction_costs::{calculate_transaction_cost, OrderType};
///
/// let trade_value = 10_000.0; // $10,000 trade
/// let cost = calculate_transaction_cost(OrderType::Market, trade_value);
/// assert_eq!(cost, 15.0); // 0.15% × $10,000 = $15
/// ```
///
/// # Notes
///
/// - Uses absolute value to handle negative trade values defensively
/// - Returns 0.0 for zero trade value
/// - Matches DQN's `FactoredAction::calculate_transaction_cost()` behavior
pub fn calculate_transaction_cost(order_type: OrderType, trade_value: f64) -> f64 {
    // Use absolute value to handle negative trade values defensively
    let abs_trade_value = trade_value.abs();
    abs_trade_value * order_type.transaction_cost()
}

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

    #[test]
    fn test_order_type_fees() {
        // Wave 2.5 Calibration: Updated fee values
        assert_eq!(OrderType::Market.transaction_cost(), 0.0015);
        assert_eq!(OrderType::LimitMaker.transaction_cost(), 0.0005);
        assert_eq!(OrderType::IoC.transaction_cost(), 0.0010);
    }

    #[test]
    fn test_calculate_transaction_cost_basic() {
        let trade_value = 10_000.0;

        let market_cost = calculate_transaction_cost(OrderType::Market, trade_value);
        assert_eq!(market_cost, 15.0, "Market: 0.15% of $10K = $15");

        let maker_cost = calculate_transaction_cost(OrderType::LimitMaker, trade_value);
        assert_eq!(maker_cost, 5.0, "LimitMaker: 0.05% of $10K = $5");

        let ioc_cost = calculate_transaction_cost(OrderType::IoC, trade_value);
        assert_eq!(ioc_cost, 10.0, "IoC: 0.10% of $10K = $10");
    }

    #[test]
    fn test_calculate_transaction_cost_zero() {
        let zero_cost = calculate_transaction_cost(OrderType::Market, 0.0);
        assert_eq!(zero_cost, 0.0, "Zero trade value should have zero cost");
    }

    #[test]
    fn test_calculate_transaction_cost_negative() {
        // Defensive: should use absolute value
        let trade_value = -10_000.0;
        let cost = calculate_transaction_cost(OrderType::Market, trade_value);
        assert_eq!(cost, 15.0, "Should use absolute value of trade");
    }

    #[test]
    fn test_calculate_transaction_cost_large() {
        let large_trade = 1_000_000.0; // $1M

        let market_cost = calculate_transaction_cost(OrderType::Market, large_trade);
        assert_eq!(market_cost, 1_500.0, "Market: 0.15% of $1M = $1,500");

        let maker_cost = calculate_transaction_cost(OrderType::LimitMaker, large_trade);
        assert_eq!(maker_cost, 500.0, "LimitMaker: 0.05% of $1M = $500");
    }

    #[test]
    fn test_fee_ordering() {
        // Verify: Market > IoC > LimitMaker
        let trade = 10_000.0;
        let market = calculate_transaction_cost(OrderType::Market, trade);
        let ioc = calculate_transaction_cost(OrderType::IoC, trade);
        let maker = calculate_transaction_cost(OrderType::LimitMaker, trade);

        assert!(market > ioc, "Market should be most expensive");
        assert!(ioc > maker, "IoC should be middle cost");
    }
}
