"""Typed response models for DepthSignal API."""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional


@dataclass
class OrderbookFeatures:
    """Response from /v1/orderbook/features/{symbol}."""
    symbol: str
    features: Dict[str, Dict[str, Optional[float]]]
    features_count: int = 0
    data_age_seconds: Optional[float] = None
    timestamp: Optional[str] = None

    @classmethod
    def from_dict(cls, d: dict) -> "OrderbookFeatures":
        return cls(
            symbol=d.get("symbol", ""),
            features=d.get("features", {}),
            features_count=d.get("features_count", 0),
            data_age_seconds=d.get("data_age_seconds"),
            timestamp=d.get("timestamp"),
        )


@dataclass
class CompositeSignals:
    """Response from /v1/orderbook/composite/{symbol}."""
    symbol: str
    composites: Dict[str, Optional[float]] = field(default_factory=dict)
    timestamp: Optional[str] = None

    @classmethod
    def from_dict(cls, d: dict) -> "CompositeSignals":
        # API returns composite signals as flat top-level keys.
        # Collect all numeric fields into composites dict.
        composites = d.get("composites", {})
        if not composites:
            reserved = {"symbol", "timestamp"}
            composites = {k: v for k, v in d.items() if k not in reserved and isinstance(v, (int, float))}
        return cls(
            symbol=d.get("symbol", ""),
            composites=composites,
            timestamp=d.get("timestamp"),
        )


@dataclass
class SupportResistanceZones:
    """Response from /v1/orderbook/support-resistance/{symbol}."""
    symbol: str
    zones: List[Dict[str, Any]] = field(default_factory=list)
    timestamp: Optional[str] = None

    @classmethod
    def from_dict(cls, d: dict) -> "SupportResistanceZones":
        return cls(
            symbol=d.get("symbol", ""),
            zones=d.get("zones", []),
            timestamp=d.get("timestamp"),
        )


@dataclass
class LiquidationData:
    """Response from /v1/flow/liquidations/{symbol}."""
    symbol: str
    rate_per_minute: float = 0.0
    long_short_imbalance: float = 0.0
    cascade_risk: float = 0.0
    recent_summary: Optional[Dict[str, int]] = None
    timestamp: Optional[str] = None

    @classmethod
    def from_dict(cls, d: dict) -> "LiquidationData":
        return cls(
            symbol=d.get("symbol", ""),
            rate_per_minute=d.get("rate_per_minute", 0.0),
            long_short_imbalance=d.get("long_short_imbalance", 0.0),
            cascade_risk=d.get("cascade_risk", 0.0),
            recent_summary=d.get("recent_summary"),
            timestamp=d.get("timestamp"),
        )


@dataclass
class FlowOverview:
    """Response from /v1/flow/overview."""
    exchanges: Dict[str, Any] = field(default_factory=dict)
    timestamp: Optional[str] = None

    @classmethod
    def from_dict(cls, d: dict) -> "FlowOverview":
        return cls(
            exchanges=d.get("exchanges", {}),
            timestamp=d.get("timestamp"),
        )


@dataclass
class LargeTradeData:
    """Response from /v1/flow/large-trades/{symbol}."""
    symbol: str
    count_5m: int = 0
    buy_sell_ratio: float = 0.0
    institutional_score: float = 0.0
    timestamp: Optional[str] = None

    @classmethod
    def from_dict(cls, d: dict) -> "LargeTradeData":
        return cls(
            symbol=d.get("symbol", ""),
            count_5m=d.get("count_5m", 0),
            buy_sell_ratio=d.get("buy_sell_ratio", 0.0),
            institutional_score=d.get("institutional_score", 0.0),
            timestamp=d.get("timestamp"),
        )


@dataclass
class CVDData:
    """Response from /v1/flow/cvd/{symbol}."""
    symbol: str
    cvd_1m: float = 0.0
    cvd_5m: float = 0.0
    cvd_slope: float = 0.0
    taker_ratio: float = 0.0
    timestamp: Optional[str] = None

    @classmethod
    def from_dict(cls, d: dict) -> "CVDData":
        return cls(
            symbol=d.get("symbol", ""),
            cvd_1m=d.get("cvd_1m", 0.0),
            cvd_5m=d.get("cvd_5m", 0.0),
            cvd_slope=d.get("cvd_slope", 0.0),
            taker_ratio=d.get("taker_ratio", 0.0),
            timestamp=d.get("timestamp"),
        )


@dataclass
class SpotFuturesData:
    """Response from /v1/flow/spot-futures/{symbol}."""
    symbol: str
    basis_spread: float = 0.0
    depth_divergence: float = 0.0
    ofi_lead_lag: float = 0.0
    whale_agreement: float = 0.0
    liquidity_ratio: float = 0.0
    timestamp: Optional[str] = None

    @classmethod
    def from_dict(cls, d: dict) -> "SpotFuturesData":
        return cls(
            symbol=d.get("symbol", ""),
            basis_spread=d.get("basis_spread", 0.0),
            depth_divergence=d.get("depth_divergence", 0.0),
            ofi_lead_lag=d.get("ofi_lead_lag", 0.0),
            whale_agreement=d.get("whale_agreement", 0.0),
            liquidity_ratio=d.get("liquidity_ratio", 0.0),
            timestamp=d.get("timestamp"),
        )


@dataclass
class DerivativesData:
    """Response from /v1/flow/derivatives."""
    symbol: str = ""
    funding_agreement: float = 0.0
    oi_divergence: float = 0.0
    ls_consensus: float = 0.0
    timestamp: Optional[str] = None

    @classmethod
    def from_dict(cls, d: dict) -> "DerivativesData":
        return cls(
            symbol=d.get("symbol", ""),
            funding_agreement=d.get("funding_agreement", 0.0),
            oi_divergence=d.get("oi_divergence", 0.0),
            ls_consensus=d.get("ls_consensus", 0.0),
            timestamp=d.get("timestamp"),
        )


@dataclass
class PositioningData:
    """Response from /v1/flow/positioning/{symbol}."""
    symbol: str
    positioning: Dict[str, Any] = field(default_factory=dict)
    timestamp: Optional[str] = None

    @classmethod
    def from_dict(cls, d: dict) -> "PositioningData":
        return cls(
            symbol=d.get("symbol", ""),
            positioning=d.get("positioning", {}),
            timestamp=d.get("timestamp"),
        )


@dataclass
class FlowAssetData:
    """Response from /v1/flow/asset/{symbol}."""
    symbol: str
    timestamp: Optional[str] = None
    exchanges: Dict[str, Any] = field(default_factory=dict)

    @classmethod
    def from_dict(cls, d: dict) -> "FlowAssetData":
        return cls(
            symbol=d.get("symbol", ""),
            timestamp=d.get("timestamp"),
            exchanges={k: v for k, v in d.items() if k not in ("symbol", "timestamp")},
        )


# --- Historical ---


@dataclass
class HistoricalFeaturesData:
    """Response from /v1/historical/features/{symbol}."""
    symbol: str = ""
    exchange: Optional[str] = None
    data: List[Dict[str, Any]] = field(default_factory=list)
    count: int = 0

    @classmethod
    def from_dict(cls, d: dict) -> "HistoricalFeaturesData":
        return cls(
            symbol=d.get("symbol", ""),
            exchange=d.get("exchange"),
            data=d.get("data", []),
            count=len(d.get("data", [])),
        )


@dataclass
class HistoricalCompositesData:
    """Response from /v1/historical/composites/{symbol}."""
    symbol: str = ""
    data: List[Dict[str, Any]] = field(default_factory=list)
    count: int = 0

    @classmethod
    def from_dict(cls, d: dict) -> "HistoricalCompositesData":
        return cls(
            symbol=d.get("symbol", ""),
            data=d.get("data", []),
            count=len(d.get("data", [])),
        )


# --- Advanced Analytics (Mar 17, 2026) ---


@dataclass
class RegimeData:
    """Response from /v1/regime/{symbol}."""
    symbol: str = ""
    regime: str = "unknown"
    confidence: float = 0.0
    distribution: Dict[str, float] = field(default_factory=dict)
    fitted: bool = False
    recent_regimes: List[str] = field(default_factory=list)
    exchange_source: Optional[str] = None
    status: str = "ok"
    data_age_seconds: Optional[float] = None

    @classmethod
    def from_dict(cls, d: dict) -> "RegimeData":
        return cls(
            symbol=d.get("symbol", ""),
            regime=d.get("regime", "unknown"),
            confidence=d.get("confidence", 0.0),
            distribution=d.get("distribution", {}),
            fitted=d.get("fitted", False),
            recent_regimes=d.get("recent_regimes", []),
            exchange_source=d.get("exchange_source"),
            status=d.get("status", "ok"),
            data_age_seconds=d.get("data_age_seconds"),
        )


@dataclass
class DriftData:
    """Response from /v1/drift/{symbol}."""
    overall_status: str = "unknown"
    drift_score: float = 0.0
    drifted_features: int = 0
    groups: Dict[str, Any] = field(default_factory=dict)
    total_features: int = 213
    scope: str = "global"
    status: str = "ok"
    data_age_seconds: Optional[float] = None

    @classmethod
    def from_dict(cls, d: dict) -> "DriftData":
        return cls(
            overall_status=d.get("overall_status", "unknown"),
            drift_score=d.get("drift_score", 0.0),
            drifted_features=d.get("drifted_features", 0),
            groups=d.get("groups", {}),
            total_features=d.get("total_features", 213),
            scope=d.get("scope", "global"),
            status=d.get("status", "ok"),
            data_age_seconds=d.get("data_age_seconds"),
        )


@dataclass
class CascadeRiskData:
    """Response from /v1/cascade-risk/{symbol}."""
    symbol: str = ""
    cascade_probability: float = 0.0
    liquidation_rate: float = 0.0
    long_short_imbalance: float = 0.0
    depth_thinning: float = 0.0
    partial: bool = False

    @classmethod
    def from_dict(cls, d: dict) -> "CascadeRiskData":
        return cls(
            symbol=d.get("symbol", ""),
            cascade_probability=d.get("cascade_probability", 0.0),
            liquidation_rate=d.get("liquidation_rate", 0.0),
            long_short_imbalance=d.get("long_short_imbalance", 0.0),
            depth_thinning=d.get("depth_thinning", 0.0),
            partial=d.get("partial", False),
        )


@dataclass
class ConsensusData:
    """Response from /v1/consensus/{symbol}."""
    directional_consensus: float = 0.0
    statistical_bias: str = "neutral"
    model_agreement: str = "0/0"
    regime_context: str = "unknown"
    confidence_band: str = "low"
    scope: str = "global"
    status: str = "ok"
    data_age_seconds: Optional[float] = None
    disclaimer: str = ""

    @classmethod
    def from_dict(cls, d: dict) -> "ConsensusData":
        return cls(
            directional_consensus=d.get("directional_consensus", 0.0),
            statistical_bias=d.get("statistical_bias", "neutral"),
            model_agreement=d.get("model_agreement", "0/0"),
            regime_context=d.get("regime_context", "unknown"),
            confidence_band=d.get("confidence_band", "low"),
            scope=d.get("scope", "global"),
            status=d.get("status", "ok"),
            data_age_seconds=d.get("data_age_seconds"),
            disclaimer=d.get("disclaimer", ""),
        )


@dataclass
class ArbitrageOpportunity:
    """Single arbitrage opportunity."""
    symbol: str = ""
    buy_exchange: str = ""
    sell_exchange: str = ""
    spread_pct: float = 0.0
    net_profit_pct: float = 0.0
    timestamp: Optional[str] = None

    @classmethod
    def from_dict(cls, d: dict) -> "ArbitrageOpportunity":
        return cls(
            symbol=d.get("symbol", ""),
            buy_exchange=d.get("buy_exchange", ""),
            sell_exchange=d.get("sell_exchange", ""),
            spread_pct=d.get("spread_pct", 0.0),
            net_profit_pct=d.get("net_profit_pct", 0.0),
            timestamp=d.get("timestamp"),
        )


@dataclass
class ArbitrageData:
    """Response from /v1/arbitrage."""
    count: int = 0
    opportunities: List[ArbitrageOpportunity] = field(default_factory=list)
    timestamp: Optional[str] = None

    @classmethod
    def from_dict(cls, d: dict) -> "ArbitrageData":
        return cls(
            count=d.get("count", 0),
            opportunities=[ArbitrageOpportunity.from_dict(o) for o in d.get("opportunities", [])],
            timestamp=d.get("timestamp"),
        )


@dataclass
class MarketProtectionData:
    """Response from /v1/market-protection/{symbol}."""
    symbol: str = ""
    integrity_score: float = 0.5
    detectors: Dict[str, float] = field(default_factory=dict)

    @classmethod
    def from_dict(cls, d: dict) -> "MarketProtectionData":
        return cls(
            symbol=d.get("symbol", ""),
            integrity_score=d.get("integrity_score", 0.5),
            detectors=d.get("detectors", {}),
        )


@dataclass
class DepthProfileData:
    """Response from /v1/depth-profile/{symbol}."""
    symbol: str = ""
    total_depth_10bp: float = 0.0
    bid_depth_10bp: float = 0.0
    ask_depth_10bp: float = 0.0
    total_depth_50bp: float = 0.0
    bid_depth_50bp: float = 0.0
    ask_depth_50bp: float = 0.0
    asymmetry_10bp: float = 0.0
    asymmetry_50bp: float = 0.0
    kyle_lambda: float = 0.0
    vpin: float = 0.0

    @classmethod
    def from_dict(cls, d: dict) -> "DepthProfileData":
        return cls(
            symbol=d.get("symbol", ""),
            total_depth_10bp=d.get("total_depth_10bp", 0.0),
            bid_depth_10bp=d.get("bid_depth_10bp", 0.0),
            ask_depth_10bp=d.get("ask_depth_10bp", 0.0),
            total_depth_50bp=d.get("total_depth_50bp", 0.0),
            bid_depth_50bp=d.get("bid_depth_50bp", 0.0),
            ask_depth_50bp=d.get("ask_depth_50bp", 0.0),
            asymmetry_10bp=d.get("asymmetry_10bp", 0.0),
            asymmetry_50bp=d.get("asymmetry_50bp", 0.0),
            kyle_lambda=d.get("kyle_lambda", 0.0),
            vpin=d.get("vpin", 0.0),
        )


@dataclass
class BasisData:
    """Response from /v1/basis/{symbol}."""
    symbol: str = ""
    basis_spread: float = 0.0
    annualized_basis: float = 0.0
    lead_lag_score: float = 0.0
    ofi_divergence: float = 0.0
    funding_rate: float = 0.0
    open_interest_change: float = 0.0
    partial: bool = False

    @classmethod
    def from_dict(cls, d: dict) -> "BasisData":
        return cls(
            symbol=d.get("symbol", ""),
            basis_spread=d.get("basis_spread", 0.0),
            annualized_basis=d.get("annualized_basis", 0.0),
            lead_lag_score=d.get("lead_lag_score", 0.0),
            ofi_divergence=d.get("ofi_divergence", 0.0),
            funding_rate=d.get("funding_rate", 0.0),
            open_interest_change=d.get("open_interest_change", 0.0),
            partial=d.get("partial", False),
        )


@dataclass
class SymbolInfo:
    """Single symbol from /v1/symbols."""
    symbol: str
    base_asset: str = ""
    quote_asset: str = ""
    exchange: str = "all"

    @classmethod
    def from_dict(cls, d: dict) -> "SymbolInfo":
        return cls(
            symbol=d.get("symbol", ""),
            base_asset=d.get("baseAsset", ""),
            quote_asset=d.get("quoteAsset", ""),
            exchange=d.get("exchange", "all"),
        )


# --- Flow Intelligence (additional) ---


@dataclass
class SentimentData:
    """Response from /v1/flow/sentiment/{symbol}."""
    symbol: str = ""
    sentiment: Dict[str, Any] = field(default_factory=dict)
    timestamp: Optional[str] = None

    @classmethod
    def from_dict(cls, d: dict) -> "SentimentData":
        reserved = {"symbol", "timestamp"}
        sentiment = d.get("sentiment", {})
        if not sentiment:
            sentiment = {k: v for k, v in d.items() if k not in reserved}
        return cls(
            symbol=d.get("symbol", ""),
            sentiment=sentiment,
            timestamp=d.get("timestamp"),
        )


# --- Aggregated Timeframes ---


@dataclass
class AggregatedFeaturesData:
    """Response from /v1/aggregated/features/{symbol}."""
    symbol: str = ""
    exchange: str = ""
    timeframe: str = ""
    features: Dict[str, Any] = field(default_factory=dict)
    source: str = ""
    timestamp: Optional[str] = None

    @classmethod
    def from_dict(cls, d: dict) -> "AggregatedFeaturesData":
        return cls(
            symbol=d.get("symbol", ""),
            exchange=d.get("exchange", ""),
            timeframe=d.get("timeframe", ""),
            features=d.get("features", {}),
            source=d.get("source", ""),
            timestamp=d.get("timestamp"),
        )


@dataclass
class AggregatedCompositesData:
    """Response from /v1/aggregated/composites/{symbol}."""
    symbol: str = ""
    timeframe: str = ""
    composites: Dict[str, Optional[float]] = field(default_factory=dict)
    source: str = ""
    timestamp: Optional[str] = None

    @classmethod
    def from_dict(cls, d: dict) -> "AggregatedCompositesData":
        return cls(
            symbol=d.get("symbol", ""),
            timeframe=d.get("timeframe", ""),
            composites=d.get("composites", {}),
            source=d.get("source", ""),
            timestamp=d.get("timestamp"),
        )


# --- Venue Analytics ---


@dataclass
class VenueSelectionData:
    """Response from /v1/venue-selection/{symbol}."""
    symbol: str = ""
    recommended_venue: Optional[str] = None
    best_spread_bps: Optional[float] = None
    worst_spread_bps: Optional[float] = None
    cost_ratio: Optional[float] = None
    exchanges_checked: int = 0
    venues: List[Dict[str, Any]] = field(default_factory=list)
    timestamp: Optional[float] = None

    @classmethod
    def from_dict(cls, d: dict) -> "VenueSelectionData":
        return cls(
            symbol=d.get("symbol", ""),
            recommended_venue=d.get("recommended_venue"),
            best_spread_bps=d.get("best_spread_bps"),
            worst_spread_bps=d.get("worst_spread_bps"),
            cost_ratio=d.get("cost_ratio"),
            exchanges_checked=d.get("exchanges_checked", 0),
            venues=d.get("venues", []),
            timestamp=d.get("timestamp"),
        )


@dataclass
class DepthResilienceData:
    """Response from /v1/depth-resilience/{symbol}."""
    symbol: str = ""
    deepest_venue: Optional[str] = None
    exchanges_checked: int = 0
    venues: List[Dict[str, Any]] = field(default_factory=list)
    timestamp: Optional[float] = None

    @classmethod
    def from_dict(cls, d: dict) -> "DepthResilienceData":
        return cls(
            symbol=d.get("symbol", ""),
            deepest_venue=d.get("deepest_venue"),
            exchanges_checked=d.get("exchanges_checked", 0),
            venues=d.get("venues", []),
            timestamp=d.get("timestamp"),
        )


@dataclass
class SpreadEventsData:
    """Response from /v1/spread-events/{symbol}."""
    symbol: str = ""
    threshold: float = 3.0
    median_spread_bps: float = 0.0
    active_events: int = 0
    status: str = "normal"
    events: List[Dict[str, Any]] = field(default_factory=list)
    exchanges_checked: int = 0
    timestamp: Optional[float] = None

    @classmethod
    def from_dict(cls, d: dict) -> "SpreadEventsData":
        return cls(
            symbol=d.get("symbol", ""),
            threshold=d.get("threshold", 3.0),
            median_spread_bps=d.get("median_spread_bps", 0.0),
            active_events=d.get("active_events", 0),
            status=d.get("status", "normal"),
            events=d.get("events", []),
            exchanges_checked=d.get("exchanges_checked", 0),
            timestamp=d.get("timestamp"),
        )


@dataclass
class ExecutionCostData:
    """Response from /v1/execution-cost/{symbol}."""
    symbol: str = ""
    trade_size_usd: float = 0.0
    cheapest_venue: Optional[str] = None
    cheapest_cost_usd: Optional[float] = None
    most_expensive_venue: Optional[str] = None
    most_expensive_cost_usd: Optional[float] = None
    potential_savings_usd: float = 0.0
    exchanges_compared: int = 0
    venues: List[Dict[str, Any]] = field(default_factory=list)
    timestamp: Optional[float] = None

    @classmethod
    def from_dict(cls, d: dict) -> "ExecutionCostData":
        return cls(
            symbol=d.get("symbol", ""),
            trade_size_usd=d.get("trade_size_usd", 0.0),
            cheapest_venue=d.get("cheapest_venue"),
            cheapest_cost_usd=d.get("cheapest_cost_usd"),
            most_expensive_venue=d.get("most_expensive_venue"),
            most_expensive_cost_usd=d.get("most_expensive_cost_usd"),
            potential_savings_usd=d.get("potential_savings_usd", 0.0),
            exchanges_compared=d.get("exchanges_compared", 0),
            venues=d.get("venues", []),
            timestamp=d.get("timestamp"),
        )


@dataclass
class CorrelationData:
    """Response from /v1/correlation/{symbol1}/{symbol2}."""
    symbol1: str = ""
    symbol2: str = ""
    correlation: Optional[float] = None
    interpretation: str = "unknown"
    common_exchanges: int = 0
    exchanges: List[str] = field(default_factory=list)
    status: str = "ok"
    timestamp: Optional[float] = None

    @classmethod
    def from_dict(cls, d: dict) -> "CorrelationData":
        return cls(
            symbol1=d.get("symbol1", ""),
            symbol2=d.get("symbol2", ""),
            correlation=d.get("correlation"),
            interpretation=d.get("interpretation", "unknown"),
            common_exchanges=d.get("common_exchanges", 0),
            exchanges=d.get("exchanges", []),
            status=d.get("status", "ok"),
            timestamp=d.get("timestamp"),
        )


@dataclass
class WhaleFlowData:
    """Response from /v1/whale-flow/{symbol}."""
    symbol: str = ""
    aggregate_direction: float = 0.0
    aggregate_pressure: str = "neutral"
    avg_whale_bid_fraction: float = 0.0
    avg_whale_ask_fraction: float = 0.0
    exchanges_reporting: int = 0
    venues: List[Dict[str, Any]] = field(default_factory=list)
    timestamp: Optional[float] = None

    @classmethod
    def from_dict(cls, d: dict) -> "WhaleFlowData":
        return cls(
            symbol=d.get("symbol", ""),
            aggregate_direction=d.get("aggregate_direction", 0.0),
            aggregate_pressure=d.get("aggregate_pressure", "neutral"),
            avg_whale_bid_fraction=d.get("avg_whale_bid_fraction", 0.0),
            avg_whale_ask_fraction=d.get("avg_whale_ask_fraction", 0.0),
            exchanges_reporting=d.get("exchanges_reporting", 0),
            venues=d.get("venues", []),
            timestamp=d.get("timestamp"),
        )
