# 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 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())
        
        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)}", level=xbmc.LOGERROR)
        return [], None


def save_scores_to_cache(games):
    """
    Save scores to local cache file.
    """
    try:
        cache_data = {
            'games': games,
            'timestamp': time.time()
        }
        
        # 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',
            'NBA': 'sports/basketball/nba',
            '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
                
                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
                period = competition.get('period', '')
                clock = competition.get('clock', '')
                
                # Build detailed status text
                detailed_status = status
                if period and status in ['In Progress', 'Halftime', 'Final']:
                    detailed_status = f"Q{period}" if not clock else f"Q{period} {clock}"
                
                # Get venue info
                venue = competition.get('venue', {})
                venue_name = venue.get('fullName', '')
                
                # 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,
                    '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', '')
                })
            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', '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',
        'NBA': 'sports/basketball/nba',
        '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
