//! Custom assertions for domain-specific testing

/// Assert that a value is within a percentage tolerance
#[macro_export]
macro_rules! assert_approx_eq {
    ($left:expr, $right:expr, $tolerance:expr) => {
        let diff = ($left - $right).abs();
        let threshold = $right.abs() * $tolerance;
        assert!(
            diff <= threshold,
            "assertion failed: `{} ≈ {}` (tolerance: {}%)\n  left: {}\n right: {}\n  diff: {} > {}",
            stringify!($left),
            stringify!($right),
            $tolerance * 100.0,
            $left,
            $right,
            diff,
            threshold
        );
    };
}

/// Assert that a value is within an absolute tolerance
#[macro_export]
macro_rules! assert_within {
    ($value:expr, $target:expr, $tolerance:expr) => {
        let diff = ($value - $target).abs();
        assert!(
            diff <= $tolerance,
            "assertion failed: `{} within {} of {}`\n  value: {}\n target: {}\n   diff: {}",
            stringify!($value),
            $tolerance,
            stringify!($target),
            $value,
            $target,
            diff
        );
    };
}

/// Assert that VaR exceedances are within expected range
pub fn assert_var_exceedances(
    returns: &[f64],
    var: f64,
    confidence: f64,
    max_deviation: f64,
) {
    let exceedances = returns.iter().filter(|&&r| r.abs() > var).count();
    let expected_exceedances = (returns.len() as f64 * (1.0 - confidence)) as usize;
    let max_allowed = (expected_exceedances as f64 * (1.0 + max_deviation)) as usize;

    assert!(
        exceedances <= max_allowed,
        "VaR exceedances {} exceed maximum {} (expected: {}, confidence: {}, tolerance: {}%)",
        exceedances,
        max_allowed,
        expected_exceedances,
        confidence,
        max_deviation * 100.0
    );
}

/// Assert that OHLC relationships are valid
pub fn assert_ohlc_valid(open: f64, high: f64, low: f64, close: f64) {
    assert!(
        high >= open,
        "High {} must be >= Open {}",
        high,
        open
    );
    assert!(
        high >= close,
        "High {} must be >= Close {}",
        high,
        close
    );
    assert!(
        low <= open,
        "Low {} must be <= Open {}",
        low,
        open
    );
    assert!(
        low <= close,
        "Low {} must be <= Close {}",
        low,
        close
    );
}

/// Assert that a position's PnL is correct
pub fn assert_pnl(
    entry_price: f64,
    current_price: f64,
    quantity: f64,
    expected_pnl: f64,
    tolerance: f64,
) {
    let actual_pnl = (current_price - entry_price) * quantity;
    assert_within!(actual_pnl, expected_pnl, tolerance);
}

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

    #[test]
    fn test_assert_approx_eq() {
        assert_approx_eq!(100.0, 101.0, 0.02); // 2% tolerance
    }

    #[test]
    #[should_panic]
    fn test_assert_approx_eq_fails() {
        assert_approx_eq!(100.0, 105.0, 0.02); // Should fail
    }

    #[test]
    fn test_assert_within() {
        assert_within!(100.5, 100.0, 0.6);
    }

    #[test]
    fn test_assert_ohlc_valid() {
        assert_ohlc_valid(100.0, 102.0, 98.0, 101.0);
    }

    #[test]
    #[should_panic]
    fn test_assert_ohlc_invalid() {
        assert_ohlc_valid(100.0, 98.0, 99.0, 101.0); // High < Open
    }

    #[test]
    fn test_assert_pnl() {
        assert_pnl(100.0, 110.0, 50.0, 500.0, 0.01);
    }
}
