"""Harvest VERIFIED team abbreviations from real Kalshi event tickers.

NEVER GUESS AN ABBREVIATION. The 2026-05-19 audit found three wrong ones that
had been assumed (Coritiba CTB->CFC, Sarpsborg S08->SAR, Ham-Kam HAM->HAK), and
a wrong abbr fails SILENTLY: kalshi_live falls through, 404s the per-market call
and the dashboard just shows Kalshi=?A/?H forever.

Every market carries `rules_primary` naming both clubs in full ("... the
Kilmarnock vs Dundee United professional Scottish Premiership soccer game"), and
the per-outcome ticker suffix is that club's abbreviation. Pairing the two gives
an abbreviation straight from the exchange.

Usage:  python research\\harvest_kalshi_abbrs.py [DIV ...]
        (no args = every Kalshi-covered division)
"""
import re
import sys
import time
import json
import requests

sys.path.insert(0, r'E:\soccer\main')
import maps

BASE = 'https://api.elections.kalshi.com/trade-api/v2'
STATUSES = ('open', 'unopened', 'closed')
# Leading `.*` is GREEDY on purpose. The rules read "If Tie is the result of the
# Kilmarnock vs Dundee United professional ...", so anchoring on the first "the"
# captures "result of the Kilmarnock". Greedy-then-backtrack makes \bthe bind to
# the LAST "the" before "vs", which is the one introducing the home side.
RULES_RE = re.compile(
    r'.*\bthe\s+(.+?)\s+vs\.?\s+(.+?)\s+(?:professional|soccer|football)',
    re.IGNORECASE)


def markets_for(series):
    out, seen = [], set()
    for status in STATUSES:
        cursor = None
        for _ in range(12):                      # bounded paging
            p = {'series_ticker': series, 'limit': 200, 'status': status}
            if cursor:
                p['cursor'] = cursor
            try:
                r = requests.get(f'{BASE}/markets', params=p, timeout=30)
            except Exception as e:
                print(f'    [warn] {series}/{status}: {type(e).__name__}')
                break
            # Do NOT swallow this. A silent break here made four of eight
            # divisions return "0 abbreviations" that looked like "Kalshi does
            # not list them", when in fact the exchange was rate-limiting after
            # the first few series and every later request 429'd.
            if r.status_code == 429:
                time.sleep(5)
                continue
            if r.status_code != 200:
                print(f'    [warn] {series}/{status}: HTTP {r.status_code}')
                break
            j = r.json()
            for m in j.get('markets', []):
                t = m.get('ticker')
                if t and t not in seen:
                    seen.add(t)
                    out.append(m)
            cursor = j.get('cursor')
            if not cursor:
                break
            time.sleep(0.4)
        time.sleep(0.6)          # breathe between statuses
    return out


def abbrs_for_series(series):
    """{abbr: full team name} verified from this series' own tickers."""
    by_event = {}
    for m in markets_for(series):
        ev = m.get('event_ticker') or ''
        tkr = m.get('ticker') or ''
        if not ev or not tkr.startswith(ev + '-'):
            continue
        leg = tkr[len(ev) + 1:]
        d = by_event.setdefault(ev, {'legs': set(), 'rules': None})
        d['legs'].add(leg)
        if not d['rules']:
            d['rules'] = m.get('rules_primary') or ''

    found = {}
    for ev, d in by_event.items():
        mm = RULES_RE.search(d['rules'] or '')
        if not mm:
            continue
        home, away = mm.group(1).strip(), mm.group(2).strip()
        legs = {l for l in d['legs'] if l != 'TIE'}
        if len(legs) != 2:
            continue
        # ticker suffix is HOME+AWAY concatenated, so whichever abbr the suffix
        # STARTS with is the home side -- that is what disambiguates the pair.
        suffix = ev.rsplit('-', 1)[-1]
        suffix = re.sub(r'^\d{2}[A-Z]{3}\d{2}', '', suffix)
        h = next((l for l in legs if suffix.startswith(l)), None)
        if h:
            a = next(iter(legs - {h}), None)
            if a:
                found.setdefault(h, home)
                found.setdefault(a, away)
    return found


def main():
    divs = sys.argv[1:] or [d for d in maps.all_divs()
                            if maps.league_kalshi_series(d)]
    result = {}
    for d in sorted(divs):
        s = maps.league_kalshi_series(d)
        if not s:
            print(f'{d}: no kalshi series')
            continue
        a = abbrs_for_series(s)
        result[d] = a
        print(f'{d} ({s}): {len(a)} abbreviations')
        for abbr, name in sorted(a.items(), key=lambda kv: kv[1]):
            print(f'    {abbr:6} {name}')
        print()

    out = r'E:\soccer\research\kalshi_abbrs_harvested.json'
    with open(out, 'w', encoding='utf-8') as fh:
        json.dump(result, fh, indent=2, ensure_ascii=False, sort_keys=True)
    print('wrote', out)


if __name__ == '__main__':
    main()
