"""Price gaps: the same market on Kalshi and Polymarket US, side by side, most you could save first.

Layer says which markets are the same bet; this script reads both venues' order books on your machine and
prints what YES costs on each. You need a Layer API key, nothing else. It only reads data, never orders.

    pip install uselayer
    export LAYER_API_KEY=lyr_...       # from uselayer.sh/dashboard/keys
    python price_gaps.py               # refreshes every 30 seconds; Ctrl+C to stop
    python price_gaps.py --once        # print the table once and exit
    python price_gaps.py --include-started   # also list games already under way (their prices go stale)
"""

import sys
import time
from datetime import datetime, timezone

from uselayer import Client
from uselayer.http import Http
from uselayer.venues.kalshi import KalshiMarkets
from uselayer.venues.polymarket_us import PolymarketUSPublic

MATCHES = 20  # how many matched markets to check each round
REFRESH_SECONDS = 30
INCLUDE_STARTED = "--include-started" in sys.argv
client = Client()  # reads LAYER_API_KEY; paper mode, so nothing is ever traded
kalshi, polymarket_us = KalshiMarkets(Http()), PolymarketUSPublic(Http())  # public order books, no venue keys


def yes_asks(m):
    """The cheapest YES on offer on each venue (price and shares), read from the venues on your machine."""
    k = kalshi.read_book(m.kalshi.market_id).book.outcome("yes").best_ask
    u = polymarket_us.book(m.polymarket_us.market_id).outcome("yes").best_ask
    return k, u  # None: nobody is selling YES there


def started(m):
    """True once the game has begun: one venue's price is often stale then, so its gap isn't real."""
    t = m.kalshi.event_time or m.polymarket_us.event_time
    return bool(t) and datetime.fromisoformat(t) <= datetime.now(timezone.utc)


def cents(price):
    return "  -- " if price is None else f"{price * 100:4.0f}¢"


def gaps():
    """One row per upcoming match, soonest first (games under way only with --include-started)."""
    rows = []
    for m in [m for m in client.matches(limit=MATCHES * 3) if INCLUDE_STARTED or not started(m)][:MATCHES]:
        try:
            k, u = yes_asks(m)
        except Exception as err:  # one venue not answering shouldn't stop the table
            print(f"skipped {m.kalshi.market_id}: {err}", file=sys.stderr)
            continue
        gap = abs(k.price - u.price) if k and u else None
        shares = min(k.size, u.size) if k and u else None  # a big gap on a few shares isn't worth much
        save = gap * shares if gap is not None and shares is not None else None  # dollars, before fees
        rows.append((save, gap, m, k and k.price, u and u.price, shares))
    # Most you could save first; markets missing a price on one side go last.
    rows.sort(key=lambda r: -1 if r[0] is None else r[0], reverse=True)
    return rows


def show(rows):
    print(f"\n{time.strftime('%H:%M:%S')}  YES price on each venue (cost to buy one share)")
    print(f"{'save':>6}  {'gap':>5}  {'Kalshi':>6}  {'Poly US':>7}  {'shares':>6}  market")
    for save, gap, m, kalshi, poly, shares in rows:
        name = ("[started] " if started(m) else "") + f"{m.kalshi.event or m.kalshi.market_id} · {m.kalshi.outcome or ''}"
        size = "    --" if shares is None else f"{shares:6.0f}"
        dollars = "    --" if save is None else f"${save:5.2f}"
        print(f"{dollars}  {cents(gap)}  {cents(kalshi):>6}  {cents(poly):>7}  {size}  {name[:56]}")
    print("shares: shares on offer at both prices. save: gap x shares, before fees.")


if __name__ == "__main__":
    while True:
        show(gaps())
        if "--once" in sys.argv:
            break
        time.sleep(REFRESH_SECONDS)
