from __future__ import annotations

from typing import Dict, List, Optional
from pydantic import BaseModel, Field


class StrategyRisk(BaseModel):
    id: str
    name: str
    status: str = "unknown"
    sharpe_ratio: float = 0.0
    sortino_ratio: float = 0.0
    max_drawdown: float = 0.0
    volatility: float = 0.0
    performance: float = 0.0
    risk_score: int = 50


class RiskSnapshot(BaseModel):
    """Point-in-time risk metrics computed from real positions and market data."""

    user_id: str = ""
    account_id: Optional[str] = None

    portfolio_var_95: float = 0.0
    portfolio_var_99: float = 0.0
    sharpe_ratio: float = 0.0
    sortino_ratio: float = 0.0
    calmar_ratio: float = 0.0
    max_drawdown: float = 0.0
    volatility: float = 0.0
    beta: float = 0.0
    alpha: float = 0.0
    information_ratio: float = 0.0
    treynor_ratio: float = 0.0

    correlation_risk: float = 0.0
    concentration_hhi: float = 0.0
    total_exposure: float = 0.0
    long_exposure: float = 0.0
    short_exposure: float = 0.0
    risk_score: int = 0

    risk_free_rate: float = 0.0

    position_weights: Dict[str, float] = Field(default_factory=dict)
    correlation_matrix: Dict[str, Dict[str, float]] = Field(default_factory=dict)
    strategy_risks: List[StrategyRisk] = Field(default_factory=list)


class RiskAlert(BaseModel):
    id: str = ""
    user_id: str = ""
    severity: str = "medium"
    alert_type: str = ""
    message: str = ""
    metadata: Dict[str, object] = Field(default_factory=dict)
    strategy_id: Optional[str] = None
    acknowledged: bool = False


class RiskThreshold(BaseModel):
    id: str = ""
    user_id: str = ""
    metric: str = ""
    threshold_value: float = 0.0
    action: str = "alert"
    enabled: bool = True


class StressScenario(BaseModel):
    name: str
    equity_shock: float
    bond_shock: float = 0.0
    commodity_shock: float = 0.0
    crypto_shock: float = 0.0
    period: str = ""
    severity: str = "medium"


class MonteCarloResult(BaseModel):
    num_simulations: int = 10000
    horizon_days: int = 1
    var_95: float = 0.0
    var_99: float = 0.0
    expected_shortfall_95: float = 0.0
    expected_shortfall_99: float = 0.0
    mean_return: float = 0.0
    median_return: float = 0.0
    worst_case: float = 0.0
    best_case: float = 0.0
    percentiles: Dict[str, float] = Field(default_factory=dict)


class RollingMetrics(BaseModel):
    """Rolling-window risk metrics over time."""

    window: int = 20
    dates: List[str] = Field(default_factory=list)
    sharpe: List[float] = Field(default_factory=list)
    volatility: List[float] = Field(default_factory=list)
    max_drawdown: List[float] = Field(default_factory=list)
    beta: List[float] = Field(default_factory=list)
    var_95: List[float] = Field(default_factory=list)


class DrawdownAnalysis(BaseModel):
    """Detailed drawdown decomposition."""

    current_drawdown: float = 0.0
    max_drawdown: float = 0.0
    max_drawdown_duration_days: int = 0
    avg_drawdown: float = 0.0
    avg_recovery_days: float = 0.0
    num_drawdowns: int = 0
    drawdown_periods: List[Dict[str, object]] = Field(default_factory=list)
    underwater_pct: float = 0.0


class PositionSizeResult(BaseModel):
    """Position sizing recommendation."""

    method: str = ""
    recommended_size: float = 0.0
    recommended_pct: float = 0.0
    max_loss: float = 0.0
    risk_per_trade: float = 0.0
    kelly_fraction: float = 0.0
    half_kelly: float = 0.0


class SharpeInference(BaseModel):
    """Statistical inference on Sharpe ratio — per Lopez de Prado (2012, 2018).

    Addresses 5 sources of error in Sharpe inference:
    1. Non-normality (skewness/kurtosis correction to standard error)
    2. Serial correlation (Lo 2002 autocorrelation adjustment)
    3. Multiple testing (Deflated Sharpe Ratio)
    4. Probabilistic Sharpe Ratio (probability SR > benchmark)
    5. Short track records (Minimum Track Record Length)
    """

    observed_sharpe: float = 0.0
    annualized_sharpe: float = 0.0
    t_statistic: float = 0.0
    p_value: float = 0.0
    confidence_interval_95: List[float] = Field(default_factory=list)
    confidence_interval_99: List[float] = Field(default_factory=list)
    num_observations: int = 0
    is_significant_95: bool = False
    is_significant_99: bool = False

    # Lopez de Prado corrections
    sharpe_se: float = 0.0
    sharpe_se_non_normal: float = 0.0
    autocorrelation_adjustment: float = 1.0
    lo_adjusted_sharpe: float = 0.0

    # Probabilistic Sharpe Ratio (PSR) — probability true SR > benchmark
    psr: float = 0.0
    psr_benchmark: float = 0.0

    # Deflated Sharpe Ratio (DSR) — adjusts for multiple testing
    deflated_sharpe: float = 0.0
    num_trials: int = 1
    expected_max_sharpe_under_null: float = 0.0

    # Track record
    min_track_record_months: float = 0.0

    # Return distribution
    skewness: float = 0.0
    kurtosis: float = 0.0


class FactorExposure(BaseModel):
    """Factor model decomposition of portfolio returns."""

    factors: Dict[str, float] = Field(default_factory=dict)
    r_squared: float = 0.0
    residual_vol: float = 0.0
    systematic_risk_pct: float = 0.0
    idiosyncratic_risk_pct: float = 0.0


class TailRiskMetrics(BaseModel):
    """Tail risk analysis beyond normal VaR."""

    skewness: float = 0.0
    kurtosis: float = 0.0
    excess_kurtosis: float = 0.0
    jarque_bera_stat: float = 0.0
    jarque_bera_p: float = 0.0
    is_normal: bool = True
    historical_var_95: float = 0.0
    historical_var_99: float = 0.0
    cornish_fisher_var_95: float = 0.0
    cornish_fisher_var_99: float = 0.0
    max_daily_loss: float = 0.0
    max_daily_gain: float = 0.0
