"""Deep test of context.py: Context, FeedData, InventorySnapshot."""
import sys
import time

# Must be able to import from the installed package
from horizon.context import Context, FeedData, InventorySnapshot
from horizon._horizon import (
    Engine, EngineStatus, Market, Position, Side, Fill, OrderSide,
    Quote, RiskConfig, Event, Outcome
)

errors = []

def check(name, condition, detail=""):
    if not condition:
        msg = f"FAIL: {name}"
        if detail:
            msg += f" -- {detail}"
        errors.append(msg)
        print(msg)
    else:
        print(f"  OK: {name}")


print("=" * 60)
print("TEST: FeedData")
print("=" * 60)

# Default construction
fd = FeedData()
check("FeedData default price", fd.price == 0.0, f"got {fd.price}")
check("FeedData default timestamp", fd.timestamp == 0.0, f"got {fd.timestamp}")
check("FeedData default bid", fd.bid == 0.0, f"got {fd.bid}")
check("FeedData default ask", fd.ask == 0.0, f"got {fd.ask}")
check("FeedData default volume_24h", fd.volume_24h == 0.0, f"got {fd.volume_24h}")
check("FeedData default source", fd.source == "", f"got {fd.source!r}")
check("FeedData default last_trade_size", fd.last_trade_size == 0.0, f"got {fd.last_trade_size}")
check("FeedData default last_trade_is_buy", fd.last_trade_is_buy == False, f"got {fd.last_trade_is_buy}")

# Parameterized construction
fd2 = FeedData(
    price=0.55,
    timestamp=time.time(),
    bid=0.52,
    ask=0.58,
    volume_24h=100000.0,
    source="binance",
    last_trade_size=10.0,
    last_trade_is_buy=True,
)
check("FeedData param price", fd2.price == 0.55, f"got {fd2.price}")
check("FeedData param bid", fd2.bid == 0.52, f"got {fd2.bid}")
check("FeedData param ask", fd2.ask == 0.58, f"got {fd2.ask}")
check("FeedData param volume", fd2.volume_24h == 100000.0)
check("FeedData param source", fd2.source == "binance")
check("FeedData param last_trade_size", fd2.last_trade_size == 10.0)
check("FeedData param last_trade_is_buy", fd2.last_trade_is_buy is True)

# is_stale tests
fd_fresh = FeedData(timestamp=time.time())
check("FeedData is_stale fresh", not fd_fresh.is_stale(30.0))

fd_old = FeedData(timestamp=time.time() - 60)
check("FeedData is_stale old", fd_old.is_stale(30.0))

fd_zero = FeedData(timestamp=0.0)
check("FeedData is_stale zero timestamp", fd_zero.is_stale(30.0))

fd_neg = FeedData(timestamp=-1.0)
check("FeedData is_stale negative timestamp", fd_neg.is_stale(30.0))

# Custom max_age
fd_custom = FeedData(timestamp=time.time() - 5)
check("FeedData is_stale custom max_age 10s (not stale)", not fd_custom.is_stale(10.0))
check("FeedData is_stale custom max_age 3s (stale)", fd_custom.is_stale(3.0))


print()
print("=" * 60)
print("TEST: InventorySnapshot")
print("=" * 60)

# Empty inventory
inv = InventorySnapshot()
check("InventorySnapshot default positions empty", len(inv.positions) == 0)
check("InventorySnapshot default net", inv.net == 0.0, f"got {inv.net}")
check("InventorySnapshot net_for_market empty", inv.net_for_market("anything") == 0.0)
check("InventorySnapshot net_for_event empty", inv.net_for_event(["a", "b"]) == 0.0)
check("InventorySnapshot positions_for_event empty", inv.positions_for_event(["a"]) == [])

# Build positions via engine fills
engine = Engine(db_path=None)
m = Market(id="test_mkt", name="Test", slug="test_mkt")

# Submit a buy order, tick at a price that crosses the bid, then tick to fill
engine.set_daily_baseline(0.0)
q_buy = Quote(bid=0.50, ask=0.60, size=10.0)
engine.submit_quotes(m.id, [q_buy], Side.Yes)
# First tick at exact ask to place orders, second tick at bid to fill
engine.tick(m.id, 0.50)  # Should fill the bid side
engine.tick(m.id, 0.50)  # Ensure matching happens
# Try different approach: submit order request directly
from horizon._horizon import OrderRequest, OrderType, TimeInForce
try:
    order_id = engine.submit_order(OrderRequest(
        market_id=m.id,
        side=Side.Yes,
        order_side=OrderSide.Buy,
        size=10.0,
        price=0.55,
    ))
    engine.tick(m.id, 0.50)  # tick below bid to fill
    engine.tick(m.id, 0.50)
except Exception as e:
    print(f"  Note: submit_order attempt: {e}")

positions = engine.positions_for_market(m.id)
check("Got positions from engine", len(positions) > 0, f"got {len(positions)} positions")

if positions:
    inv2 = InventorySnapshot(positions=positions)
    check("InventorySnapshot with positions", len(inv2.positions) > 0)
    net = inv2.net
    check("InventorySnapshot net positive after buy", net > 0, f"got {net}")
    net_mkt = inv2.net_for_market("test_mkt")
    check("InventorySnapshot net_for_market matches", net_mkt == net, f"net={net}, net_mkt={net_mkt}")
    net_other = inv2.net_for_market("other_mkt")
    check("InventorySnapshot net_for_market other == 0", net_other == 0.0, f"got {net_other}")

    # net_for_event
    net_event = inv2.net_for_event(["test_mkt"])
    check("InventorySnapshot net_for_event includes test_mkt", net_event > 0, f"got {net_event}")
    net_event_miss = inv2.net_for_event(["other_mkt"])
    check("InventorySnapshot net_for_event miss == 0", net_event_miss == 0.0, f"got {net_event_miss}")

    # positions_for_event
    pfe = inv2.positions_for_event(["test_mkt"])
    check("InventorySnapshot positions_for_event found", len(pfe) > 0, f"got {len(pfe)}")
    pfe_miss = inv2.positions_for_event(["nope"])
    check("InventorySnapshot positions_for_event miss empty", len(pfe_miss) == 0)


print()
print("=" * 60)
print("TEST: Context")
print("=" * 60)

# Default construction
ctx = Context()
check("Context default feeds empty", ctx.feeds == {})
check("Context default inventory empty positions", len(ctx.inventory.positions) == 0)
check("Context default market None", ctx.market is None)
check("Context default event None", ctx.event is None)
check("Context default status None", ctx.status is None)
check("Context default params empty", ctx.params == {})

# Parameterized construction
ctx2 = Context(
    feeds={"btc": FeedData(price=0.65, bid=0.63, ask=0.67)},
    inventory=InventorySnapshot(),
    market=Market(id="mkt1", name="Market1", slug="mkt1"),
    status=engine.status(),
    params={"bankroll": 1000},
)
check("Context feeds populated", "btc" in ctx2.feeds)
check("Context feeds data correct", ctx2.feeds["btc"].price == 0.65)
check("Context market set", ctx2.market is not None)
check("Context market id", ctx2.market.id == "mkt1")
check("Context status set", ctx2.status is not None)
check("Context params set", ctx2.params.get("bankroll") == 1000)

# With Event
outcomes = [
    Outcome(name="TeamA", market_id="mkt_a", yes_token_id="tok_a_yes"),
    Outcome(name="TeamB", market_id="mkt_b", yes_token_id="tok_b_yes"),
]
event = Event(id="event1", name="Championship", outcomes=outcomes)
ctx3 = Context(event=event)
check("Context event set", ctx3.event is not None)
check("Context event id", ctx3.event.id == "event1")
check("Context event name", ctx3.event.name == "Championship")
check("Context event outcomes count", len(ctx3.event.outcomes) == 2)


print()
print("=" * 60)
print("SUMMARY")
print("=" * 60)
if errors:
    print(f"\n{len(errors)} ERRORS:")
    for e in errors:
        print(f"  {e}")
    sys.exit(1)
else:
    print("\nAll context.py tests PASSED")
