//! VaR Tests for Zero Position Portfolios and Edge Cases
//! Target: +10% coverage for VaR calculations with extreme scenarios
//!
//! Focus Areas:
//! - Zero position portfolios
//! - Single position VaR calculations
//! - Extreme volatility scenarios
//! - Insufficient historical data
//! - Correlation matrix edge cases

#![allow(
    unused_crate_dependencies,
    clippy::assign_op_pattern,
    clippy::doc_markdown,
    clippy::indexing_slicing,
    clippy::redundant_clone,
    clippy::useless_vec
)]

use chrono::{Duration, Utc};
use common::types::{DecimalExt, Price, Quantity, Symbol};
use risk::var_calculator::var_engine::{BoundedVec, HistoricalPrice, PositionInfo};
use rust_decimal::Decimal;

// Helper macro for creating Decimal values
macro_rules! dec {
    ($val:expr) => {
        Decimal::try_from($val).expect("Failed to create Decimal")
    };
}

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

    #[tokio::test]
    async fn test_var_with_empty_portfolio() {
        // Zero positions should result in zero VaR
        let positions: Vec<PositionInfo> = vec![];

        // Calculate portfolio VaR (sum manually since Price doesn't implement Sum)
        let mut total_value = Price::ZERO;
        for position in &positions {
            total_value = total_value + position.market_value;
        }

        assert_eq!(total_value, Price::ZERO);
    }

    #[tokio::test]
    async fn test_var_with_zero_value_position() {
        let position = PositionInfo {
            symbol: Symbol::from("AAPL"),
            quantity: Quantity::new(0.0).unwrap(),
            market_value: Price::ZERO,
            average_cost: Price::new(150.0).unwrap(),
            unrealized_pnl: Price::ZERO,
            realized_pnl: Price::ZERO,
            currency: "USD".to_owned(),
            timestamp: Utc::now(),
        };

        assert_eq!(position.market_value, Price::ZERO);
        assert_eq!(position.quantity.to_f64(), 0.0);
    }

    #[tokio::test]
    async fn test_var_with_all_zero_positions() {
        let positions = vec![
            PositionInfo {
                symbol: Symbol::from("AAPL"),
                quantity: Quantity::new(0.0).unwrap(),
                market_value: Price::ZERO,
                average_cost: Price::new(150.0).unwrap(),
                unrealized_pnl: Price::ZERO,
                realized_pnl: Price::ZERO,
                currency: "USD".to_owned(),
                timestamp: Utc::now(),
            },
            PositionInfo {
                symbol: Symbol::from("MSFT"),
                quantity: Quantity::new(0.0).unwrap(),
                market_value: Price::ZERO,
                average_cost: Price::new(300.0).unwrap(),
                unrealized_pnl: Price::ZERO,
                realized_pnl: Price::ZERO,
                currency: "USD".to_owned(),
                timestamp: Utc::now(),
            },
        ];

        let mut total_value = Price::ZERO;
        for position in &positions {
            total_value = total_value + position.market_value;
        }

        assert_eq!(total_value, Price::ZERO);
    }

    #[tokio::test]
    async fn test_var_with_fractional_zero_position() {
        // Position with infinitesimally small value
        let position = PositionInfo {
            symbol: Symbol::from("BTC"),
            quantity: Quantity::new(0.00000001).unwrap(),
            market_value: Price::new(0.0005).unwrap(),
            average_cost: Price::new(50000.0).unwrap(),
            unrealized_pnl: Price::ZERO,
            realized_pnl: Price::ZERO,
            currency: "USD".to_owned(),
            timestamp: Utc::now(),
        };

        assert!(position.market_value > Price::ZERO);
        assert!(position.market_value < Price::new(0.01).unwrap());
    }
}

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

    #[tokio::test]
    async fn test_single_position_high_volatility() {
        let position = PositionInfo {
            symbol: Symbol::from("BTC-USD"),
            quantity: Quantity::new(1.0).unwrap(),
            market_value: Price::new(45000.0).unwrap(),
            average_cost: Price::new(40000.0).unwrap(),
            unrealized_pnl: Price::new(5000.0).unwrap(),
            realized_pnl: Price::ZERO,
            currency: "USD".to_owned(),
            timestamp: Utc::now(),
        };

        // High volatility asset should have significant position value
        assert!(position.market_value > Price::new(40000.0).unwrap());
    }

    #[tokio::test]
    async fn test_single_position_low_volatility() {
        let position = PositionInfo {
            symbol: Symbol::from("T-BILL"),
            quantity: Quantity::new(1000.0).unwrap(),
            market_value: Price::new(100000.0).unwrap(),
            average_cost: Price::new(100000.0).unwrap(),
            unrealized_pnl: Price::ZERO,
            realized_pnl: Price::ZERO,
            currency: "USD".to_owned(),
            timestamp: Utc::now(),
        };

        // Low volatility asset
        assert_eq!(position.unrealized_pnl, Price::ZERO);
    }

    #[tokio::test]
    async fn test_single_short_position() {
        let position = PositionInfo {
            symbol: Symbol::from("SPY"),
            quantity: Quantity::new(-100.0).unwrap(),
            market_value: Price::new(-45000.0).unwrap(),
            average_cost: Price::new(450.0).unwrap(),
            unrealized_pnl: Price::new(-1000.0).unwrap(),
            realized_pnl: Price::ZERO,
            currency: "USD".to_owned(),
            timestamp: Utc::now(),
        };

        // Short position has negative quantity and market value
        assert!(position.quantity.to_f64() < 0.0);
        assert!(position.market_value.to_f64() < 0.0);
    }
}

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

    #[tokio::test]
    async fn test_var_with_market_crash_returns() {
        // Simulate 2008-style market crash returns
        let crash_returns = vec![
            dec!(-0.10),
            dec!(-0.15),
            dec!(-0.08),
            dec!(-0.20),
            dec!(-0.12),
            dec!(-0.18),
            dec!(-0.09),
            dec!(-0.25),
            dec!(-0.07),
            dec!(-0.11),
        ];

        let mean_return: Decimal =
            crash_returns.iter().sum::<Decimal>() / Decimal::from(crash_returns.len());

        // Mean return should be significantly negative
        assert!(mean_return < dec!(-0.10));

        // Volatility should be extreme
        let variance: Decimal = crash_returns
            .iter()
            .map(|&r| (r - mean_return) * (r - mean_return))
            .sum::<Decimal>()
            / Decimal::from(crash_returns.len());

        let std_dev = variance.sqrt().unwrap_or(dec!(0.0));
        assert!(std_dev > dec!(0.05));
    }

    #[tokio::test]
    async fn test_var_with_flash_crash_scenario() {
        // Flash crash: sudden extreme drop followed by partial recovery
        let flash_crash_returns = vec![
            dec!(0.01),
            dec!(0.02),
            dec!(-0.40), // Flash crash
            dec!(0.15),
            dec!(0.10),
            dec!(0.05), // Partial recovery
            dec!(0.02),
            dec!(0.01),
            dec!(0.01),
        ];

        // Check for outlier detection
        let mut sorted = flash_crash_returns.clone();
        sorted.sort();

        let min_return = sorted.first().unwrap();
        let max_return = sorted.last().unwrap();

        // Extreme range indicates flash crash
        let range = *max_return - *min_return;
        assert!(range > dec!(0.50));
    }

    #[tokio::test]
    async fn test_var_with_black_swan_event() {
        // Black swan: unprecedented extreme loss
        let black_swan_returns = vec![
            dec!(0.01),
            dec!(0.015),
            dec!(0.02),
            dec!(0.01),
            dec!(-0.60), // Black swan event
            dec!(-0.10),
            dec!(-0.05),
            dec!(0.02),
        ];

        let min_return = black_swan_returns.iter().min().unwrap();

        // Extreme loss beyond normal market behavior
        assert!(*min_return < dec!(-0.50));
    }

    #[tokio::test]
    async fn test_var_with_zero_volatility() {
        // Perfectly stable returns (unrealistic but edge case)
        let stable_returns = vec![
            dec!(0.01),
            dec!(0.01),
            dec!(0.01),
            dec!(0.01),
            dec!(0.01),
            dec!(0.01),
            dec!(0.01),
            dec!(0.01),
        ];

        let mean = stable_returns.iter().sum::<Decimal>() / Decimal::from(stable_returns.len());

        let variance: Decimal = stable_returns
            .iter()
            .map(|&r| (r - mean) * (r - mean))
            .sum::<Decimal>()
            / Decimal::from(stable_returns.len());

        // Variance should be exactly zero
        assert_eq!(variance, dec!(0.0));
    }
}

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

    #[tokio::test]
    async fn test_var_with_one_day_history() {
        let mut price_history = BoundedVec::<HistoricalPrice>::new(1);

        price_history.push(HistoricalPrice {
            symbol: "AAPL".to_owned(),
            date: Utc::now(),
            open: Price::new(150.0).unwrap(),
            high: Price::new(152.0).unwrap(),
            low: Price::new(149.0).unwrap(),
            price: Price::new(151.0).unwrap(),
            volume: Quantity::new(1_000_000.0).unwrap(),
        });

        assert_eq!(price_history.len(), 1);

        // Cannot calculate meaningful VaR with 1 observation
        // This should be handled gracefully
    }

    #[tokio::test]
    async fn test_var_with_two_days_history() {
        let mut price_history = BoundedVec::<HistoricalPrice>::new(2);

        price_history.push(HistoricalPrice {
            symbol: "AAPL".to_owned(),
            date: Utc::now() - Duration::days(1),
            open: Price::new(150.0).unwrap(),
            high: Price::new(152.0).unwrap(),
            low: Price::new(149.0).unwrap(),
            price: Price::new(151.0).unwrap(),
            volume: Quantity::new(1_000_000.0).unwrap(),
        });

        price_history.push(HistoricalPrice {
            symbol: "AAPL".to_owned(),
            date: Utc::now(),
            open: Price::new(151.0).unwrap(),
            high: Price::new(153.0).unwrap(),
            low: Price::new(150.0).unwrap(),
            price: Price::new(152.0).unwrap(),
            volume: Quantity::new(1_100_000.0).unwrap(),
        });

        assert_eq!(price_history.len(), 2);

        // Can calculate one return, but insufficient for reliable VaR
    }

    #[tokio::test]
    async fn test_var_with_sparse_history() {
        // Only 5 days of history (minimum for some VaR methods)
        let mut price_history = BoundedVec::<HistoricalPrice>::new(5);

        for i in 0..5 {
            price_history.push(HistoricalPrice {
                symbol: "AAPL".to_owned(),
                date: Utc::now() - Duration::days(i),
                open: Price::new(150.0 + i as f64).unwrap(),
                high: Price::new(152.0 + i as f64).unwrap(),
                low: Price::new(149.0 + i as f64).unwrap(),
                price: Price::new(151.0 + i as f64).unwrap(),
                volume: Quantity::new(1_000_000.0).unwrap(),
            });
        }

        assert_eq!(price_history.len(), 5);

        // Minimum viable data, but results may be unreliable
    }

    #[tokio::test]
    async fn test_var_with_missing_recent_data() {
        // Historical data with gap in recent period
        let mut price_history = BoundedVec::<HistoricalPrice>::new(10);

        // Add old data (30-40 days ago)
        for i in 30..40 {
            price_history.push(HistoricalPrice {
                symbol: "AAPL".to_owned(),
                date: Utc::now() - Duration::days(i),
                open: Price::new(150.0).unwrap(),
                high: Price::new(152.0).unwrap(),
                low: Price::new(149.0).unwrap(),
                price: Price::new(151.0).unwrap(),
                volume: Quantity::new(1_000_000.0).unwrap(),
            });
        }

        assert_eq!(price_history.len(), 10);

        // Data is stale - may not reflect current market conditions
    }
}

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

    #[tokio::test]
    async fn test_perfect_positive_correlation() {
        // Two assets perfectly correlated (move together)
        let returns_a = vec![dec!(0.02), dec!(0.03), dec!(-0.01), dec!(0.015)];
        let returns_b = vec![dec!(0.02), dec!(0.03), dec!(-0.01), dec!(0.015)];

        // Calculate correlation coefficient
        assert_eq!(returns_a, returns_b);

        // Portfolio VaR should be sum of individual VaRs
    }

    #[tokio::test]
    async fn test_perfect_negative_correlation() {
        // Two assets perfectly inversely correlated (hedge)
        let returns_a = vec![dec!(0.02), dec!(0.03), dec!(-0.01), dec!(0.015)];
        let returns_b = vec![dec!(-0.02), dec!(-0.03), dec!(0.01), dec!(-0.015)];

        // Check inverse relationship
        for (a, b) in returns_a.iter().zip(returns_b.iter()) {
            assert_eq!(*a, -*b);
        }

        // Portfolio VaR should be close to zero (perfect hedge)
    }

    #[tokio::test]
    async fn test_zero_correlation() {
        // Two completely uncorrelated assets
        let returns_a = vec![dec!(0.02), dec!(-0.01), dec!(0.03), dec!(-0.02)];
        let returns_b = vec![dec!(-0.01), dec!(0.02), dec!(-0.02), dec!(0.03)];

        // No clear pattern between returns
        assert_ne!(returns_a, returns_b);

        // Portfolio VaR benefits from diversification
    }

    #[tokio::test]
    async fn test_singular_correlation_matrix() {
        // Two assets where one is a linear combination of the other (c = 1.5 * a),
        // making the correlation matrix singular.
        let returns_a = vec![dec!(0.02), dec!(0.03), dec!(-0.01)];
        let returns_c = vec![dec!(0.03), dec!(0.045), dec!(-0.015)];

        // returns_c = 1.5 * returns_a
        for i in 0..returns_a.len() {
            assert_eq!(returns_c[i], returns_a[i] * dec!(1.5));
        }

        // Correlation matrix is singular - cannot be inverted
        // VaR calculation should handle this gracefully
    }

    #[tokio::test]
    async fn test_high_dimensional_correlation() {
        // Portfolio with many assets (10+)
        let num_assets = 15;
        let mut all_returns = Vec::new();

        for asset_idx in 0..num_assets {
            let returns = vec![
                dec!(0.01) + dec!(asset_idx as f64 * 0.001),
                dec!(-0.02) + dec!(asset_idx as f64 * 0.001),
                dec!(0.015) + dec!(asset_idx as f64 * 0.001),
            ];
            all_returns.push(returns);
        }

        assert_eq!(all_returns.len(), num_assets);

        // High-dimensional correlation matrix (15x15)
        // Computational complexity increases significantly
    }
}

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

    #[test]
    fn test_bounded_vec_capacity_enforcement() {
        let mut bounded = BoundedVec::<i32>::new(3);

        bounded.push(1);
        bounded.push(2);
        bounded.push(3);
        bounded.push(4); // Should be silently dropped

        assert_eq!(bounded.len(), 3);
    }

    #[test]
    fn test_bounded_vec_empty() {
        let bounded = BoundedVec::<f64>::new(10);
        assert!(bounded.is_empty());
        assert_eq!(bounded.len(), 0);
    }

    #[test]
    fn test_bounded_vec_iteration() {
        let mut bounded = BoundedVec::<i32>::new(5);
        bounded.push(10);
        bounded.push(20);
        bounded.push(30);

        let values: Vec<i32> = bounded.iter().copied().collect();
        assert_eq!(values, vec![10, 20, 30]);
    }

    #[test]
    fn test_bounded_vec_zero_capacity() {
        let mut bounded = BoundedVec::<String>::new(0);
        bounded.push("test".to_owned());

        assert_eq!(bounded.len(), 0);
        assert!(bounded.is_empty());
    }
}
