//! Risk management configuration structures
//!
//! Provides configuration types for risk management components including
//! stress testing scenarios, asset class definitions, and market shock parameters.

use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// Configuration for stress testing scenarios
///
/// Defines how stress scenarios are configured and applied to portfolios.
///
/// Supports both individual instrument shocks and asset class-based shocks
/// for more flexible and maintainable stress testing.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct StressScenarioConfig {
    /// Unique identifier for this stress test scenario
    pub id: String,
    /// Human-readable name describing the scenario
    pub name: String,
    /// Description of the stress scenario and its historical context
    pub description: String,
    /// Individual instrument-specific shocks (symbol -> shock percentage)
    pub instrument_shocks: HashMap<String, f64>,
    /// Asset class-based shocks that apply to all instruments in a class
    pub asset_class_shocks: HashMap<AssetClass, f64>,
    /// Global volatility multiplier to apply across all instruments
    pub volatility_multiplier: f64,
    /// Asset class-specific volatility multipliers
    pub volatility_multipliers: HashMap<AssetClass, f64>,
    /// Correlation adjustments between asset classes
    pub correlation_adjustments: HashMap<String, f64>,
    /// Liquidity haircuts to apply per asset class
    pub liquidity_haircuts: HashMap<AssetClass, f64>,
    /// Whether this scenario is active and available for use
    pub is_active: bool,
}

/// Asset class definitions for grouping instruments
///
/// Provides a hierarchical way to apply stress shocks to groups
/// of related instruments rather than hardcoding individual symbols.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum AssetClass {
    /// Large-cap US equities (S&P 500 companies)
    LargeCapEquity,
    /// Small-cap US equities
    SmallCapEquity,
    /// Technology sector equities
    Technology,
    /// Financial sector equities
    Financials,
    /// Healthcare sector equities
    Healthcare,
    /// Energy sector equities
    Energy,
    /// Consumer discretionary equities
    ConsumerDiscretionary,
    /// Consumer staples equities
    ConsumerStaples,
    /// Industrial sector equities
    Industrials,
    /// Materials sector equities
    Materials,
    /// Real estate sector equities
    RealEstate,
    /// Utilities sector equities
    Utilities,
    /// Communication services sector equities
    CommunicationServices,
    /// US Treasury bonds
    USBonds,
    /// Corporate bonds
    CorporateBonds,
    /// High-yield bonds
    HighYieldBonds,
    /// International developed market equities
    InternationalEquity,
    /// Emerging market equities
    EmergingMarkets,
    /// Commodities
    Commodities,
    /// Foreign exchange
    ForeignExchange,
    /// Cryptocurrencies
    Crypto,
    /// Alternative investments
    Alternatives,
}

/// Asset class mapping configuration
///
/// Maps individual instrument symbols to their asset classes for
/// applying class-based stress shocks and risk calculations.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct AssetClassMapping {
    /// Symbol to asset class mappings
    pub mappings: HashMap<String, AssetClass>,
    /// Default asset class for unmapped symbols
    pub default_class: AssetClass,
}

/// Complete risk configuration containing all risk-related settings
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct RiskConfig {
    /// Available stress test scenarios
    pub stress_scenarios: Vec<StressScenarioConfig>,
    /// Asset class mappings for instruments
    pub asset_class_mapping: AssetClassMapping,
    /// Default volatility settings
    pub default_volatility_multiplier: f64,
    /// Maximum allowed portfolio loss percentage
    pub max_portfolio_loss_pct: f64,
    /// VaR confidence level (e.g., 0.95 for 95% confidence)
    pub var_confidence_level: f64,
    /// Time horizon for VaR calculations in days
    pub var_time_horizon_days: u32,
}

impl Default for RiskConfig {
    fn default() -> Self {
        Self {
            stress_scenarios: create_default_stress_scenarios(),
            asset_class_mapping: create_default_asset_class_mapping(),
            default_volatility_multiplier: 1.0,
            max_portfolio_loss_pct: 20.0,
            var_confidence_level: 0.95,
            var_time_horizon_days: 1,
        }
    }
}

impl StressScenarioConfig {
    /// Get the effective shock for a given instrument symbol
    ///
    /// Returns the instrument-specific shock if available, otherwise
    /// returns the asset class shock based on the symbol's asset class mapping.
    pub fn get_shock_for_symbol(
        &self,
        symbol: &str,
        asset_mapping: &AssetClassMapping,
    ) -> Option<f64> {
        // First check for instrument-specific shock
        if let Some(shock) = self.instrument_shocks.get(symbol) {
            return Some(*shock);
        }

        // Then check for asset class shock
        if let Some(asset_class) = asset_mapping.mappings.get(symbol) {
            return self.asset_class_shocks.get(asset_class).copied();
        }

        // Fall back to default asset class shock
        self.asset_class_shocks
            .get(&asset_mapping.default_class)
            .copied()
    }

    /// Get volatility multiplier for a given instrument symbol
    pub fn get_volatility_multiplier_for_symbol(
        &self,
        symbol: &str,
        asset_mapping: &AssetClassMapping,
    ) -> f64 {
        // Check for asset class-specific volatility multiplier
        if let Some(asset_class) = asset_mapping.mappings.get(symbol) {
            if let Some(multiplier) = self.volatility_multipliers.get(asset_class) {
                return *multiplier;
            }
        }

        // Fall back to default asset class
        if let Some(multiplier) = self
            .volatility_multipliers
            .get(&asset_mapping.default_class)
        {
            return *multiplier;
        }

        // Fall back to global multiplier
        self.volatility_multiplier
    }
}

/// Create default stress test scenarios based on historical events
fn create_default_stress_scenarios() -> Vec<StressScenarioConfig> {
    vec![
        StressScenarioConfig {
            id: "market_crash_2008".to_owned(),
            name: "2008 Financial Crisis".to_owned(),
            description: "Simulates the market conditions during the 2008 financial crisis with severe equity declines and financial sector stress".to_owned(),
            instrument_shocks: HashMap::new(),
            asset_class_shocks: {
                let mut shocks = HashMap::new();
                shocks.insert(AssetClass::LargeCapEquity, -37.0);
                shocks.insert(AssetClass::SmallCapEquity, -45.0);
                shocks.insert(AssetClass::Financials, -55.0);
                shocks.insert(AssetClass::Technology, -40.0);
                shocks.insert(AssetClass::RealEstate, -60.0);
                shocks.insert(AssetClass::EmergingMarkets, -50.0);
                shocks.insert(AssetClass::HighYieldBonds, -25.0);
                shocks
            },
            volatility_multiplier: 2.5,
            volatility_multipliers: HashMap::new(),
            correlation_adjustments: HashMap::new(),
            liquidity_haircuts: {
                let mut haircuts = HashMap::new();
                haircuts.insert(AssetClass::SmallCapEquity, 0.15);
                haircuts.insert(AssetClass::EmergingMarkets, 0.20);
                haircuts.insert(AssetClass::HighYieldBonds, 0.10);
                haircuts
            },
            is_active: true,
        },
        StressScenarioConfig {
            id: "covid_crash_2020".to_owned(),
            name: "COVID-19 Market Crash".to_owned(),
            description: "Simulates the market crash of March 2020 due to COVID-19 pandemic with broad-based equity declines".to_owned(),
            instrument_shocks: HashMap::new(),
            asset_class_shocks: {
                let mut shocks = HashMap::new();
                shocks.insert(AssetClass::LargeCapEquity, -34.0);
                shocks.insert(AssetClass::SmallCapEquity, -40.0);
                shocks.insert(AssetClass::Energy, -50.0);
                shocks.insert(AssetClass::Financials, -45.0);
                shocks.insert(AssetClass::RealEstate, -35.0);
                shocks.insert(AssetClass::Technology, -25.0);
                shocks.insert(AssetClass::EmergingMarkets, -45.0);
                shocks
            },
            volatility_multiplier: 3.0,
            volatility_multipliers: HashMap::new(),
            correlation_adjustments: HashMap::new(),
            liquidity_haircuts: HashMap::new(),
            is_active: true,
        },
        StressScenarioConfig {
            id: "flash_crash_2010".to_owned(),
            name: "Flash Crash 2010".to_owned(),
            description: "Simulates the May 6, 2010 flash crash with rapid market decline and liquidity issues".to_owned(),
            instrument_shocks: HashMap::new(),
            asset_class_shocks: {
                let mut shocks = HashMap::new();
                shocks.insert(AssetClass::LargeCapEquity, -9.0);
                shocks.insert(AssetClass::SmallCapEquity, -15.0);
                shocks.insert(AssetClass::Technology, -12.0);
                shocks
            },
            volatility_multiplier: 5.0,
            volatility_multipliers: HashMap::new(),
            correlation_adjustments: HashMap::new(),
            liquidity_haircuts: {
                let mut haircuts = HashMap::new();
                haircuts.insert(AssetClass::LargeCapEquity, 0.05);
                haircuts.insert(AssetClass::SmallCapEquity, 0.20);
                haircuts.insert(AssetClass::Technology, 0.10);
                haircuts
            },
            is_active: true,
        },
        StressScenarioConfig {
            id: "volatility_spike".to_owned(),
            name: "Volatility Spike".to_owned(),
            description: "Simulates a sudden spike in market volatility without significant price moves".to_owned(),
            instrument_shocks: HashMap::new(),
            asset_class_shocks: HashMap::new(),
            volatility_multiplier: 3.0,
            volatility_multipliers: {
                let mut multipliers = HashMap::new();
                multipliers.insert(AssetClass::SmallCapEquity, 4.0);
                multipliers.insert(AssetClass::EmergingMarkets, 3.5);
                multipliers.insert(AssetClass::HighYieldBonds, 2.5);
                multipliers
            },
            correlation_adjustments: HashMap::new(),
            liquidity_haircuts: HashMap::new(),
            is_active: true,
        },
        StressScenarioConfig {
            id: "interest_rate_shock".to_owned(),
            name: "Interest Rate Shock".to_owned(),
            description: "Simulates a sudden rise in interest rates affecting bonds and rate-sensitive sectors".to_owned(),
            instrument_shocks: HashMap::new(),
            asset_class_shocks: {
                let mut shocks = HashMap::new();
                shocks.insert(AssetClass::USBonds, -8.0);
                shocks.insert(AssetClass::CorporateBonds, -12.0);
                shocks.insert(AssetClass::RealEstate, -15.0);
                shocks.insert(AssetClass::Utilities, -10.0);
                shocks.insert(AssetClass::Financials, 5.0); // Banks benefit from higher rates
                shocks
            },
            volatility_multiplier: 1.5,
            volatility_multipliers: HashMap::new(),
            correlation_adjustments: HashMap::new(),
            liquidity_haircuts: HashMap::new(),
            is_active: true,
        },
    ]
}

/// Create default asset class mapping for common symbols
fn create_default_asset_class_mapping() -> AssetClassMapping {
    let mut mappings = HashMap::new();

    // Large Cap Technology
    mappings.insert("AAPL".to_owned(), AssetClass::Technology);
    mappings.insert("MSFT".to_owned(), AssetClass::Technology);
    mappings.insert("GOOGL".to_owned(), AssetClass::Technology);
    mappings.insert("GOOG".to_owned(), AssetClass::Technology);
    mappings.insert("AMZN".to_owned(), AssetClass::Technology);
    mappings.insert("META".to_owned(), AssetClass::Technology);
    mappings.insert("TSLA".to_owned(), AssetClass::Technology);
    mappings.insert("NVDA".to_owned(), AssetClass::Technology);

    // Large Cap Financials
    mappings.insert("JPM".to_owned(), AssetClass::Financials);
    mappings.insert("BAC".to_owned(), AssetClass::Financials);
    mappings.insert("WFC".to_owned(), AssetClass::Financials);
    mappings.insert("GS".to_owned(), AssetClass::Financials);
    mappings.insert("MS".to_owned(), AssetClass::Financials);

    // ETFs
    mappings.insert("SPY".to_owned(), AssetClass::LargeCapEquity);
    mappings.insert("QQQ".to_owned(), AssetClass::Technology);
    mappings.insert("IWM".to_owned(), AssetClass::SmallCapEquity);
    mappings.insert("VTI".to_owned(), AssetClass::LargeCapEquity);
    mappings.insert("EEM".to_owned(), AssetClass::EmergingMarkets);
    mappings.insert("VEA".to_owned(), AssetClass::InternationalEquity);
    mappings.insert("TLT".to_owned(), AssetClass::USBonds);
    mappings.insert("HYG".to_owned(), AssetClass::HighYieldBonds);

    // Healthcare
    mappings.insert("JNJ".to_owned(), AssetClass::Healthcare);
    mappings.insert("PFE".to_owned(), AssetClass::Healthcare);
    mappings.insert("UNH".to_owned(), AssetClass::Healthcare);

    // Energy
    mappings.insert("XOM".to_owned(), AssetClass::Energy);
    mappings.insert("CVX".to_owned(), AssetClass::Energy);

    AssetClassMapping {
        mappings,
        default_class: AssetClass::LargeCapEquity,
    }
}

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

    #[test]
    fn test_stress_scenario_config_creation() {
        let config = StressScenarioConfig {
            id: "test".to_owned(),
            name: "Test Scenario".to_owned(),
            description: "Test description".to_owned(),
            instrument_shocks: HashMap::new(),
            asset_class_shocks: {
                let mut shocks = HashMap::new();
                shocks.insert(AssetClass::Technology, -10.0);
                shocks
            },
            volatility_multiplier: 2.0,
            volatility_multipliers: HashMap::new(),
            correlation_adjustments: HashMap::new(),
            liquidity_haircuts: HashMap::new(),
            is_active: true,
        };

        assert_eq!(config.id, "test");
        assert_eq!(config.volatility_multiplier, 2.0);
    }

    #[test]
    fn test_asset_class_mapping() {
        let mapping = create_default_asset_class_mapping();

        assert_eq!(mapping.mappings.get("AAPL"), Some(&AssetClass::Technology));
        assert_eq!(
            mapping.mappings.get("SPY"),
            Some(&AssetClass::LargeCapEquity)
        );
        assert_eq!(mapping.default_class, AssetClass::LargeCapEquity);
    }

    #[test]
    fn test_get_shock_for_symbol() {
        let config = StressScenarioConfig {
            id: "test".to_owned(),
            name: "Test".to_owned(),
            description: "Test".to_owned(),
            instrument_shocks: {
                let mut shocks = HashMap::new();
                shocks.insert("AAPL".to_owned(), -15.0);
                shocks
            },
            asset_class_shocks: {
                let mut shocks = HashMap::new();
                shocks.insert(AssetClass::Technology, -10.0);
                shocks.insert(AssetClass::LargeCapEquity, -5.0);
                shocks
            },
            volatility_multiplier: 1.0,
            volatility_multipliers: HashMap::new(),
            correlation_adjustments: HashMap::new(),
            liquidity_haircuts: HashMap::new(),
            is_active: true,
        };

        let mapping = create_default_asset_class_mapping();

        // Should get instrument-specific shock
        assert_eq!(config.get_shock_for_symbol("AAPL", &mapping), Some(-15.0));

        // Should get asset class shock for GOOGL (Technology)
        assert_eq!(config.get_shock_for_symbol("GOOGL", &mapping), Some(-10.0));

        // Should get default class shock for unknown symbol
        assert_eq!(config.get_shock_for_symbol("UNKNOWN", &mapping), Some(-5.0));
    }
}
