"""Types et modèles de données pour le SDK STRIGIVAL."""

from dataclasses import dataclass
from datetime import datetime
from enum import Enum
from typing import Optional, List, Dict, Any


class Protocol(str, Enum):
    """Protocoles DeFi supportés."""
    UNISWAP_V3 = "uniswap_v3"
    AAVE_V3 = "aave_v3"
    ONE_INCH = "1inch"
    GMX = "gmx"


class TradeAction(str, Enum):
    """Actions de trading."""
    SWAP = "swap"
    SUPPLY = "supply"
    BORROW = "borrow"
    REPAY = "repay"
    WITHDRAW = "withdraw"


@dataclass
class Quote:
    """Quote pour un trade."""
    quote_id: str
    expires_at: datetime
    
    # Amounts
    amount_in: int
    amount_out: int
    amount_out_min: int
    
    # Pricing
    price: float
    price_impact_bps: int
    
    # Gas
    estimated_gas: int
    gas_price_gwei: float
    gas_cost_usd: float
    
    # Route
    protocol: str
    route: List[str]
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "Quote":
        """Crée un Quote depuis un dictionnaire."""
        return cls(
            quote_id=data["quote_id"],
            expires_at=datetime.fromisoformat(data["expires_at"].replace("Z", "+00:00")),
            amount_in=int(data["amount_in"]),
            amount_out=int(data["amount_out"]),
            amount_out_min=int(data["amount_out_min"]),
            price=data["price"],
            price_impact_bps=data["price_impact_bps"],
            estimated_gas=data["estimated_gas"],
            gas_price_gwei=data["gas_price_gwei"],
            gas_cost_usd=data["gas_cost_usd"],
            protocol=data["protocol"],
            route=data["route"],
        )
    
    def is_expired(self) -> bool:
        """Vérifie si le quote est expiré."""
        return datetime.utcnow() > self.expires_at


@dataclass
class Trade:
    """Résultat d'un trade exécuté."""
    tx_hash: str
    success: bool
    
    # Execution
    gas_used: Optional[int] = None
    effective_gas_price: Optional[int] = None
    block_number: Optional[int] = None
    
    # Result
    amount_in: Optional[int] = None
    amount_out: Optional[int] = None
    slippage_actual_bps: Optional[int] = None
    
    # Error
    error: Optional[str] = None
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "Trade":
        """Crée un Trade depuis un dictionnaire."""
        return cls(
            tx_hash=data.get("tx_hash", ""),
            success=data.get("success", False),
            gas_used=data.get("gas_used"),
            effective_gas_price=int(data["effective_gas_price"]) if data.get("effective_gas_price") else None,
            block_number=data.get("block_number"),
            amount_in=int(data["amount_in"]) if data.get("amount_in") else None,
            amount_out=int(data["amount_out"]) if data.get("amount_out") else None,
            slippage_actual_bps=data.get("slippage_actual_bps"),
            error=data.get("error"),
        )


@dataclass
class VaultStatus:
    """Statut d'un vault."""
    vault_address: str
    trader_address: str
    
    # State
    total_assets: int
    available_liquidity: int
    pending_withdrawals: int
    
    # Permissions
    is_authorized: bool
    daily_trade_limit: int
    daily_trades_used: int
    
    # Risk
    circuit_breaker_level: int
    can_trade: bool
    required_actions: List[str]
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "VaultStatus":
        """Crée un VaultStatus depuis un dictionnaire."""
        return cls(
            vault_address=data["vault_address"],
            trader_address=data["trader_address"],
            total_assets=int(data["total_assets"]),
            available_liquidity=int(data["available_liquidity"]),
            pending_withdrawals=int(data["pending_withdrawals"]),
            is_authorized=data["is_authorized"],
            daily_trade_limit=int(data["daily_trade_limit"]),
            daily_trades_used=int(data["daily_trades_used"]),
            circuit_breaker_level=data["circuit_breaker_level"],
            can_trade=data["can_trade"],
            required_actions=data.get("required_actions", []),
        )


@dataclass
class DrawdownInfo:
    """Information sur le drawdown."""
    max_drawdown: float
    current_drawdown: float
    peak_date: Optional[datetime]
    trough_date: Optional[datetime]
    recovery_date: Optional[datetime]
    duration_days: int


@dataclass
class WinRateInfo:
    """Information sur le win rate."""
    win_rate: float
    total_trades: int
    profit_factor: float
    avg_win: float
    avg_loss: float
    largest_win: float
    largest_loss: float


@dataclass
class RiskMetrics:
    """Métriques de risque."""
    var_95: float
    var_99: float
    expected_shortfall_95: float
    skewness: float
    kurtosis: float
    downside_deviation: float


@dataclass
class KPIs:
    """KPIs institutionnelles d'un vault."""
    vault_address: str
    calculated_at: datetime
    
    # Performance
    total_return: float
    annual_return: float
    monthly_return: float
    weekly_return: float
    daily_return: float
    
    # Risk-adjusted
    sharpe_ratio: float
    sortino_ratio: float
    calmar_ratio: float
    
    # Volatility
    volatility_annual: float
    volatility_monthly: float
    
    # Drawdown
    drawdown: DrawdownInfo
    
    # Trading
    win_rate: WinRateInfo
    
    # Risk
    risk_metrics: RiskMetrics
    
    # Benchmark
    alpha: float
    beta: float
    correlation: float
    information_ratio: float
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "KPIs":
        """Crée des KPIs depuis un dictionnaire."""
        perf = data.get("performance", {})
        risk_adj = data.get("risk_adjusted", {})
        vol = data.get("volatility", {})
        dd = data.get("drawdown", {})
        trading = data.get("trading", {})
        risk = data.get("risk", {})
        bench = data.get("benchmark", {})
        
        return cls(
            vault_address=data["vault_address"],
            calculated_at=datetime.fromisoformat(data["calculated_at"].replace("Z", "+00:00")),
            total_return=perf.get("total_return", 0),
            annual_return=perf.get("annual_return", 0),
            monthly_return=perf.get("monthly_return", 0),
            weekly_return=perf.get("weekly_return", 0),
            daily_return=perf.get("daily_return", 0),
            sharpe_ratio=risk_adj.get("sharpe_ratio", 0),
            sortino_ratio=risk_adj.get("sortino_ratio", 0),
            calmar_ratio=risk_adj.get("calmar_ratio", 0),
            volatility_annual=vol.get("annual", 0),
            volatility_monthly=vol.get("monthly", 0),
            drawdown=DrawdownInfo(
                max_drawdown=dd.get("max_drawdown", 0),
                current_drawdown=dd.get("current_drawdown", 0),
                peak_date=datetime.fromisoformat(dd["peak_date"]) if dd.get("peak_date") else None,
                trough_date=datetime.fromisoformat(dd["trough_date"]) if dd.get("trough_date") else None,
                recovery_date=datetime.fromisoformat(dd["recovery_date"]) if dd.get("recovery_date") else None,
                duration_days=dd.get("duration_days", 0),
            ),
            win_rate=WinRateInfo(
                win_rate=trading.get("win_rate", 0),
                total_trades=trading.get("total_trades", 0),
                profit_factor=trading.get("profit_factor", 0),
                avg_win=trading.get("avg_win", 0),
                avg_loss=trading.get("avg_loss", 0),
                largest_win=trading.get("largest_win", 0),
                largest_loss=trading.get("largest_loss", 0),
            ),
            risk_metrics=RiskMetrics(
                var_95=risk.get("var_95", 0),
                var_99=risk.get("var_99", 0),
                expected_shortfall_95=risk.get("expected_shortfall_95", 0),
                skewness=risk.get("skewness", 0),
                kurtosis=risk.get("kurtosis", 0),
                downside_deviation=risk.get("downside_deviation", 0),
            ),
            alpha=bench.get("alpha", 0),
            beta=bench.get("beta", 1),
            correlation=bench.get("correlation", 0),
            information_ratio=bench.get("information_ratio", 0),
        )


@dataclass
class Token:
    """Information sur un token."""
    address: str
    symbol: str
    decimals: int
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "Token":
        """Crée un Token depuis un dictionnaire."""
        return cls(
            address=data["address"],
            symbol=data["symbol"],
            decimals=data["decimals"],
        )


@dataclass
class TradeHistory:
    """Élément d'historique de trade."""
    tx_hash: str
    timestamp: datetime
    protocol: str
    action: str
    token_in: str
    token_out: str
    amount_in: int
    amount_out: int
    pnl: Optional[float]
    gas_cost_usd: float
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "TradeHistory":
        """Crée un TradeHistory depuis un dictionnaire."""
        return cls(
            tx_hash=data["tx_hash"],
            timestamp=datetime.fromisoformat(data["timestamp"].replace("Z", "+00:00")),
            protocol=data["protocol"],
            action=data["action"],
            token_in=data["token_in"],
            token_out=data["token_out"],
            amount_in=int(data["amount_in"]),
            amount_out=int(data["amount_out"]),
            pnl=data.get("pnl"),
            gas_cost_usd=data["gas_cost_usd"],
        )
