"""Cross-platform price aggregation.

Provides unified price views across multiple exchanges for the same
market/event. Supports composite pricing, best bid/ask across venues,
and price divergence detection.
"""

from __future__ import annotations

import logging
from dataclasses import dataclass

logger = logging.getLogger("horizon.aggregator")


@dataclass(frozen=True, slots=True)
class AggregatedPrice:
    """Unified price across exchanges for one market."""
    market_id: str
    best_bid: float
    best_ask: float
    best_bid_exchange: str
    best_ask_exchange: str
    composite_mid: float
    divergence: float
    num_exchanges: int


class PriceAggregator:
    """Aggregates prices across exchanges for the same market/event.

    Usage:
        agg = PriceAggregator()
        agg.add_mapping("btc_election", ["poly_feed", "kalshi_feed"])
        result = agg.aggregate(engine, "btc_election")
    """

    def __init__(self) -> None:
        # market_id -> list of (feed_name, exchange_name)
        self._mappings: dict[str, list[tuple[str, str]]] = {}

    def add_mapping(
        self,
        market_id: str,
        feed_exchange_pairs: list[tuple[str, str]],
    ) -> None:
        """Register which feeds map to this market on which exchanges.

        Args:
            market_id: Logical market identifier.
            feed_exchange_pairs: List of (feed_name, exchange_name) tuples.
                e.g., [("poly_btc", "polymarket"), ("kalshi_btc", "kalshi")]
        """
        self._mappings[market_id] = list(feed_exchange_pairs)

    def aggregate(self, engine, market_id: str) -> AggregatedPrice | None:
        """Compute aggregated price for a market across all mapped exchanges.

        Returns None if no feed data is available.
        """
        pairs = self._mappings.get(market_id, [])
        if not pairs:
            return None

        # Single-pass: fetch each snapshot once and derive all metrics
        bids: list[tuple[float, str]] = []
        asks: list[tuple[float, str]] = []
        mids: list[float] = []
        num_exchanges = 0

        for feed_name, exchange_name in pairs:
            snap = engine.feed_snapshot(feed_name)
            if snap is None or snap.price <= 0:
                continue
            num_exchanges += 1
            if snap.bid > 0:
                bids.append((snap.bid, exchange_name))
            if snap.ask > 0:
                asks.append((snap.ask, exchange_name))
            if snap.bid > 0 and snap.ask > 0:
                mids.append((snap.bid + snap.ask) / 2.0)

        if not bids and not asks:
            return None

        # Best bid = highest bid across exchanges
        best_bid, best_bid_ex = max(bids, key=lambda x: x[0]) if bids else (0.0, "")
        # Best ask = lowest ask across exchanges
        best_ask, best_ask_ex = min(asks, key=lambda x: x[0]) if asks else (0.0, "")

        composite_mid = sum(mids) / len(mids) if mids else 0.0

        # Divergence: max spread between exchanges
        divergence = 0.0
        if len(mids) >= 2:
            divergence = max(mids) - min(mids)

        return AggregatedPrice(
            market_id=market_id,
            best_bid=best_bid,
            best_ask=best_ask,
            best_bid_exchange=best_bid_ex,
            best_ask_exchange=best_ask_ex,
            composite_mid=composite_mid,
            divergence=divergence,
            num_exchanges=num_exchanges,
        )

    def divergence_alert(
        self, engine, market_id: str, threshold: float = 0.02,
    ) -> bool:
        """Check if price divergence across exchanges exceeds threshold."""
        result = self.aggregate(engine, market_id)
        if result is None:
            return False
        return result.divergence > threshold
