"""Fund module types: enums, configs, and dataclasses."""

from __future__ import annotations

from dataclasses import dataclass, field
from enum import Enum
from typing import Any, Callable


# ---------------------------------------------------------------------------
# Enums
# ---------------------------------------------------------------------------

class StrategyState(str, Enum):
    """Strategy lifecycle states."""
    PENDING = "pending"
    RUNNING = "running"
    PAUSED = "paused"
    STOPPING = "stopping"
    STOPPED = "stopped"
    FAILED = "failed"
    RETIRED = "retired"


class AllocationMethod(str, Enum):
    """Capital allocation methods."""
    EQUAL = "equal"
    RISK_PARITY = "risk_parity"
    PERFORMANCE = "performance"
    CUSTOM = "custom"


class FeeType(str, Enum):
    """Types of fees tracked."""
    EXCHANGE = "exchange"
    SLIPPAGE = "slippage"
    MANAGEMENT = "management"
    PERFORMANCE = "performance"


# ---------------------------------------------------------------------------
# Config dataclasses
# ---------------------------------------------------------------------------

@dataclass
class StrategyConfig:
    """Configuration for deploying a strategy within the fund.

    Args:
        name: Unique strategy name.
        pipeline: Pipeline function list.
        markets: Market slugs/tickers to trade.
        exchange: Exchange config (Polymarket(...), Kalshi(...), etc.).
                  None for paper mode.
        feeds: Feed configurations.
        risk: Risk config for this strategy. Will be scaled by allocation.
        params: Arbitrary parameters passed to pipeline Context.
        mode: "paper" or "live".
        max_drawdown_pct: Strategy-level drawdown limit (0-100).
        events: Event objects for multi-outcome trading.
    """
    name: str
    pipeline: list[Callable] | dict[str, list[Callable]]
    markets: list[str] = field(default_factory=list)
    exchange: Any = None
    feeds: dict[str, Any] = field(default_factory=dict)
    risk: Any = None  # Risk | RiskConfig | None
    params: dict[str, Any] = field(default_factory=dict)
    mode: str = "paper"
    max_drawdown_pct: float = 20.0
    events: list[Any] = field(default_factory=list)


@dataclass
class FundConfig:
    """Fund-level configuration.

    Args:
        total_capital: Total fund capital in USD.
        allocation_method: How to allocate capital across strategies.
        custom_weights: Weights for CUSTOM allocation method.
        max_strategies: Maximum number of concurrent strategies.
        max_fund_drawdown_pct: Fund-wide drawdown limit (0-100).
        max_strategy_drawdown_pct: Per-strategy drawdown limit (0-100).
        max_correlation: Max pairwise strategy correlation before alert.
        rebalance_interval_secs: How often to run oversight loop.
        settlement_poll_interval_secs: How often to check for resolved markets.
        nav_snapshot_interval_secs: How often to snapshot NAV.
        management_fee_bps: Annual management fee in basis points.
        performance_fee_pct: Performance fee as % of profits above HWM.
    """
    total_capital: float = 100_000.0
    allocation_method: AllocationMethod = AllocationMethod.EQUAL
    custom_weights: dict[str, float] = field(default_factory=dict)
    max_strategies: int = 20
    max_fund_drawdown_pct: float = 15.0
    max_strategy_drawdown_pct: float = 25.0
    max_correlation: float = 0.85
    rebalance_interval_secs: float = 60.0
    settlement_poll_interval_secs: float = 300.0
    nav_snapshot_interval_secs: float = 300.0
    management_fee_bps: float = 200.0  # 2% annual
    performance_fee_pct: float = 20.0  # 20% of profits
    max_failed_strategies: int = 3
    auto_rebalance_interval_ticks: int = 60
    universe_refresh_interval_ticks: int = 120
    strategy_cycle_secs: float = 0.5
    auto_restart_max_retries: int = 3
    pending_decision_expiry_secs: float = 86400.0  # 24h
    fee_accrual_interval_secs: float = 86400.0  # accrue fees daily
    kill_switch_recovery_drawdown_pct: float = 5.0

    # Agentic fund settings
    alert_webhook_url: str | None = None
    promotion_enabled: bool = False
    ledger_enabled: bool = False
    self_healing_enabled: bool = False
    adaptive_thresholds_enabled: bool = False

    # Quant flow settings
    optimization_enabled: bool = False  # Use PortfolioOptimizer instead of CapitalAllocator
    regime_risk_enabled: bool = False  # Regime-conditional risk limits
    attribution_interval_ticks: int = 60  # Performance attribution snapshot interval

    def __post_init__(self) -> None:
        if not (0 <= self.management_fee_bps <= 500):
            raise ValueError(f"management_fee_bps must be 0-500, got {self.management_fee_bps}")
        if not (0 <= self.performance_fee_pct <= 50):
            raise ValueError(f"performance_fee_pct must be 0-50, got {self.performance_fee_pct}")
        import math
        if self.total_capital <= 0 or math.isinf(self.total_capital) or math.isnan(self.total_capital):
            raise ValueError(f"total_capital must be a finite positive number, got {self.total_capital}")
        if not (0 < self.max_fund_drawdown_pct <= 100):
            raise ValueError(f"max_fund_drawdown_pct must be 0-100, got {self.max_fund_drawdown_pct}")
        if not (0 < self.max_strategy_drawdown_pct <= 100):
            raise ValueError(f"max_strategy_drawdown_pct must be 0-100, got {self.max_strategy_drawdown_pct}")
        if not (0 < self.max_correlation <= 1.0):
            raise ValueError(f"max_correlation must be 0-1.0, got {self.max_correlation}")


# ---------------------------------------------------------------------------
# Data dataclasses
# ---------------------------------------------------------------------------

@dataclass(frozen=True)
class NAVSnapshot:
    """Point-in-time fund NAV snapshot."""
    timestamp: float
    total_nav: float
    cash: float
    positions_value: float
    unrealized_pnl: float
    realized_pnl: float
    high_water_mark: float
    drawdown_pct: float
    strategy_navs: dict[str, float] = field(default_factory=dict)


@dataclass(frozen=True)
class StrategyStatus:
    """Status of a single strategy."""
    name: str
    state: StrategyState
    allocated_capital: float
    current_nav: float
    pnl: float
    drawdown_pct: float
    open_orders: int
    active_positions: int
    uptime_secs: float
    error: str | None = None


@dataclass(frozen=True)
class MarketScore:
    """Fitness score for a market."""
    market_id: str
    exchange: str
    composite_score: float
    liquidity_score: float
    edge_score: float
    fit_score: float
    volume_24h: float
    spread: float
    price: float
    details: dict[str, Any] = field(default_factory=dict)


@dataclass(frozen=True)
class SettlementEvent:
    """A market resolution/settlement event."""
    market_id: str
    strategy_name: str
    timestamp: float
    outcome: str  # "yes", "no", "unknown"
    pnl: float
    position_size: float
    side: str


@dataclass(frozen=True)
class FundReport:
    """Full fund status report."""
    timestamp: float
    nav: NAVSnapshot
    strategies: list[StrategyStatus]
    total_strategies: int
    running_strategies: int
    recent_settlements: list[SettlementEvent]
    allocation: dict[str, float]
