"""Tests for inventory skew modifier."""

import os

os.environ.setdefault("HORIZON_API_KEY", "test-key-123")

from horizon._horizon import Market, Quote, Side, Position
from horizon.context import Context, FeedData, InventorySnapshot
from horizon.inventory_skew import inventory_skewer


def _make_ctx(market_id="mkt1", inventory=0.0, bid=0.45, ask=0.55):
    market = Market(id=market_id, name=market_id, slug=market_id)
    positions = []
    if inventory > 0:
        positions = [_mock_position(market_id, Side.Yes, abs(inventory))]
    elif inventory < 0:
        positions = [_mock_position(market_id, Side.No, abs(inventory))]
    return Context(
        feeds={"feed": FeedData(price=0.5, bid=bid, ask=ask, timestamp=1.0)},
        inventory=InventorySnapshot(positions=positions),
        market=market,
        params={},
    )


def _mock_position(market_id, side, size):
    """Create a position-like object for testing."""
    class MockPosition:
        def __init__(self, market_id, side, size):
            self.market_id = market_id
            self.side = side
            self.size = size
    return MockPosition(market_id, side, size)


class TestInventorySkewer:
    def test_no_inventory_no_skew(self):
        skewer = inventory_skewer()
        ctx = _make_ctx(inventory=0.0)
        quotes = [Quote(bid=0.45, ask=0.55, size=5.0)]
        result = skewer(ctx, quotes)
        assert len(result) == 1
        assert abs(result[0].bid - 0.45) < 1e-6
        assert abs(result[0].ask - 0.55) < 1e-6

    def test_long_inventory_pushes_down(self):
        skewer = inventory_skewer(max_skew=0.03, max_position=100.0)
        ctx = _make_ctx(inventory=50.0)
        quotes = [Quote(bid=0.45, ask=0.55, size=5.0)]
        result = skewer(ctx, quotes)
        assert len(result) == 1
        # Long → negative skew adjustment → prices pushed down
        assert result[0].bid < 0.45
        assert result[0].ask < 0.55

    def test_short_inventory_pushes_up(self):
        skewer = inventory_skewer(max_skew=0.03, max_position=100.0)
        ctx = _make_ctx(inventory=-50.0)
        quotes = [Quote(bid=0.45, ask=0.55, size=5.0)]
        result = skewer(ctx, quotes)
        assert len(result) == 1
        # Short → positive adjustment → prices pushed up
        assert result[0].bid > 0.45
        assert result[0].ask > 0.55

    def test_max_skew_capped(self):
        skewer = inventory_skewer(max_skew=0.02, max_position=10.0, skew_factor=1.0)
        ctx = _make_ctx(inventory=100.0)  # Way over max_position
        quotes = [Quote(bid=0.50, ask=0.52, size=5.0)]
        result = skewer(ctx, quotes)
        # Skew should be capped (inventory_ratio clamped to 1.0)
        assert len(result) >= 0  # May be empty if crossed

    def test_clamped_to_prediction_market_range(self):
        skewer = inventory_skewer(max_skew=0.5, max_position=10.0)
        ctx = _make_ctx(inventory=10.0)
        quotes = [Quote(bid=0.03, ask=0.05, size=5.0)]
        result = skewer(ctx, quotes)
        for q in result:
            assert q.bid >= 0.01
            assert q.ask <= 0.99

    def test_bid_less_than_ask_enforced(self):
        skewer = inventory_skewer(max_skew=0.5, max_position=10.0)
        ctx = _make_ctx(inventory=5.0)
        # Very tight spread that could cross
        quotes = [Quote(bid=0.499, ask=0.501, size=5.0)]
        result = skewer(ctx, quotes)
        for q in result:
            assert q.bid < q.ask

    def test_empty_quotes(self):
        skewer = inventory_skewer()
        ctx = _make_ctx(inventory=10.0)
        assert skewer(ctx, []) == []
        assert skewer(ctx, None) == []

    def test_multiple_quotes(self):
        skewer = inventory_skewer(max_skew=0.02, max_position=100.0)
        ctx = _make_ctx(inventory=50.0)
        quotes = [
            Quote(bid=0.40, ask=0.50, size=5.0),
            Quote(bid=0.45, ask=0.55, size=3.0),
        ]
        result = skewer(ctx, quotes)
        assert len(result) == 2
        # Both should be pushed down
        assert result[0].bid < 0.40
        assert result[1].bid < 0.45

    def test_skew_factor_amplifies(self):
        skewer_normal = inventory_skewer(max_skew=0.03, skew_factor=1.0, max_position=100.0)
        skewer_double = inventory_skewer(max_skew=0.03, skew_factor=2.0, max_position=100.0)
        ctx = _make_ctx(inventory=50.0)
        quotes = [Quote(bid=0.45, ask=0.55, size=5.0)]
        r1 = skewer_normal(ctx, quotes)
        r2 = skewer_double(ctx, quotes)
        # Double skew_factor should push more
        assert r2[0].bid < r1[0].bid

    def test_per_market_isolation(self):
        skewer = inventory_skewer(max_skew=0.03, max_position=100.0)
        # Market A: long
        ctx_a = _make_ctx(market_id="mkt_a", inventory=50.0)
        quotes = [Quote(bid=0.45, ask=0.55, size=5.0)]
        result_a = skewer(ctx_a, quotes)
        # Market B: no inventory
        ctx_b = _make_ctx(market_id="mkt_b", inventory=0.0)
        result_b = skewer(ctx_b, quotes)
        # A should be skewed, B should not
        assert result_a[0].bid < result_b[0].bid

    def test_function_name(self):
        skewer = inventory_skewer()
        assert skewer.__name__ == "inventory_skewer"
