//! Portfolio model types for multi-strategy backtesting.
//!
//! A `PortfolioDef` combines multiple strategies into a single portfolio
//! with configurable allocation, rebalancing, and risk management.

use std::collections::HashMap;

use serde::{Deserialize, Serialize};

use crate::orchestrator::{BacktestResult, StrategyPlan};

/// A named strategy with its capital allocation weight.
#[derive(Clone, Debug)]
pub struct StrategySlot {
    /// Unique name for this strategy slot.
    pub name: String,
    /// Compiled strategy plan (signals + position sizing).
    pub plan: StrategyPlan,
    /// Fraction of total capital allocated to this strategy (0.0 to 1.0).
    pub weight: f64,
}

/// Defines how multiple strategies combine into one portfolio.
#[derive(Clone, Debug)]
pub struct PortfolioDef {
    /// Named strategies with their allocation weights.
    pub strategies: Vec<StrategySlot>,
    /// Risk management rules (circuit breakers).
    pub risk_rules: Vec<RiskRule>,
    /// Rebalancing configuration.
    pub rebalance: RebalanceConfig,
}

/// Risk rules — user-configurable circuit breakers.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum RiskRule {
    /// Kill all positions if portfolio drawdown exceeds threshold.
    MaxDrawdown { threshold_pct: f64 },
    /// Kill a specific strategy if its cumulative P&L drops below threshold.
    StrategyKillSwitch { strategy: String, max_loss_pct: f64 },
    /// Cap total gross exposure (sum of |position_value|) as fraction of equity.
    MaxGrossExposure { max_pct: f64 },
    /// Cap total net exposure (sum of position_value) as fraction of equity.
    MaxNetExposure { max_pct: f64 },
}

/// When/how to rebalance allocations back to target weights.
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
pub enum RebalanceConfig {
    /// Never rebalance — allocations drift with P&L.
    #[default]
    None,
    /// Rebalance every N bars.
    Periodic { every_n_bars: u32 },
    /// Rebalance when any strategy's weight drifts > threshold from target.
    Threshold { drift_pct: f64 },
}

/// A rebalancing event recorded during the simulation.
#[derive(Clone, Debug)]
pub struct RebalanceEvent {
    /// Bar index when rebalancing occurred.
    pub bar: usize,
    /// Timestamp in nanoseconds.
    pub timestamp_ns: i64,
    /// Capital per strategy before rebalancing.
    pub before: Vec<f64>,
    /// Capital per strategy after rebalancing.
    pub after: Vec<f64>,
}

/// A risk event recorded during the simulation.
#[derive(Clone, Debug)]
pub struct RiskEvent {
    /// Bar index when the risk rule triggered.
    pub bar: usize,
    /// Timestamp in nanoseconds.
    pub timestamp_ns: i64,
    /// Which rule triggered.
    pub rule: String,
    /// Description of the action taken.
    pub action: String,
}

/// Result of a portfolio backtest.
#[derive(Clone, Debug)]
pub struct PortfolioResult {
    /// Combined portfolio-level result (equity curve, metrics, trades from all strategies).
    pub combined: BacktestResult,
    /// Per-strategy breakdown.
    pub per_strategy: HashMap<String, PerStrategyResult>,
    /// Rebalancing events.
    pub rebalance_events: Vec<RebalanceEvent>,
    /// Risk events (circuit breakers triggered).
    pub risk_events: Vec<RiskEvent>,
}

/// Per-strategy result within a portfolio.
#[derive(Clone, Debug)]
pub struct PerStrategyResult {
    /// Strategy name.
    pub name: String,
    /// Equity curve for this strategy's allocation.
    pub equity_curve: Vec<f64>,
    /// Number of trades generated by this strategy.
    pub trade_count: usize,
    /// Final capital allocated to this strategy.
    pub final_capital: f64,
    /// Whether the strategy was killed by a risk rule.
    pub killed: bool,
}
