"""
Odds-coverage watcher for candidate leagues (Liga MX, Peru Liga 1).

Both are real on Kalshi + Poly but we could NOT confirm API-Football Pinnacle
coverage on 2026-06-28 because their next fixtures were outside the ~7-day odds
window (odds only populate near kickoff). The "0 books on FINISHED fixtures"
test is INVALID (proven: finished EPL fixtures also return 0 books — AF purges
odds weeks after a game). So the only reliable probe is an UPCOMING fixture
INSIDE the window.

This script checks each candidate league's upcoming fixtures, classifies odds
coverage, and emails a verdict when it becomes DEFINITIVE (ready-to-wire or
genuinely-uncovered) — or a one-time "still pending" reminder. Dedupes on a
small state file so a daily schedule won't spam. Read-only against APIs; the
only thing it writes is its own state file and an email. It does NOT wire
anything — wiring stays a manual decision.

Run manually any time:  python research/check_new_league_odds.py
"""
import os, json, smtplib, requests
from datetime import datetime, timezone
from pathlib import Path
from email.mime.text import MIMEText

API_KEY = "81b487be73030f5dec980d2a1ef2c406"
HEADERS = {"x-apisports-key": API_KEY}
AF_BASE = "https://v3.football.api-sports.io"
WINDOW_DAYS = 8          # treat fixtures within this many days as "in the odds window"
STATE = Path(r"E:\soccer\research\check_new_league_odds_state.json")

# league_code -> (af_id, seasons to probe, friendly name, kalshi series, poly tag)
CANDIDATES = {
    "MX1": dict(af=262, seasons=[2026, 2025], name="Liga MX",
                kalshi="KXLIGAMXGAME", poly="liga-mx"),
    "PE1": dict(af=281, seasons=[2026, 2025], name="Peru Liga 1",
                kalshi="KXPERLIGA1GAME", poly="peru-liga-1"),
}

def _load_env(path):
    env = {}
    try:
        for line in open(path):
            line = line.strip()
            if line and not line.startswith('#') and '=' in line:
                k, v = line.split('=', 1)
                env[k.strip()] = v.strip()
    except Exception:
        pass
    return env

_env = _load_env(r"E:\soccer\.env")

def send_email(subject, body):
    msg = MIMEText(body, "plain")
    msg["Subject"] = subject
    msg["From"] = _env.get("EMAIL_SENDER", "")
    msg["To"] = _env.get("EMAIL_RECIPIENT", "")
    with smtplib.SMTP_SSL("smtp.gmail.com", 465) as srv:
        srv.login(_env.get("EMAIL_SENDER", ""), _env.get("EMAIL_APP_PASSWORD", ""))
        srv.sendmail(msg["From"], msg["To"], msg.as_string())

def _upcoming(af, seasons, n=12):
    """De-duped upcoming fixtures across the given seasons, sorted by kickoff."""
    seen = {}
    for s in seasons:
        try:
            r = requests.get(f"{AF_BASE}/fixtures", headers=HEADERS,
                             params={"league": af, "season": s, "next": n}, timeout=20)
            for f in r.json().get("response", []):
                seen[f["fixture"]["id"]] = f
        except Exception:
            pass
    return sorted(seen.values(), key=lambda f: f["fixture"]["date"])

def _books(fid):
    try:
        r = requests.get(f"{AF_BASE}/odds", headers=HEADERS,
                         params={"fixture": fid}, timeout=20)
        resp = r.json().get("response", [])
        if not resp:
            return []
        return [b["name"] for b in resp[0].get("bookmakers", [])]
    except Exception:
        return []

def classify(cfg):
    """Return (verdict, detail_lines). verdict in:
       READY | NO_PINNACLE | NO_COVERAGE | PENDING | NO_FIXTURES."""
    now = datetime.now(timezone.utc)
    fx = _upcoming(cfg["af"], cfg["seasons"])
    if not fx:
        return "NO_FIXTURES", ["  no upcoming fixtures scheduled at all"]
    lines = []
    in_window = []
    for f in fx:
        dt = datetime.fromisoformat(f["fixture"]["date"].replace("Z", "+00:00"))
        days = (dt - now).total_seconds() / 86400.0
        if days <= WINDOW_DAYS:
            in_window.append((f, dt, days))
    nxt = fx[0]
    nxt_dt = nxt["fixture"]["date"][:16]
    if not in_window:
        return "PENDING", [f"  next fixture {nxt_dt} (>{WINDOW_DAYS}d out, "
                           f"outside odds window — recheck closer to then)"]
    any_pin = any_book = False
    for f, dt, days in in_window:
        bms = _books(f["fixture"]["id"])
        pin = any("Pinnacle" in b for b in bms)
        any_pin = any_pin or pin
        any_book = any_book or bool(bms)
        lines.append(f"  {f['fixture']['date'][:16]} ({days:.1f}d) "
                     f"{f['teams']['home']['name'][:16]:16s} v "
                     f"{f['teams']['away']['name'][:16]:16s} "
                     f"books={len(bms)} {'PINNACLE' if pin else ''}")
    if any_pin:
        return "READY", lines
    if any_book:
        return "NO_PINNACLE", lines
    # in-window fixtures with zero books on any of them => genuinely uncovered
    return "NO_COVERAGE", lines

_DEFINITIVE = {"READY", "NO_PINNACLE", "NO_COVERAGE"}
_ACTION = {
    "READY":       "ODDS CONFIRMED — ready to wire (Pinnacle present in-window)",
    "NO_PINNACLE": "Other books but NO Pinnacle — softer fair only; decide before wiring",
    "NO_COVERAGE": "No bookmaker odds in-window — genuinely uncovered (like USC); do NOT wire",
    "PENDING":     "Still outside the odds window — will keep checking",
    "NO_FIXTURES": "No fixtures scheduled yet",
}

def main():
    results = {code: classify(cfg) for code, cfg in CANDIDATES.items()}
    state = {}
    if STATE.exists():
        try: state = json.loads(STATE.read_text())
        except Exception: state = {}

    body = ["Odds-coverage watch for candidate Kalshi+Poly leagues.",
            "(Wiring is still manual — this only tells you WHEN it's safe to decide.)", ""]
    email_worthy = False
    for code, (verdict, lines) in results.items():
        cfg = CANDIDATES[code]
        prev = state.get(code)
        body.append(f"== {cfg['name']} ({code}, AF {cfg['af']}, {cfg['kalshi']}) ==")
        body.append(f"   VERDICT: {verdict} — {_ACTION[verdict]}")
        body += lines
        body.append("")
        # email if a league reached a NEW definitive verdict, or first-ever run
        if verdict in _DEFINITIVE and prev != verdict:
            email_worthy = True
        if prev is None:                      # first time we see this league
            email_worthy = True
        state[code] = verdict

    STATE.write_text(json.dumps(state, indent=2))
    report = "\n".join(body)
    print(report)

    if email_worthy:
        ready = [CANDIDATES[c]["name"] for c, (v, _) in results.items() if v == "READY"]
        tag = (" — " + ", ".join(ready) + " READY") if ready else ""
        try:
            send_email(f"League odds watch{tag}", report)
            print("\n[emailed]")
        except Exception as e:
            print(f"\n[email FAILED: {e}]")
    else:
        print("\n[no email — no new definitive verdict]")

if __name__ == "__main__":
    main()
