#!/usr/bin/env python3
"""
SportsGameOdds API - Comprehensive Odds Fetcher

Replaces BallDontLie for odds fetching with the more reliable SportsGameOdds API.
Supports 55+ leagues including NBA, NFL, MLB, NHL, NCAA, EPL, UFC, and more.

Usage:
    python scripts/fetch_sportsGameOdds.py [league] [--props] [--all]

    league: nba, nfl, mlb, nhl, ncaab, ncaaf, epl, laliga, mma, etc. (default: nba)
    --props: Include player props
    --all: Fetch all major leagues

Examples:
    python scripts/fetch_sportsGameOdds.py nba
    python scripts/fetch_sportsGameOdds.py nfl --props
    python scripts/fetch_sportsGameOdds.py --all
"""

import os
import sys
import json
import time
import hashlib
import argparse
import psycopg2
from datetime import datetime, timezone
from pathlib import Path
from typing import Dict, List, Any, Optional
import requests

# Import shared normalization from sportsclaw scripts
_NORMALIZE_DIR = "/home/administrator/.openclaw/agents/sportsclaw/workspace/scripts"
if _NORMALIZE_DIR not in sys.path:
    sys.path.insert(0, _NORMALIZE_DIR)
from utils.normalize import normalize_league, normalize_prop_data

# Load environment
ENV_FILE = Path(__file__).parent.parent / ".env"
if ENV_FILE.exists():
    with open(ENV_FILE) as f:
        for line in f:
            if line.strip() and not line.startswith("#") and "=" in line:
                key, val = line.strip().split("=", 1)
                os.environ.setdefault(key, val.strip('"').strip("'"))

# Configuration
SGO_BASE_URL = os.environ.get("SPORTSGAMEODDS_BASE_URL", "https://api.sportsgameodds.com/v2")
SGO_API_KEY = os.environ.get("SPORTSGAMEODDS_API_KEY")
SPORTS_DB_URL = os.environ.get("SPORTS_DATABASE_URL", "")

if not SGO_API_KEY:
    print("ERROR: SPORTSGAMEODDS_API_KEY not set in environment")
    sys.exit(1)

# League mappings
LEAGUE_MAP = {
    "nba": "NBA",
    "nfl": "NFL",
    "mlb": "MLB",
    "nhl": "NHL",
    "ncaab": "NCAAB",
    "ncaaf": "NCAAF",
    "wnba": "WNBA",
    "mma": "UFC",
    "ufc": "UFC",
    "epl": "EPL",
    "laliga": "LA_LIGA",
    "seriea": "IT_SERIE_A",
    "bundesliga": "BUNDESLIGA",
    "ligue1": "FR_LIGUE_1",
    "ucl": "UEFA_CHAMPIONS_LEAGUE",
    "mls": "MLS",
    "cfl": "CFL",
    "ahl": "AHL",
}

# Reverse mapping: SGO API league ID → canonical lowercase DB name
SGO_TO_DB_LEAGUE = {v: k for k, v in LEAGUE_MAP.items()}
# Fix duplicates (ufc→mma)
SGO_TO_DB_LEAGUE["UFC"] = "mma"
# Canonical DB names (post-normalization)
SGO_TO_DB_LEAGUE["LA_LIGA"] = "laliga"
SGO_TO_DB_LEAGUE["IT_SERIE_A"] = "seriea"
SGO_TO_DB_LEAGUE["FR_LIGUE_1"] = "ligue1"
SGO_TO_DB_LEAGUE["UEFA_CHAMPIONS_LEAGUE"] = "ucl"

# Major leagues for --all flag
MAJOR_LEAGUES = ["nba", "nfl", "mlb", "nhl", "ncaab", "ncaaf", "epl", "mma"]


class SportsGameOddsClient:
    """Client for SportsGameOdds API v2"""

    def __init__(self, api_key: str, base_url: str = SGO_BASE_URL):
        self.api_key = api_key
        self.base_url = base_url
        self.session = requests.Session()
        self.session.headers.update({
            "x-api-key": api_key,
            "Content-Type": "application/json",
        })
        self.requests_made = 0
        self.objects_used = 0

    def _request(self, endpoint: str, params: Dict = None) -> Dict:
        """Make API request with error handling"""
        url = f"{self.base_url}/{endpoint}"
        try:
            resp = self.session.get(url, params=params, timeout=30)
            self.requests_made += 1

            if resp.status_code == 401:
                return {"success": False, "error": "Invalid API key"}
            if resp.status_code == 429:
                return {"success": False, "error": "Rate limited - quota exceeded"}
            if resp.status_code == 404:
                return {"success": False, "error": f"Endpoint not found: {endpoint}"}

            resp.raise_for_status()
            data = resp.json()

            # Track objects used
            if "data" in data and isinstance(data["data"], list):
                self.objects_used += len(data["data"])

            return data
        except requests.exceptions.RequestException as e:
            return {"success": False, "error": str(e)}

    def get_events(self, league_id: str, odds_available: bool = True,
                   include_alt_lines: bool = False, limit: int = 100,
                   cursor: str = None) -> Dict:
        """Fetch events with odds for a league"""
        params = {
            "leagueID": league_id,
            "oddsAvailable": str(odds_available).lower(),
            "limit": limit,
        }
        if include_alt_lines:
            params["includeAltLines"] = "true"
        if cursor:
            params["cursor"] = cursor

        return self._request("events", params)

    def get_all_events(self, league_id: str, **kwargs) -> List[Dict]:
        """Fetch all events with pagination"""
        all_events = []
        cursor = None

        while True:
            result = self.get_events(league_id, cursor=cursor, **kwargs)
            if not result.get("success", True) or "error" in result:
                print(f"  Error: {result.get('error', 'Unknown error')}")
                break

            events = result.get("data", [])
            if not events:
                break

            all_events.extend(events)
            print(f"  Fetched {len(all_events)} events...")

            cursor = result.get("nextCursor")
            if not cursor:
                break

            time.sleep(0.5)  # Rate limiting

        return all_events

    def get_players(self, league_id: str = None, limit: int = 500) -> Dict:
        """Fetch player information"""
        params = {"limit": limit}
        if league_id:
            params["leagueID"] = league_id
        return self._request("players", params)

    def get_teams(self, league_id: str = None) -> Dict:
        """Fetch team information"""
        params = {}
        if league_id:
            params["leagueID"] = league_id
        return self._request("teams", params)

    def get_usage(self) -> Dict:
        """Check API usage"""
        return self._request("account/usage")


def parse_odds_value(value) -> Optional[int]:
    """Parse odds string like '+100' or '-110' to integer"""
    if value is None:
        return None
    if isinstance(value, (int, float)):
        return int(value)
    if isinstance(value, str):
        try:
            return int(value.replace("+", ""))
        except ValueError:
            return None
    return None


def parse_line_value(value) -> Optional[float]:
    """Parse line value to float"""
    if value is None:
        return None
    if isinstance(value, (int, float)):
        return float(value)
    if isinstance(value, str):
        try:
            return float(value)
        except ValueError:
            return None
    return None


def parse_event_odds(event: Dict) -> Dict:
    """Parse event and extract key odds data"""
    odds_data = event.get("odds", {})

    # Extract moneylines - try bookOdds first (actual odds), then fairOdds
    home_ml = None
    away_ml = None
    ml_home = odds_data.get("points-home-game-ml-home", {})
    ml_away = odds_data.get("points-away-game-ml-away", {})

    if ml_home:
        home_ml = parse_odds_value(ml_home.get("bookOdds") or ml_home.get("fairOdds"))
    if ml_away:
        away_ml = parse_odds_value(ml_away.get("bookOdds") or ml_away.get("fairOdds"))

    # Extract spreads - use bookSpread/fairSpread for spread lines
    spread_home = None
    spread_away = None
    sp_home = odds_data.get("points-home-game-sp-home", {})
    sp_away = odds_data.get("points-away-game-sp-away", {})

    if sp_home:
        spread_home = parse_line_value(sp_home.get("bookSpread") or sp_home.get("fairSpread"))
    if sp_away:
        spread_away = parse_line_value(sp_away.get("bookSpread") or sp_away.get("fairSpread"))

    # Extract totals - use bookOverUnder or fairOverUnder
    total = None
    ou_over = odds_data.get("points-all-game-ou-over", {})
    if ou_over:
        total = parse_line_value(ou_over.get("bookOverUnder") or ou_over.get("fairOverUnder"))

    return {
        "moneylineHome": home_ml,
        "moneylineAway": away_ml,
        "spreadHome": spread_home,
        "spreadAway": spread_away,
        "total": total,
        "oddsSource": "sportsgameodds",
        "oddsUpdatedAt": datetime.now(timezone.utc).isoformat(),
    }


def _clean_sgo_player_id(player_id: str) -> str:
    """Convert SGO playerID (FIRST_LAST_N_LEAGUE) to readable 'First Last'."""
    import re
    cleaned = re.sub(r'_\d+_[A-Z]+$', '', player_id)
    parts = cleaned.split('_')
    return ' '.join(p.capitalize() for p in parts)


def extract_player_props(event: Dict) -> List[Dict]:
    """Extract player prop lines from event"""
    props = []
    odds_data = event.get("odds", {})
    event_id = event.get("eventID")
    sgo_league = event.get("leagueID")
    league = SGO_TO_DB_LEAGUE.get(sgo_league, sgo_league.lower() if sgo_league else "unknown")

    # Extract game context for PPL fields
    teams = event.get("teams", {})
    home_names = teams.get("home", {}).get("names", {})
    away_names = teams.get("away", {}).get("names", {})
    home_team = (home_names.get("short") or home_names.get("medium") or home_names.get("long") or "").upper() or None
    away_team = (away_names.get("short") or away_names.get("medium") or away_names.get("long") or "").upper() or None
    game_start = event.get("status", {}).get("startsAt")

    for odd_id, odd_data in odds_data.items():
        # Player props have format: {stat}-{playerID}-{period}-{betType}-{side}
        parts = odd_id.split("-")
        if len(parts) >= 5 and parts[3] == "ou":
            stat = parts[0]
            player_id = parts[1]
            period = parts[2]
            side = parts[4]  # over or under

            # Skip team-level stats (home, away, all)
            if player_id in ["home", "away", "all"]:
                continue

            line = odd_data.get("line")
            odds = odd_data.get("fairOdds") or odd_data.get("bookOdds")

            if line is not None:
                # Extract fair odds and book-specific lines
                fair_odds_raw = odd_data.get("fairOdds")
                fair_ou_raw = odd_data.get("fairOverUnder")
                by_book = odd_data.get("byBookmaker", {})
                fd_data = by_book.get("fanduel", {})
                dk_data = by_book.get("draftkings", {})

                # Build readable player name and marketName for DIGIMON
                player_name = _clean_sgo_player_id(player_id)
                stat_label = stat.capitalize()
                market_name = f"{player_name} {stat_label} Over/Under" if side in ("over", "under") else f"{player_name} {stat_label}"

                prop = {
                    "league": normalize_league(league),
                    "gameId": event_id,
                    "playerExternalId": player_id,
                    "propType": stat,
                    "market": f"{period}-{side}",
                    "lineValue": float(line),
                    "oddsAmerican": int(odds) if odds else None,
                    "vendor": "sportsgameodds",
                    "marketName": market_name,
                    "homeTeam": home_team,
                    "awayTeam": away_team,
                    "gameStart": str(game_start) if game_start else None,
                    "snapshotAt": datetime.now(timezone.utc).isoformat(),
                    "integrityStatus": "active",
                    "raw": odd_data,
                    "fairOdds": int(float(fair_odds_raw)) if fair_odds_raw else None,
                    "fairLine": float(fair_ou_raw) if fair_ou_raw else None,
                    "fdLine": float(fd_data["overUnder"]) if fd_data.get("overUnder") else None,
                    "fdOverOdds": int(float(fd_data["odds"])) if fd_data.get("odds") else None,
                    "dkLine": float(dk_data["overUnder"]) if dk_data.get("overUnder") else None,
                    "dkOverOdds": int(float(dk_data["odds"])) if dk_data.get("odds") else None,
                }
                normalize_prop_data(prop)
                props.append(prop)

    return props


def get_db_connection():
    """Get PostgreSQL connection"""
    if not SPORTS_DB_URL:
        return None

    try:
        # Parse connection string
        url = SPORTS_DB_URL.replace("postgresql://", "")
        if "?" in url:
            url = url.split("?")[0]

        user_pass, host_db = url.split("@")
        user, password = user_pass.split(":")
        host_port, database = host_db.split("/")

        if ":" in host_port:
            host, port = host_port.split(":")
        else:
            host, port = host_port, "5432"

        conn = psycopg2.connect(
            host=host,
            port=int(port),
            database=database,
            user=user,
            password=password,
        )
        return conn
    except Exception as e:
        print(f"Database connection error: {e}")
        return None


def upsert_games(conn, events: List[Dict], league: str):
    """Upsert games to SportsGame table"""
    if not conn or not events:
        return 0, 0

    cursor = conn.cursor()
    inserted = 0
    updated = 0

    for event in events:
        try:
            event_id = event.get("eventID")

            # Extract team info from nested structure
            teams = event.get("teams", {})
            home_team_data = teams.get("home", {})
            away_team_data = teams.get("away", {})

            # Get team names (prefer short name, fall back to teamID)
            home_team = (
                home_team_data.get("names", {}).get("short") or
                home_team_data.get("teamID", "").split("_")[0] if home_team_data.get("teamID") else "UNK"
            )
            away_team = (
                away_team_data.get("names", {}).get("short") or
                away_team_data.get("teamID", "").split("_")[0] if away_team_data.get("teamID") else "UNK"
            )

            # Normalize to full names for SportsGame
            try:
                from team_names import normalize_to_full
                home_team = normalize_to_full(home_team, league)
                away_team = normalize_to_full(away_team, league)
            except ImportError:
                pass

            # Extract start time from status object
            status_data = event.get("status", {})
            start_time = status_data.get("startsAt")

            # Get status string
            if status_data.get("completed"):
                status = "completed"
            elif status_data.get("live"):
                status = "live"
            elif status_data.get("cancelled"):
                status = "cancelled"
            else:
                status = "scheduled"

            # Parse scores if available (check for score object)
            score = event.get("score", {})
            home_score = score.get("home", {}).get("total") if isinstance(score.get("home"), dict) else score.get("home")
            away_score = score.get("away", {}).get("total") if isinstance(score.get("away"), dict) else score.get("away")

            # Parse odds
            odds = parse_event_odds(event)

            # Parse start time
            if start_time:
                game_date = datetime.fromisoformat(start_time.replace("Z", "+00:00"))
                season = game_date.year if game_date.month >= 7 else game_date.year - 1
            else:
                game_date = datetime.now(timezone.utc)
                season = game_date.year

            # Use savepoint so one event's failure doesn't abort the batch
            cursor.execute("SAVEPOINT event_sp")

            # Check if exists by externalGameId (globally unique)
            cursor.execute('''
                SELECT id FROM "SportsGame"
                WHERE "externalGameId" = %s
            ''', (event_id,))

            existing = cursor.fetchone()

            if existing:
                # Update existing
                cursor.execute('''
                    UPDATE "SportsGame" SET
                        "homeScore" = COALESCE(%s, "homeScore"),
                        "awayScore" = COALESCE(%s, "awayScore"),
                        status = %s,
                        "moneylineHome" = COALESCE(%s, "moneylineHome"),
                        "moneylineAway" = COALESCE(%s, "moneylineAway"),
                        "spreadHome" = COALESCE(%s, "spreadHome"),
                        "spreadAway" = COALESCE(%s, "spreadAway"),
                        total = COALESCE(%s, total),
                        "oddsSource" = %s,
                        "oddsUpdatedAt" = NOW(),
                        "updatedAt" = NOW()
                    WHERE id = %s
                ''', (
                    home_score, away_score, status,
                    odds["moneylineHome"], odds["moneylineAway"],
                    odds["spreadHome"], odds["spreadAway"], odds["total"],
                    "sportsgameodds", existing[0]
                ))
                updated += 1
            else:
                # Insert new — handle both unique constraints
                cursor.execute('''
                    INSERT INTO "SportsGame" (
                        league, season, "gameDate", "homeTeam", "awayTeam",
                        "externalGameId", "homeScore", "awayScore", status,
                        "moneylineHome", "moneylineAway", "spreadHome", "spreadAway",
                        total, "oddsSource", "oddsUpdatedAt", "createdAt", "updatedAt"
                    ) VALUES (
                        %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW(), NOW()
                    )
                    ON CONFLICT (league, season, "gameDate", "homeTeam", "awayTeam") DO UPDATE SET
                        "externalGameId" = COALESCE("SportsGame"."externalGameId", EXCLUDED."externalGameId"),
                        "moneylineHome" = COALESCE(EXCLUDED."moneylineHome", "SportsGame"."moneylineHome"),
                        "moneylineAway" = COALESCE(EXCLUDED."moneylineAway", "SportsGame"."moneylineAway"),
                        "spreadHome" = COALESCE(EXCLUDED."spreadHome", "SportsGame"."spreadHome"),
                        "spreadAway" = COALESCE(EXCLUDED."spreadAway", "SportsGame"."spreadAway"),
                        total = COALESCE(EXCLUDED.total, "SportsGame".total),
                        "oddsSource" = EXCLUDED."oddsSource",
                        "oddsUpdatedAt" = NOW(),
                        "updatedAt" = NOW()
                ''', (
                    league, season, game_date, home_team, away_team,
                    event_id, home_score, away_score, status,
                    odds["moneylineHome"], odds["moneylineAway"],
                    odds["spreadHome"], odds["spreadAway"], odds["total"],
                    "sportsgameodds"
                ))
                inserted += 1

            cursor.execute("RELEASE SAVEPOINT event_sp")

        except Exception as e:
            print(f"  Error upserting event {event.get('eventID')}: {e}")
            # Rollback this event's changes but keep prior events
            try:
                cursor.execute("ROLLBACK TO SAVEPOINT event_sp")
            except Exception:
                pass
            continue

    conn.commit()
    cursor.close()
    return inserted, updated


def upsert_player_props(conn, props: List[Dict]):
    """Upsert player props to PlayerPropLine table"""
    if not conn or not props:
        return 0

    cursor = conn.cursor()
    inserted = 0

    for prop in props:
        try:
            cursor.execute('''
                INSERT INTO "PlayerPropLine" (
                    league, "gameId", "playerExternalId", "propType", market,
                    "lineValue", "oddsAmerican", vendor, raw, "createdAt",
                    "fairOdds", "fairLine", "fdLine", "fdOverOdds", "dkLine", "dkOverOdds",
                    "marketName", "homeTeam", "awayTeam", "gameStart", "snapshotAt", "integrityStatus"
                ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), %s, %s, %s, %s, %s, %s,
                    %s, %s, %s, %s, %s, %s)
                ON CONFLICT (league, "gameId", "playerExternalId", vendor, "propType", "lineValue", "oddsAmerican")
                DO UPDATE SET
                    "fairOdds" = COALESCE(EXCLUDED."fairOdds", "PlayerPropLine"."fairOdds"),
                    "fairLine" = COALESCE(EXCLUDED."fairLine", "PlayerPropLine"."fairLine"),
                    "fdLine" = COALESCE(EXCLUDED."fdLine", "PlayerPropLine"."fdLine"),
                    "fdOverOdds" = COALESCE(EXCLUDED."fdOverOdds", "PlayerPropLine"."fdOverOdds"),
                    "dkLine" = COALESCE(EXCLUDED."dkLine", "PlayerPropLine"."dkLine"),
                    "dkOverOdds" = COALESCE(EXCLUDED."dkOverOdds", "PlayerPropLine"."dkOverOdds"),
                    "marketName" = COALESCE(EXCLUDED."marketName", "PlayerPropLine"."marketName"),
                    "homeTeam" = COALESCE(EXCLUDED."homeTeam", "PlayerPropLine"."homeTeam"),
                    "awayTeam" = COALESCE(EXCLUDED."awayTeam", "PlayerPropLine"."awayTeam"),
                    "gameStart" = COALESCE(EXCLUDED."gameStart", "PlayerPropLine"."gameStart"),
                    "snapshotAt" = COALESCE(EXCLUDED."snapshotAt", "PlayerPropLine"."snapshotAt"),
                    "integrityStatus" = COALESCE(EXCLUDED."integrityStatus", "PlayerPropLine"."integrityStatus")
            ''', (
                prop["league"], prop.get("gameId"), prop["playerExternalId"],
                prop["propType"], prop.get("market"), prop["lineValue"],
                prop.get("oddsAmerican"), prop.get("vendor", "sportsgameodds"),
                json.dumps(prop.get("raw", {})),
                prop.get("fairOdds"), prop.get("fairLine"),
                prop.get("fdLine"), prop.get("fdOverOdds"),
                prop.get("dkLine"), prop.get("dkOverOdds"),
                prop.get("marketName"), prop.get("homeTeam"), prop.get("awayTeam"),
                prop.get("gameStart"), prop.get("snapshotAt"), prop.get("integrityStatus"),
            ))
            if cursor.rowcount > 0:
                inserted += 1
        except Exception as e:
            continue

    conn.commit()
    cursor.close()
    return inserted


def save_to_external_feed(conn, events: List[Dict], league: str, source: str = "sportsgameodds"):
    """Save raw events to ExternalFeedRecord for audit trail"""
    if not conn or not events:
        return 0

    cursor = conn.cursor()
    inserted = 0

    for event in events:
        try:
            raw_json = json.dumps(event, default=str)
            content_hash = hashlib.md5(raw_json.encode()).hexdigest()

            cursor.execute('''
                INSERT INTO "ExternalFeedRecord" (
                    source, kind, league, "contentHash", raw, "fetchedAt", "createdAt"
                ) VALUES (%s, %s, %s, %s, %s, NOW(), NOW())
                ON CONFLICT (source, "contentHash") DO NOTHING
            ''', (source, "odds", league, content_hash, raw_json))

            if cursor.rowcount > 0:
                inserted += 1
        except Exception as e:
            continue

    conn.commit()
    cursor.close()
    return inserted


def main():
    parser = argparse.ArgumentParser(description="Fetch odds from SportsGameOdds API")
    parser.add_argument("league", nargs="?", default="nba", help="League to fetch (default: nba)")
    parser.add_argument("--props", action="store_true", help="Include player props")
    parser.add_argument("--all", action="store_true", help="Fetch all major leagues")
    parser.add_argument("--alt-lines", action="store_true", help="Include alternate lines")
    parser.add_argument("--limit", type=int, default=100, help="Max events per request")
    parser.add_argument("--dry-run", action="store_true", help="Don't save to database")
    args = parser.parse_args()

    print("=" * 60)
    print("SPORTSGAMEODDS API ODDS FETCHER")
    print("=" * 60)

    client = SportsGameOddsClient(SGO_API_KEY)

    # Check usage first
    usage = client.get_usage()
    if usage.get("success") != False:
        print(f"API Usage: {usage.get('data', {})}")

    # Determine leagues to fetch
    if args.all:
        leagues = MAJOR_LEAGUES
    else:
        leagues = [args.league.lower()]

    # Connect to database
    conn = None if args.dry_run else get_db_connection()
    if conn:
        print("Connected to PostgreSQL database")
    elif not args.dry_run:
        print("WARNING: Could not connect to database, will only print results")

    total_events = 0
    total_props = 0
    total_inserted = 0
    total_updated = 0

    for league in leagues:
        league_id = LEAGUE_MAP.get(league, league)
        # Normalize the DB league name
        db_league = normalize_league(league)
        print(f"\n{'='*40}")
        print(f"Fetching {league_id} (db: {db_league})...")
        print(f"{'='*40}")

        # Fetch events
        events = client.get_all_events(
            league_id,
            include_alt_lines=args.alt_lines,
            limit=args.limit
        )

        if not events:
            print(f"  No events found for {league_id}")
            continue

        print(f"  Total events: {len(events)}")
        total_events += len(events)

        # Count events with odds
        events_with_odds = sum(1 for e in events if e.get("odds"))
        print(f"  Events with odds: {events_with_odds}")

        # Save to database
        if conn:
            inserted, updated = upsert_games(conn, events, db_league)
            print(f"  Database: {inserted} inserted, {updated} updated")
            total_inserted += inserted
            total_updated += updated

            # Save raw data
            feed_count = save_to_external_feed(conn, events, db_league)
            print(f"  External feed records: {feed_count}")

        # Extract and save player props if requested
        if args.props:
            all_props = []
            for event in events:
                props = extract_player_props(event)
                all_props.extend(props)

            if all_props:
                print(f"  Player props found: {len(all_props)}")
                total_props += len(all_props)

                if conn:
                    props_inserted = upsert_player_props(conn, all_props)
                    print(f"  Props inserted: {props_inserted}")

        # Sample output
        if events:
            print("\n  Sample events:")
            for event in events[:3]:
                teams = event.get("teams", {})
                home = teams.get("home", {}).get("names", {}).get("medium") or teams.get("home", {}).get("teamID", "?")
                away = teams.get("away", {}).get("names", {}).get("medium") or teams.get("away", {}).get("teamID", "?")
                start = event.get("status", {}).get("startsAt", "?")[:16] if event.get("status", {}).get("startsAt") else "?"
                odds = parse_event_odds(event)
                print(f"    {away} @ {home} ({start})")
                print(f"      ML: {odds['moneylineAway']}/{odds['moneylineHome']} | Spread: {odds['spreadHome']} | Total: {odds['total']}")

    # Summary
    print("\n" + "=" * 60)
    print("SUMMARY")
    print("=" * 60)
    print(f"Total events fetched: {total_events}")
    print(f"Total player props: {total_props}")
    print(f"Database inserts: {total_inserted}")
    print(f"Database updates: {total_updated}")
    print(f"API requests made: {client.requests_made}")
    print(f"Objects used: {client.objects_used}")

    # Final usage check
    final_usage = client.get_usage()
    if final_usage.get("data"):
        print(f"Remaining quota: {final_usage['data']}")

    if conn:
        conn.close()


if __name__ == "__main__":
    main()
