# widgets.py - Widget System with Caching
# This module handles widget functionality including Live Scores with caching

import xbmc
import xbmcgui
import xbmcaddon
import xbmcvfs
import requests
import json
import time
import os
import random
from tools import debug_log

# Cache file path for Live Scores
SCORES_CACHE_PATH = xbmcvfs.translatePath("special://userdata/addon_data/plugin.video.jet_guide/scores_cache.json")

# Cache format version - bump when the on-disk schema changes so old caches
# (with the short "In Progress" status, missing broadcaster, etc.) are rebuilt.
CACHE_FORMAT_VERSION = 2

# Cache TTL in seconds (default 5 minutes)
DEFAULT_CACHE_TTL = 300  # 5 minutes


def get_cached_scores():
    """
    Read cached scores from local file.
    Returns (games_list, cache_age_seconds) or ([], None) if no cache exists.
    """
    try:
        if not xbmcvfs.exists(SCORES_CACHE_PATH):
            return [], None

        with xbmcvfs.File(SCORES_CACHE_PATH, "r") as f:
            cache_data = json.loads(f.read())

        # Invalidate caches from older format versions
        if cache_data.get('format_version') != CACHE_FORMAT_VERSION:
            debug_log(f"DEBUG: Cache format version mismatch "
                      f"(have {cache_data.get('format_version')}, want {CACHE_FORMAT_VERSION}), "
                      f"invalidating", level=xbmc.LOGINFO)
            try:
                xbmcvfs.delete(SCORES_CACHE_PATH)
            except Exception:
                pass
            return [], None

        cached_games = cache_data.get('games', [])
        timestamp = cache_data.get('timestamp', 0)
        cache_age = time.time() - timestamp if timestamp else None

        debug_log(f"DEBUG: Loaded {len(cached_games)} games from cache, age: {cache_age}s", level=xbmc.LOGINFO)
        return cached_games, cache_age

    except Exception as e:
        debug_log(f"Error reading cached scores: {str(e)}", xbmc.LOGERROR)
        return [], None


def save_scores_to_cache(games):
    """
    Save scores to local cache file.
    """
    try:
        cache_data = {
            'games': games,
            'timestamp': time.time(),
            'format_version': CACHE_FORMAT_VERSION
        }
        
        # Ensure directory exists
        cache_dir = os.path.dirname(SCORES_CACHE_PATH)
        if not os.path.exists(cache_dir):
            os.makedirs(cache_dir, exist_ok=True)
        
        with xbmcvfs.File(SCORES_CACHE_PATH, "w") as f:
            f.write(json.dumps(cache_data, indent=2))
        
        debug_log(f"DEBUG: Saved {len(games)} games to cache", level=xbmc.LOGINFO)
        return True
        
    except Exception as e:
        debug_log(f"Error saving scores to cache: {str(e)}", level=xbmc.LOGERROR)
        return False


def fetch_espn_scores_with_cache(sport_endpoint, sport_name, force_refresh=False):
    """
    Fetch scores from ESPN API with caching support.
    Uses cache if fresh, otherwise fetches from API and updates cache.
    
    Args:
        sport_endpoint: ESPN API endpoint (e.g., 'sports/basketball/nba')
        sport_name: Display name (e.g., 'NBA')
        force_refresh: If True, ignore cache and fetch fresh data
    
    Returns:
        List of game dictionaries
    """
    games = []
    
    # Get cache TTL from settings (default 5 minutes)
    try:
        current_addon = xbmcaddon.Addon()
        cache_ttl_setting = current_addon.getSetting('live_scores_cache_ttl') or '5'
        cache_ttl = int(cache_ttl_setting) * 60  # Convert minutes to seconds
    except:
        cache_ttl = DEFAULT_CACHE_TTL
    
    # Check cache first (unless force refresh)
    if not force_refresh:
        cached_games, cache_age = get_cached_scores()
        
        if cached_games and cache_age is not None:
            if cache_age < cache_ttl:
                # Get the sports that are in the cache
                cached_sports = set(g.get('sport') for g in cached_games)
                
                # Check if requested sport is in cache
                if sport_name and sport_name != 'All':
                    if sport_name in cached_sports:
                        debug_log(f"DEBUG: Using cached scores for {sport_name} (age: {cache_age}s, TTL: {cache_ttl}s)", level=xbmc.LOGINFO)
                        cached_games = [g for g in cached_games if g.get('sport') == sport_name]
                        return cached_games
                    else:
                        # Requested sport not in cache, need to fetch fresh data
                        debug_log(f"DEBUG: {sport_name} not in cache (only {cached_sports}), fetching fresh data", level=xbmc.LOGINFO)
                else:
                    # 'All' or no sport specified, use all cached games
                    debug_log(f"DEBUG: Using cached scores (age: {cache_age}s, TTL: {cache_ttl}s)", level=xbmc.LOGINFO)
                    return cached_games
            else:
                debug_log(f"DEBUG: Cache expired (age: {cache_age}s, TTL: {cache_ttl}s), fetching fresh data", level=xbmc.LOGINFO)
    
    # Fetch fresh data from ESPN API
    debug_log(f"DEBUG: Fetching fresh scores from ESPN for {sport_name}", level=xbmc.LOGINFO)
    
    try:
        sport_map = {
            'NFL': 'sports/football/nfl',
            'MLB': 'sports/baseball/mlb',
            'NCAABASE': 'sports/baseball/college-baseball',
            'NBA': 'sports/basketball/nba',
        'NBA Summer League': 'sports/basketball/nba-summer-las-vegas',
        'NBA Utah Summer League': 'sports/basketball/nba-summer-utah',
        'NBA California Classic': 'sports/basketball/nba-summer-california',
            'NBA Utah Summer League': 'sports/basketball/nba-summer-utah',
            'NBA California Classic': 'sports/basketball/nba-summer-california',
            'NCAAB': 'sports/basketball/mens-college-basketball',
            'WNBA': 'sports/basketball/wnba',
            'NHL': 'sports/hockey/nhl',
            'MLS': 'sports/soccer/usa.1',
            'CFL': 'sports/football/nfl',
            'La Liga': 'sports/soccer/esp.1',
            'Bundesliga': 'sports/soccer/ger.1',
            'Serie A': 'sports/soccer/ita.1',
            'Ligue 1': 'sports/soccer/fra.1',
            'Champions League': 'sports/soccer/uefa.champions',
        }
        
        espn_sport = sport_map.get(sport_name)
        if not espn_sport:
            debug_log(f"DEBUG: No ESPN endpoint for sport: {sport_name}", level=xbmc.LOGINFO)
            return games
        
        url = f"https://site.api.espn.com/apis/site/v2/{espn_sport}/scoreboard"
        debug_log(f"DEBUG: Fetching ESPN scores from {url}", level=xbmc.LOGINFO)
        
        response = requests.get(
            url,
            headers={
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
            },
            timeout=10
        )
        response.raise_for_status()
        
        # Parse JSON response from ESPN
        data = response.json()
        events = data.get('events', [])
        
        debug_log(f"DEBUG: Found {len(events)} events for {sport_name}", level=xbmc.LOGINFO)
        
        for event in events:
            try:
                competition = event.get('competitions', [{}])[0]
                competitors = competition.get('competitors', [])
                
                if len(competitors) < 2:
                    continue
                
                # Get team info
                team1 = competitors[0].get('team', {})
                team2 = competitors[1].get('team', {})
                
                team1_name = team1.get('displayName', '')
                team2_name = team2.get('displayName', '')
                
                if not team1_name or not team2_name:
                    continue
                
                # Get team logos - ESPN scoreboard API returns logo directly on team object
                team1_logo = team1.get('logo', '')
                team2_logo = team2.get('logo', '')
                
                # Debug: Log logo extraction for first game only
                if len(games) == 0:
                    debug_log(f"DEBUG: Team1 {team1_name} logo: {team1_logo[:80] if team1_logo else 'NONE'}", level=xbmc.LOGINFO)
                    debug_log(f"DEBUG: Team2 {team2_name} logo: {team2_logo[:80] if team2_logo else 'NONE'}", level=xbmc.LOGINFO)
                
                score1 = str(competitors[0].get('score', '0'))
                score2 = str(competitors[1].get('score', '0'))
                
                # Get game status
                status_obj = competition.get('status', {})
                status_type = status_obj.get('type', {})
                status = status_type.get('description', 'Scheduled')

                # Get period and clock for live games (these live on the status object).
                # ESPN returns period as int (e.g. 2) and displayClock as float/int
                # (e.g. 5.3 or 330.0) - coerce to str so setProperty() accepts them.
                period = str(status_obj.get('period', '') or '')
                clock = str(status_obj.get('displayClock', '') or '')

                # Build detailed status text (matches sportsgrid.py format)
                detailed_status = status
                if period and clock:
                    
                    if "mlb" in espn_sport:
                        detailed_status = f"{status} - {period} inning"
                    else:
                        detailed_status = f"{status} - {period}Q {clock}"
                elif period:
                    detailed_status = f"{status} - {period}"

                # Handle MLB special case for detailed status
                
                
                # Get venue info
                venue = competition.get('venue', {})
                venue_name = venue.get('fullName', '')

                # Get broadcast networks (e.g. "ESPN", "Fox", "TNT")
                broadcasts = competition.get('broadcasts', []) or []
                broadcast_names = []
                for b in broadcasts:
                    for n in b.get('names', []) or []:
                        if n and n not in broadcast_names:
                            broadcast_names.append(n)
                broadcast_str = ', '.join(broadcast_names) if broadcast_names else 'TBD'
                
                # Get team statistics from the scoreboard response
                team1_stats = {}
                team2_stats = {}
                team1_leaders = {}
                team2_leaders = {}
                
                # Extract stats for team1
                team1_statistics = competitors[0].get('statistics', [])
                for stat in team1_statistics:
                    team1_stats[stat.get('name', '')] = {
                        'abbreviation': stat.get('abbreviation', ''),
                        'displayValue': stat.get('displayValue', '')
                    }
                
                # Extract stats for team2
                team2_statistics = competitors[1].get('statistics', [])
                for stat in team2_statistics:
                    team2_stats[stat.get('name', '')] = {
                        'abbreviation': stat.get('abbreviation', ''),
                        'displayValue': stat.get('displayValue', '')
                    }
                
                # Extract leaders for each team
                team1_leaders_data = competitors[0].get('leaders', [])
                team2_leaders_data = competitors[1].get('leaders', [])
                
                for leader in team1_leaders_data:
                    stat_name = leader.get('name', '')
                    leaders_list = leader.get('leaders', [])
                    if leaders_list:
                        top_leader = leaders_list[0]
                        athlete = top_leader.get('athlete', {})
                        athlete_name = athlete.get('displayName', top_leader.get('athlete', {}).get('fullName', ''))
                        value = top_leader.get('displayValue', '')
                        if stat_name and athlete_name:
                            team1_leaders[stat_name] = {
                                'name': athlete_name,
                                'value': value
                            }
                
                for leader in team2_leaders_data:
                    stat_name = leader.get('name', '')
                    leaders_list = leader.get('leaders', [])
                    if leaders_list:
                        top_leader = leaders_list[0]
                        athlete = top_leader.get('athlete', {})
                        athlete_name = athlete.get('displayName', top_leader.get('athlete', {}).get('fullName', ''))
                        value = top_leader.get('displayValue', '')
                        if stat_name and athlete_name:
                            team2_leaders[stat_name] = {
                                'name': athlete_name,
                                'value': value
                            }
                
                # Try to get statistics from leaders in the competition (backup)
                leaders = competition.get('leaders', [])
                stats_data = {}
                if leaders:
                    for leader in leaders:
                        stat_name = leader.get('name', '')
                        leaders_list = leader.get('leaders', [])
                        if leaders_list:
                            for l in leaders_list:
                                athlete = l.get('athlete', {})
                                athlete_name = athlete.get('fullName', '')
                                value = l.get('displayValue', '')
                                if stat_name and athlete_name:
                                    stats_data[f"{stat_name}_leader"] = f"{athlete_name}: {value}"
                
                games.append({
                    'team1': team1_name,
                    'team2': team2_name,
                    'team1_logo': team1_logo,
                    'team2_logo': team2_logo,
                    'score': f"{score1} - {score2}",
                    'status': detailed_status,
                    'full_status': status,
                    'sport': sport_name,
                    'venue': venue_name,
                    'period': period,
                    'clock': clock,
                    'stats': stats_data,
                    'team1_stats': team1_stats,
                    'team2_stats': team2_stats,
                    'team1_leaders': team1_leaders,
                    'team2_leaders': team2_leaders,
                    'event_id': event.get('id', ''),
                    'broadcast': broadcast_str
                })
            except Exception as e:
                debug_log(f"DEBUG: Error parsing event: {e}", level=xbmc.LOGDEBUG)
                continue
        
        debug_log(f"DEBUG: Parsed {len(games)} games from ESPN for {sport_name}", level=xbmc.LOGINFO)
        
        # Save to cache
        if games:
            save_scores_to_cache(games)
        
    except requests.exceptions.HTTPError as e:
        debug_log(f"HTTP Error fetching {sport_name} scores from ESPN: {str(e)}", level=xbmc.LOGERROR)
    except Exception as e:
        debug_log(f"Error fetching {sport_name} scores from ESPN: {str(e)}", level=xbmc.LOGERROR)
    
    return games


def fetch_all_scores_with_cache(sport_preference, force_refresh=False):
    """
    Fetch scores for all selected sports with caching support.
    
    Args:
        sport_preference: 'All', a single sport name, or a list of sport names
        force_refresh: If True, ignore cache and fetch fresh data
    
    Returns:
        List of game dictionaries
    """
    # Normalize to list
    if isinstance(sport_preference, list):
        sports_list = sport_preference
    elif sport_preference == 'All':
        sports_list = ['NFL', 'MLB', 'NBA', 'NBA Summer League', 'NBA Utah Summer League', 'NBA California Classic', 'NHL', 'MLS', 'NCAAB', 'WNBA', 'CFL', 'La Liga', 'Bundesliga', 'Serie A', 'Ligue 1', 'Champions League']
    else:
        sports_list = [sport_preference]
    
    debug_log(f"DEBUG: Fetching scores for sports: {sports_list}", level=xbmc.LOGINFO)
    
    # Check cache first (unless force refresh)
    if not force_refresh:
        cached_games, cache_age = get_cached_scores()
        
        if cached_games and cache_age is not None:
            # Get cache TTL from settings
            try:
                current_addon = xbmcaddon.Addon()
                cache_ttl_setting = current_addon.getSetting('live_scores_cache_ttl') or '5'
                cache_ttl = int(cache_ttl_setting) * 60
            except:
                cache_ttl = DEFAULT_CACHE_TTL
            
            # Check if cache is valid (age within TTL)
            if cache_age < cache_ttl:
                # Get the sports that are in the cache
                cached_sports = set(g.get('sport') for g in cached_games)
                debug_log(f"DEBUG: Cached sports: {cached_sports}", level=xbmc.LOGINFO)
                
                # Filter to only the requested sports
                if sports_list:
                    requested_set = set(sports_list)
                    if requested_set.issubset(cached_sports):
                        # All requested sports are in cache, use filtered cache
                        debug_log(f"DEBUG: Using cached scores for {sports_list} (age: {cache_age}s)", level=xbmc.LOGINFO)
                        cached_games = [g for g in cached_games if g.get('sport') in requested_set]
                        return cached_games
                    else:
                        # Some requested sports not in cache, need to fetch fresh data
                        missing = requested_set - cached_sports
                        debug_log(f"DEBUG: Missing sports in cache: {missing}, fetching fresh data", level=xbmc.LOGINFO)
                else:
                    # No specific sports requested, use all cached games
                    debug_log(f"DEBUG: Using cached scores for all sports (age: {cache_age}s)", level=xbmc.LOGINFO)
                    return cached_games
            else:
                debug_log(f"DEBUG: Cache expired (age: {cache_age}s, TTL: {cache_ttl}s), fetching fresh data", level=xbmc.LOGINFO)
    
    # Fetch fresh data
    games = []
    
    # ESPN API path mapping
    sport_map = {
        'NFL': 'sports/football/nfl',
        'MLB': 'sports/baseball/mlb',
        'NCAABASE': 'sports/baseball/college-baseball',
        'NBA': 'sports/basketball/nba',
        'NBA Summer League': 'sports/basketball/nba-summer-las-vegas',
        'NCAAB': 'sports/basketball/mens-college-basketball',
        'WNBA': 'sports/basketball/wnba',
        'NHL': 'sports/hockey/nhl',
        'MLS': 'sports/soccer/usa.1',
        'CFL': 'sports/football/nfl',
        'La Liga': 'sports/soccer/esp.1',
        'Bundesliga': 'sports/soccer/ger.1',
        'Serie A': 'sports/soccer/ita.1',
        'Ligue 1': 'sports/soccer/fra.1',
        'Champions League': 'sports/soccer/uefa.champions',
    }
    
    # Fetch each enabled sport
    for sport in sports_list:
        espn_path = sport_map.get(sport, sport)
        debug_log(f"DEBUG: Fetching {sport} from {espn_path}", level=xbmc.LOGINFO)
        scores = fetch_espn_scores_with_cache(espn_path, sport, force_refresh=True)
        games.extend(scores)
    
    return games


def clear_scores_cache():
    """
    Clear the cached scores file.
    """
    try:
        if xbmcvfs.exists(SCORES_CACHE_PATH):
            xbmcvfs.delete(SCORES_CACHE_PATH)
            debug_log("DEBUG: Scores cache cleared", level=xbmc.LOGINFO)
            return True
        return False
    except Exception as e:
        debug_log(f"Error clearing scores cache: {str(e)}", level=xbmc.LOGERROR)
        return False


def get_cache_info():
    """
    Get information about the current cache.
    Returns dict with cache_age, game_count, or None if no cache.
    """
    try:
        if not xbmcvfs.exists(SCORES_CACHE_PATH):
            return None
        
        with xbmcvfs.File(SCORES_CACHE_PATH, "r") as f:
            cache_data = json.loads(f.read())
        
        timestamp = cache_data.get('timestamp', 0)
        cache_age = time.time() - timestamp if timestamp else None
        game_count = len(cache_data.get('games', []))
        
        return {
            'cache_age': cache_age,
            'game_count': game_count,
            'timestamp': timestamp
        }
    except:
        return None
