#!/usr/bin/env python3
"""
The Odds API Enhanced Fetcher

Unified fetcher for odds, player props, and historical data from The Odds API.
Tracks quota usage and handles all sports/markets.

Usage:
    python theoddsapi_enhanced_fetcher.py --live              # Live odds for all sports
    python theoddsapi_enhanced_fetcher.py --props             # Player props for upcoming games
    python theoddsapi_enhanced_fetcher.py --scores            # Live scores
    python theoddsapi_enhanced_fetcher.py --events            # Event list (free)
    python theoddsapi_enhanced_fetcher.py --quota             # Check quota status
"""
import argparse
import requests
import psycopg2
import json
import os
from datetime import datetime, timezone, timedelta
from typing import Optional, Dict, List, Tuple

BASE_URL = 'https://api.the-odds-api.com/v4'

# Sport configurations
SPORTS = {
    'nba': {
        'key': 'basketball_nba',
        'prop_markets': [
            'player_points', 'player_rebounds', 'player_assists',
            'player_threes', 'player_points_rebounds_assists',
            'player_double_double'
        ]
    },
    'nfl': {
        'key': 'americanfootball_nfl',
        'prop_markets': [
            'player_pass_tds', 'player_pass_yds', 'player_rush_yds',
            'player_reception_yds', 'player_receptions', 'player_anytime_td'
        ]
    },
    'nhl': {
        'key': 'icehockey_nhl',
        'prop_markets': [
            'player_goals', 'player_shots_on_goal', 'player_goal_scorer_anytime'
        ]
    },
    'mlb': {
        'key': 'baseball_mlb',
        'prop_markets': [
            'batter_home_runs', 'pitcher_strikeouts', 'batter_hits_runs_rbis'
        ]
    },
    'ncaab': {
        'key': 'basketball_ncaab',
        'prop_markets': ['player_points', 'player_rebounds', 'player_assists']
    },
    'ncaaf': {
        'key': 'americanfootball_ncaaf',
        'prop_markets': ['player_pass_tds', 'player_rush_yds', 'player_anytime_td']
    }
}

# Core markets
CORE_MARKETS = ['h2h', 'spreads', 'totals']


def load_config() -> Tuple[str, str]:
    """Load database URL and API key from .env"""
    env_path = '/var/www/html/eventheodds/.env'
    db_url = None
    api_key = None

    try:
        with open(env_path, 'r') as f:
            for line in f:
                if line.startswith('SPORTS_DATABASE_URL='):
                    db_url = line.split('=', 1)[1].strip().strip('"').split('?')[0]
                elif line.startswith('THE_ODDS_API_KEY='):
                    api_key = line.split('=', 1)[1].strip().strip('"')
    except FileNotFoundError:
        pass

    db_url = db_url or os.environ.get('SPORTS_DATABASE_URL', '').split('?')[0]
    api_key = api_key or os.environ.get('THE_ODDS_API_KEY')

    return db_url, api_key


class TheOddsAPIFetcher:
    def __init__(self, api_key: str, db_url: str):
        self.api_key = api_key
        self.db_url = db_url
        self.session = requests.Session()
        self.quota_used = 0
        self.quota_remaining = None

    def _request(self, endpoint: str, params: dict = None) -> Optional[dict]:
        """Make API request and track quota"""
        if params is None:
            params = {}
        params['apiKey'] = self.api_key

        url = f"{BASE_URL}{endpoint}"

        try:
            resp = self.session.get(url, params=params, timeout=30)

            # Track quota from headers
            self.quota_used = int(resp.headers.get('x-requests-used', 0))
            self.quota_remaining = int(resp.headers.get('x-requests-remaining', 0))
            last_cost = int(resp.headers.get('x-requests-last', 0))

            if resp.status_code == 401:
                print(f"ERROR: Invalid API key")
                return None
            if resp.status_code == 422:
                print(f"INFO: No data for {endpoint}")
                return None
            if resp.status_code == 429:
                print(f"ERROR: Quota exceeded")
                return None
            if resp.status_code != 200:
                print(f"ERROR: API returned {resp.status_code}")
                return None

            return resp.json()

        except Exception as e:
            print(f"ERROR: Request failed: {e}")
            return None

    def check_quota(self) -> Dict:
        """Check current quota status"""
        # Sports endpoint is free and returns quota headers
        self._request('/sports/')
        return {
            'used': self.quota_used,
            'remaining': self.quota_remaining,
            'total': self.quota_used + (self.quota_remaining or 0)
        }

    def get_events(self, sport_key: str) -> List[dict]:
        """Get events for a sport (FREE endpoint)"""
        events = self._request(f'/sports/{sport_key}/events')
        return events or []

    def get_odds(self, sport_key: str, regions: str = 'us',
                 markets: List[str] = None, event_ids: List[str] = None) -> List[dict]:
        """Get odds for a sport"""
        if markets is None:
            markets = CORE_MARKETS

        params = {
            'regions': regions,
            'markets': ','.join(markets),
            'oddsFormat': 'american'
        }

        if event_ids:
            params['eventIds'] = ','.join(event_ids)

        odds = self._request(f'/sports/{sport_key}/odds', params)
        return odds or []

    def get_event_odds(self, sport_key: str, event_id: str,
                       regions: str = 'us', markets: List[str] = None) -> Optional[dict]:
        """Get detailed odds for a single event including props"""
        params = {
            'regions': regions,
            'oddsFormat': 'american'
        }

        if markets:
            params['markets'] = ','.join(markets)

        return self._request(f'/sports/{sport_key}/events/{event_id}/odds', params)

    def get_scores(self, sport_key: str, days_from: int = 1) -> List[dict]:
        """Get scores for live and recent games"""
        params = {}
        if days_from > 0:
            params['daysFrom'] = days_from

        scores = self._request(f'/sports/{sport_key}/scores', params)
        return scores or []

    def get_historical_odds(self, sport_key: str, date: str,
                           regions: str = 'us', markets: List[str] = None) -> Optional[dict]:
        """Get historical odds at a specific timestamp"""
        if markets is None:
            markets = CORE_MARKETS

        params = {
            'date': date,
            'regions': regions,
            'markets': ','.join(markets),
            'oddsFormat': 'american'
        }

        return self._request(f'/historical/sports/{sport_key}/odds', params)


def ensure_tables(conn):
    """Ensure required database tables exist"""
    cur = conn.cursor()

    # Check if OddsSnapshot needs theoddsapiEventId column
    cur.execute("""
        SELECT column_name FROM information_schema.columns
        WHERE table_name = 'OddsSnapshot' AND column_name = 'theoddsapiEventId'
    """)
    if not cur.fetchone():
        try:
            cur.execute('ALTER TABLE "OddsSnapshot" ADD COLUMN "theoddsapiEventId" VARCHAR(100)')
            conn.commit()
        except:
            conn.rollback()

    cur.execute('''
        CREATE INDEX IF NOT EXISTS idx_odds_snapshot_toapi_event
        ON "OddsSnapshot"(league, "theoddsapiEventId", "snapshotAt")
    ''')

    # PropSnapshot table for player props
    cur.execute('''
        CREATE TABLE IF NOT EXISTS "PropSnapshot" (
            id SERIAL PRIMARY KEY,
            league VARCHAR(20),
            "theoddsapiEventId" VARCHAR(100),
            "gameDate" TIMESTAMP,
            "homeTeam" VARCHAR(100),
            "awayTeam" VARCHAR(100),
            bookmaker VARCHAR(50),
            "marketType" VARCHAR(50),
            "playerName" VARCHAR(100),
            "propLine" DOUBLE PRECISION,
            "overPrice" INTEGER,
            "underPrice" INTEGER,
            "capturedAt" TIMESTAMP DEFAULT NOW(),
            source VARCHAR(50) DEFAULT 'theoddsapi',
            "createdAt" TIMESTAMP DEFAULT NOW()
        )
    ''')

    cur.execute('''
        CREATE INDEX IF NOT EXISTS idx_prop_snapshot_event
        ON "PropSnapshot"(league, "theoddsapiEventId", "playerName", "capturedAt")
    ''')

    conn.commit()
    cur.close()


def capture_live_odds(fetcher: TheOddsAPIFetcher, conn, leagues: List[str] = None):
    """Capture live odds for all sports"""
    if leagues is None:
        leagues = ['nba', 'nfl', 'nhl']

    cur = conn.cursor()
    total_captured = 0

    print(f"\n{'='*60}")
    print("CAPTURING LIVE ODDS")
    print(f"Time: {datetime.now(timezone.utc).isoformat()}")
    print(f"{'='*60}")

    for league in leagues:
        sport_config = SPORTS.get(league)
        if not sport_config:
            continue

        sport_key = sport_config['key']
        odds = fetcher.get_odds(sport_key)

        if not odds:
            print(f"{league.upper()}: No events")
            continue

        print(f"\n{league.upper()}: {len(odds)} events")

        for event in odds:
            event_id = event.get('id')
            home = event.get('home_team', '')
            away = event.get('away_team', '')
            commence = event.get('commence_time')

            commence_dt = None
            if commence:
                try:
                    commence_dt = datetime.fromisoformat(commence.replace('Z', '+00:00'))
                except:
                    pass

            for book in event.get('bookmakers', []):
                book_name = book.get('key', 'unknown')

                # Collect all market data for this bookmaker
                ml_home = ml_away = None
                spread_home = spread_away = None
                spread_home_odds = spread_away_odds = None
                total_line = None
                over_odds = under_odds = None

                for market in book.get('markets', []):
                    market_key = market.get('key')
                    outcomes = market.get('outcomes', [])

                    if market_key == 'h2h':
                        for o in outcomes:
                            if o.get('name') == home:
                                ml_home = o.get('price')
                            elif o.get('name') == away:
                                ml_away = o.get('price')

                    elif market_key == 'spreads':
                        for o in outcomes:
                            if o.get('name') == home:
                                spread_home = o.get('point')
                                spread_home_odds = o.get('price')
                            elif o.get('name') == away:
                                spread_away = o.get('point')
                                spread_away_odds = o.get('price')

                    elif market_key == 'totals':
                        for o in outcomes:
                            if o.get('name') == 'Over':
                                total_line = o.get('point')
                                over_odds = o.get('price')
                            elif o.get('name') == 'Under':
                                under_odds = o.get('price')

                # Insert single combined record per bookmaker
                cur.execute('''
                    INSERT INTO "OddsSnapshot" (
                        league, "theoddsapiEventId", "externalGameId", "gameDate",
                        "homeTeam", "awayTeam", bookmaker, "snapshotType", "snapshotAt",
                        "moneylineHome", "moneylineAway",
                        "spreadHome", "spreadAway", "spreadHomeOdds", "spreadAwayOdds",
                        total, "totalOverOdds", "totalUnderOdds", source
                    ) VALUES (
                        %s, %s, %s, %s, %s, %s, %s, 'live', NOW(),
                        %s, %s, %s, %s, %s, %s, %s, %s, %s, 'theoddsapi'
                    )
                    ON CONFLICT (league, "externalGameId", "snapshotAt", source, bookmaker)
                    DO UPDATE SET
                        "moneylineHome" = EXCLUDED."moneylineHome",
                        "moneylineAway" = EXCLUDED."moneylineAway",
                        "spreadHome" = EXCLUDED."spreadHome",
                        "spreadAway" = EXCLUDED."spreadAway",
                        "spreadHomeOdds" = EXCLUDED."spreadHomeOdds",
                        "spreadAwayOdds" = EXCLUDED."spreadAwayOdds",
                        total = EXCLUDED.total,
                        "totalOverOdds" = EXCLUDED."totalOverOdds",
                        "totalUnderOdds" = EXCLUDED."totalUnderOdds"
                ''', (
                    league, event_id, event_id, commence_dt,
                    home, away, book_name,
                    ml_home, ml_away,
                    spread_home, spread_away, spread_home_odds, spread_away_odds,
                    total_line, over_odds, under_odds
                ))
                total_captured += 1

    conn.commit()
    cur.close()

    print(f"\n{'='*60}")
    print(f"CAPTURED: {total_captured} odds records")
    print(f"QUOTA: {fetcher.quota_used} used, {fetcher.quota_remaining} remaining")
    print(f"{'='*60}")

    return total_captured


def capture_player_props(fetcher: TheOddsAPIFetcher, conn, leagues: List[str] = None):
    """Capture player props for upcoming games"""
    if leagues is None:
        leagues = ['nba', 'nfl']

    cur = conn.cursor()
    total_captured = 0

    print(f"\n{'='*60}")
    print("CAPTURING PLAYER PROPS")
    print(f"Time: {datetime.now(timezone.utc).isoformat()}")
    print(f"{'='*60}")

    for league in leagues:
        sport_config = SPORTS.get(league)
        if not sport_config:
            continue

        sport_key = sport_config['key']
        prop_markets = sport_config['prop_markets']

        # Get events (free)
        events = fetcher.get_events(sport_key)
        if not events:
            print(f"{league.upper()}: No events")
            continue

        # Filter to games starting within 24 hours
        now = datetime.now(timezone.utc)
        upcoming_events = []

        for event in events:
            commence = event.get('commence_time')
            if commence:
                try:
                    commence_dt = datetime.fromisoformat(commence.replace('Z', '+00:00'))
                    if now < commence_dt < now + timedelta(hours=24):
                        upcoming_events.append(event)
                except:
                    pass

        print(f"\n{league.upper()}: {len(upcoming_events)} upcoming games (next 24h)")

        for event in upcoming_events[:10]:  # Limit to 10 games per sport
            event_id = event.get('id')
            home = event.get('home_team', '')
            away = event.get('away_team', '')
            commence = event.get('commence_time')

            commence_dt = None
            if commence:
                try:
                    commence_dt = datetime.fromisoformat(commence.replace('Z', '+00:00'))
                except:
                    pass

            # Get props for this event
            event_odds = fetcher.get_event_odds(sport_key, event_id, markets=prop_markets)

            if not event_odds:
                continue

            for book in event_odds.get('bookmakers', []):
                book_name = book.get('key', 'unknown')

                for market in book.get('markets', []):
                    market_key = market.get('key')

                    if not market_key.startswith('player_'):
                        continue

                    for outcome in market.get('outcomes', []):
                        player_name = outcome.get('description', outcome.get('name', ''))
                        prop_line = outcome.get('point')
                        price = outcome.get('price')
                        outcome_type = outcome.get('name', '').lower()

                        # Skip if no line
                        if prop_line is None:
                            continue

                        over_price = price if outcome_type == 'over' else None
                        under_price = price if outcome_type == 'under' else None

                        cur.execute('''
                            INSERT INTO "PropSnapshot" (
                                league, "theoddsapiEventId", "gameDate", "homeTeam", "awayTeam",
                                bookmaker, "marketType", "playerName", "propLine",
                                "overPrice", "underPrice", "capturedAt"
                            ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, date_trunc('minute', NOW()))
                            ON CONFLICT DO NOTHING
                        ''', (
                            league, event_id, commence_dt, home, away,
                            book_name, market_key, player_name, prop_line,
                            over_price, under_price
                        ))
                        total_captured += 1

    conn.commit()
    cur.close()

    print(f"\n{'='*60}")
    print(f"CAPTURED: {total_captured} prop records")
    print(f"QUOTA: {fetcher.quota_used} used, {fetcher.quota_remaining} remaining")
    print(f"{'='*60}")

    return total_captured


def show_quota(fetcher: TheOddsAPIFetcher):
    """Display quota status"""
    quota = fetcher.check_quota()

    print(f"\n{'='*60}")
    print("THE ODDS API QUOTA STATUS")
    print(f"{'='*60}")
    print(f"Used:      {quota['used']:,}")
    print(f"Remaining: {quota['remaining']:,}")
    print(f"Total:     {quota['total']:,}")
    print(f"Usage:     {quota['used'] / quota['total'] * 100:.2f}%" if quota['total'] else "N/A")
    print(f"{'='*60}")


def main():
    parser = argparse.ArgumentParser(description='The Odds API Enhanced Fetcher')
    parser.add_argument('--live', action='store_true', help='Capture live odds')
    parser.add_argument('--props', action='store_true', help='Capture player props')
    parser.add_argument('--scores', action='store_true', help='Capture scores')
    parser.add_argument('--events', action='store_true', help='List events')
    parser.add_argument('--quota', action='store_true', help='Check quota')
    parser.add_argument('--leagues', type=str, default='nba,nfl,nhl', help='Comma-separated leagues')

    args = parser.parse_args()

    db_url, api_key = load_config()

    if not api_key:
        print("ERROR: THE_ODDS_API_KEY not configured")
        return 1

    if not db_url:
        print("ERROR: SPORTS_DATABASE_URL not configured")
        return 1

    fetcher = TheOddsAPIFetcher(api_key, db_url)
    leagues = [l.strip() for l in args.leagues.split(',')]

    # Default to quota check if no action specified
    if not any([args.live, args.props, args.scores, args.events]):
        args.quota = True

    if args.quota:
        show_quota(fetcher)

    if args.events:
        for league in leagues:
            sport_config = SPORTS.get(league)
            if sport_config:
                events = fetcher.get_events(sport_config['key'])
                print(f"\n{league.upper()}: {len(events)} events")
                for e in events[:5]:
                    print(f"  {e['away_team']} @ {e['home_team']} - {e['commence_time']}")

    if args.live or args.props:
        conn = psycopg2.connect(db_url)
        ensure_tables(conn)

        if args.live:
            capture_live_odds(fetcher, conn, leagues)

        if args.props:
            capture_player_props(fetcher, conn, leagues)

        conn.close()

    return 0


if __name__ == '__main__':
    exit(main())
