#!/usr/bin/env python3
"""Build betting/cbb_historical.json from BallDontLie ncaab games + TheOdds-derived odds snapshot.

Inputs:
- data/ncaab/games.json (scores + teams)
- data/betting/ncaab_historical.json (odds snapshot; schema varies)

Because BallDontLie ncaab odds.game_id does not match games.id on this server,
we join odds by (date, home_team, away_team) instead.
"""

import json
from pathlib import Path
from typing import Dict, Any, Optional, Tuple

BASE = Path('/var/www/html/eventheodds/data')
GAMES = BASE / 'ncaab' / 'games.json'
ODDS_SNAPSHOT = BASE / 'betting' / 'ncaab_historical.json'
OUT = BASE / 'betting' / 'cbb_historical.json'


def norm(s: str) -> str:
    return ' '.join((s or '').strip().lower().split())


def to_float(x) -> Optional[float]:
    if x is None:
        return None
    s = str(x).strip()
    if not s or s.lower() in ('na','nan'):
        return None
    try:
        return float(s)
    except Exception:
        return None


def to_int(x) -> Optional[int]:
    if x is None:
        return None
    s = str(x).strip()
    if not s or s.lower() in ('na','nan'):
        return None
    try:
        return int(float(s))
    except Exception:
        return None


def extract_odds_from_snapshot(rec: dict) -> dict:
    """Normalize various odds snapshot schemas into our standard odds dict."""
    # Some snapshots store flat keys: home_ml / away_ml / spread / total
    if 'home_ml' in rec or 'away_ml' in rec:
        return {
            'moneylineHome': to_int(rec.get('home_ml')),
            'moneylineAway': to_int(rec.get('away_ml')),
            'spreadHome': to_float(rec.get('spread')),
            'spreadAway': None,
            'totalLine': to_float(rec.get('total')),
            'source': (rec.get('odds') or {}).get('source') if isinstance(rec.get('odds'), dict) else (rec.get('source') or 'the-odds-api'),
        }

    # Common: rec['odds'] contains sportsbooks
    o = rec.get('odds') if isinstance(rec.get('odds'), dict) else {}
    sportsbooks = o.get('sportsbooks') if isinstance(o.get('sportsbooks'), dict) else {}

    # choose a preferred sportsbook with ML if available
    preferred = ['fanduel','draftkings','betmgm','bovada','lowvig','betonlineag','betus']
    chosen = None
    for k in preferred:
        if k in sportsbooks:
            chosen = sportsbooks[k]
            break
    if not chosen and sportsbooks:
        chosen = next(iter(sportsbooks.values()))

    if isinstance(chosen, dict):
        return {
            'moneylineHome': to_int(chosen.get('home_ml')),
            'moneylineAway': to_int(chosen.get('away_ml')),
            'spreadHome': to_float(chosen.get('spread')),
            'spreadAway': None,
            'totalLine': to_float(chosen.get('total')),
            'source': o.get('source') or 'the-odds-api',
        }

    # fallback
    return {
        'moneylineHome': None,
        'moneylineAway': None,
        'spreadHome': None,
        'spreadAway': None,
        'totalLine': None,
        'source': 'none',
    }


def main():
    if not GAMES.exists():
        raise SystemExit(f"Missing {GAMES}")

    games = json.load(open(GAMES))

    odds_lookup: Dict[Tuple[str,str,str], dict] = {}
    if ODDS_SNAPSHOT.exists():
        try:
            snap = json.load(open(ODDS_SNAPSHOT))
            for r in snap:
                date = (r.get('date') or '')[:10]
                home = r.get('homeTeam') or r.get('home_team')
                away = r.get('awayTeam') or r.get('away_team')
                if not date or not home or not away:
                    continue
                odds_lookup[(date, norm(home), norm(away))] = extract_odds_from_snapshot(r)
        except Exception:
            pass

    out = []
    matched_odds = 0

    for g in games:
        gid = g.get('id')
        date = (g.get('date') or '')[:10]
        if not gid or not date:
            continue

        home = g.get('home_team') or {}
        away = g.get('visitor_team') or {}
        home_name = home.get('full_name') or home.get('name')
        away_name = away.get('full_name') or away.get('name')
        if not home_name or not away_name:
            continue

        hs = g.get('home_score') or 0
        a_s = g.get('away_score') or 0
        if hs == 0 and a_s == 0:
            continue

        season = g.get('season')
        season_int = int(season) if isinstance(season, int) or (isinstance(season, str) and str(season).isdigit()) else int(date[:4])

        odds = odds_lookup.get((date, norm(home_name), norm(away_name)))
        if not odds:
            odds = {
                'moneylineHome': None,
                'moneylineAway': None,
                'spreadHome': None,
                'spreadAway': None,
                'totalLine': None,
                'source': 'none',
            }
        else:
            matched_odds += 1

        # Ensure spreadAway present if we have spreadHome and not away
        if odds.get('spreadHome') is not None and odds.get('spreadAway') is None:
            odds['spreadAway'] = -float(odds['spreadHome'])

        has_real = any(odds.get(k) is not None for k in ('moneylineHome','moneylineAway','spreadHome','totalLine')) and odds.get('source') not in ('none', None)

        margin = hs - a_s
        winner = 'home' if margin > 0 else ('away' if margin < 0 else 'draw')

        out.append({
            'id': f"cbb-{gid}",
            'bdl_game_id': gid,
            'sport': 'cbb',
            'date': date,
            'season': season_int,
            'homeTeam': home_name,
            'awayTeam': away_name,
            'scores': {'homeScore': hs, 'awayScore': a_s},
            'odds': odds,
            'hasRealOdds': bool(has_real),
            'result': {'winner': winner, 'margin': margin, 'totalPoints': hs + a_s}
        })

    out.sort(key=lambda x: x.get('date',''))
    OUT.parent.mkdir(parents=True, exist_ok=True)
    json.dump(out, open(OUT,'w'), indent=2)
    real = sum(1 for g in out if g.get('hasRealOdds'))
    print(f"Wrote {OUT} games={len(out)} matched_odds={matched_odds} hasRealOdds={real} ({(real/len(out)*100) if out else 0:.1f}%)")


if __name__ == '__main__':
    main()
