"""
WHY are Kalshi's non-Poly leagues so weak (-42% ROI, z-2.07, n=75)?  Diagnostic.

For the [0.05,0.12) mult-de-vig Kalshi strategy (bet at ask) on the poly_backfill set,
break out per league and compare market-quality signals between the PROFITABLE
Poly-covered leagues and the losing non-Poly ones:
  - WR vs implied(ask): is the loss a wrong-side / bad-fair problem (WR << implied) or
    just unlucky variance (WR ~ implied)?
  - pin vig (overround-1): is Pinnacle's fair noisier/wider in these leagues?
  - kalshi spread (ask-bid) and entry ask: worse execution / thinner books?
  - snapshots/fixture & distinct fixtures: data-density / staleness?
  - concentration: is the -42% one or two leagues, or broad?
Read-only.
"""
import csv, glob, os, math
from datetime import datetime
from collections import defaultdict

DIR = r"E:\soccer\poly_backfill"
BAND = (0.05, 0.12)
_FMTS = ['%Y-%m-%d %H:%M:%S', '%m/%d/%Y %H:%M', '%m/%d/%Y %H:%M:%S', '%Y-%m-%d %H:%M']
def _pts(s):
    for f in _FMTS:
        try: return datetime.strptime(s, f)
        except (ValueError, TypeError): pass
    return None
def _f(x):
    try: return float(x)
    except (TypeError, ValueError): return None
def _result(rows):
    for r in reversed(rows):
        v = (r.get('ftr') or '').upper().strip()
        if v in ('H', 'D', 'A'): return v
    return None
def fair_mult(o):
    r = [1.0/x for x in o]; s = sum(r); return [ri/s for ri in r]

def load():
    fixtures = defaultdict(list)
    for path in glob.glob(os.path.join(DIR, "*_polybackfill.csv")):
        with open(path, newline='', encoding='utf-8') as fh:
            for r in csv.DictReader(fh):
                fixtures[(r.get('game_date'), r.get('league'),
                          r.get('home'), r.get('away'))].append(r)
    fx = []
    for (gd, league, home, away), rows in fixtures.items():
        if not gd: continue
        pre = sorted([(_pts(r['timestamp']), r) for r in rows
                      if (r.get('status') or '').upper() == 'PRE' and _pts(r['timestamp'])],
                     key=lambda t: t[0])
        if not pre: continue
        ftr = _result(rows)
        if ftr is None: continue
        has_poly = any(_f(r.get('poly_home_price')) or _f(r.get('poly_away_price'))
                       for _, r in pre)
        fx.append(dict(gd=gd, league=league, home=home, away=away, ftr=ftr,
                       pre=[r for _, r in pre], npre=len(pre), has_poly=has_poly))
    return fx

def kalshi_bet(fx):
    """First in-band [0.05,0.12) Kalshi bet, both-sides dedupe to higher CLV.
       Returns dict w/ diagnostics or None."""
    cands = []
    for side, sidx in (('home', 0), ('away', 2)):
        for r in fx['pre']:
            ph, pd_, pa = _f(r.get('pin_home')), _f(r.get('pin_draw')), _f(r.get('pin_away'))
            if None in (ph, pd_, pa) or not (ph > 1 and pd_ > 1 and pa > 1): continue
            mid = _f(r.get(f'kalshi_{side}_mid')); ask = _f(r.get(f'kalshi_{side}_ask'))
            bid = _f(r.get(f'kalshi_{side}_bid'))
            if mid is None or ask is None or not (0 < mid < 1) or not (0 < ask < 1): continue
            pf = fair_mult((ph, pd_, pa))[sidx]
            clv = (pf - mid) / pf if pf else None
            if clv is None or not (BAND[0] <= clv < BAND[1]): continue
            won = (fx['ftr'] == 'H' and side == 'home') or (fx['ftr'] == 'A' and side == 'away')
            vig = (1/ph + 1/pd_ + 1/pa) - 1
            spread = (ask - bid) if (bid is not None and bid > 0) else None
            cands.append(dict(clv=clv, ask=ask, won=won, vig=vig, spread=spread,
                              side=side, pf=pf, mid=mid)); break
    if not cands: return None
    b = max(cands, key=lambda d: d['clv'])
    return b

def agg(bets):
    n = len(bets)
    if not n: return None
    wins = sum(b['won'] for b in bets)
    net = sum(((1-b['ask'])/b['ask']) if b['won'] else -1.0 for b in bets)
    mu = sum(b['ask'] for b in bets); var = sum(b['ask']*(1-b['ask']) for b in bets)
    z = (wins-mu)/math.sqrt(var) if var > 0 else 0.0
    sp = [b['spread'] for b in bets if b['spread'] is not None]
    return dict(n=n, wr=wins/n*100, iwr=mu/n*100, roi=net/n*100, z=z,
                vig=sum(b['vig'] for b in bets)/n*100,
                spread=(sum(sp)/len(sp)*100 if sp else float('nan')),
                ask=sum(b['ask'] for b in bets)/n,
                clv=sum(b['clv'] for b in bets)/n*100,
                nobid=100*sum(1 for b in bets if b['spread'] is None)/n)

def main():
    fx = load()
    bets = []
    for f in fx:
        b = kalshi_bet(f)
        if b:
            b['league'] = f['league']; b['has_poly'] = f['has_poly']
            b['npre'] = f['npre']; bets.append(b)
    poly_lg = {b['league'] for f in fx for b in [None] if False}  # placeholder
    # leagues that ever carried a poly price
    poly_leagues = {f['league'] for f in fx if f['has_poly']}

    nonpoly = [b for b in bets if b['league'] not in poly_leagues]
    inpoly  = [b for b in bets if b['league'] in poly_leagues]

    print("AGGREGATE comparison (Kalshi [0.05,0.12) at ask):")
    print(f"  {'group':22s} {'n':>4} {'WR%':>5} {'impl%':>6} {'ROI%':>7} {'z':>6} "
          f"{'pinVig%':>8} {'kSprd%':>7} {'ask':>5} {'CLV%':>5} {'npre':>5}")
    for tag, grp in [('Poly-covered leagues', inpoly), ('NON-Poly leagues', nonpoly)]:
        a = agg(grp)
        npre = sum(b['npre'] for b in grp)/len(grp) if grp else 0
        print(f"  {tag:22s} {a['n']:4d} {a['wr']:5.1f} {a['iwr']:6.1f} {a['roi']:+7.1f} "
              f"{a['z']:+6.2f} {a['vig']:8.2f} {a['spread']:7.2f} {a['ask']:5.2f} "
              f"{a['clv']:5.1f} {npre:5.1f}")

    print("\nPer NON-Poly league:")
    print(f"  {'lg':6s} {'n':>3} {'WR%':>5} {'impl%':>6} {'ROI%':>7} {'z':>6} "
          f"{'pinVig%':>8} {'kSprd%':>7} {'ask':>5} {'nobid%':>6}")
    byl = defaultdict(list)
    for b in nonpoly: byl[b['league']].append(b)
    for lg in sorted(byl, key=lambda l: agg(byl[l])['roi']):
        a = agg(byl[lg])
        print(f"  {lg:6s} {a['n']:3d} {a['wr']:5.1f} {a['iwr']:6.1f} {a['roi']:+7.1f} "
              f"{a['z']:+6.2f} {a['vig']:8.2f} {a['spread']:7.2f} {a['ask']:5.2f} {a['nobid']:6.1f}")
    print(f"\n  non-Poly leagues present: {sorted(byl)}")
    print(f"  Poly-covered leagues:     {sorted(poly_leagues)}")

if __name__ == '__main__':
    main()
