"""Tests for market lifecycle management."""

import time

import pytest
from horizon import Engine, RiskConfig, OrderRequest, OrderSide, Side
from horizon.lifecycle import (
    register_expiry,
    register_market_expiries,
    check_lifecycle,
    time_to_expiry,
    approaching_expiry,
)
from horizon._horizon import Market


@pytest.fixture
def engine():
    config = RiskConfig(
        max_position_per_market=1000.0,
        max_portfolio_notional=10000.0,
        max_order_size=500.0,
        dedup_window_ms=0,
    )
    return Engine(risk_config=config, paper_fee_rate=0.0)


class TestEngineLifecycle:
    def test_set_and_get_expiry(self, engine):
        ts = time.time() + 3600
        engine.set_market_expiry("mkt_1", ts)
        expiries = engine.market_expiries()
        assert "mkt_1" in expiries
        assert abs(expiries["mkt_1"] - ts) < 1e-6

    def test_is_market_expired_false(self, engine):
        engine.set_market_expiry("mkt_1", time.time() + 3600)
        assert not engine.is_market_expired("mkt_1")

    def test_is_market_expired_true(self, engine):
        engine.set_market_expiry("mkt_1", time.time() - 1.0)
        assert engine.is_market_expired("mkt_1")

    def test_is_market_expired_unknown(self, engine):
        assert not engine.is_market_expired("unknown_mkt")

    def test_check_lifecycle_no_expiries(self, engine):
        acted = engine.check_lifecycle()
        assert acted == []

    def test_check_lifecycle_cancels_approaching(self, engine):
        """Orders should be canceled when market is within lead time of expiry."""
        # Set expiry in 10 seconds
        engine.set_market_expiry("mkt_1", time.time() + 10.0)
        engine.set_lifecycle_lead_secs(60.0)  # 60s lead > 10s remaining

        # Place an order
        req = OrderRequest(
            market_id="mkt_1", side=Side.Yes, order_side=OrderSide.Buy,
            size=10.0, price=0.50,
        )
        engine.submit_order(req)
        assert len(engine.open_orders()) == 1

        # Lifecycle check should cancel it
        acted = engine.check_lifecycle()
        assert "mkt_1" in acted
        assert len(engine.open_orders()) == 0

    def test_check_lifecycle_ignores_far_future(self, engine):
        """Markets far from expiry should not be affected."""
        engine.set_market_expiry("mkt_1", time.time() + 86400)  # 24h away
        engine.set_lifecycle_lead_secs(300.0)  # 5min lead

        req = OrderRequest(
            market_id="mkt_1", side=Side.Yes, order_side=OrderSide.Buy,
            size=10.0, price=0.50,
        )
        engine.submit_order(req)

        acted = engine.check_lifecycle()
        assert acted == []
        assert len(engine.open_orders()) == 1

    def test_set_lifecycle_lead_secs(self, engine):
        engine.set_lifecycle_lead_secs(600.0)
        # Verify by behavior: set expiry 500s out, lead=600 → should trigger
        engine.set_market_expiry("mkt_1", time.time() + 500.0)
        req = OrderRequest(
            market_id="mkt_1", side=Side.Yes, order_side=OrderSide.Buy,
            size=10.0, price=0.50,
        )
        engine.submit_order(req)
        acted = engine.check_lifecycle()
        assert "mkt_1" in acted


class TestLifecycleHelpers:
    def test_register_expiry_float(self, engine):
        ts = time.time() + 3600
        assert register_expiry(engine, "mkt_1", ts)
        assert "mkt_1" in engine.market_expiries()

    def test_register_expiry_iso_string(self, engine):
        assert register_expiry(engine, "mkt_1", "2030-01-01T00:00:00Z")
        assert "mkt_1" in engine.market_expiries()

    def test_register_expiry_none(self, engine):
        assert not register_expiry(engine, "mkt_1", None)

    def test_register_expiry_bad_string(self, engine):
        assert not register_expiry(engine, "mkt_1", "not-a-date")

    def test_register_market_expiries(self, engine):
        m1 = Market(id="mkt_1", expiry="2030-06-15T12:00:00Z")
        m2 = Market(id="mkt_2", expiry=None)
        m3 = Market(id="mkt_3", expiry="2030-12-31T23:59:59Z")
        count = register_market_expiries(engine, [m1, m2, m3])
        assert count == 2

    def test_check_lifecycle_wrapper(self, engine):
        acted = check_lifecycle(engine)
        assert acted == []

    def test_time_to_expiry(self, engine):
        engine.set_market_expiry("mkt_1", time.time() + 100.0)
        tte = time_to_expiry(engine, "mkt_1")
        assert tte is not None
        assert 99.0 <= tte <= 101.0

    def test_time_to_expiry_unknown(self, engine):
        assert time_to_expiry(engine, "unknown") is None

    def test_approaching_expiry_true(self, engine):
        engine.set_market_expiry("mkt_1", time.time() + 100.0)
        assert approaching_expiry(engine, "mkt_1", lead_secs=200.0)

    def test_approaching_expiry_false(self, engine):
        engine.set_market_expiry("mkt_1", time.time() + 3600.0)
        assert not approaching_expiry(engine, "mkt_1", lead_secs=300.0)

    def test_approaching_expiry_unknown(self, engine):
        assert not approaching_expiry(engine, "unknown")
