"""Cross-market hedging pipeline modifier for hz.run().

Generates hedge quotes when portfolio delta exceeds a threshold,
using correlations between markets to calculate exposure.

Usage:
    hz.run(
        pipeline=[
            model,
            quoter,
            hz.cross_hedger(
                correlations={("btc_up", "btc_down"): -1.0},
                max_delta=50.0,
            ),
        ],
        ...
    )
"""

from __future__ import annotations

from typing import Callable

from horizon._horizon import Quote
from horizon.context import Context


def cross_hedger(
    correlations: dict[tuple[str, str], float] | None = None,
    max_delta: float = 50.0,
    hedge_size: float = 5.0,
    hedge_spread: float = 0.02,
    event_correlated: bool = True,
) -> Callable[[Context, list[Quote] | None], list[Quote]]:
    """Create a cross-market hedging modifier.

    Monitors portfolio delta and appends hedge quotes when exposure
    exceeds ``max_delta``.

    Correlation resolution (priority):
    1. Static: ``correlations`` dict (key or reversed key).
    2. Event-based: outcomes in same ``ctx.event`` → -1.0.
    3. Default: 0.0.

    Args:
        correlations: Static correlation map {(market_a, market_b): corr}.
        max_delta: Delta threshold that triggers hedging.
        hedge_size: Size of hedge quotes.
        hedge_spread: Spread for hedge quotes (tighter = more aggressive).
        event_correlated: If True, markets in the same event have corr=-1.0.

    Returns:
        Pipeline function: (Context, list[Quote] | None) -> list[Quote]
    """
    _corr = correlations or {}
    _position_cache: dict[str, float] = {}

    def _get_correlation(mkt_a: str, mkt_b: str, ctx: Context) -> float:
        """Resolve correlation between two markets."""
        # 1. Static lookup
        if (mkt_a, mkt_b) in _corr:
            return _corr[(mkt_a, mkt_b)]
        if (mkt_b, mkt_a) in _corr:
            return _corr[(mkt_b, mkt_a)]

        # 2. Event-based: same event → -1.0
        if event_correlated and ctx.event is not None:
            event_markets = {o.market_id for o in ctx.event.outcomes}
            if mkt_a in event_markets and mkt_b in event_markets:
                return -1.0

        # 3. Default
        return 0.0

    def _hedge(ctx: Context, quotes: list[Quote] | None) -> list[Quote]:
        result = list(quotes) if quotes else []

        market_id = ctx.market.id if ctx.market else None
        if market_id is None:
            return result

        # Update position cache
        inventory = ctx.inventory.net_for_market(market_id) if ctx.market else 0.0
        _position_cache[market_id] = inventory

        # Calculate portfolio delta relative to current market
        portfolio_delta = 0.0
        for other_mkt, other_pos in _position_cache.items():
            if other_mkt == market_id:
                continue
            corr = _get_correlation(market_id, other_mkt, ctx)
            portfolio_delta += other_pos * corr

        # Add own position
        portfolio_delta += inventory

        # Check if hedging is needed
        excess_delta = abs(portfolio_delta) - max_delta
        if excess_delta <= 0:
            return result

        # Get mid price for hedge quotes
        mid = 0.0
        for fd in ctx.feeds.values():
            if fd.bid > 0 and fd.ask > 0:
                mid = (fd.bid + fd.ask) / 2.0
                break
            if fd.price > 0:
                mid = fd.price
                break

        if mid <= 0:
            return result

        # Generate aggressive hedge quote
        qty = min(hedge_size, excess_delta)

        if portfolio_delta > 0:
            # Long exposure → sell to hedge
            hedge_bid = max(0.01, mid - hedge_spread)
            hedge_ask = min(0.99, mid + hedge_spread / 2.0)
        else:
            # Short exposure → buy to hedge
            hedge_bid = max(0.01, mid - hedge_spread / 2.0)
            hedge_ask = min(0.99, mid + hedge_spread)

        # Ensure bid < ask
        if hedge_bid >= hedge_ask:
            hedge_mid = (hedge_bid + hedge_ask) / 2.0
            hedge_bid = max(0.01, hedge_mid - 0.005)
            hedge_ask = min(0.99, hedge_mid + 0.005)

        if hedge_bid < hedge_ask:
            result.append(Quote(bid=hedge_bid, ask=hedge_ask, size=qty))

        return result

    _hedge.__name__ = "cross_hedger"
    return _hedge
