"""Tests for the paper exchange."""

import pytest

from horizon._horizon import (
    Engine,
    OrderRequest,
    OrderSide,
    RiskConfig,
    Side,
)


@pytest.fixture
def engine():
    config = RiskConfig(max_position_per_market=1000.0, max_portfolio_notional=10000.0)
    return Engine(risk_config=config, paper_fee_rate=0.001)


def buy_order(market="mkt_1", price=0.55, size=10.0):
    return OrderRequest(
        market_id=market,
        side=Side.Yes,
        order_side=OrderSide.Buy,
        size=size,
        price=price,
    )


def sell_order(market="mkt_1", price=0.60, size=10.0):
    return OrderRequest(
        market_id=market,
        side=Side.Yes,
        order_side=OrderSide.Sell,
        size=size,
        price=price,
    )


class TestPaperExchange:
    def test_submit_rests_order(self, engine):
        engine.submit_order(buy_order(price=0.55))
        # Price above -> no fill
        fills = engine.tick("mkt_1", 0.60)
        assert fills == 0
        assert engine.recent_fills() == []

    def test_buy_fills_when_price_drops(self, engine):
        engine.submit_order(buy_order(price=0.55))
        fills = engine.tick("mkt_1", 0.55)
        assert fills == 1

        recent = engine.recent_fills()
        assert len(recent) == 1
        assert abs(recent[0].price - 0.55) < 1e-10

        positions = engine.positions()
        assert len(positions) == 1
        assert abs(positions[0].size - 10.0) < 1e-10

    def test_sell_fills_when_price_rises(self, engine):
        # First buy to have a position
        engine.submit_order(buy_order(price=0.55))
        engine.tick("mkt_1", 0.55)

        # Then sell
        engine.submit_order(sell_order(price=0.60))
        fills = engine.tick("mkt_1", 0.60)
        assert fills == 1

    def test_multiple_orders_fill(self, engine):
        engine.submit_order(buy_order(price=0.55, size=5.0))
        engine.submit_order(buy_order(price=0.50, size=5.0))

        # Only the 0.55 order should fill at 0.53
        fills = engine.tick("mkt_1", 0.53)
        assert fills == 1

        # Now both should fill at 0.50
        fills = engine.tick("mkt_1", 0.50)
        assert fills == 1  # only the remaining 0.50 order

    def test_cancel_prevents_fill(self, engine):
        order_id = engine.submit_order(buy_order(price=0.55))
        engine.cancel(order_id)

        fills = engine.tick("mkt_1", 0.50)
        assert fills == 0

    def test_cancel_all(self, engine):
        engine.submit_order(buy_order(price=0.50))
        engine.submit_order(buy_order(price=0.51))
        engine.submit_order(buy_order(price=0.52))

        count = engine.cancel_all()
        assert count == 3

        fills = engine.tick("mkt_1", 0.40)
        assert fills == 0
