pub mod portfolio;
pub mod bet_sizing;

use crate::{Order, Side};
use dashmap::DashMap;
use parking_lot::RwLock;
use serde::{Deserialize, Serialize};
use std::sync::Arc;

pub use portfolio::{PortfolioRisk, RiskModel, CorrelationMatrix, ConcentrationMetrics};
pub use bet_sizing::{BetSizer, BetSizingParameters, PositionSize, MarketRegime, DynamicSizer};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RiskLimits {
    pub max_position_size: f64,
    pub max_order_size: f64,
    pub max_daily_loss: f64,
    pub max_gross_exposure: f64,
    pub max_symbol_exposure: f64,
}

impl Default for RiskLimits {
    fn default() -> Self {
        Self {
            max_position_size: 100_000.0,
            max_order_size: 10_000.0,
            max_daily_loss: 5_000.0,
            max_gross_exposure: 1_000_000.0,
            max_symbol_exposure: 50_000.0,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RiskCheckResult {
    pub passed: bool,
    pub violations: Vec<String>,
    pub checks: RiskChecks,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RiskChecks {
    pub order_size: bool,
    pub position_size: bool,
    pub daily_loss: bool,
    pub gross_exposure: bool,
    pub symbol_exposure: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RiskMetrics {
    pub current_exposure: f64,
    pub daily_pnl: f64,
    pub position_count: usize,
    pub gross_exposure: f64,
    pub max_position_size: f64,
    pub utilization_pct: f64,
}

pub struct RiskEngine {
    limits: Arc<RwLock<RiskLimits>>,
    symbol_exposures: DashMap<String, f64>,
    daily_pnl: Arc<RwLock<f64>>,
}

impl RiskEngine {
    pub fn new() -> Self {
        Self::with_limits(RiskLimits::default())
    }

    pub fn with_limits(limits: RiskLimits) -> Self {
        Self {
            limits: Arc::new(RwLock::new(limits)),
            symbol_exposures: DashMap::new(),
            daily_pnl: Arc::new(RwLock::new(0.0)),
        }
    }

    pub fn update_limits(&self, new_limits: RiskLimits) {
        *self.limits.write() = new_limits;
    }

    pub fn check_order(&self, order: &Order, current_position: Option<f64>) -> RiskCheckResult {
        let mut violations = Vec::new();
        let limits = self.limits.read();
        
        // Check order size
        if order.quantity > limits.max_order_size {
            violations.push(format!(
                "Order size {} exceeds limit {}",
                order.quantity, limits.max_order_size
            ));
        }

        // Check position size after order
        let current_pos = current_position.unwrap_or(0.0);
        let new_position = match order.side {
            Side::Buy => current_pos + order.quantity,
            Side::Sell => current_pos - order.quantity,
        };

        if new_position.abs() > limits.max_position_size {
            violations.push(format!(
                "Position size {} would exceed limit {}",
                new_position.abs(), limits.max_position_size
            ));
        }

        // Check symbol exposure
        let symbol_exposure = self.symbol_exposures
            .get(&order.symbol)
            .map(|e| *e)
            .unwrap_or(0.0);
        
        let new_exposure = symbol_exposure + order.quantity;
        if new_exposure > limits.max_symbol_exposure {
            violations.push(format!(
                "Symbol exposure {} would exceed limit {}",
                new_exposure, limits.max_symbol_exposure
            ));
        }

        // Check daily loss
        let daily_pnl = *self.daily_pnl.read();
        if daily_pnl < -limits.max_daily_loss {
            violations.push(format!(
                "Daily loss {} exceeds limit {}",
                -daily_pnl, limits.max_daily_loss
            ));
        }

        // Calculate gross exposure
        let gross_exposure = self.calculate_gross_exposure();
        if gross_exposure > limits.max_gross_exposure {
            violations.push(format!(
                "Gross exposure {} exceeds limit {}",
                gross_exposure, limits.max_gross_exposure
            ));
        }

        RiskCheckResult {
            passed: violations.is_empty(),
            violations,
            checks: RiskChecks {
                order_size: order.quantity <= limits.max_order_size,
                position_size: new_position.abs() <= limits.max_position_size,
                daily_loss: daily_pnl >= -limits.max_daily_loss,
                gross_exposure: gross_exposure <= limits.max_gross_exposure,
                symbol_exposure: new_exposure <= limits.max_symbol_exposure,
            },
        }
    }

    pub fn update_position(&self, symbol: &str, new_position: f64) {
        if new_position.abs() < 0.0001 {
            self.symbol_exposures.remove(symbol);
        } else {
            self.symbol_exposures.insert(symbol.to_string(), new_position.abs());
        }
    }

    pub fn update_daily_pnl(&self, pnl_change: f64) {
        let mut daily_pnl = self.daily_pnl.write();
        *daily_pnl += pnl_change;
    }

    pub fn reset_daily_metrics(&self) {
        *self.daily_pnl.write() = 0.0;
    }

    fn calculate_gross_exposure(&self) -> f64 {
        self.symbol_exposures
            .iter()
            .map(|entry| *entry.value())
            .sum()
    }
    
    fn calculate_total_exposure(&self) -> f64 {
        self.calculate_gross_exposure()
    }

    pub fn get_risk_metrics(&self) -> RiskMetrics {
        let limits = self.limits.read();
        let gross_exposure = self.calculate_gross_exposure();
        
        RiskMetrics {
            current_exposure: 0.0,
            daily_pnl: *self.daily_pnl.read(),
            position_count: self.symbol_exposures.len(),
            gross_exposure,
            max_position_size: limits.max_position_size,
            utilization_pct: (gross_exposure / limits.max_gross_exposure * 100.0).min(100.0),
        }
    }
}