import os
import base64
import datetime
import requests
from dotenv import load_dotenv
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.backends import default_backend

load_dotenv()

KEY_ID   = os.getenv("KALSHI_API_KEY_ID")
KEY_PATH = os.getenv("KALSHI_PRIVATE_KEY_PATH")
BASE_URL = "https://api.elections.kalshi.com"

def load_private_key(file_path):
    with open(file_path, "rb") as f:
        return serialization.load_pem_private_key(
            f.read(), password=None, backend=default_backend()
        )

def sign_request(private_key, timestamp, method, path):
    message = (timestamp + method + path).encode("utf-8")
    signature = private_key.sign(
        message,
        padding.PSS(mgf=padding.MGF1(hashes.SHA256()),
                    salt_length=padding.PSS.DIGEST_LENGTH),
        hashes.SHA256()
    )
    return base64.b64encode(signature).decode("utf-8")

def get_headers(method, path):
    ts  = str(int(datetime.datetime.now().timestamp() * 1000))
    pk  = load_private_key(KEY_PATH)
    sig = sign_request(pk, ts, method, path)
    return {
        "KALSHI-ACCESS-KEY":       KEY_ID,
        "KALSHI-ACCESS-TIMESTAMP": ts,
        "KALSHI-ACCESS-SIGNATURE": sig,
        "Content-Type":            "application/json"
    }

def is_parlay(m):
    """Return True if this market is a multi-leg parlay — skip these."""
    if m.get("mve_collection_ticker"):
        return True
    if m.get("mve_selected_legs"):
        return True
    ticker = m.get("ticker", "")
    if "MVE" in ticker.upper() or "MULTI" in ticker.upper():
        return True
    return False

def get_all_open_markets():
    all_markets = []
    cursor = None
    page   = 0
    while True:
        path   = "/trade-api/v2/markets"
        params = "?status=open&limit=100"
        if cursor:
            params += f"&cursor={cursor}"
        headers  = get_headers("GET", path)
        response = requests.get(BASE_URL + path + params, headers=headers)
        if response.status_code != 200:
            print(f"Error fetching markets: {response.status_code}")
            break
        data    = response.json()
        markets = data.get("markets", [])
        # Strip parlays immediately
        singles = [m for m in markets if not is_parlay(m)]
        all_markets.extend(singles)
        page  += 1
        cursor = data.get("cursor")
        if not cursor or not markets:
            break
        if page >= 10:
            break
    return all_markets

def seconds_until(market):
    for field in ["expected_expiration_time", "close_time", "expiration_time"]:
        val = market.get(field)
        if val:
            try:
                t   = datetime.datetime.fromisoformat(val.replace("Z", "+00:00"))
                now = datetime.datetime.now(datetime.timezone.utc)
                return (t - now).total_seconds()
            except:
                continue
    return None

def fmt_time(s):
    if s is None: return "Unknown"
    if s < 0:     return "Closed"
    m = int(s // 60);  h = int(m // 60)
    if h >= 24:   return f"{h//24}d {h%24}h"
    if h  > 0:    return f"{h}h {m%60}m"
    return f"{m}m"

def score(m):
    vol    = float(m.get("volume_24h_fp") or m.get("volume_fp") or 0)
    ask    = float(m.get("yes_ask_dollars") or 0)
    bid    = float(m.get("yes_bid_dollars") or 0)
    spread = (ask - bid) if (ask and bid) else 1.0
    return round(vol / (spread + 0.01), 2)

def scan_markets():
    print("\n" + "="*72)
    print("  KALSHI MARKET SCANNER  —  Singles Only, No Parlays")
    print("="*72)
    print(f"  {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    print("="*72)

    print("  Fetching markets...")
    markets = get_all_open_markets()
    print(f"  Singles fetched: {len(markets)}")

    short_term = []
    near_zero  = []

    for m in markets:
        secs = seconds_until(m)
        if secs is None:
            continue

        ask  = float(m.get("yes_ask_dollars") or 0)
        bid  = float(m.get("yes_bid_dollars") or 0)
        mid  = round((ask + bid) / 2, 4) if (ask and bid) else bid
        vol  = float(m.get("volume_24h_fp") or m.get("volume_fp") or 0)
        cat  = m.get("category") or ""

        entry = {
            "ticker"    : m.get("ticker", ""),
            "title"     : m.get("title", "No title"),
            "category"  : cat,
            "mid"       : mid,
            "bid"       : bid,
            "ask"       : ask,
            "volume"    : vol,
            "secs"      : secs,
            "time_left" : fmt_time(secs),
            "score"     : score(m)
        }

        # Short term window: 10 minutes → 48 hours with real liquidity
        if 600 <= secs <= 172800 and (ask > 0 or bid > 0):
            short_term.append(entry)

        # Near zero: priced 1–8 cents, any timeframe
        if 0 < mid <= 0.08 and vol > 0:
            near_zero.append(entry)

    short_term.sort(key=lambda x: x["score"], reverse=True)
    near_zero.sort(key=lambda x: x["mid"])

    # ── Short Term ───────────────────────────────────────────────────
    print(f"\n  📅 SHORT TERM  (10min–48hr, liquid)  —  {len(short_term)} found")
    print("  " + "-"*70)
    if short_term:
        print(f"  {'Closes':>8}  {'Mid':>6}  {'Bid':>6}  {'Ask':>6}  {'Vol 24h':>9}  Title")
        print(f"  {'-------':>8}  {'---':>6}  {'---':>6}  {'---':>6}  {'---------':>9}  -----")
        for e in short_term[:30]:
            print(f"  {e['time_left']:>8}  ${e['mid']:.3f}  ${e['bid']:.3f}  ${e['ask']:.3f}  {e['volume']:>9,.0f}  {e['title'][:42]}")
    else:
        print("  None found — try running during active trading hours.")

    # ── Near Zero ────────────────────────────────────────────────────
    print(f"\n  🎯 NEAR-ZERO  (1–8 cents, has volume)  —  {len(near_zero)} found")
    print("  " + "-"*70)
    if near_zero:
        print(f"  {'Price':>7}  {'Closes':>8}  {'Vol 24h':>9}  Title")
        print(f"  {'-----':>7}  {'------':>8}  {'---------':>9}  -----")
        for e in near_zero[:30]:
            print(f"  ${e['mid']:.3f}   {e['time_left']:>8}  {e['volume']:>9,.0f}  {e['title'][:42]}")
    else:
        print("  None found.")

    print(f"\n  📊 Scanned: {len(markets)} singles  |  Short term: {len(short_term)}  |  Near zero: {len(near_zero)}")
    print("="*72)

if __name__ == "__main__":
    scan_markets()
