"""Inventory skew pipeline modifier for hz.run().

Adjusts bid/ask quotes based on current inventory to encourage
position reduction. Long positions push prices down (encourage selling),
short positions push prices up (encourage buying).

Usage:
    hz.run(
        pipeline=[model, quoter, hz.inventory_skewer()],
        ...
    )
"""

from __future__ import annotations

import time
from typing import Callable

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


def inventory_skewer(
    max_skew: float = 0.03,
    half_life_secs: float = 300.0,
    skew_factor: float = 1.0,
    max_position: float = 100.0,
) -> Callable[[Context, list[Quote]], list[Quote]]:
    """Create an inventory skew modifier.

    Shifts bid and ask prices based on current inventory to encourage
    mean reversion of positions. As inventory ages, urgency increases.

    Args:
        max_skew: Maximum price skew (e.g. 0.03 = 3 cents).
        half_life_secs: Position age half-life for urgency calculation.
        skew_factor: Multiplier for skew intensity (1.0 = normal).
        max_position: Position size that maps to maximum skew.

    Returns:
        Pipeline function: (Context, list[Quote]) -> list[Quote]
    """
    # Per-market position entry time tracking
    _entry_times: dict[str, float] = {}
    _prev_positions: dict[str, float] = {}

    def _skew(ctx: Context, quotes: list[Quote] | None) -> list[Quote]:
        if not quotes:
            return []

        market_id = ctx.market.id if ctx.market else "__default__"
        inventory = ctx.inventory.net_for_market(market_id) if ctx.market else ctx.inventory.net

        now = time.time()

        # Track entry time: reset when position flips to zero
        prev_pos = _prev_positions.get(market_id, 0.0)
        if abs(inventory) < 1e-10:
            _entry_times.pop(market_id, None)
        elif abs(prev_pos) < 1e-10 and abs(inventory) >= 1e-10:
            _entry_times[market_id] = now
        elif market_id not in _entry_times:
            _entry_times[market_id] = now
        _prev_positions[market_id] = inventory

        # Compute skew
        if abs(inventory) < 1e-10 or max_position <= 0:
            return list(quotes)

        inventory_ratio = max(-1.0, min(1.0, inventory / max_position)) * skew_factor

        # Urgency grows from 1.0 to 2.0 as position ages
        entry_time = _entry_times.get(market_id, now)
        age_secs = max(0.0, now - entry_time)
        urgency = 1.0 + (1.0 - decay_weight(age_secs, half_life_secs))

        skew = inventory_ratio * urgency * max_skew

        result = []
        for q in quotes:
            # Both bid and ask shift by -skew
            # Long inventory (positive skew) → push prices down → encourage selling
            new_bid = q.bid - skew
            new_ask = q.ask - skew

            # Clamp to prediction market range [0.01, 0.99]
            new_bid = max(0.01, min(0.99, new_bid))
            new_ask = max(0.01, min(0.99, new_ask))

            # Ensure bid < ask
            if new_bid >= new_ask:
                mid = (new_bid + new_ask) / 2.0
                new_bid = max(0.01, mid - 0.005)
                new_ask = min(0.99, mid + 0.005)
                if new_bid >= new_ask:
                    continue

            result.append(Quote(bid=new_bid, ask=new_ask, size=q.size))

        return result

    _skew.__name__ = "inventory_skewer"
    return _skew
