//! Historical Simulation `VaR` calculation
//! REPLACES: `var_1d_95`: `Price::ZERO` with real `VaR` calculations

// REMOVED: Direct Decimal usage - use canonical types
use crate::error::{RiskError, RiskResult};
use chrono::{DateTime, Utc};
use common::types::{Price, Symbol};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
// Removed broker_integration - not available in this simplified risk crate
use crate::var_calculator::var_engine::{HistoricalPrice, PositionInfo};

/// Historical Simulation Value at Risk (`VaR`) calculator using empirical distribution
///
/// Historical Simulation is a non-parametric method for calculating `VaR` that uses
/// actual historical price movements to estimate potential future losses. This approach
/// does not assume any particular distribution and captures the actual empirical
/// distribution of returns including fat tails, skewness, and other real market characteristics.
///
/// # Methodology
///
/// 1. **Historical Data Collection**: Gather historical price data for the specified lookback period
/// 2. **Returns Calculation**: Calculate period-over-period returns from historical prices
/// 3. **Scenario Generation**: Apply historical returns to current portfolio positions
/// 4. **Distribution Analysis**: Sort profit/loss scenarios to create empirical distribution
/// 5. **`VaR` Estimation**: Extract quantile corresponding to confidence level
///
/// # Mathematical Foundation
///
/// For a portfolio with current value V₀, historical returns R₁, R₂, ..., Rₙ:
/// - P&L scenarios: P&Lᵢ = V₀ × Rᵢ
/// - Sorted scenarios: P&L₍₁₎ ≤ P&L₍₂₎ ≤ ... ≤ P&L₍ₙ₎
/// - `VaR` at confidence α: `VaR_α` = -P&L₍⌊(1-α)×n⌋₎
///
/// # Advantages
///
/// - **Model-free**: No distributional assumptions required
/// - **Fat tail capture**: Naturally incorporates extreme events from history
/// - **Correlation capture**: Implicitly includes historical correlations
/// - **Intuitive**: Easy to explain and validate
///
/// # Limitations
///
/// - **Historical bias**: Assumes future will resemble past
/// - **Limited scenarios**: Cannot model unprecedented events
/// - **Data requirements**: Needs substantial historical data
/// - **Non-stationarity**: May not capture regime changes
///
/// # Use Cases
///
/// - Daily risk monitoring and reporting
/// - Regulatory capital calculations (Basel II/III)
/// - Portfolio optimization with realistic risk constraints
/// - Backtesting and model validation
///
/// # Example
///
/// ```rust
/// use risk::var_calculator::historical_simulation::HistoricalSimulationVaR;
/// use std::collections::HashMap;
///
/// // Create 95% confidence VaR calculator with 1-year lookback
/// let var_calculator = HistoricalSimulationVaR::new(0.95, 252);
///
/// // Or use predefined configurations
/// let standard_calc = HistoricalSimulationVaR::standard();      // 95%, 252 days
/// let conservative_calc = HistoricalSimulationVaR::conservative(); // 99%, 252 days
/// ```
#[derive(Debug, Clone)]
pub struct HistoricalSimulationVaR {
    /// Confidence level for `VaR` calculation (e.g., 0.95 for 95% `VaR`)
    ///
    /// Common confidence levels:
    /// - 0.95 (95%): Standard risk management and daily monitoring
    /// - 0.99 (99%): Regulatory requirements (Basel III, Solvency II)
    /// - 0.975 (97.5%): Internal risk limits and stress testing
    /// - 0.999 (99.9%): Extreme event analysis
    confidence_level: f64,

    /// Number of historical trading days to include in lookback window
    ///
    /// Typical values:
    /// - 252: One year of trading days (standard)
    /// - 504: Two years for more stable estimates
    /// - 126: Half year for more responsive estimates
    /// - 63: Quarter for highly dynamic markets
    ///
    /// Trade-off considerations:
    /// - Longer periods: More stable estimates, less responsive to regime changes
    /// - Shorter periods: More responsive, but higher estimation error
    lookback_days: usize,
}

/// Value at Risk calculation result for a single position
///
/// Contains comprehensive `VaR` metrics for a specific symbol/position including
/// 1-day and 10-day `VaR` estimates, Expected Shortfall, and calculation metadata.
///
/// # Risk Metrics Included
///
/// - **1-Day `VaR`**: Potential loss over 1 trading day at specified confidence level
/// - **10-Day `VaR`**: Scaled `VaR` using square-root-of-time rule for longer horizon
/// - **Expected Shortfall**: Average loss given that loss exceeds `VaR` threshold
/// - **Observation Count**: Number of historical data points used in calculation
///
/// # Scaling Methodology
///
/// 10-day `VaR` uses the square-root-of-time scaling rule:
/// `VaR₁₀` = `VaR₁` × √10
///
/// This assumes:
/// - Returns are independent and identically distributed
/// - No autocorrelation in returns
/// - Constant volatility over the scaling period
///
/// # Usage in Risk Management
///
/// - Daily risk reporting and monitoring
/// - Position limit enforcement
/// - Regulatory capital calculations
/// - Performance attribution analysis
///
/// # Example
///
/// ```rust
/// // Typical VaR result interpretation
/// if var_result.var_1d > position_limit {
///     println!("Position exceeds 1-day VaR limit: ${} > ${}",
///              var_result.var_1d, position_limit);
/// }
///
/// // Expected Shortfall provides tail risk insight
/// let tail_risk_ratio = var_result.expected_shortfall / var_result.var_1d;
/// println!("Tail risk multiplier: {:.2}x", tail_risk_ratio);
/// ```
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VaRResult {
    /// Symbol identifier for the position being analyzed
    ///
    /// Unique identifier for the financial instrument (e.g., "AAPL", "EURUSD")
    pub symbol: Symbol,

    /// Confidence level used for `VaR` calculation
    ///
    /// The probability that actual losses will not exceed the `VaR` estimate.
    /// For example, 0.95 means 95% confidence that losses won't exceed `VaR`.
    pub confidence_level: f64,

    /// 1-day Value at Risk in absolute currency terms
    ///
    /// Maximum expected loss over 1 trading day at the specified confidence level.
    /// Always expressed as a positive value representing potential loss amount.
    ///
    /// Example: $50,000 means 95% confidence that daily loss won't exceed $50,000.
    pub var_1d: Price,

    /// 10-day Value at Risk scaled using square-root-of-time rule
    ///
    /// `VaR` estimate for a 10-day holding period, calculated as:
    /// `var_10d` = `var_1d` × √10 ≈ `var_1d` × 3.16
    ///
    /// Used for:
    /// - Regulatory reporting (many jurisdictions require 10-day `VaR`)
    /// - Longer-term risk assessment
    /// - Capital adequacy calculations
    pub var_10d: Price,

    /// Expected Shortfall (Conditional `VaR`) at the same confidence level
    ///
    /// Average loss given that the loss exceeds the `VaR` threshold.
    /// Provides additional insight into tail risk beyond `VaR`.
    ///
    /// Always ≥ `VaR`, with larger values indicating fatter tail distributions.
    pub expected_shortfall: Price,

    /// Number of historical return observations used in the calculation
    ///
    /// Indicates the sample size for the empirical distribution.
    /// Higher values generally provide more reliable estimates but may
    /// include less relevant historical periods.
    pub historical_observations: usize,

    /// Timestamp when the `VaR` calculation was performed
    ///
    /// Used for:
    /// - Audit trails and compliance reporting
    /// - Determining freshness of risk calculations
    /// - Historical analysis of risk evolution
    pub calculated_at: DateTime<Utc>,
}

/// Portfolio-level Value at Risk calculation result with diversification analysis
///
/// Comprehensive portfolio `VaR` metrics that account for correlations between
/// positions and quantify the diversification benefit from portfolio construction.
///
/// # Key Metrics
///
/// - **Total Portfolio `VaR`**: Risk of the entire portfolio accounting for correlations
/// - **Component `VaRs`**: Individual position `VaRs` for decomposition analysis
/// - **Diversification Benefit**: Risk reduction achieved through diversification
///
/// # Diversification Benefit Calculation
///
/// Diversification Benefit = Σ(Component `VaRs`) - Portfolio `VaR`
///
/// Where:
/// - Σ(Component VaRs): Sum of individual position `VaRs`
/// - Portfolio `VaR`: `VaR` of the combined portfolio
/// - Positive values indicate effective diversification
///
/// # Mathematical Foundation
///
/// Portfolio `VaR` accounts for correlations through joint simulation:
/// - Each historical scenario applies to all positions simultaneously
/// - Portfolio P&L = `Σ(Position_i` × `Return_i`) for each scenario
/// - `VaR` extracted from joint P&L distribution
///
/// # Risk Management Applications
///
/// - **Limit Management**: Ensure portfolio `VaR` stays within bounds
/// - **Capital Allocation**: Optimize diversification benefits
/// - **Performance Attribution**: Decompose risk by component
/// - **Regulatory Reporting**: Meet portfolio-level capital requirements
///
/// # Example Analysis
///
/// ```rust
/// // Analyze diversification effectiveness
/// let diversification_ratio = portfolio_result.diversification_benefit /
///                           portfolio_result.component_vars.values()
///                               .map(|v| v.var_1d).sum::<Price>();
///
/// if diversification_ratio > 0.20 {
///     println!("Strong diversification: {:.1}% risk reduction",
///              diversification_ratio * 100.0);
/// }
/// ```
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PortfolioVaRResult {
    /// Unique identifier for the portfolio being analyzed
    ///
    /// Used for tracking, reporting, and audit purposes.
    /// Examples: "`MAIN_TRADING`", "`HEDGE_FUND_A`", "`CLIENT_12345`"
    pub portfolio_id: String,

    /// Total portfolio 1-day `VaR` accounting for correlations
    ///
    /// The diversified `VaR` of the entire portfolio, which incorporates
    /// correlations between positions. Typically less than the sum of
    /// individual component `VaRs` due to diversification benefits.
    pub total_var_1d: Price,

    /// Total portfolio 10-day `VaR` using square-root-of-time scaling
    ///
    /// Scaled version of 1-day portfolio `VaR`: `total_var_10d` = `total_var_1d` × √10
    /// Used for regulatory reporting and longer-term risk assessment.
    pub total_var_10d: Price,

    /// Individual `VaR` results for each component position
    ///
    /// Map of symbol → `VaRResult` for portfolio decomposition analysis.
    /// Allows identification of risk contributors and concentration analysis.
    ///
    /// Key insights:
    /// - Largest component `VaRs` indicate risk concentrations
    /// - Comparison with portfolio `VaR` shows diversification effects
    /// - Used for position sizing and risk budgeting decisions
    pub component_vars: HashMap<String, VaRResult>,

    /// Diversification benefit from portfolio construction
    ///
    /// Calculated as: Σ(Component `VaRs`) - Portfolio `VaR`
    ///
    /// Positive values indicate risk reduction through diversification.
    /// Higher values suggest more effective portfolio construction.
    ///
    /// Typical ranges:
    /// - 0-10%: Limited diversification
    /// - 10-30%: Good diversification  
    /// - 30%+: Excellent diversification
    pub diversification_benefit: Price,

    /// Confidence level used for all `VaR` calculations
    ///
    /// Applied consistently across portfolio and component calculations
    /// to ensure comparable risk metrics.
    pub confidence_level: f64,

    /// Timestamp when the portfolio `VaR` calculation was performed
    ///
    /// Critical for:
    /// - Regulatory reporting timestamps
    /// - Risk monitoring and alerting
    /// - Historical risk analysis
    pub calculated_at: DateTime<Utc>,
}

impl HistoricalSimulationVaR {
    /// Creates a new Historical Simulation `VaR` calculator with custom parameters
    ///
    /// # Arguments
    ///
    /// * `confidence_level` - Confidence level between 0.0 and 1.0 (e.g., 0.95 for 95%)
    /// * `lookback_days` - Number of historical trading days to include in calculation
    ///
    /// # Returns
    ///
    /// New `HistoricalSimulationVaR` instance ready for calculations
    ///
    /// # Parameter Guidelines
    ///
    /// **Confidence Level Selection:**
    /// - 0.95 (95%): Standard daily risk monitoring
    /// - 0.99 (99%): Regulatory requirements, stress testing
    /// - 0.975 (97.5%): Internal risk limits
    ///
    /// **Lookback Period Selection:**
    /// - 252 days: One year (most common, balances stability vs responsiveness)
    /// - 504 days: Two years (more stable, less responsive to recent changes)
    /// - 126 days: Half year (more responsive to market regime changes)
    /// - 63 days: Quarter (highly responsive, higher estimation error)
    ///
    /// # Examples
    ///
    /// ```rust
    /// use risk::var_calculator::historical_simulation::HistoricalSimulationVaR;
    ///
    /// // Standard configuration for daily risk monitoring
    /// let daily_var = HistoricalSimulationVaR::new(0.95, 252);
    ///
    /// // Conservative configuration for regulatory reporting
    /// let regulatory_var = HistoricalSimulationVaR::new(0.99, 252);
    ///
    /// // Responsive configuration for volatile markets
    /// let responsive_var = HistoricalSimulationVaR::new(0.95, 126);
    /// ```
    ///
    /// # Performance Considerations
    ///
    /// Longer lookback periods require more computation but provide more stable estimates.
    /// Consider the trade-off between accuracy and computational cost for your use case.
    #[must_use]
    pub const fn new(confidence_level: f64, lookback_days: usize) -> Self {
        Self {
            confidence_level,
            lookback_days,
        }
    }

    /// Creates a `VaR` calculator with standard market risk parameters
    ///
    /// Uses 95% confidence level with 252 trading days (1 year) lookback period.
    /// This configuration is widely used in the financial industry for daily
    /// risk monitoring and represents a good balance between stability and responsiveness.
    ///
    /// # Returns
    ///
    /// `HistoricalSimulationVaR` configured with:
    /// - Confidence level: 95% (0.95)
    /// - Lookback period: 252 trading days (≈ 1 calendar year)
    ///
    /// # Use Cases
    ///
    /// - Daily portfolio risk monitoring
    /// - Position limit enforcement
    /// - Risk-adjusted performance measurement
    /// - Internal risk reporting
    ///
    /// # Equivalent To
    ///
    /// ```rust
    /// HistoricalSimulationVaR::new(0.95, 252)
    /// ```
    ///
    /// # Example
    ///
    /// ```rust
    /// use risk::var_calculator::historical_simulation::HistoricalSimulationVaR;
    ///
    /// let var_calc = HistoricalSimulationVaR::standard();
    /// // Ready for standard daily VaR calculations
    /// ```
    #[must_use]
    pub const fn standard() -> Self {
        Self::new(0.95, 252)
    }

    /// Creates a `VaR` calculator with conservative parameters for regulatory compliance
    ///
    /// Uses 99% confidence level with 252 trading days lookback period.
    /// This configuration meets most regulatory requirements (Basel III, Solvency II)
    /// and provides more conservative risk estimates for capital adequacy calculations.
    ///
    /// # Returns
    ///
    /// `HistoricalSimulationVaR` configured with:
    /// - Confidence level: 99% (0.99)
    /// - Lookback period: 252 trading days (≈ 1 calendar year)
    ///
    /// # Regulatory Applications
    ///
    /// - Basel III market risk capital requirements
    /// - Solvency II standard formula calculations
    /// - Internal Capital Adequacy Assessment Process (ICAAP)
    /// - Stress testing and scenario analysis
    ///
    /// # Risk Implications
    ///
    /// 99% `VaR` estimates will be significantly higher than 95% `VaR`:
    /// - Captures more extreme tail events
    /// - Provides greater protection against unexpected losses
    /// - Results in higher capital requirements
    ///
    /// # Equivalent To
    ///
    /// ```rust
    /// HistoricalSimulationVaR::new(0.99, 252)
    /// ```
    ///
    /// # Example
    ///
    /// ```rust
    /// use risk::var_calculator::historical_simulation::HistoricalSimulationVaR;
    ///
    /// let regulatory_calc = HistoricalSimulationVaR::conservative();
    /// // Ready for regulatory capital calculations
    /// ```
    #[must_use]
    pub const fn conservative() -> Self {
        Self::new(0.99, 252)
    }

    /// Calculates Value at Risk for a single position using historical simulation
    ///
    /// This method applies historical price movements to the current position to generate
    /// a distribution of potential profit/loss scenarios, then extracts `VaR` at the
    /// specified confidence level.
    ///
    /// # Arguments
    ///
    /// * `symbol` - Symbol identifier for the position
    /// * `position` - Current position information (quantity, market value, etc.)
    /// * `historical_prices` - Historical price data for the symbol
    ///
    /// # Returns
    ///
    /// * `Ok(VaRResult)` - Complete `VaR` analysis including 1-day, 10-day `VaR` and Expected Shortfall
    /// * `Err(RiskError)` - If calculation fails due to insufficient data or other errors
    ///
    /// # Algorithm Steps
    ///
    /// 1. **Data Validation**: Ensure sufficient historical data (≥ `lookback_days`)
    /// 2. **Returns Calculation**: Compute period-over-period returns from price data
    /// 3. **Scenario Generation**: Apply returns to current position value
    /// 4. **Distribution Creation**: Sort P&L scenarios from worst to best
    /// 5. **`VaR` Extraction**: Find quantile corresponding to confidence level
    /// 6. **Scaling**: Apply square-root-of-time rule for 10-day `VaR`
    /// 7. **Expected Shortfall**: Calculate average loss beyond `VaR` threshold
    ///
    /// # Data Requirements
    ///
    /// - Historical prices must cover at least `lookback_days` periods
    /// - Prices should be adjusted for splits and dividends
    /// - Data should be clean (no missing values, outliers reviewed)
    /// - Consistent frequency (daily, weekly, etc.)
    ///
    /// # Mathematical Detail
    ///
    /// For position value V and historical returns R₁, ..., Rₙ:
    /// - P&L scenarios: `ΔV_i` = V × `R_i`
    /// - Sorted scenarios: ΔV_(1) ≤ ... ≤ ΔV_(n)
    /// - `VaR` index: k = ⌊(1 - α) × n⌋
    /// - `VaR` estimate: `VaR` = -ΔV_(k)
    ///
    /// # Examples
    ///
    /// ```rust
    /// use risk::var_calculator::historical_simulation::HistoricalSimulationVaR;
    /// use common::types::{Symbol, Price};
    ///
    /// let var_calc = HistoricalSimulationVaR::standard();
    ///
    /// // Calculate VaR for AAPL position
    /// let symbol = Symbol::from("AAPL");
    /// let var_result = var_calc.calculate_position_var(
    ///     &symbol,
    ///     &position_info,
    ///     &historical_price_data
    /// )?;
    ///
    /// println!("1-day 95% VaR: ${}", var_result.var_1d);
    /// println!("Expected Shortfall: ${}", var_result.expected_shortfall);
    /// ```
    ///
    /// # Errors
    ///
    /// - `RiskError::Calculation` with operation "`historical_var`" if insufficient data
    /// - `RiskError::Calculation` with operation "`returns_calculation`" if price data invalid
    /// - `RiskError::Calculation` with operation "`var_scaling`" if scaling fails
    ///
    /// # Performance Notes
    ///
    /// Computational complexity is O(n log n) due to sorting of scenarios.
    /// For high-frequency calculations, consider caching sorted historical returns.
    pub fn calculate_position_var(
        &self,
        symbol: &Symbol,
        position: &PositionInfo,
        historical_prices: &[HistoricalPrice],
    ) -> RiskResult<VaRResult> {
        if historical_prices.len() < self.lookback_days {
            return Err(RiskError::Calculation {
                operation: "historical_var".to_owned(),
                reason: format!(
                    "Insufficient historical data: {} days required, {} available",
                    self.lookback_days,
                    historical_prices.len()
                ),
            });
        }

        // Calculate daily returns from historical prices
        let returns = self.calculate_returns(historical_prices)?;

        // Calculate position value changes based on returns
        let position_value = position.quantity.to_f64() * position.market_value.to_f64();
        let mut pnl_scenarios: Vec<f64> = returns
            .iter()
            .map(|return_rate| position_value * return_rate)
            .collect();

        // Sort P&L scenarios (worst losses first)
        pnl_scenarios.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));

        // Calculate VaR at confidence level
        let var_index = ((1.0 - self.confidence_level) * pnl_scenarios.len() as f64) as usize;
        let var_loss = pnl_scenarios
            .get(var_index.min(pnl_scenarios.len().saturating_sub(1)))
            .copied()
            .unwrap_or(0.0);

        // VaR is positive for losses (negate negative P&L)
        let var_one_day = Price::from_f64(var_loss.abs()).unwrap_or(Price::ZERO);

        // Scale to 10-day VaR (square root of time scaling)
        let var_ten_day = (var_one_day * 10.0_f64.sqrt()).map_err(|e| RiskError::Calculation {
            operation: "var_scaling".to_owned(),
            reason: format!("Failed to scale VaR to 10 days: {e:?}"),
        })?;

        // Calculate Expected Shortfall (average of losses beyond VaR)
        let es_scenarios: Vec<f64> = pnl_scenarios.iter().take(var_index + 1).copied().collect();
        let expected_shortfall = if es_scenarios.is_empty() {
            Price::ZERO
        } else {
            let sum: f64 = es_scenarios.iter().sum();
            let avg = sum / es_scenarios.len() as f64;
            // ES is positive for losses (negate negative P&L)
            Price::from_f64(avg.abs()).unwrap_or(Price::ZERO)
        };

        Ok(VaRResult {
            symbol: symbol.to_string().into(),
            confidence_level: self.confidence_level,
            var_1d: var_one_day,
            var_10d: var_ten_day,
            expected_shortfall,
            historical_observations: returns.len(),
            calculated_at: Utc::now(),
        })
    }

    /// Calculates portfolio Value at Risk accounting for correlations between positions
    ///
    /// This method performs joint simulation across all portfolio positions to capture
    /// correlation effects and calculate diversified portfolio `VaR`. The resulting
    /// portfolio `VaR` typically differs from the sum of individual position `VaRs`
    /// due to diversification benefits or concentration risks.
    ///
    /// # Arguments
    ///
    /// * `portfolio_id` - Unique identifier for the portfolio
    /// * `positions` - Map of symbol → position information for all holdings
    /// * `historical_prices` - Map of symbol → historical price data
    ///
    /// # Returns
    ///
    /// * `Ok(PortfolioVaRResult)` - Complete portfolio analysis with diversification metrics
    /// * `Err(RiskError)` - If calculation fails due to data issues or mismatched inputs
    ///
    /// # Correlation Methodology
    ///
    /// Unlike simple `VaR` aggregation, this method captures correlations through:
    /// 1. **Joint Simulation**: Each historical scenario applies to all positions simultaneously
    /// 2. **Portfolio P&L**: Sum position-level P&L for each scenario
    /// 3. **Empirical Distribution**: Create portfolio-level loss distribution
    /// 4. **Diversified `VaR`**: Extract `VaR` from joint distribution
    ///
    /// # Mathematical Foundation
    ///
    /// For portfolio with positions i = 1...n and historical scenario t:
    /// - Portfolio `P&L_t` = Σᵢ (`Position_Value_i` × `Return_i,t`)
    /// - Portfolio `VaR` = Quantile(Portfolio P&L Distribution, 1-α)
    /// - Diversification Benefit = Σᵢ(Individual `VaR_i`) - Portfolio `VaR`
    ///
    /// # Key Outputs
    ///
    /// - **Portfolio `VaR`**: Risk of combined portfolio
    /// - **Component `VaRs`**: Individual position risks for decomposition
    /// - **Diversification Benefit**: Risk reduction from portfolio construction
    /// - **Correlation Effects**: Implicit in the difference between sum and portfolio `VaR`
    ///
    /// # Data Requirements
    ///
    /// - All symbols must have historical data covering the lookback period
    /// - Historical data should be time-aligned across symbols
    /// - Position information must be current and accurate
    /// - Minimum overlap period across all historical series
    ///
    /// # Examples
    ///
    /// ```rust
    /// use std::collections::HashMap;
    /// use risk::var_calculator::historical_simulation::HistoricalSimulationVaR;
    ///
    /// let var_calc = HistoricalSimulationVaR::standard();
    ///
    /// // Portfolio with multiple positions
    /// let mut positions = HashMap::new();
    /// positions.insert(symbol_aapl, position_aapl);
    /// positions.insert(symbol_googl, position_googl);
    /// positions.insert(symbol_msft, position_msft);
    ///
    /// let portfolio_result = var_calc.calculate_portfolio_var(
    ///     "EQUITY_PORTFOLIO",
    ///     &positions,
    ///     &historical_data
    /// )?;
    ///
    /// // Analyze diversification effectiveness
    /// let component_sum = portfolio_result.component_vars.values()
    ///     .map(|v| v.var_1d).sum::<Price>();
    /// let diversification_pct = portfolio_result.diversification_benefit / component_sum;
    ///
    /// println!("Portfolio VaR: ${}", portfolio_result.total_var_1d);
    /// println!("Diversification benefit: {:.1}%", diversification_pct * 100.0);
    /// ```
    ///
    /// # Risk Management Applications
    ///
    /// - **Limit Monitoring**: Ensure portfolio `VaR` stays within risk appetite
    /// - **Capital Allocation**: Optimize portfolio construction for diversification
    /// - **Performance Attribution**: Identify risk contributors vs diversifiers
    /// - **Regulatory Reporting**: Meet portfolio-level capital requirements
    ///
    /// # Errors
    ///
    /// - `RiskError::Calculation` with operation "`portfolio_var`" if insufficient data
    /// - `RiskError::Calculation` if position/price data misalignment
    /// - `RiskError::Calculation` with operation "`portfolio_var_scaling`" if scaling fails
    ///
    /// # Performance Considerations
    ///
    /// Portfolio `VaR` calculation scales linearly with number of positions and
    /// historical periods. For large portfolios, consider:
    /// - Parallel processing of component `VaRs`
    /// - Caching of historical return calculations
    /// - Approximate methods for real-time applications
    pub fn calculate_portfolio_var(
        &self,
        portfolio_id: &str,
        positions: &HashMap<Symbol, PositionInfo>,
        historical_prices: &HashMap<Symbol, Vec<HistoricalPrice>>,
    ) -> RiskResult<PortfolioVaRResult> {
        let mut component_vars = HashMap::new();
        let mut portfolio_pnl_scenarios: Vec<f64> = Vec::new();

        // Get the minimum number of observations across all symbols
        let min_observations = historical_prices.values().map(Vec::len).min().unwrap_or(0);

        if min_observations < self.lookback_days {
            return Err(RiskError::Calculation {
                operation: "portfolio_var".to_owned(),
                reason: format!(
                    "Insufficient historical data across portfolio: {} days required",
                    self.lookback_days
                ),
            });
        }

        // Initialize portfolio P&L scenarios
        for _ in 0..min_observations - 1 {
            portfolio_pnl_scenarios.push(0.0);
        }

        // Calculate component VaRs and aggregate portfolio scenarios
        for (symbol, position) in positions {
            if let Some(symbol_prices) = historical_prices.get(symbol) {
                // Calculate component VaR
                let component_var = self.calculate_position_var(symbol, position, symbol_prices)?;
                component_vars.insert(symbol.to_string(), component_var);

                // Add to portfolio scenarios
                let returns = self.calculate_returns(symbol_prices)?;
                let position_value = position.quantity.to_f64() * position.market_value.to_f64();

                for (i, return_rate) in returns.into_iter().enumerate() {
                    if let Some(scenario) = portfolio_pnl_scenarios.get_mut(i) {
                        let pnl_change = position_value * return_rate;
                        *scenario += pnl_change;
                    }
                }
            }
        }

        // Calculate portfolio VaR from aggregated scenarios
        let mut sorted_portfolio_pnl = portfolio_pnl_scenarios.clone();
        sorted_portfolio_pnl.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));

        let var_index =
            ((1.0 - self.confidence_level) * sorted_portfolio_pnl.len() as f64) as usize;
        let var_loss = sorted_portfolio_pnl
            .get(var_index.min(sorted_portfolio_pnl.len().saturating_sub(1)))
            .copied()
            .unwrap_or(0.0);

        // VaR is positive for losses
        let total_var_one_day = Price::from_f64(var_loss.abs()).unwrap_or(Price::ZERO);

        // Scale to 10-day VaR
        let total_var_ten_day =
            (total_var_one_day * 10.0_f64.sqrt()).map_err(|e| RiskError::Calculation {
                operation: "portfolio_var_scaling".to_owned(),
                reason: format!("Failed to scale portfolio VaR to 10 days: {e:?}"),
            })?;

        // Calculate diversification benefit
        let component_var_sum = component_vars
            .values()
            .map(|var| var.var_1d)
            .fold(Price::ZERO, |acc, price| {
                Price::from_f64(acc.to_f64() + price.to_f64()).unwrap_or(Price::ZERO)
            });
        let diversification_benefit = component_var_sum - total_var_one_day;

        Ok(PortfolioVaRResult {
            portfolio_id: portfolio_id.to_owned(),
            total_var_1d: total_var_one_day,
            total_var_10d: total_var_ten_day,
            component_vars,
            diversification_benefit,
            confidence_level: self.confidence_level,
            calculated_at: Utc::now(),
        })
    }

    /// Calculates daily returns from historical price data
    ///
    /// Computes period-over-period returns using simple return formula:
    /// `Return_t` = (`Price_t` - Price_{t-1}) / Price_{t-1}
    ///
    /// # Arguments
    ///
    /// * `historical_prices` - Vector of historical price data in chronological order
    ///
    /// # Returns
    ///
    /// * `Ok(Vec<Price>)` - Vector of returns (length = `prices.len()` - 1)
    /// * `Err(RiskError)` - If insufficient data or calculation errors
    ///
    /// # Implementation Details
    ///
    /// - Uses simple returns (not log returns) for intuitive interpretation
    /// - Handles zero prices by returning calculation error
    /// - Preserves precision through Decimal arithmetic
    /// - Returns vector has n-1 elements for n price points
    ///
    /// # Error Conditions
    ///
    /// - Fewer than 2 price points (cannot calculate returns)
    /// - Zero prices in historical data (division by zero)
    /// - Price conversion errors
    fn calculate_returns(&self, historical_prices: &[HistoricalPrice]) -> RiskResult<Vec<f64>> {
        if historical_prices.len() < 2 {
            return Err(RiskError::Calculation {
                operation: "returns_calculation".to_owned(),
                reason: "Need at least 2 price points to calculate returns".to_owned(),
            });
        }

        let mut returns = Vec::new();

        for window in historical_prices.windows(2) {
            let (prev_price, curr_price) = match (window.first(), window.get(1)) {
                (Some(prev), Some(curr)) => (prev.price.to_f64(), curr.price.to_f64()),
                _ => continue, // Skip invalid windows
            };

            if prev_price == 0.0 {
                return Err(RiskError::Calculation {
                    operation: "returns_calculation".to_owned(),
                    reason: "Zero price found in historical data".to_owned(),
                });
            }

            let return_rate = (curr_price - prev_price) / prev_price;
            returns.push(return_rate);
        }

        Ok(returns)
    }

    /// Calculates rolling Value at Risk estimates over time
    ///
    /// Produces a time series of `VaR` estimates using a rolling window approach.
    /// Each estimate uses the previous `window_size` observations, providing
    /// insight into how `VaR` evolves over time and enabling trend analysis.
    ///
    /// # Arguments
    ///
    /// * `symbol` - Symbol identifier for the position
    /// * `position` - Position information (assumed constant for analysis period)
    /// * `historical_prices` - Complete historical price dataset
    /// * `window_size` - Number of observations to include in each rolling window
    ///
    /// # Returns
    ///
    /// * `Ok(Vec<VaRResult>)` - Time series of `VaR` estimates
    /// * `Err(RiskError)` - If insufficient data or calculation errors
    ///
    /// # Rolling Window Methodology
    ///
    /// For historical data of length N and window size W:
    /// - Window 1: observations [0, W]
    /// - Window 2: observations [1, W+1]
    /// - ...
    /// - Window N-W: observations [N-W-1, N-1]
    /// - Total rolling estimates: N - W
    ///
    /// # Applications
    ///
    /// - **Trend Analysis**: Identify increasing/decreasing risk patterns
    /// - **Model Validation**: Compare rolling `VaR` with actual losses
    /// - **Regime Detection**: Spot structural breaks in risk characteristics
    /// - **Dynamic Hedging**: Adjust hedge ratios based on evolving risk
    ///
    /// # Window Size Selection
    ///
    /// Trade-offs in window size selection:
    /// - **Smaller windows** (30-60 days): More responsive to recent changes, higher noise
    /// - **Medium windows** (120-180 days): Balanced responsiveness and stability
    /// - **Larger windows** (250+ days): More stable, less responsive to regime changes
    ///
    /// # Examples
    ///
    /// ```rust
    /// use risk::var_calculator::historical_simulation::HistoricalSimulationVaR;
    ///
    /// let var_calc = HistoricalSimulationVaR::standard();
    ///
    /// // Calculate 6-month rolling VaR with 3-month windows
    /// let rolling_vars = var_calc.calculate_rolling_var(
    ///     &symbol,
    ///     &position,
    ///     &price_history,  // 6 months of data
    ///     63               // 3-month rolling window
    /// )?;
    ///
    /// // Analyze VaR trend
    /// let recent_var = rolling_vars.last().unwrap().var_1d;
    /// let earlier_var = rolling_vars.first().unwrap().var_1d;
    /// let var_trend = (recent_var - earlier_var) / earlier_var;
    ///
    /// if var_trend > 0.20 {
    ///     println!("VaR has increased by {:.1}% - consider risk reduction", var_trend * 100.0);
    /// }
    /// ```
    ///
    /// # Performance Considerations
    ///
    /// - Each rolling window requires separate `VaR` calculation
    /// - Consider parallel processing for large datasets
    /// - Memory usage scales with (`data_length` - `window_size`)
    ///
    /// # Errors
    ///
    /// - `RiskError::Calculation` with operation "`rolling_var`" if insufficient data
    /// - Propagates errors from individual `VaR` calculations
    ///
    /// # Interpretation Guidelines
    ///
    /// - **Increasing trend**: Rising market risk or volatility
    /// - **Decreasing trend**: Improving market conditions or reduced exposure
    /// - **Sudden spikes**: Market stress events or structural breaks
    /// - **Stable patterns**: Consistent risk regime
    pub fn calculate_rolling_var(
        &self,
        symbol: &Symbol,
        position: &PositionInfo,
        historical_prices: &[HistoricalPrice],
        window_size: usize,
    ) -> RiskResult<Vec<VaRResult>> {
        if historical_prices.len() < window_size + 1 {
            return Err(RiskError::Calculation {
                operation: "rolling_var".to_owned(),
                reason: format!(
                    "Insufficient data for rolling VaR: {} required, {} available",
                    window_size + 1,
                    historical_prices.len()
                ),
            });
        }

        let mut rolling_vars = Vec::new();

        for i in window_size..historical_prices.len() {
            if let Some(window_prices) = historical_prices.get(i.saturating_sub(window_size)..=i) {
                let temp_calculator =
                    HistoricalSimulationVaR::new(self.confidence_level, window_size);
                let var_result =
                    temp_calculator.calculate_position_var(symbol, position, window_prices)?;
                rolling_vars.push(var_result);
            }
        }

        Ok(rolling_vars)
    }
}

#[cfg(test)]
#[allow(clippy::float_cmp, clippy::str_to_string)]
mod tests {
    use super::*;
    use chrono::Duration;
    use common::types::Quantity;
    // operations module removed - use direct imports from common

    fn create_test_historical_prices(
        symbol: &Symbol,
        days: usize,
        base_price: f64,
    ) -> Result<Vec<HistoricalPrice>, Box<dyn std::error::Error>> {
        let mut prices = Vec::new();
        let mut current_price = base_price;

        for i in 0..days {
            // Simple random walk simulation
            let change = if i % 2 == 0 { 0.02 } else { -0.015 }; // +2% or -1.5%
            current_price *= 1.0 + change;

            prices.push(HistoricalPrice {
                symbol: symbol.to_string(),
                date: Utc::now() - Duration::days(days as i64 - i as i64),
                open: Price::from_f64(current_price * 0.999)?,
                high: Price::from_f64(current_price * 1.005)?,
                low: Price::from_f64(current_price * 0.995)?,
                price: Price::from_f64(current_price)?,
                volume: Quantity::from_f64(1000000.0)?,
            });
        }

        Ok(prices)
    }

    fn create_test_position(
        symbol: &Symbol,
        quantity: f64,
        market_price: f64,
    ) -> Result<PositionInfo, Box<dyn std::error::Error>> {
        Ok(PositionInfo {
            symbol: symbol.to_string().into(),
            quantity: Quantity::from_f64(quantity)?,
            market_value: Price::from_f64(quantity * market_price)?,
            average_cost: Price::from_f64(market_price * 0.95)?,
            unrealized_pnl: Price::from_f64(quantity * market_price * 0.05)?,
            realized_pnl: Price::ZERO,
            currency: "USD".to_string(),
            timestamp: Utc::now(),
        })
    }

    #[test]
    fn test_var_calculator_creation() {
        let calculator = HistoricalSimulationVaR::standard();
        assert_eq!(calculator.confidence_level, 0.95);
        assert_eq!(calculator.lookback_days, 252);

        let conservative = HistoricalSimulationVaR::conservative();
        assert_eq!(conservative.confidence_level, 0.99);
    }

    #[test]
    fn test_returns_calculation() -> Result<(), Box<dyn std::error::Error>> {
        let calculator = HistoricalSimulationVaR::standard();
        let prices = create_test_historical_prices(&Symbol::from("AAPL".to_string()), 10, 100.0)?;
        let returns = calculator.calculate_returns(&prices)?;

        assert_eq!(returns.len(), 9); // n-1 returns from n prices
        assert!(returns
            .iter()
            .all(|r| r.abs() < Price::from_f64(0.1).unwrap_or(Price::ZERO))); // Reasonable returns
        Ok(())
    }
    #[test]
    fn test_position_var_calculation() -> Result<(), Box<dyn std::error::Error>> {
        let calculator = HistoricalSimulationVaR::standard();
        let prices = create_test_historical_prices(&Symbol::from("AAPL".to_string()), 300, 150.0)?;
        let position = create_test_position(&Symbol::from("AAPL".to_string()), 100.0, 150.0)?;

        let var_result = calculator.calculate_position_var(
            &Symbol::from("AAPL".to_string()),
            &position,
            &prices,
        )?;

        assert_eq!(var_result.symbol, Symbol::from("AAPL".to_string()));
        assert_eq!(var_result.confidence_level, 0.95);
        assert!(var_result.var_1d > Price::ZERO);
        assert!(var_result.var_10d > var_result.var_1d);
        assert!(var_result.expected_shortfall >= var_result.var_1d);
        assert_eq!(var_result.historical_observations, 299); // 300 prices = 299 returns
        Ok(())
    }

    #[test]
    fn test_portfolio_var_calculation() -> Result<(), Box<dyn std::error::Error>> {
        let calculator = HistoricalSimulationVaR::standard();

        // Create test portfolio
        let mut positions = HashMap::new();
        positions.insert(
            Symbol::from("AAPL".to_string()),
            create_test_position(&Symbol::from("AAPL".to_string()), 100.0, 150.0)?,
        );
        positions.insert(
            Symbol::from("GOOGL".to_string()),
            create_test_position(&Symbol::from("GOOGL".to_string()), 50.0, 2800.0)?,
        );

        // Create historical data
        let mut historical_prices = HashMap::new();
        historical_prices.insert(
            Symbol::from("AAPL".to_string()),
            create_test_historical_prices(&Symbol::from("AAPL".to_string()), 300, 150.0)?,
        );
        historical_prices.insert(
            Symbol::from("GOOGL".to_string()),
            create_test_historical_prices(&Symbol::from("GOOGL".to_string()), 300, 2800.0)?,
        );

        let portfolio_var =
            calculator.calculate_portfolio_var("TEST_PORTFOLIO", &positions, &historical_prices)?;

        assert_eq!(portfolio_var.portfolio_id, "TEST_PORTFOLIO");
        assert!(portfolio_var.total_var_1d > Price::ZERO);
        assert!(portfolio_var.total_var_10d > portfolio_var.total_var_1d);
        assert_eq!(portfolio_var.component_vars.len(), 2);
        assert!(portfolio_var.component_vars.contains_key("AAPL"));
        assert!(portfolio_var.component_vars.contains_key("GOOGL"));

        // Diversification benefit should be positive (portfolio VaR < sum of component VaRs)
        assert!(portfolio_var.diversification_benefit > Price::ZERO);
        Ok(())
    }

    #[test]
    fn test_insufficient_data_error() -> Result<(), Box<dyn std::error::Error>> {
        let calculator = HistoricalSimulationVaR::standard();
        let prices = create_test_historical_prices(&Symbol::from("AAPL".to_string()), 100, 150.0)?; // Only 100 days, need 252
        let position = create_test_position(&Symbol::from("AAPL".to_string()), 100.0, 150.0)?;

        let result = calculator.calculate_position_var(
            &Symbol::from("AAPL".to_string()),
            &position,
            &prices,
        );
        assert!(result.is_err());

        if let Err(RiskError::Calculation { operation, reason }) = result {
            assert_eq!(operation, "historical_var");
            assert!(reason.contains("Insufficient historical data"));
        }
        Ok(())
    }

    #[test]
    fn test_rolling_var() -> Result<(), Box<dyn std::error::Error>> {
        let calculator = HistoricalSimulationVaR::new(0.95, 50); // Shorter window for testing
        let prices = create_test_historical_prices(&Symbol::from("AAPL".to_string()), 200, 150.0)?;
        let position = create_test_position(&Symbol::from("AAPL".to_string()), 100.0, 150.0)?;

        let rolling_vars = calculator.calculate_rolling_var(
            &Symbol::from("AAPL".to_string()),
            &position,
            &prices,
            50,
        )?;

        assert_eq!(rolling_vars.len(), 200 - 50); // 150 rolling windows
        assert!(rolling_vars.iter().all(|var| var.var_1d > Price::ZERO));
        Ok(())
    }
}
