"""Tests for the Engine."""

import pytest

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


@pytest.fixture
def engine():
    config = RiskConfig(max_position_per_market=100.0)
    return Engine(risk_config=config)


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=5.0):
    return OrderRequest(
        market_id=market,
        side=Side.Yes,
        order_side=OrderSide.Sell,
        size=size,
        price=price,
    )


class TestEngine:
    def test_create_engine(self):
        engine = Engine()
        assert engine.exchange_name() == "paper"

    def test_submit_order(self, engine):
        order_id = engine.submit_order(buy_order())
        assert order_id.startswith("p")

    def test_status(self, engine):
        status = engine.status()
        assert status.running is True
        assert status.open_orders == 0
        assert status.active_positions == 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

    def test_positions_empty(self, engine):
        assert engine.positions() == []

    def test_recent_fills_empty(self, engine):
        assert engine.recent_fills() == []

    def test_process_fill(self, engine):
        fill = Fill(
            fill_id="f1",
            order_id="o1",
            market_id="mkt_1",
            side=Side.Yes,
            order_side=OrderSide.Buy,
            price=0.55,
            size=10.0,
        )
        engine.process_fill(fill)
        positions = engine.positions()
        assert len(positions) == 1
        assert abs(positions[0].size - 10.0) < 1e-10
        assert abs(positions[0].avg_entry_price - 0.55) < 1e-10

    def test_mark_price_updates_unrealized_pnl(self, engine):
        fill = Fill(
            fill_id="f1",
            order_id="o1",
            market_id="mkt_1",
            side=Side.Yes,
            order_side=OrderSide.Buy,
            price=0.50,
            size=10.0,
        )
        engine.process_fill(fill)
        engine.update_mark_price("mkt_1", Side.Yes, 0.60)

        positions = engine.positions()
        assert len(positions) == 1
        # unrealized = (0.60 - 0.50) * 10 = 1.0
        assert abs(positions[0].unrealized_pnl - 1.0) < 1e-10


# ---------------------------------------------------------------------------
# paper_partial_fill_ratio validation tests (5C fix verification)
# ---------------------------------------------------------------------------


class TestPaperPartialFillRatioValidation:
    def test_valid_ratio(self):
        engine = Engine(paper_partial_fill_ratio=0.5)
        assert engine.exchange_name() == "paper"

    def test_ratio_one(self):
        engine = Engine(paper_partial_fill_ratio=1.0)
        assert engine.exchange_name() == "paper"

    def test_ratio_zero_rejects(self):
        with pytest.raises(ValueError, match="paper_partial_fill_ratio"):
            Engine(paper_partial_fill_ratio=0.0)

    def test_ratio_negative_rejects(self):
        with pytest.raises(ValueError, match="paper_partial_fill_ratio"):
            Engine(paper_partial_fill_ratio=-0.5)

    def test_ratio_above_one_rejects(self):
        with pytest.raises(ValueError, match="paper_partial_fill_ratio"):
            Engine(paper_partial_fill_ratio=1.5)

    def test_ratio_nan_rejects(self):
        with pytest.raises(ValueError, match="paper_partial_fill_ratio"):
            Engine(paper_partial_fill_ratio=float("nan"))
