import xbmc
import xbmcgui
import xbmcaddon
import xbmcvfs
import xbmcgui
import os
import re
import urllib.parse
from datetime import datetime, timedelta, timezone
import time
# from epggrid import EPGGrid
import requests
from tools import show_tools, debug_log
from themes import apply_theme_to_window

ADDON = xbmcaddon.Addon()
TEMP_PATH = xbmcvfs.translatePath('special://temp')

# Default sport thumbnails mapping (used when ESPN doesn't provide team logos)
# Maps sport names to local thumbnail files
SPORT_THUMBNAILS = {
    'nfl': 'nfl.png',
    'nba': 'nba.png',
    'nhl': 'nhl.png',
    'wnba': 'wnba.png',
    'mlb': 'baseball.png',
    'cfl': 'cfb.png',
    'ncaab': 'basketball.png',
    'mls': 'soccer.png',
    'premierleague': 'soccer.png',
    'laliga': 'soccer.png',
    'bundesliga': 'soccer.png',
    'seriea': 'soccer.png',
    'ligue1': 'soccer.png',
    'championsleague': 'soccer.png',
}

# Override ESPN team colors when they're incorrect
# Format: lowercase team name -> correct hex color (without #)
TEAM_COLOR_OVERRIDES = {
    # MLB
    'losangelesdodgers': '005A9C',  # Dodgers Blue (not red)
    'losangelesangels': 'BA0021',  # Angels Red
    'newyorkyankees': '003087',  # Yankees Navy Blue
    'bostonredsox': 'BD3039',  # Red Sox Red
    # NBA
    'losangeleslakers': '552583',  # Lakers Purple
    'detroitpistons': 'C8102E',  # Pistons Red
    'chicagobulls': 'CE1141',  # Bulls Red
    # NFL
    'newenglandpatriots': '002244',  # Patriots Navy Blue
    'dallascowboys': '003594',  # Cowboys Blue
    'greenbaypackers': '203731',  # Packers Green
    # NHL
    'detroitredwings': 'CE1126',  # Red Wings Red
    'torontomapleleafs': '003E7E',  # Maple Leafs Blue
    # Soccer
    'vfbstuttgart': 'E32219',  # Stuttgart Red
    'hamburgsv': '003D7C',  # HSV Blue
}

SPORT_STATS_CONFIG = {
    'nba': {
        'stat_labels': ['Points', 'Rebounds', 'Assists', 'FG%', '3PT%'],
        'stat_keys': ['points', 'rebounds', 'assists', 'fieldGoalPct', 'threePointPct'],
        'leader_labels': ['Points Leader', 'Rebounds Leader', 'Assists Leader'],
        'leader_keys': ['leader_points', 'leader_rebounds', 'leader_assists'],
    },
    'wnba': {
        'stat_labels': ['Points', 'Rebounds', 'Assists', 'FG%', '3PT%'],
        'stat_keys': ['points', 'rebounds', 'assists', 'fieldGoalPct', 'threePointPct'],
        'leader_labels': ['Points Leader', 'Rebounds Leader', 'Assists Leader'],
        'leader_keys': ['leader_points', 'leader_rebounds', 'leader_assists'],
    },
    'nfl': {
        'stat_labels': ['Points', 'Total Yards', 'Pass Yards', 'Rush Yards', 'Turnovers'],
        'stat_keys': ['points', 'totalYards', 'passingYards', 'rushingYards', 'turnovers'],
        'leader_labels': ['Passing Leader', 'Rushing Leader', 'Receiving Leader'],
        'leader_keys': ['leader_passing', 'leader_rushing', 'leader_receiving'],
    },
    'mlb': {
        'stat_labels': ['Runs', 'Hits', 'Home Runs', 'RBI', 'ERA'],
        'stat_keys': ['runs', 'hits', 'homeRuns', 'rbi', 'era'],
        'leader_labels': ['Batting Avg Leader', 'Home Run Leader', 'RBI Leader', 'ERA Leader', 'Strikeout Leader'],
        'leader_keys': ['leader_avg', 'leader_homeRuns', 'leader_RBIs', 'leader_era', 'leader_strikeouts'],
    },
    'nhl': {
        'stat_labels': ['Goals', 'Assists', 'Points', 'Shots', 'Saves'],
        'stat_keys': ['goals', 'assists', 'points', 'shotsOnGoal', 'saves'],
        'leader_labels': ['Goals Leader', 'Assists Leader', 'Points Leader'],
        'leader_keys': ['leader_goals', 'leader_assists', 'leader_points'],
    },
    'mls': {
        'stat_labels': ['Goals', 'Assists', 'Shots', 'Shots On Goal', 'Corners'],
        'stat_keys': ['goals', 'assists', 'shots', 'shotsOnGoal', 'corners'],
        'leader_labels': ['Goals Leader', 'Assists Leader', 'Points Leader'],
        'leader_keys': ['leader_goals', 'leader_assists', 'leader_points'],
    },
    'cfl': {
        'stat_labels': ['Points', 'Total Yards', 'Pass Yards', 'Rush Yards', 'Turnovers'],
        'stat_keys': ['points', 'totalYards', 'passingYards', 'rushingYards', 'turnovers'],
        'leader_labels': ['Passing Leader', 'Rushing Leader', 'Receiving Leader'],
        'leader_keys': ['leader_passing', 'leader_rushing', 'leader_receiving'],
    },
    'ncaab': {
        'stat_labels': ['Points', 'Rebounds', 'Assists', 'FG%', '3PT%'],
        'stat_keys': ['points', 'rebounds', 'assists', 'fieldGoalPct', 'threePointPct'],
        'leader_labels': ['Points Leader', 'Rebounds Leader', 'Assists Leader'],
        'leader_keys': ['leader_points', 'leader_rebounds', 'leader_assists'],
    },
    'premierleague': {
        'stat_labels': ['Goals', 'Assists', 'Shots', 'Shots On Goal', 'Clean Sheets'],
        'stat_keys': ['goals', 'assists', 'shots', 'shotsOnGoal', 'cleanSheets'],
        'leader_labels': ['Goals Leader', 'Assists Leader', 'Clean Sheets Leader'],
        'leader_keys': ['leader_goals', 'leader_assists', 'leader_cleansheets'],
    },
    'laliga': {
        'stat_labels': ['Goals', 'Assists', 'Shots', 'Shots On Goal', 'Clean Sheets'],
        'stat_keys': ['goals', 'assists', 'shots', 'shotsOnGoal', 'cleanSheets'],
        'leader_labels': ['Goals Leader', 'Assists Leader', 'Clean Sheets Leader'],
        'leader_keys': ['leader_goals', 'leader_assists', 'leader_cleansheets'],
    },
    'bundesliga': {
        'stat_labels': ['Goals', 'Assists', 'Shots', 'Shots On Goal', 'Clean Sheets'],
        'stat_keys': ['goals', 'assists', 'shots', 'shotsOnGoal', 'cleanSheets'],
        'leader_labels': ['Goals Leader', 'Assists Leader', 'Clean Sheets Leader'],
        'leader_keys': ['leader_goals', 'leader_assists', 'leader_cleansheets'],
    },
    'seriea': {
        'stat_labels': ['Goals', 'Assists', 'Shots', 'Shots On Goal', 'Clean Sheets'],
        'stat_keys': ['goals', 'assists', 'shots', 'shotsOnGoal', 'cleanSheets'],
        'leader_labels': ['Goals Leader', 'Assists Leader', 'Clean Sheets Leader'],
        'leader_keys': ['leader_goals', 'leader_assists', 'leader_cleansheets'],
    },
    'ligue1': {
        'stat_labels': ['Goals', 'Assists', 'Shots', 'Shots On Goal', 'Clean Sheets'],
        'stat_keys': ['goals', 'assists', 'shots', 'shotsOnGoal', 'cleanSheets'],
        'leader_labels': ['Goals Leader', 'Assists Leader', 'Clean Sheets Leader'],
        'leader_keys': ['leader_goals', 'leader_assists', 'leader_cleansheets'],
    },
    'championsleague': {
        'stat_labels': ['Goals', 'Assists', 'Shots', 'Shots On Goal', 'Clean Sheets'],
        'stat_keys': ['goals', 'assists', 'shots', 'shotsOnGoal', 'cleanSheets'],
        'leader_labels': ['Goals Leader', 'Assists Leader', 'Clean Sheets Leader'],
        'leader_keys': ['leader_goals', 'leader_assists', 'leader_cleansheets'],
    },
}

def get_sport_stats_config(sport):
    return SPORT_STATS_CONFIG.get(sport.lower(), SPORT_STATS_CONFIG.get('nba'))

class TimeListControl(xbmcgui.ControlList):
    def __init__(self, *args, **kwargs):
        self.grid = kwargs.pop('grid', None)
        super().__init__(*args, **kwargs)
        TimeoutError
    def onAction(self, action):
        if action.getId() == 7:  # ACTION_SELECT_ITEM
            if self.grid:
                self.grid.handleGameSelection()
        super().onAction(action)

class SportsGrid(xbmcgui.WindowXML):
    @classmethod
    def is_dialog_locked(cls):
        """Check if a dialog is currently locked"""
        return cls._dialog_lock
    
    @classmethod
    def acquire_dialog_lock(cls, owner):
        """Acquire dialog lock to prevent concurrent dialogs"""
        if cls._dialog_lock:
            return False
        cls._dialog_lock = True
        cls._dialog_locked_by = owner
        return True
    
    @classmethod
    def release_dialog_lock(cls, owner):
        """Release dialog lock"""
        if cls._dialog_locked_by == owner:
            cls._dialog_lock = False
            cls._dialog_locked_by = None
    
    # Class-level dialog lock to prevent concurrent busydialog crashes
    _dialog_lock = False
    _dialog_locked_by = None
    
    # API rate limiting cache - stores team data to reduce API calls
    # Cache duration: 5 minutes to avoid rate limiting
    _team_cache = {}
    _cache_duration = 300  # 5 minutes
    
    def _api_rate_limit_delay(self):
        """Small delay between API calls to avoid rate limiting"""
        time.sleep(0.3)  # 300ms delay between calls
    
    def _get_cached_team_data(self, team_name, data_type):
        """Get cached team data if available and not expired"""
        cache_key = f"{team_name}_{data_type}"
        if cache_key in self._team_cache:
            cached = self._team_cache[cache_key]
            if time.time() - cached.get('timestamp', 0) < self._cache_duration:
                debug_log(f"SportsGrid: Using cached {data_type} for {team_name}", xbmc.LOGINFO)
                return cached.get('data')
        return None
    
    def _set_cached_team_data(self, team_name, data_type, data):
        """Cache team data"""
        cache_key = f"{team_name}_{data_type}"
        self._team_cache[cache_key] = {
            'timestamp': time.time(),
            'data': data
        }
    
    # Sport-specific team name mappings
    TEAM_MAPPINGS = {
        'mlb': {
            'chi': 'chicago',
            'la': 'los angeles',
            'ny': 'new york',
            'chi cubs': 'chicago cubs',
            'chi white sox': 'chicago white sox',
            'la angels': 'los angeles angels',
            'la dodgers': 'los angeles dodgers',
            'ny yankees': 'new york yankees',
            'ny mets': 'new york mets',
            'athletics': 'oakland athletics',
            'a\'s': 'oakland athletics',
            'jays': 'toronto blue jays',
            'rays': 'tampa bay rays',
            'nats': 'washington nationals',
            'detroit': 'detroit tigers',
            'cincinnati': 'cincinnati reds',
            'toronto': 'toronto blue jays',
            'philadelphia': 'philadelphia phillies',
            'miami': 'miami marlins',
            'washington': 'washington nationals',
            'colorado': 'colorado rockies',
            'atlanta': 'atlanta braves',
            'boston': 'boston red sox',
            'baltimore': 'baltimore orioles',
            'tampa bay': 'tampa bay rays',
            'minnesota': 'minnesota twins',
            'houston': 'houston astros',
            'st. louis': 'st. louis cardinals',
            'milwaukee': 'milwaukee brewers',
            'kansas city': 'kansas city royals',
            'pittsburgh': 'pittsburgh pirates',
            'texas': 'texas rangers',
            'san diego': 'san diego padres',
            'arizona': 'arizona diamondbacks',
            'cleveland': 'cleveland guardians',
            'seattle': 'seattle mariners',
            'san francisco': 'san francisco giants'
        },
        'nba': {
            'cleveland': 'cleveland cavaliers',
            'boston': 'boston celtics',
            'new york': 'new york knicks',
            'indiana': 'indiana pacers',
            'milwaukee': 'milwaukee bucks',
            'detroit': 'detroit pistons',
            'orlando': 'orlando magic',
            'atlanta': 'atlanta hawks',
            'chicago': 'chicago bulls',
            'miami': 'miami heat',
            'toronto': 'toronto raptors',
            'brooklyn': 'brooklyn nets',
            'philadelphia': 'philadelphia 76ers',
            'charlotte': 'charlotte hornets',
            'washington': 'washington wizards',
            'oklahoma city': 'oklahoma city thunder',
            'houston': 'houston rockets',
            'la lakers': 'los angeles lakers',
            'la clippers': 'los angeles clippers',
            'denver': 'denver nuggets',
            'minnesota': 'minnesota timberwolves',
            'golden state': 'golden state warriors',
            'memphis': 'memphis grizzlies',
            'sacramento': 'sacramento kings',
            'dallas': 'dallas mavericks',
            'phoenix': 'phoenix suns',
            'portland': 'portland trail blazers',
            'san antonio': 'san antonio spurs',
            'new orleans': 'new orleans pelicans',
            'utah': 'utah jazz'
        },
        'wnba': {
            'atlanta': 'atlanta dream',
            'chicago': 'chicago sky',
            'connecticut': 'connecticut sun',
            'dallas': 'dallas wings',
            'indiana': 'indiana fever',
            'las vegas': 'las vegas aces',
            'minnesota': 'minnesota lynx',
            'new york': 'new york liberty',
            'phoenix': 'phoenix mercury',
            'seattle': 'seattle storm',
            'washington': 'washington mystics',
            'fever': 'indiana fever',
            'sky': 'chicago sky',
            'sun': 'connecticut sun',
            'wings': 'dallas wings',
            'aces': 'las vegas aces',
            'lynx': 'minnesota lynx',
            'liberty': 'new york liberty',
            'mercury': 'phoenix mercury',
            'storm': 'seattle storm',
            'mystics': 'washington mystics',
            'dream': 'atlanta dream'
        },
        'nhl': {
            'boston': 'boston bruins',
            'buffalo': 'buffalo sabres',
            'detroit': 'detroit red wings',
            'florida': 'florida panthers',
            'montreal': 'montreal canadiens',
            'ottawa': 'ottawa senators',
            'tampa bay': 'tampa bay lightning',
            'toronto': 'toronto maple leafs',
            'carolina': 'carolina hurricanes',
            'columbus': 'columbus blue jackets',
            'new jersey': 'new jersey devils',
            'ny islanders': 'new york islanders',
            'ny rangers': 'new york rangers',
            'philadelphia': 'philadelphia flyers',
            'pittsburgh': 'pittsburgh penguins',
            'washington': 'washington capitals',
            'arizona': 'arizona coyotes',
            'chicago': 'chicago blackhawks',
            'colorado': 'colorado avalanche',
            'dallas': 'dallas stars',
            'minnesota': 'minnesota wild',
            'nashville': 'nashville predators',
            'st. louis': 'st. louis blues',
            'winnipeg': 'winnipeg jets',
            'anaheim': 'anaheim ducks',
            'calgary': 'calgary flames',
            'edmonton': 'edmonton oilers',
            'los angeles': 'los angeles kings',
            'san jose': 'san jose sharks',
            'seattle': 'seattle kraken',
            'vancouver': 'vancouver canucks',
            'vegas': 'vegas golden knights'
        },
        'nfl': {
            'buffalo': 'buffalo bills',
            'miami': 'miami dolphins',
            'new england': 'new england patriots',
            'ny jets': 'new york jets',
            'baltimore': 'baltimore ravens',
            'cincinnati': 'cincinnati bengals',
            'cleveland': 'cleveland browns',
            'pittsburgh': 'pittsburgh steelers',
            'houston': 'houston texans',
            'indianapolis': 'indianapolis colts',
            'jacksonville': 'jacksonville jaguars',
            'tennessee': 'tennessee titans',
            'denver': 'denver broncos',
            'kansas city': 'kansas city chiefs',
            'la chargers': 'los angeles chargers',
            'las vegas': 'las vegas raiders',
            'dallas': 'dallas cowboys',
            'ny giants': 'new york giants',
            'philadelphia': 'philadelphia eagles',
            'washington': 'washington commanders',
            'chicago': 'chicago bears',
            'detroit': 'detroit lions',
            'green bay': 'green bay packers',
            'minnesota': 'minnesota vikings',
            'atlanta': 'atlanta falcons',
            'carolina': 'carolina panthers',
            'new orleans': 'new orleans saints',
            'tampa bay': 'tampa bay buccaneers',
            'arizona': 'arizona cardinals',
            'la rams': 'los angeles rams',
            'san francisco': 'san francisco 49ers',
            'seattle': 'seattle seahawks'
        },
        'mls': {
            'atlanta': 'atlanta united',
            'austin': 'austin fc',
            'charlotte': 'charlotte fc',
            'chicago': 'chicago fire',
            'colorado': 'colorado rapids',
            'columbus': 'columbus crew',
            'd.c.': 'dc united',
            'dc': 'dc united',
            'dallas': 'fc dallas',
            'houston': 'houston dynamo',
            'los angeles': 'los angeles fc',
            'la': 'los angeles fc',
            'la galaxy': 'la galaxy',
            'miami': 'inter miami',
            'minnesota': 'minnesota united',
            'montreal': 'cf montreal',
            'nashville': 'nashville sc',
            'new england': 'new england revolution',
            'new york': 'new york city fc',
            'nyc': 'new york city fc',
            'ny red bulls': 'new york red bulls',
            'orlando': 'orlando city',
            'philadelphia': 'philadelphia union',
            'portland': 'portland timbers',
            'real salt lake': 'real salt lake',
            'san jose': 'san jose earthquakes',
            'seattle': 'seattle sounders',
            'sporting kc': 'sporting kansas city',
            'st. louis': 'st. louis city',
            'toronto': 'toronto fc',
            'vancouver': 'vancouver whitecaps'
        },
        'premierleague': {
            'arsenal': 'arsenal',
            'aston villa': 'aston villa',
            'bournemouth': 'bournemouth',
            'brentford': 'brentford',
            'brighton': 'brighton & hove albion',
            'chelsea': 'chelsea',
            'crystal palace': 'crystal palace',
            'everton': 'everton',
            'fulham': 'fulham',
            'liverpool': 'liverpool',
            'manchester city': 'manchester city',
            'man city': 'manchester city',
            'manchester united': 'manchester united',
            'man united': 'manchester united',
            'newcastle': 'newcastle united',
            'nottingham': 'nottingham forest',
            'southampton': 'southampton',
            'tottenham': 'tottenham hotspur',
            'west ham': 'west ham united',
            'wolves': 'wolverhampton wanderers'
        },
        'laliga': {
            'alaves': 'alaves',
            'almería': 'almeria',
            'athletic bilbao': 'athletic bilbao',
            'athletic': 'athletic bilbao',
            'atletico madrid': 'atletico madrid',
            'atletico': 'atletico madrid',
            'barcelona': 'fc barcelona',
            'barca': 'fc barcelona',
            'celta vigo': 'celta vigo',
            'celta': 'celta vigo',
            'espanyol': 'espanyol',
            'girona': 'girona',
            'granada': 'granada',
            'las palmas': 'las palmas',
            'levante': 'levante',
            'mallorca': 'mallorca',
            'osasuna': 'osasuna',
            'rayo vallecano': 'rayo vallecano',
            'real betis': 'real betis',
            'real madrid': 'real madrid',
            'real sociedad': 'real sociedad',
            'sevilla': 'sevilla',
            'valencia': 'valencia',
            'villarreal': 'villarreal'
        },
        'bundesliga': {
            'augsburg': 'augsburg',
            'bayern munich': 'bayern munich',
            'bayern': 'bayern munich',
            'bochum': 'bochum',
            'darmstadt': 'darmstadt',
            'dortmund': 'borussia dortmund',
            'eintracht frankfurt': 'eintracht frankfurt',
            'freiburg': 'freiburg',
            'hertha': 'hertha bsc',
            'hoffenheim': 'hoffenheim',
            'koln': 'koln',
            'leverkusen': 'bayer leverkusen',
            'mainz': 'mainz',
            'monchengladbach': 'borussia monchengladbach',
            'rb leipzig': 'rb leipzig',
            'stuttgart': 'vfb stuttgart',
            'union berlin': 'union berlin',
            'werder bremen': 'werder bremen',
            'wolfsburg': 'wolfsburg'
        },
        'seriea': {
            'atalanta': 'atalanta',
            'bologna': 'bologna',
            'cagliari': 'cagliari',
            'empoli': 'empoli',
            'fiorentina': 'fiorentina',
            'frosinone': 'frosinone',
            'genoa': 'genoa',
            'inter milan': 'inter milan',
            'inter': 'inter milan',
            'juventus': 'juventus',
            'lazio': 'lazio',
            'lecce': 'lecce',
            'milan': 'ac milan',
            'ac milan': 'ac milan',
            'monza': 'monza',
            'napoli': 'napoli',
            'roma': 'roma',
            'salernitana': 'salernitana',
            'sassuolo': 'sassuolo',
            'torino': 'torino',
            'udinese': 'udinese',
            'venezia': 'venezia',
            'verona': 'verona'
        },
        'ligue1': {
            'brest': 'brest',
            'clermont': 'clermont',
            'le havre': 'le havre',
            'lens': 'lens',
            'lille': 'lille',
            'lyon': 'lyon',
            'marseille': 'marseille',
            'monaco': 'monaco',
            'montpellier': 'montpellier',
            'nantes': 'nantes',
            'nice': 'nice',
            'paris': 'paris saint-germain',
            'psg': 'paris saint-germain',
            'reims': 'reims',
            'rennes': 'rennes',
            'strasbourg': 'strasbourg',
            'toulouse': 'toulouse'
        },
        'championsleague': {
            'arsenal': 'arsenal',
            'atletico madrid': 'atletico madrid',
            'barcelona': 'fc barcelona',
            'bayern': 'bayern munich',
            'bayern munich': 'bayern munich',
            'benfica': 'benfica',
            'borussia dortmund': 'borussia dortmund',
            'chelsea': 'chelsea',
            'dortmund': 'borussia dortmund',
            'fc barcelona': 'fc barcelona',
            'inter milan': 'inter milan',
            'juventus': 'juventus',
            'liverpool': 'liverpool',
            'manchester city': 'manchester city',
            'manchester united': 'manchester united',
            'marseille': 'marseille',
            'milan': 'ac milan',
            'ac milan': 'ac milan',
            'monaco': 'monaco',
            'napoli': 'napoli',
            'paris saint-germain': 'paris saint-germain',
            'psg': 'paris saint-germain',
            'real madrid': 'real madrid',
            'roma': 'roma',
            'sevilla': 'sevilla',
            'tottenham': 'tottenham hotspur',
            'valencia': 'valencia'
        }
    }

    @classmethod
    def normalize_team_name(cls, name, sport):
        """Normalize team name for comparison"""
        if not name:
            return ''
        # Clean up the input name
        name = name.lower().strip()
        original = name
        # Remove playoff seeds and qualifiers (e.g., "(4)", "-z", "-y", "-x")
        name = re.sub(r'\(\d+\)|-[xyz]', '', name).strip()
        # Special case for MLB A's/Athletics
        if sport == 'mlb' and name in ['a\'s', 'athletics', 'oakland a\'s', 'oakland athletics']:
            return 'oakland athletics'
        # Remove division prefix
        if name.startswith(('e-', 'c-', 'w-')):
            name = name[2:].strip()
        # Try direct mapping first
        sport_mappings = cls.TEAM_MAPPINGS.get(sport, {})
        mapped = sport_mappings.get(name)
        if mapped:
            return mapped
        # Split into parts and handle duplicates
        parts = name.split()
        if len(parts) >= 2:
            # Try city mapping
            city = ' '.join(parts[:-1])  # Everything except last word
            nickname = parts[-1]
            # Check if nickname repeats city
            if nickname in city:
                name = city  # Use city as base if nickname is redundant
            else:
                name = ' '.join(parts)
            # Check city mapping again
            mapped_city = sport_mappings.get(city)
            if mapped_city:
                result = mapped_city
                return result
            # Try full team name
            mapped = sport_mappings.get(name)
            if mapped:
                return mapped
        return name

    def set_time_slots(self):
        """Set up sports-appropriate time slots for the grid."""
        now = datetime.now().astimezone(self.local_tz)
        # Create predefined time slots for games
        self.time_slots = [
            # Morning games
            (now.replace(hour=9, minute=0, second=0, microsecond=0),
             now.replace(hour=12, minute=0, second=0, microsecond=0)),
            # Early afternoon games
            (now.replace(hour=12, minute=0, second=0, microsecond=0),
             now.replace(hour=15, minute=0, second=0, microsecond=0)),
            # Late afternoon games
            (now.replace(hour=15, minute=0, second=0, microsecond=0),
             now.replace(hour=18, minute=0, second=0, microsecond=0)),
            # Evening games
            (now.replace(hour=18, minute=0, second=0, microsecond=0),
             now.replace(hour=21, minute=0, second=0, microsecond=0)),
            # Night games
            (now.replace(hour=21, minute=0, second=0, microsecond=0),
             now.replace(hour=23, minute=59, second=59, microsecond=999999))
        ]

    def __init__(self, xml_file: str, addon_path: str, sport: str):
        # Call parent constructor first to ensure window is properly initialized
        super().__init__(xml_file, addon_path)
        # Initialize controls
        self.time_panel = None
        self.channel_panel = None
        self.sport = sport.lower()
        self.local_tz = datetime.now().astimezone().tzinfo
        self.is_android = 'ANDROID_DATA' in os.environ or 'ANDROID_ROOT' in os.environ
        self.android_optimize_mode = False
        self.android_input_debounce_ms = 120
        self.last_interaction_time = time.time()
        # Initialize basic attributes needed by EPGGrid
        self.all_channels = []
        self.channels = []
        self.programs = []
        self.is_sports_view = True
        self.program_cache = {}
        self.visible_rows = 13
        self.current_page = 0
        self.time_scroll_pos = 0
        self.time_slots = []
        self.selected_program = None
        # Set up initial time slots
        self.set_time_slots()

        if self.is_android:
            try:
                self.android_optimize_mode = ADDON.getSettingBool('android_optimize_mode')
            except Exception:
                self.android_optimize_mode = ADDON.getSetting('android_optimize_mode') == 'true'
            try:
                self.android_input_debounce_ms = max(50, int(ADDON.getSetting('android_input_debounce_ms') or '120'))
            except Exception:
                self.android_input_debounce_ms = 120

        # Get sports data
        
        try:
            # Load data
            scores = self.get_scores()
            debug_log(f"SportsGrid: Found {len(scores)} games for {self.sport}", xbmc.LOGINFO)
            standings_data = self.get_standings()
            debug_log(f"SportsGrid: Found {len(standings_data)} standings entries for {self.sport}", xbmc.LOGINFO)
            # Format data
            self.all_channels = self.format_channels(standings_data)
            debug_log(f"SportsGrid: Created {len(self.all_channels)} channels", xbmc.LOGINFO)
            self.channels = self.all_channels[:]
            self.programs = self.format_programs(scores)
            debug_log(f"SportsGrid: Created {len(self.programs)} programs", xbmc.LOGINFO)
            
        except Exception as e:
            debug_log(f"SportsGrid: Exception in load: {str(e)}", xbmc.LOGERROR)
            xbmcgui.Dialog().ok("Error", "Failed to load sports data. Please try again.")
            self.channels = []
            self.programs = []
            self.close()
            return
        
        if not self.channels:
            xbmcgui.Dialog().ok("Error", "No sports data available. Please try again.")
            self.close()
            return

    def onInit(self):
        """Override onInit to handle sports-specific initialization"""
        # Apply theme colors to window first
        try:
            apply_theme_to_window(self)
        except Exception as e:
            debug_log(f"SportsGrid: Error applying theme: {str(e)}", xbmc.LOGERROR)
        
        try:
            debug_log("SportsGrid: onInit starting", xbmc.LOGINFO)
            # Initialize panels before parent onInit
            self.channel_panel = None
            self.time_panel = None
            # Call parent onInit
            super().onInit()
            debug_log("SportsGrid: super().onInit() done", xbmc.LOGINFO)
            # Set window title
            self.setProperty('SportTitle', self.sport.upper())
            # Verify we have channels (standings)
            if not self.channels:
                xbmcgui.Dialog().ok("Error", "No sports data available. Please try again.")
                self.close()
                return
            # Initialize and populate panels
            self.channel_panel = self.getControl(100)
            self.time_panel = self.getControl(101)
            debug_log(f"SportsGrid: Got controls: channel={self.channel_panel}, time={self.time_panel}", xbmc.LOGINFO)
            # Configure panels
            self.channel_panel.setEnabled(True)
            self.time_panel.setEnabled(True)
            # Populate data
            self.populate_channel_panel()
            self.populate_time_panel()
            debug_log("SportsGrid: Populated panels", xbmc.LOGINFO)
            # Initial focus and selection - start with teams panel
            # Fix: Check if control exists and is focusable before setting focus
            if self.channel_panel.size() > 0:
                self.channel_panel.selectItem(0)
                # Only try to focus if the control is valid and visible
                try:
                    if self.channel_panel:
                        self.setFocusId(100)
                except Exception as e:
                    debug_log(f"SportsGrid: Could not set focus to control 100: {str(e)}", xbmc.LOGWARNING)
                    # Android/skin fallback: try time panel if teams panel cannot receive focus
                    try:
                        if self.time_panel and self.time_panel.size() > 0:
                            self.time_panel.selectItem(0)
                            self.setFocusId(101)
                    except Exception:
                        pass
            # Make sure both panels are visible and enabled
            self.channel_panel.setVisible(True)
            self.time_panel.setVisible(True)
            
            # Update initial info display
            self.update_selected_info()
            
            # Hide loading overlay (now safely handles missing control 999)
            self.hide_loading()
            debug_log("SportsGrid: onInit complete", xbmc.LOGINFO)
                
        except Exception as e:
            debug_log(f"SportsGrid: onInit error: {str(e)}", xbmc.LOGERROR)
            xbmcgui.Dialog().ok("Error", "Failed to initialize display. Please try again.")
            self.close()
            return

    def onFocus(self, controlId):
        """Handle focus changes to update info panel"""
        try:
            debug_log(f"SportsGrid: onFocus called with controlId={controlId}", xbmc.LOGINFO)
            if controlId in [100, 101]:
                # Small delay to ensure focus is fully set
                xbmc.sleep(50)
                self.update_selected_info()
        except Exception as e:
            debug_log(f"SportsGrid: onFocus error: {str(e)}", xbmc.LOGERROR)

    def onAction(self, action):
        """Handle actions to update info panel when selection changes"""
        try:
            action_id = action.getId()
            # Navigation actions: up, down, left, right, page up, page down
            nav_actions = [1, 2, 3, 4, 5, 6, 7, 105, 106, 107, 108]
            if action_id in nav_actions:
                focused_id = self.getFocusId()
                if focused_id in [100, 101]:
                    self.update_selected_info()
        except Exception as e:
            debug_log(f"SportsGrid: onAction error: {str(e)}", xbmc.LOGERROR)
        super().onAction(action)

    def get_standings(self):
        try:
            sport_map = {
                'mlb': 'sports/baseball/mlb',
                'nba': 'sports/basketball/nba',
                'ncaab': 'sports/basketball/mens-college-basketball',
                'nhl': 'sports/hockey/nhl',
                'wnba': 'sports/basketball/wnba',
                'nfl': 'sports/football/nfl',
                'mls': 'sports/soccer/usa.1',
                'premierleague': 'sports/soccer/eng.1',
                'laliga': 'sports/soccer/esp.1',
                'bundesliga': 'sports/soccer/ger.1',
                'seriea': 'sports/soccer/ita.1',
                'ligue1': 'sports/soccer/fra.1',
                'championsleague': 'sports/soccer/uefa.champions'
            }
            espn_sport = sport_map.get(self.sport, f'sports/{self.sport}')
            current_year = datetime.now().year
            soccer_leagues = ['mls', 'premierleague', 'laliga', 'bundesliga', 'seriea', 'ligue1', 'championsleague']
            if self.sport in soccer_leagues:
                years_to_try = [2025, 2024, 2026, current_year - 1]
            else:
                years_to_try = [current_year, current_year - 1]
            data = None
            for year in years_to_try:
                try:
                    url = f"https://site.api.espn.com/apis/v2/{espn_sport}/standings?season={year}"
                    debug_log(f"SportsGrid: Fetching standings from {url}", xbmc.LOGINFO)
                    response = requests.get(
                        url,
                        headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'},
                        timeout=10
                    )
                    if response.status_code == 200:
                        data = response.json()
                        break
                except Exception as e:
                    debug_log(f"SportsGrid: Error fetching standings for {year}: {e}", xbmc.LOGWARNING)
                    continue
            if not data:
                debug_log("SportsGrid: No standings data available", xbmc.LOGWARNING)
                return []
            
            # Extract league logo from the response
            self.league_logo = ''
            debug_log(f"SportsGrid: API response keys: {data.keys()}", xbmc.LOGINFO)
            leagues = data.get('leagues', [])
            debug_log(f"SportsGrid: Found {len(leagues)} leagues in response", xbmc.LOGINFO)
            if leagues:
                first_league = leagues[0]
                debug_log(f"SportsGrid: First league: {first_league.get('name')}", xbmc.LOGINFO)
                league_logos = first_league.get('logos', [])
                debug_log(f"SportsGrid: League has {len(league_logos)} logos", xbmc.LOGINFO)
                if league_logos:
                    self.league_logo = league_logos[0].get('href', '')
                    debug_log(f"SportsGrid: Found league logo: {self.league_logo}", xbmc.LOGINFO)
            else:
                debug_log("SportsGrid: No leagues found in response", xbmc.LOGWARNING)
            
            standings = []
            children = data.get('children', [])
            debug_log(f"SportsGrid: Found {len(children)} children in standings data", xbmc.LOGINFO)
            for league in children:
                league_name = league.get('name', '')
                debug_log(f"SportsGrid: Processing league: {league_name}", xbmc.LOGINFO)
                # Track current division/conference
                current_division = ''
                if league_name:
                    # Check if it's a division or conference header
                    # Include MLB patterns: AL West, NL East, American League, National League
                    league_name_lower = league_name.lower()
                    is_header = any(word in league_name_lower for word in ['division', 'conference']) or \
                               league_name_lower in ['american league', 'national league'] or \
                               (len(league_name) >= 4 and league_name[:2] in ['AL', 'NL'] and 'West' in league_name or 'East' in league_name or 'Central' in league_name)
                    if is_header:
                        current_division = league_name
                        debug_log(f"SportsGrid: Found division header: {league_name}", xbmc.LOGINFO)
                        standings.append({'team_name': league_name, 'is_division_header': True})
                league_standings = league.get('standings', {})
                entries = league_standings.get('entries', [])
                for entry in entries:
                    team = entry.get('team', {})
                    team_name = team.get('displayName', '')
                    if not team_name:
                        continue
                    if any(word in team_name.lower() for word in ['division', 'conference', 'league', 'wild card']) or \
                       team_name.lower() in ['american league', 'national league'] or \
                       (len(team_name) >= 4 and team_name[:2] in ['AL', 'NL'] and ('West' in team_name or 'East' in team_name or 'Central' in team_name)):
                        # Update current division for wildcard/league headers
                        current_division = team_name
                        standings.append({'team_name': team_name, 'is_division_header': True})
                        continue
                    logos = team.get('logos', [])
                    logo_url = ''
                    if logos:
                        logo_url = logos[0].get('href', '')
                    normalized_name = self.normalize_team_name(team_name, self.sport)
                    record = {'team_name': normalized_name, 'logo_url': logo_url, 'division': current_division}
                    stats = entry.get('stats', [])
                    for stat in stats:
                        stat_name = stat.get('name', '')
                        stat_type = stat.get('type', '')
                        display_value = stat.get('displayValue', '')
                        if stat_name == 'wins':
                            record['wins'] = display_value
                        elif stat_name == 'losses':
                            record['losses'] = display_value
                        elif stat_name == 'winPercent':
                            record['pct'] = display_value
                        elif stat_name == 'gamesBehind':
                            record['gb'] = display_value
                        elif stat_name == 'Home' or stat_type == 'home':
                            record['home'] = display_value
                        elif stat_name == 'Road' or stat_type == 'road':
                            record['away'] = display_value
                        elif stat_name == 'Last Ten Games' or stat_type == 'lasttengames':
                            record['last10'] = display_value
                        elif stat_name == 'streak':
                            record['streak'] = display_value
                        elif 'division' in stat_name.lower() or 'vsdivision' in stat_name.lower() or 'vsdiv' in stat_name.lower() or stat_type == 'division' or stat_type == 'vsDivision':
                            if 'division_record' not in record:
                                record['division_record'] = display_value
                        elif 'conference' in stat_name.lower() or 'vsconference' in stat_name.lower() or 'vsconf' in stat_name.lower() or stat_type == 'conference' or stat_type == 'vsConference':
                            if 'conference_record' not in record:
                                record['conference_record'] = display_value
                        elif stat_name == 'differential':
                            record['differential'] = display_value
                            debug_log(f"SportsGrid: Found differential for {team_name}: {display_value}", xbmc.LOGINFO)
                        elif stat_name == 'avgPointsFor' or stat_name == 'avgPointsAgainst':
                            record[stat_name] = display_value
                        elif stat_name == 'runDifferential' or stat_name == 'goalDifferential':
                            record[stat_name] = display_value
                            debug_log(f"SportsGrid: Found {stat_name} for {team_name}: {display_value}", xbmc.LOGINFO)
                    standings.append(record)
            return standings
        except Exception as e:
            debug_log(f"SportsGrid: Error in get_standings: {str(e)}", xbmc.LOGERROR)
            return []

    def populate_channel_panel(self):
        """Populate or repopulate the channel panel"""
        debug_log("SportsGrid DEBUG: Entered populate_channel_panel", xbmc.LOGINFO)
        self.channel_panel.reset()
        for idx, channel in enumerate(self.channels):
            team_name = channel.get('name', '')
            record = channel.get('record', '')
            item = xbmcgui.ListItem(label=team_name.upper())
            item.setProperty('record', record)
            logo_url = channel.get('logo', '')
            debug_log(f"SportsGrid: Team {team_name}, logo: {logo_url[:50] if logo_url else 'None'}...", xbmc.LOGINFO)
            if logo_url:
                item.setArt({'thumb': logo_url, 'icon': logo_url})
                item.setProperty('logo', logo_url)
            
            for stat in ['wins', 'losses', 'pct', 'gb', 'home', 'away', 'strk']:
                value = channel.get(stat, '')
                if value:
                    item.setProperty(stat, str(value))
            # last10 is stored as 'l10' in channel
            l10_value = channel.get('l10', '')
            if l10_value:
                item.setProperty('l10', str(l10_value))
            
            # Division/Conference info
            division = channel.get('division', '')
            if division:
                item.setProperty('division', division)
            
            # Division/Conference records
            division_record = channel.get('division_record', '')
            if division_record:
                item.setProperty('division_record', division_record)
            conference_record = channel.get('conference_record', '')
            if conference_record:
                item.setProperty('conference_record', conference_record)
            
            # Point/Run differential
            differential = channel.get('differential', '')
            if differential:
                item.setProperty('differential', differential)
            
            self.channel_panel.addItem(item)
            
    def populate_time_panel(self):
        """Populate or repopulate the time panel"""
        self.time_panel.reset()
        sorted_programs = sorted(
            self.programs,
            key=lambda x: (
                x.get('time', datetime.max) if isinstance(x.get('time'), datetime) else datetime.max,
                'A' if 'final' in str(x.get('status', '')).lower() else
                'B' if any(hint in str(x.get('status', '')).lower() for hint in ['live', 'quarter', 'period', 'inning']) else 'C'
            )
        )
        for idx, program in enumerate(sorted_programs, 1):
            title = program.get('title', '')
            desc = program.get('desc', '')
            status = program.get('status', '')
            # Check if game is live for badge
            is_live = 'live' in str(status).lower() or 'in progress' in str(status).lower()
            if isinstance(program.get('time'), datetime):
                time_str = program['time'].strftime('%I:%M %p').lstrip('0')
            else:
                time_str = 'TBD'
            item = xbmcgui.ListItem(label=title.upper())
            item.setProperty('time', time_str)
            item.setProperty('game', title)
            item.setProperty('status', status)
            item.setProperty('is_live', '1' if is_live else '')
            item.setProperty('description', desc)
            self.time_panel.addItem(item)

    def get_scores(self):
        try:
            sport_map = {
                'mlb': 'sports/baseball/mlb',
                'nba': 'sports/basketball/nba',
                'ncaab': 'sports/basketball/mens-college-basketball',
                'nhl': 'sports/hockey/nhl',
                'wnba': 'sports/basketball/wnba',
                'nfl': 'sports/football/nfl',
                'mls': 'sports/soccer/usa.1',
                'premierleague': 'sports/soccer/eng.1',
                'laliga': 'sports/soccer/esp.1',
                'bundesliga': 'sports/soccer/ger.1',
                'seriea': 'sports/soccer/ita.1',
                'ligue1': 'sports/soccer/fra.1',
                'championsleague': 'sports/soccer/uefa.champions'
            }
            espn_sport = sport_map.get(self.sport, f'sports/{self.sport}')
            url = f"https://site.api.espn.com/apis/site/v2/{espn_sport}/scoreboard"
            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()
            data = response.json()
            games = []
            events = data.get('events', [])
            for event in events:
                competition = event.get('competitions', [{}])[0]
                competitors = competition.get('competitors', [])
                if len(competitors) < 2:
                    continue
                team1 = competitors[0].get('team', {})
                team2 = competitors[1].get('team', {})
                team1_name = team1.get('displayName', '')
                team2_name = team2.get('displayName', '')
                
                # Skip All-Star type games
                if team1_name.startswith('Team ') or team2_name.startswith('Team '):
                    continue
                
                if not team1_name or not team2_name:
                    continue
                team1_norm = self.normalize_team_name(team1_name, self.sport)
                team2_norm = self.normalize_team_name(team2_name, self.sport)
                score1 = str(competitors[0].get('score', ''))
                score2 = str(competitors[1].get('score', ''))
                status_obj = competition.get('status', {})
                status_type = status_obj.get('type', {})
                status = status_type.get('description', 'TBD')
                period = status_obj.get('period', '')
                clock = status_obj.get('displayClock', '')
                if period and clock:
                    status = f"{status} - {period}Q {clock}"
                elif period:
                    status = f"{status} - {period}"
                networks = competition.get('broadcasts', [])
                network = networks[0].get('names', ['TBD'])[0] if networks else 'TBD'
                games.append({
                    'team1': team1_norm,
                    'team2': team2_norm,
                    'score1': score1,
                    'score2': score2,
                    'status': status,
                    'network': network,
                    'time': None
                })
            return games
        except Exception as e:
            debug_log(f"SportsGrid: Error in get_scores: {str(e)}", xbmc.LOGERROR)
            return []

    def get_team_game_stats(self, team_name):
        """Get the latest game stats for a specific team"""
        try:
            # Check cache first
            cached = self._get_cached_team_data(team_name, 'game_stats')
            if cached is not None:
                return cached
            
            sport_map = {
                'mlb': 'sports/baseball/mlb',
                'nba': 'sports/basketball/nba',
                'nhl': 'sports/hockey/nhl',
                'wnba': 'sports/basketball/wnba',
                'nfl': 'sports/football/nfl',
            }
            espn_sport = sport_map.get(self.sport, f'sports/{self.sport}')
            today_str = datetime.now().strftime('%Y%m%d')
            url = f"https://site.api.espn.com/apis/site/v2/{espn_sport}/scoreboard?dates={today_str}"
            debug_log(f"SportsGrid: Fetching game stats from {url}", xbmc.LOGINFO)
            response = requests.get(
                url,
                headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'},
                timeout=10
            )
            self._api_rate_limit_delay()
            if response.status_code != 200:
                debug_log(f"SportsGrid: API returned status {response.status_code}", xbmc.LOGERROR)
                return {}
            data = response.json()
            events = data.get('events', [])
            debug_log(f"SportsGrid: Got {len(events)} events for date {today_str}", xbmc.LOGINFO)
            config = get_sport_stats_config(self.sport)
            
            # Sort events by date to get most recent first
            def get_event_date(evt):
                date_str = evt.get('date', '')
                if date_str:
                    try:
                        return datetime.strptime(date_str[:19], '%Y-%m-%dT%H:%M:%S')
                    except:
                        pass
                return datetime.min
            events = sorted(events, key=get_event_date, reverse=True)
            
            for event in events:
                competition = event.get('competitions', [{}])[0]
                competitors = competition.get('competitors', [])
                
                for team_data in competitors:
                    if not isinstance(team_data, dict):
                        continue
                    team = team_data.get('team', {})
                    if not isinstance(team, dict):
                        continue
                    team_display = team.get('displayName', '').lower()
                    if team_name.lower() in team_display or team_display in team_name.lower():
                        debug_log(f"SportsGrid: Matched team {team_display} in {event.get('name', 'unknown')}", xbmc.LOGINFO)
                for team_data in competitors:
                    if not isinstance(team_data, dict):
                        continue
                    team = team_data.get('team', {})
                    if not isinstance(team, dict):
                        continue
                    team_display = team.get('displayName', '').lower()
                    if team_name.lower() in team_display or team_display in team_name.lower():
                        stats = team_data.get('statistics', [])
                        if not isinstance(stats, list):
                            stats = []
                        game_stats = {}
                        for stat in stats:
                            if not isinstance(stat, dict):
                                continue
                            name = stat.get('name', '')
                            value = stat.get('displayValue', '')
                            for api_key in config['stat_keys']:
                                if name == api_key:
                                    game_stats[api_key] = value
                                    break
                        leaders = team_data.get('leaders', [])
                        if not isinstance(leaders, list):
                            leaders = []
                        for leader in leaders:
                            if not isinstance(leader, dict):
                                continue
                            lname = leader.get('name', '')
                            leaders_list = leader.get('leaders', [])
                            if isinstance(leaders_list, list) and leaders_list:
                                top_leader = leaders_list[0]
                                if isinstance(top_leader, dict):
                                    athlete = top_leader.get('athlete', {})
                                    athlete_name = ''
                                    athlete_headshot = ''
                                    if isinstance(athlete, dict):
                                        athlete_name = athlete.get('displayName', '')
                                        athlete_headshot = athlete.get('headshot', '')
                                    value = top_leader.get('displayValue', '')
                                    for api_key in config['leader_keys']:
                                        api_key_base = api_key.replace('leader_', '')
                                        if lname == api_key_base:
                                            game_stats[api_key] = f"{athlete_name}: {value}"
                                            game_stats[f"{api_key}_headshot"] = athlete_headshot
                                            break
                        team_id = team_data.get('id', '')
                        team_score = ''
                        score_data = team_data.get('score')
                        if isinstance(score_data, dict):
                            team_score = score_data.get('displayValue') or score_data.get('value') or score_data.get('points') or ''
                        elif isinstance(score_data, (int, float)):
                            team_score = str(score_data)
                        elif isinstance(score_data, str):
                            team_score = score_data
                        for opp_data in competitors:
                            if not isinstance(opp_data, dict):
                                continue
                            if opp_data.get('id', '') != team_id:
                                opp_team = opp_data.get('team', {})
                                if isinstance(opp_team, dict):
                                    game_stats['opponent'] = opp_team.get('displayName', '')
                                opp_score_data = opp_data.get('score')
                                if isinstance(opp_score_data, dict):
                                    game_stats['opponent_score'] = opp_score_data.get('displayValue') or opp_score_data.get('value') or opp_score_data.get('points') or ''
                                elif isinstance(opp_score_data, (int, float)):
                                    game_stats['opponent_score'] = str(opp_score_data)
                                elif isinstance(opp_score_data, str):
                                    game_stats['opponent_score'] = opp_score_data
                                game_stats['team_score'] = team_score
                                break
                        
                        # Get pitcher stats from probables - they're in each competitor, not competition
                        debug_log(f"SportsGrid: Checking team_data keys: {list(team_data.keys())}", xbmc.LOGINFO)
                        probables = team_data.get('probables', [])
                        debug_log(f"SportsGrid: Found {len(probables)} probables for team", xbmc.LOGINFO)
                        if probables:
                            for pitcher_data in probables:
                                pitcher_stat_type = pitcher_data.get('name', '')
                                debug_log(f"SportsGrid: Pitcher type: {pitcher_stat_type}", xbmc.LOGINFO)
                                if pitcher_stat_type == 'probableStartingPitcher':
                                    pitcher_athlete = pitcher_data.get('athlete', {})
                                    if isinstance(pitcher_athlete, dict):
                                        pitcher_name = pitcher_athlete.get('displayName', '')
                                        pitcher_headshot = pitcher_athlete.get('headshot', '')
                                        pitcher_stats = pitcher_data.get('statistics', [])
                                        pitcher_era = ''
                                        pitcher_record = ''
                                        for pstat in pitcher_stats:
                                            pname = pstat.get('name', '')
                                            pvalue = pstat.get('displayValue', '')
                                            debug_log(f"SportsGrid: Pitcher stat: {pname} = {pvalue}", xbmc.LOGINFO)
                                            if pname == 'ERA':
                                                pitcher_era = pvalue
                                            elif pname == 'wins':
                                                wins = pvalue
                                                # Try to get losses too
                                                for ps in pitcher_stats:
                                                    if ps.get('name') == 'losses':
                                                        losses = ps.get('displayValue', '')
                                                        pitcher_record = f"{wins}-{losses}"
                                                        break
                                        if pitcher_name:
                                            debug_log(f"SportsGrid: Found pitcher: {pitcher_name}, ERA: {pitcher_era}, Record: {pitcher_record}", xbmc.LOGINFO)
                                            game_stats['leader_era'] = f"{pitcher_name}: {pitcher_era}"
                                            game_stats['leader_era_headshot'] = pitcher_headshot
                                            game_stats['leader_strikeouts'] = f"{pitcher_name}: Record {pitcher_record}"
                                            game_stats['leader_strikeouts_headshot'] = pitcher_headshot
                                            game_stats['leader_era_alt'] = f"{pitcher_name}: {pitcher_era} ERA"
                                            game_stats['leader_era_alt_headshot'] = pitcher_headshot
                                            game_stats['leader_strikeouts_alt'] = f"{pitcher_name}: {pitcher_record}"
                                            game_stats['leader_strikeouts_alt_headshot'] = pitcher_headshot
                        
                        game_stats['_sport'] = self.sport
                        self._set_cached_team_data(team_name, 'game_stats', game_stats)
                        return game_stats
            return {}
        except Exception as e:
            debug_log(f"SportsGrid: Error in get_team_game_stats: {str(e)}", xbmc.LOGERROR)
            return {}

    def get_team_recent_games(self, team_name):
        """Get the last 5 games for a team using date range API"""
        try:
            # Check cache first
            cached = self._get_cached_team_data(team_name, 'recent_games')
            if cached is not None:
                return cached
            
            sport_map = {
                'mlb': 'sports/baseball/mlb',
                'nba': 'sports/basketball/nba',
                'nhl': 'sports/hockey/nhl',
                'wnba': 'sports/basketball/wnba',
                'nfl': 'sports/football/nfl',
                'mls': 'sports/soccer/usa.1',
                'premierleague': 'sports/soccer/eng.1',
                'laliga': 'sports/soccer/esp.1',
                'bundesliga': 'sports/soccer/ger.1',
                'seriea': 'sports/soccer/ita.1',
                'ligue1': 'sports/soccer/fra.1',
                'championsleague': 'sports/soccer/uefa.champions'
            }
            espn_sport = sport_map.get(self.sport, f'sports/{self.sport}')
            
            # Calculate date range - go back 60 days to ensure we get enough games
            now = datetime.now()
            end_date = now
            start_date = now - timedelta(days=60)
            start_str = start_date.strftime('%Y%m%d')
            end_str = end_date.strftime('%Y%m%d')
            
            debug_log(f"SportsGrid: Date range for recent games: {start_str} to {end_str}", xbmc.LOGINFO)
            
            # Query scoreboard with date range - this gets ALL games in the date range
            url = f"https://site.api.espn.com/apis/site/v2/{espn_sport}/scoreboard?dates={start_str}-{end_str}"
            debug_log(f"SportsGrid: Fetching recent games from {url}", xbmc.LOGINFO)
            
            response = requests.get(
                url,
                headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'},
                timeout=15
            )
            
            data = {}
            events = []
            
            if response.status_code == 200:
                data = response.json()
                events = data.get('events', [])
            
            # If scoreboard returns limited events, try team-specific schedule
            if len(events) < 5:
                # Try team-specific schedule API
                team_abbrev = ''.join([c for c in team_name.lower() if c.isalpha()])[:3]
                if 'clippers' in team_name.lower():
                    team_abbrev = 'lac'
                elif 'lakers' in team_name.lower():
                    team_abbrev = 'lal'
                elif 'knicks' in team_name.lower():
                    team_abbrev = 'nyk'
                elif 'warriors' in team_name.lower():
                    team_abbrev = 'gsw'
                elif 'spurs' in team_name.lower():
                    team_abbrev = 'sas'
                elif 'heat' in team_name.lower():
                    team_abbrev = 'mia'
                
                if team_abbrev:
                    team_url = f"https://site.api.espn.com/apis/site/v2/{espn_sport}/teams/{team_abbrev}/schedule?season={datetime.now().year}"
                    debug_log(f"SportsGrid: Trying team schedule: {team_url}", xbmc.LOGINFO)
                    team_resp = requests.get(team_url, headers={'User-Agent': 'Mozilla/5.0'}, timeout=15)
                    if team_resp.status_code == 200:
                        team_data = team_resp.json()
                        events = team_data.get('events', [])
                        debug_log(f"SportsGrid: Got {len(events)} events from team schedule", xbmc.LOGINFO)
            
            if not events:
                debug_log(f"SportsGrid: No events found in date range", xbmc.LOGINFO)
                return []
            
            recent_games = []
            
            for event in events:
                competition = event.get('competitions', [{}])[0]
                status = competition.get('status', {})
                status_type = status.get('type', {})
                game_status = status_type.get('description', '')
                
                # Only get final games
                if 'final' not in game_status.lower():
                    continue
                
                # Get date for sorting
                date_str = event.get('date', '')
                
                competitors = competition.get('competitors', [])
                if len(competitors) < 2:
                    continue
                
                team1_data = competitors[0]
                team2_data = competitors[1]
                
                team1 = team1_data.get('team', {})
                team2 = team2_data.get('team', {})
                team1_name = team1.get('displayName', '')
                team2_name = team2.get('displayName', '')

                # Check if this team is in this game - use flexible matching
                def teams_match(search_name, api_name):
                    """Check if team names match with variations like LA vs Los Angeles"""
                    s = search_name.lower().replace(' ', '').replace('.', '')
                    a = api_name.lower().replace(' ', '').replace('.', '')
                    # Check direct match
                    if s in a or a in s:
                        return True
                    # Handle common abbreviations
                    replacements = [('losangeles', 'la'), ('new york', 'ny'), ('golden state', 'gs'), ('goldenstate', 'gs')]
                    for full, abbr in replacements:
                        s2 = s.replace(full, abbr)
                        a2 = a.replace(full, abbr)
                        if s2 in a2 or a2 in s2:
                            return True
                    return False
                
                team1_match = teams_match(team_name, team1_name)
                team2_match = teams_match(team_name, team2_name)
                
                if not team1_match and not team2_match:
                    continue
                
                debug_log(f"SportsGrid: Matched game for {team_name}: {team1_name} vs {team2_name}", xbmc.LOGINFO)
                
                team1_score = team1_data.get('score', {})
                team2_score = team2_data.get('score', {})
                
                debug_log(f"SportsGrid: Score data - team1: {team1_score}, team2: {team2_score}", xbmc.LOGINFO)
                
                # Try different fields for score
                if isinstance(team1_score, dict):
                    team1_score = team1_score.get('value') or team1_score.get('points') or team1_score.get('displayValue', 0)
                if isinstance(team2_score, dict):
                    team2_score = team2_score.get('value') or team2_score.get('points') or team2_score.get('displayValue', 0)
                
                # Determine opponent and result
                if team1_match:
                    opponent = team2_name
                    team_score = team1_score
                    opp_score = team2_score
                else:
                    opponent = team1_name
                    team_score = team2_score
                    opp_score = team1_score
                
                # Determine win/loss
                result = 'W' if team_score > opp_score else 'L'
                
                recent_games.append({
                    'result': result,
                    'score': f"{team_score}-{opp_score}",
                    'opponent': opponent,
                    'date': date_str
                })
            
            debug_log(f"SportsGrid: All games before sort: {[(g['date'], g['opponent'], g['score']) for g in recent_games]}", xbmc.LOGINFO)
            
            # Sort by date (newest first)
            recent_games.sort(key=lambda x: x.get('date', ''), reverse=True)
            
            debug_log(f"SportsGrid: All games after sort: {[(g['date'], g['opponent'], g['score']) for g in recent_games]}", xbmc.LOGINFO)
            
            # Take top 5 after sorting
            recent_games = recent_games[:5]
            
            debug_log(f"SportsGrid: Found {len(recent_games)} recent games for {team_name}", xbmc.LOGINFO)
            self._set_cached_team_data(team_name, 'recent_games', recent_games)
            return recent_games
        except Exception as e:
            debug_log(f"SportsGrid: Error in get_team_recent_games: {str(e)}", xbmc.LOGERROR)
            return []

    def get_next_game(self, team_name):
        """Get the next upcoming game for a team"""
        try:
            # Check cache first
            cached = self._get_cached_team_data(team_name, 'next_game')
            if cached is not None:
                return cached
            
            sport_map = {
                'mlb': 'sports/baseball/mlb',
                'nba': 'sports/basketball/nba',
                'nhl': 'sports/hockey/nhl',
                'wnba': 'sports/basketball/wnba',
                'nfl': 'sports/football/nfl',
                'mls': 'sports/soccer/usa.1',
                'premierleague': 'sports/soccer/eng.1',
                'laliga': 'sports/soccer/esp.1',
                'bundesliga': 'sports/soccer/ger.1',
                'seriea': 'sports/soccer/ita.1',
                'ligue1': 'sports/soccer/fra.1',
                'championsleague': 'sports/soccer/uefa.champions'
            }
            espn_sport = sport_map.get(self.sport, f'sports/{self.sport}')
            
            # Query with future dates to get upcoming games
            now = datetime.now()
            start_date = now
            end_date = now + timedelta(days=14)  # Look ahead 14 days
            start_str = start_date.strftime('%Y%m%d')
            end_str = end_date.strftime('%Y%m%d')
            
            url = f"https://site.api.espn.com/apis/site/v2/{espn_sport}/scoreboard?dates={start_str}-{end_str}"
            debug_log(f"SportsGrid: Fetching next game from {url}", xbmc.LOGINFO)
            
            response = requests.get(
                url,
                headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'},
                timeout=15
            )
            if response.status_code != 200:
                debug_log(f"SportsGrid: Failed to get next game, status {response.status_code}", xbmc.LOGWARNING)
                return {}
            
            data = response.json()
            events = data.get('events', [])
            
            for event in events:
                competition = event.get('competitions', [{}])[0]
                status = competition.get('status', {})
                status_type = status.get('type', {})
                game_status = status_type.get('description', '')
                
                # Skip final/postponed games - we want future games
                if 'final' in game_status.lower() or 'postponed' in game_status.lower():
                    continue
                
                competitors = competition.get('competitors', [])
                if len(competitors) < 2:
                    continue
                
                team1 = competitors[0].get('team', {})
                team2 = competitors[1].get('team', {})
                team1_name = team1.get('displayName', '')
                team2_name = team2.get('displayName', '')
                
                # Check if this team is in this game
                if team_name.lower() not in team1_name.lower() and team_name.lower() not in team2_name.lower():
                    continue
                
                # Get game time
                date_obj = event.get('date', '')
                game_time = ''
                if date_obj:
                    try:
                        game_dt = datetime.fromisoformat(date_obj.replace('Z', '+00:00'))
                        local_tz = datetime.now().astimezone().tzinfo
                        game_dt_local = game_dt.astimezone(local_tz)
                        game_time = game_dt_local.strftime('%a, %b %d at %I:%M %p')
                    except Exception:
                        game_time = date_obj
                
                # Determine opponent
                if team_name.lower() in team1_name.lower():
                    opponent = team2_name
                    opponent_logo = team2.get('logos', [{}])[0].get('href', '') if team2.get('logos') else ''
                else:
                    opponent = team1_name
                    opponent_logo = team1.get('logos', [{}])[0].get('href', '') if team1.get('logos') else ''
                
                # Get network/broadcast info
                networks = competition.get('broadcasts', [])
                network = networks[0].get('names', ['TBD'])[0] if networks else 'TBD'
                
                debug_log(f"SportsGrid: Found next game for {team_name}: vs {opponent} on {game_time}", xbmc.LOGINFO)
                result = {
                    'opponent': opponent,
                    'time': game_time,
                    'network': network,
                    'opponent_logo': opponent_logo
                }
                self._set_cached_team_data(team_name, 'next_game', result)
                return result
            
            debug_log(f"SportsGrid: No upcoming games found for {team_name}", xbmc.LOGINFO)
            self._set_cached_team_data(team_name, 'next_game', {})
            return {}
        except Exception as e:
            debug_log(f"SportsGrid: Error in get_next_game: {str(e)}", xbmc.LOGERROR)
            return {}

    def get_team_season_leaders(self, team_name):
        """Get team's season statistical rankings using the core statistics API"""
        try:
            # Check cache first
            cached = self._get_cached_team_data(team_name, 'season_leaders')
            if cached is not None:
                return cached
            
            sport_map = {
                'mlb': ('baseball', 'mlb'),
                'nba': ('basketball', 'nba'),
                'nhl': ('hockey', 'nhl'),
                'wnba': ('basketball', 'wnba'),
                'nfl': ('football', 'nfl'),
                'mls': ('soccer', 'usa.1'),
                'premierleague': ('soccer', 'eng.1'),
                'laliga': ('soccer', 'esp.1'),
                'bundesliga': ('soccer', 'ger.1'),
                'seriea': ('soccer', 'ita.1'),
                'ligue1': ('soccer', 'fra.1'),
                'championsleague': ('soccer', 'uefa.champions')
            }
            
            sport_info = sport_map.get(self.sport)
            if not sport_info:
                return {}
            
            espn_sport, league = sport_info
            
            # First get the team ID from the teams endpoint
            # Try the sports.core.api endpoint instead
            current_year = datetime.now().year
            teams_url = f"https://sports.core.api.espn.com/v2/sports/{espn_sport}/leagues/{league}/seasons/{current_year}/teams"
            debug_log(f"SportsGrid: Finding team ID from {teams_url}", xbmc.LOGINFO)
            
            teams_response = requests.get(
                teams_url,
                headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'},
                timeout=15
            )
            if teams_response.status_code != 200:
                debug_log(f"SportsGrid: Teams API status: {teams_response.status_code}", xbmc.LOGWARNING)
                return {}
            
            teams_data = teams_response.json()
            debug_log(f"SportsGrid: Teams API response keys: {teams_data.keys()}", xbmc.LOGINFO)
            
            teams_list = teams_data.get('teams', [])
            if not teams_list:
                # Try alternative keys
                teams_list = teams_data.get('items', [])
            
            # Fetch all pages if there are multiple pages
            page_count = teams_data.get('pageCount', 1)
            current_page = teams_data.get('pageIndex', 1)
            debug_log(f"SportsGrid: Page info - current: {current_page}, total: {page_count}", xbmc.LOGINFO)
            
            if page_count > 1:
                debug_log(f"SportsGrid: Fetching all {page_count} pages of teams", xbmc.LOGINFO)
                for page in range(2, page_count + 1):
                    page_url = f"{teams_url}?page={page}"
                    page_response = requests.get(
                        page_url,
                        headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'},
                        timeout=15
                    )
                    if page_response.status_code == 200:
                        page_data = page_response.json()
                        page_teams = page_data.get('teams', []) or page_data.get('items', [])
                        if page_teams:
                            teams_list.extend(page_teams)
            
            debug_log(f"SportsGrid: Teams list length: {len(teams_list)}", xbmc.LOGINFO)
            if teams_list:
                # Debug: log first item structure
                debug_log(f"SportsGrid: First team item: {teams_list[0]}", xbmc.LOGINFO)
            
            # Debug: log first few team names
            sample = []
            for t in teams_list[:5]:
                if t:
                    sample.append(t.get('displayName') or t.get('name') or str(t))
            debug_log(f"SportsGrid: Teams data sample: {sample}", xbmc.LOGINFO)
            
            team_id = None
            team_color = ''
            team_alt_color = ''
            team_name_clean = team_name.lower().replace(' ', '').replace('.', '').replace('-', '').replace("'", "")
            debug_log(f"SportsGrid: Looking for team: '{team_name}' (cleaned: '{team_name_clean}')", xbmc.LOGINFO)
            
            # Debug: Collect and log all team names we're checking against
            all_team_names = []
            
            # First need to fetch the actual team data from the $ref URLs
            for team in teams_list:
                if not team:
                    continue
                # Handle $ref format - extract ID from URL
                team_ref = team.get('$ref', '')
                if team_ref:
                    # Extract team ID from URL like: .../teams/1?lang=en&region=us
                    import re
                    match = re.search(r'/teams/(\d+)', team_ref)
                    if match:
                        test_id = match.group(1)
                        # Fetch team details to get name
                        team_detail_url = team_ref
                        team_detail_response = requests.get(
                            team_detail_url,
                            headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'},
                            timeout=10
                        )
                        if team_detail_response.status_code == 200:
                            team_detail = team_detail_response.json()
                            team_display_name = team_detail.get('displayName', '') or team_detail.get('name', '')
                            team_abbrev = team_detail.get('abbreviation', '').lower()
                            all_team_names.append(f"{team_display_name} ({team_abbrev})")
            
            # First need to fetch the actual team data from the $ref URLs
            for team in teams_list:
                if not team:
                    continue
                # Handle $ref format - extract ID from URL
                team_ref = team.get('$ref', '')
                if team_ref:
                    # Extract team ID from URL like: .../teams/1?lang=en&region=us
                    import re
                    match = re.search(r'/teams/(\d+)', team_ref)
                    if match:
                        test_id = match.group(1)
                        # Fetch team details to get name
                        team_detail_url = team_ref
                        team_detail_response = requests.get(
                            team_detail_url,
                            headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'},
                            timeout=10
                        )
                        if team_detail_response.status_code == 200:
                            team_detail = team_detail_response.json()
                            team_display_name = team_detail.get('displayName', '') or team_detail.get('name', '')
                            team_location = team_detail.get('location', {}).get('name', '').lower().replace(' ', '').replace('-', '') if isinstance(team_detail.get('location'), dict) else ''
                            team_abbrev = team_detail.get('abbreviation', '').lower()
                            
                            if team_display_name:
                                name2 = team_display_name.lower().replace(' ', '').replace('.', '').replace('-', '').replace("'", "")
                                
                                # Check for abbreviation match first (most reliable)
                                abbrev_match = team_abbrev == team_name_clean or team_abbrev in team_name_clean
                                
                                # Check for full name match (both directions)
                                full_name_match = team_name_clean == name2
                                
                                # Check if location + nickname matches
                                location_nickname_match = False
                                if team_location:
                                    team_nickname = team_display_name.lower().replace(team_location.title(), '').replace(' ', '').replace('-', '').replace("'", "")
                                    location_nickname_match = team_location in team_name_clean and team_nickname in team_name_clean
                                
                                # Check for partial match where both significant words match
                                partial_match = False
                                matched_words = 0
                                search_terms = team_name_clean
                                if len(search_terms) >= 4:  # Only check if search term is meaningful
                                    # Extract key words from team name (ignore common words)
                                    ignore_words = ['la', 'ny', 'gs', 'no', 'utah', 'san', 'antonio', 'los', 'angeles', 'new', 'orleans', 'golden', 'state', 'warriors']
                                    key_words = [w for w in [team_name_clean[i:i+3] for i in range(len(team_name_clean)-2)] if w not in ignore_words]
                                    matched_words = sum(1 for kw in key_words if kw in name2)
                                    if matched_words >= 2 and len(key_words) > 0:
                                        partial_match = matched_words >= min(2, len(key_words))
                                
                                # Prioritize matches: exact > location+nickname > abbrev > partial
                                # Only use partial match if it's very strong (at least 3 matched words)
                                strong_partial = matched_words >= 3 if partial_match else False
                                
                                if full_name_match or location_nickname_match:
                                    team_id = test_id
                                    team_color = team_detail.get('color', '')
                                    team_alt_color = team_detail.get('alternateColor', '')
                                    debug_log(f"SportsGrid: Matched team: {team_display_name} (abbrev: {team_abbrev}, location: {team_location}) with ID {team_id}, color: {team_color}", xbmc.LOGINFO)
                                    break
                                elif abbrev_match:
                                    team_id = test_id
                                    team_color = team_detail.get('color', '')
                                    team_alt_color = team_detail.get('alternateColor', '')
                                    debug_log(f"SportsGrid: Matched team (abbrev): {team_display_name} (abbrev: {team_abbrev}, location: {team_location}) with ID {team_id}, color: {team_color}", xbmc.LOGINFO)
                                    break
                                elif strong_partial:
                                    team_id = test_id
                                    team_color = team_detail.get('color', '')
                                    team_alt_color = team_detail.get('alternateColor', '')
                                    debug_log(f"SportsGrid: Matched team (partial): {team_display_name} (abbrev: {team_abbrev}, location: {team_location}) with ID {team_id}, color: {team_color}, matched_words: {matched_words}", xbmc.LOGINFO)
                                    break
            
            if not team_id:
                debug_log(f"SportsGrid: All teams checked: {all_team_names}", xbmc.LOGINFO)
                debug_log(f"SportsGrid: Could not find team ID for {team_name}", xbmc.LOGINFO)
                return {}
            
            # Get team statistics
            season_type = 2  # regular season
            stats_url = f"https://sports.core.api.espn.com/v2/sports/{espn_sport}/leagues/{league}/seasons/{current_year}/types/{season_type}/teams/{team_id}/statistics"
            debug_log(f"SportsGrid: Fetching team stats from {stats_url}", xbmc.LOGINFO)
            
            stats_response = requests.get(
                stats_url,
                headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'},
                timeout=15
            )
            if stats_response.status_code != 200:
                debug_log(f"SportsGrid: Failed to get team stats, status {stats_response.status_code}", xbmc.LOGWARNING)
                return {}
            
            stats_data = stats_response.json()
            debug_log(f"SportsGrid: Stats API response keys: {stats_data.keys()}", xbmc.LOGINFO)
            
            # Stats are in splits -> categories
            splits = stats_data.get('splits', {})
            categories = splits.get('categories', [])
            
            leaders = {}
            for category in categories:
                stats = category.get('stats', [])
                for stat in stats:
                    stat_name = stat.get('name', '')
                    display_value = stat.get('displayValue', '')
                    short_name = stat.get('shortDisplayName', '')
                    
                    # Look for key stats based on sport
                    should_include = False
                    
                    if self.sport == 'nba':
                        # Key NBA team stats - check if has rank and use rank value
                        if stat_name in ['points', 'rebounds', 'assists', 'steals', 'blocks', 'turnovers', 'pace', 'pointDifferential']:
                            should_include = True
                    elif self.sport == 'nfl':
                        # Key NFL team stats  
                        if stat_name in ['pointsFor', 'pointsAgainst', 'pointDifferential', 'totalYards', 'passYards', 'rushYards']:
                            should_include = True
                    elif self.sport == 'mlb':
                        # Key MLB team stats
                        if stat_name in ['runs', 'runsAllowed', 'runDifferential', 'homeRuns', 'era', 'whip']:
                            should_include = True
                    elif self.sport == 'nhl':
                        # Key NHL team stats
                        if stat_name in ['goalsFor', 'goalsAgainst', 'goalDifferential', 'powerPlayPct', 'penaltyKillPct']:
                            should_include = True
                    elif self.sport in ['mls', 'premierleague', 'laliga', 'bundesliga', 'seriea', 'ligue1', 'championsleague']:
                        # Soccer stats - include goal differential
                        if stat_name in ['goalsFor', 'goalsAgainst', 'goalDifferential', 'goalsAgainst', 'cleanSheets', 'possessionPct']:
                            should_include = True
                    
                    if should_include and short_name:
                        # Use rank if available, otherwise use per game value
                        rank_val = stat.get('rankDisplayValue', '')
                        if rank_val:
                            display = f"#{rank_val}"
                        else:
                            per_game = stat.get('perGameDisplayValue', '')
                            display = per_game if per_game else display_value
                        if display:
                            leaders[stat_name] = {
                                'name': short_name,
                                'value': display
                            }
            
            if leaders:
                debug_log(f"SportsGrid: Found season stats for {team_name}: {leaders}", xbmc.LOGINFO)
            
            debug_log(f"SportsGrid: Final season leaders result: {leaders}", xbmc.LOGINFO)
            
            # Also get player leaders for the team
            player_leaders = self.get_team_player_leaders(team_name, team_id, espn_sport, league)
            if player_leaders:
                leaders['player_leaders'] = player_leaders
            
            # Add team colors (with override check)
            if team_color:
                team_name_clean = team_name.lower().replace(' ', '').replace('.', '').replace('-', '').replace("'", "")
                override_color = TEAM_COLOR_OVERRIDES.get(team_name_clean, '')
                if override_color:
                    leaders['team_color'] = override_color
                    debug_log(f"SportsGrid: Overriding team color for {team_name} from {team_color} to {override_color}", xbmc.LOGINFO)
                else:
                    leaders['team_color'] = team_color
                leaders['team_alt_color'] = team_alt_color
            
            self._set_cached_team_data(team_name, 'season_leaders', leaders)
            return leaders
        except Exception as e:
            debug_log(f"SportsGrid: Error in get_team_season_leaders: {str(e)}", xbmc.LOGERROR)
            return {}

    def get_team_player_leaders(self, team_name, team_id, espn_sport, league):
        """Get the team's leading players (top scorers, etc.)"""
        try:
            current_year = datetime.now().year
            
            # First, get roster to build athlete ID -> name lookup
            athlete_names = {}
            roster_url = f"https://site.api.espn.com/apis/site/v2/sports/{espn_sport}/{league}/teams/{team_id}/roster?season={current_year}"
            debug_log(f"SportsGrid: Fetching roster for names from {roster_url}", xbmc.LOGINFO)
            try:
                roster_response = requests.get(
                    roster_url,
                    headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'},
                    timeout=15
                )
                if roster_response.status_code == 200:
                    roster_data = roster_response.json()
                    for athlete in roster_data.get('athletes', []):
                        athlete_id = str(athlete.get('id', ''))
                        full_name = athlete.get('fullName', '')
                        if athlete_id and full_name:
                            athlete_names[athlete_id] = full_name
                    debug_log(f"SportsGrid: Got {len(athlete_names)} athlete names", xbmc.LOGINFO)
            except Exception as e:
                debug_log(f"SportsGrid: Error fetching roster: {str(e)}", xbmc.LOGWARNING)
            
            # Try the stats endpoint with athletes
            team_url = f"https://sports.core.api.espn.com/v2/sports/{espn_sport}/leagues/{league}/seasons/{current_year}/types/2/teams/{team_id}/statistics?enable=ranks"
            debug_log(f"SportsGrid: Fetching player leaders from {team_url}", xbmc.LOGINFO)
            
            response = requests.get(
                team_url,
                headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'},
                timeout=15
            )
            if response.status_code != 200:
                debug_log(f"SportsGrid: Failed to get player leaders, status {response.status_code}", xbmc.LOGWARNING)
                return {}
            
            data = response.json()
            
            debug_log(f"SportsGrid: Player API response keys: {data.keys()}", xbmc.LOGINFO)
            
            # Get team data - may need to follow $ref
            team_data = data.get('team', {})
            
            # Check if team_data is a $ref that needs to be fetched
            if isinstance(team_data, dict) and '$ref' in team_data:
                ref_url = team_data.get('$ref', '')
                debug_log(f"SportsGrid: Following $ref to get team data: {ref_url}", xbmc.LOGINFO)
                ref_response = requests.get(
                    ref_url,
                    headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'},
                    timeout=15
                )
                if ref_response.status_code == 200:
                    team_data = ref_response.json()
                    debug_log(f"SportsGrid: Team data after $ref: keys={team_data.keys()}", xbmc.LOGINFO)
                else:
                    debug_log(f"SportsGrid: Failed to follow $ref, status {ref_response.status_code}", xbmc.LOGWARNING)
                    return {}
            
            debug_log(f"SportsGrid: Team data keys: {team_data.keys() if isinstance(team_data, dict) else type(team_data)}", xbmc.LOGINFO)
            debug_log(f"SportsGrid: Data top-level keys: {data.keys()}", xbmc.LOGINFO)
            
            # Check for leaders in multiple places
            leaders = team_data.get('leaders', []) if isinstance(team_data, dict) else []
            
            # If leaders is a $ref, follow it
            if isinstance(leaders, dict) and '$ref' in leaders:
                leaders_ref = leaders.get('$ref', '')
                debug_log(f"SportsGrid: Following leaders $ref: {leaders_ref}", xbmc.LOGINFO)
                leaders_response = requests.get(
                    leaders_ref,
                    headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'},
                    timeout=15
                )
                if leaders_response.status_code == 200:
                    leaders_data = leaders_response.json()
                    debug_log(f"SportsGrid: Leaders $ref response keys: {leaders_data.keys()}", xbmc.LOGINFO)
                    # The leaders are in 'categories' not 'leaders'
                    leaders = leaders_data.get('categories', [])
                    debug_log(f"SportsGrid: Found {len(leaders)} leader categories from $ref", xbmc.LOGINFO)
                else:
                    debug_log(f"SportsGrid: Failed to get leaders $ref, status {leaders_response.status_code}", xbmc.LOGWARNING)
                    leaders = []
            
            # If no leaders in team_data, check top level of response
            if not leaders:
                leaders = data.get('leaders', [])
                debug_log(f"SportsGrid: Checking top-level leaders: {len(leaders)} categories", xbmc.LOGINFO)
            
            # Check for athletes with stats in splits
            if not leaders:
                splits = team_data.get('splits', {}) if isinstance(team_data, dict) else {}
                debug_log(f"SportsGrid: Splits keys: {splits.keys() if splits else 'None'}", xbmc.LOGINFO)
                categories = splits.get('categories', []) if splits else []
                if categories:
                    debug_log(f"SportsGrid: Stats categories: {[c.get('name') for c in categories]}", xbmc.LOGINFO)
            
            # Priority order for stat categories - most important first
            stat_priority = {
                'pointsPerGame': 1,
                'assistsPerGame': 2,
                'reboundsPerGame': 3,
                'defensiveReboundsPerGame': 4,
                'offensiveReboundsPerGame': 5,
                'stealsPerGame': 6,
                'blocksPerGame': 7,
                'fieldGoalsMadePerGame': 8,
                'threePointersMadePerGame': 9,
                'freeThrowsMadePerGame': 10,
                'turnoversPerGame': 11,
                'foulsPerGame': 12,
            }
            
            # Friendly stat name mapping
            stat_friendly_names = {
                'pointsPerGame': 'Points',
                'assistsPerGame': 'Assists',
                'reboundsPerGame': 'Rebounds',
                'defensiveReboundsPerGame': 'Def Reb',
                'offensiveReboundsPerGame': 'Off Reb',
                'stealsPerGame': 'Steals',
                'blocksPerGame': 'Blocks',
                'fieldGoalsMadePerGame': 'FG Made',
                'threePointersMadePerGame': '3PM',
                'freeThrowsMadePerGame': 'FT Made',
                'turnoversPerGame': 'Turnovers',
                'foulsPerGame': 'Fouls',
            }
            
            # Sort categories by priority
            sorted_leaders = []
            if leaders:
                for category in leaders:
                    if not isinstance(category, dict):
                        continue
                    stat_name = category.get('name', '')
                    priority = stat_priority.get(stat_name, 999)
                    sorted_leaders.append((priority, category))
                sorted_leaders.sort(key=lambda x: x[0])
            
            player_leaders = []
            player_stat_map = {}  # Track which stat each player is already leading in
            
            # Process sorted leaders - get top player from each priority category
            # For each category, check if the top player is already shown for a higher priority stat
            for priority, category in sorted_leaders:
                stat_name = category.get('name', '')
                stat_display = category.get('displayName', '')
                stat_leaders = category.get('leaders', [])
                
                if stat_leaders and len(stat_leaders) > 0:
                    top_player = stat_leaders[0]
                    if not isinstance(top_player, dict):
                        continue
                    player_stat = top_player.get('displayValue', '')
                    
                    # Get player name from athlete $ref
                    player_name = ''
                    athlete_info = top_player.get('athlete', {})
                    if isinstance(athlete_info, dict) and '$ref' in athlete_info:
                        athlete_ref = athlete_info.get('$ref', '')
                        import re
                        match = re.search(r'/athletes/(\d+)', athlete_ref)
                        if match:
                            athlete_id = match.group(1)
                            player_name = athlete_names.get(athlete_id, '')
                    
                    if player_stat and player_name:
                        friendly_stat = stat_friendly_names.get(stat_name, stat_name)
                        
                        # Only add if player not already in our results (to show different players)
                        if player_name not in player_stat_map:
                            player_leaders.append((player_name, f"{player_stat} {friendly_stat}"))
                            player_stat_map[player_name] = friendly_stat
                            
                            if len(player_leaders) >= 3:
                                break
            
            # If no leaders, try getting from roster endpoint athletes
            if not player_leaders:
                # Check for athletes at top level of response (roster endpoint)
                athletes = data.get('athletes', [])
                debug_log(f"SportsGrid: No leaders, checking top-level athletes. Count: {len(athletes)}", xbmc.LOGINFO)
                
                if athletes and len(athletes) > 0:
                    first_athlete = athletes[0] if isinstance(athletes[0], dict) else {}
                    debug_log(f"SportsGrid: First athlete keys: {first_athlete.keys()}", xbmc.LOGINFO)
                    
                    # Check if athlete has statistics
                    if 'statistics' in first_athlete:
                        stats = first_athlete.get('statistics', {})
                        debug_log(f"SportsGrid: First athlete stats: {stats}", xbmc.LOGINFO)
            
            debug_log(f"SportsGrid: Found {len(player_leaders)} player leaders: {player_leaders}", xbmc.LOGINFO)
            
            # Take top 3 (already sorted by stat priority and deduplicated)
            result = dict(player_leaders[:3])
            
            debug_log(f"SportsGrid: Player leaders (top 3): {result}", xbmc.LOGINFO)
            return result
            
        except Exception as e:
            debug_log(f"SportsGrid: Error in get_team_player_leaders: {str(e)}", xbmc.LOGERROR)
            return {}

    def _extract_primary_stat(self, stat_str):
        """Extract primary number from stat string for sorting"""
        import re
        numbers = re.findall(r'\d+', str(stat_str))
        return int(numbers[0]) if numbers else 0

    def format_channels(self, standings):
        """Format standings data as channels for the grid"""
        channels = []
        channel_number = 1
        
        # Get default logo - prefer league logo from API, then fall back to local thumbnail
        default_logo = getattr(self, 'league_logo', '')
        debug_log(f"SportsGrid: league_logo attribute: {default_logo}", xbmc.LOGINFO)
        
        # If no league logo from API, try local thumbnail
        if not default_logo:
            default_thumb = SPORT_THUMBNAILS.get(self.sport, '')
            if default_thumb:
                default_logo = f"special://home/addons/plugin.video.jet_guide/resources/skins/default/720p/media/sports/{default_thumb}"
        
        debug_log(f"SportsGrid: Using default logo: {default_logo}", xbmc.LOGINFO)
        
        # Sort standings by division/conference blocks, then by wins (descending), then losses (ascending)
        def sort_key(entry):
            try:
                wins = float(entry.get('wins', 0))
                losses = float(entry.get('losses', 0))
                # Use negative wins for descending order
                return (-wins, losses)
            except Exception:
                return (0, 0)
        
        # Group by division/conference headers, keep their order, but sort teams within each group
        sorted_standings = []
        group = []
        for entry in standings:
            # Check if this is a division/conference header
            if entry.get('is_division_header'):
                # Sort previous group if exists
                if group:
                    group.sort(key=sort_key)
                    sorted_standings.extend(group)
                    group = []
                sorted_standings.append(entry)
            else:
                group.append(entry)
        if group:
            group.sort(key=sort_key)
            sorted_standings.extend(group)
        
        for entry in sorted_standings:
            # Skip division/conference header entries
            if entry.get('is_division_header'):
                continue
            stats = []
            if entry.get('wins') and entry.get('losses'):
                stats.append(f"{entry['wins']} - {entry['losses']}")
            if entry.get('pct'):
                stats.append(f"{entry['pct']}")
            if entry.get('gb'):
                stats.append(f"{entry['gb']}")
            if entry.get('home'):
                stats.append(f"{entry['home']}")
            if entry.get('away') or entry.get('road'):
                stats.append(f"{entry.get('away', entry.get('road', ''))}")
            if entry.get('last10') or entry.get('last5'):
                stats.append(f"{entry.get('last10', entry.get('last5', ''))}")
            if entry.get('streak'):
                stats.append(f"{entry['streak']}")
            record = " | ".join(stats)
            
            # Use ESPN logo if available, otherwise use default sport thumbnail
            logo = entry.get('logo_url', '')
            if not logo and default_logo:
                logo = default_logo
            
            channel = {
                'name': entry['team_name'],
                'tvg_id': f"sports_{self.sport}_{channel_number}",
                'logo': logo,
                'group': self.sport.upper(),
                'record': record,
                'label': str(channel_number),
                'label2': entry['team_name'],
                'wins': entry.get('wins', ''),
                'losses': entry.get('losses', ''),
                'pct': entry.get('pct', ''),
                'gb': entry.get('gb', ''),
                'home': entry.get('home', ''),
                'away': entry.get('away', ''),
                'l10': entry.get('last10', ''),
                'strk': entry.get('streak', ''),
                'division': entry.get('division', ''),
                'division_record': entry.get('division_record', ''),
                'conference_record': entry.get('conference_record', '')
            }
            channels.append(channel)
            channel_number += 1
        return channels

    def format_programs(self, games):
        """Format game data as programs for the grid"""
        programs = []
        processed_games = set()
        if not self.time_slots:
            self.set_time_slots()
        now = datetime.now().astimezone(self.local_tz)
        
        for game in games:
            game_id = f"{game['team1']}-{game['team2']}"
            if game_id in processed_games:
                continue
            processed_games.add(game_id)
            status_text = game['status'] if game['status'] else "TBD"
            
            # Simplified - just use default time slot and focus on status
            slot_start, slot_end = self.time_slots[3]  # Default to evening slot
            time_text = status_text  # Use status as time display
            
            team1_norm = game['team1']
            team2_norm = game['team2']
            
            team1_channel = next((c['tvg_id'] for c in self.channels
                                if c['name'] == team1_norm), None)
            team2_channel = next((c['tvg_id'] for c in self.channels
                                if c['name'] == team2_norm), None)
            
            # Add program even if only one channel is found
            if team1_channel or team2_channel:
                score_text = f" ({game['score1']}-{game['score2']})" if game['score1'] and game['score2'] else ""
                if 'final' in status_text.lower():
                    prefix = "[FINAL]"
                elif 'postponed' in status_text.lower() or 'ppd' in status_text.lower():
                    prefix = "[POSTPONED]"
                elif any(hint in status_text.lower() for hint in ['inning', 'quarter', 'period', 'live', 'bot', 'top']):
                    prefix = f"[[COLORlime]Live[/COLOR] - {status_text}]"
                elif 'scheduled' in status_text.lower():
                    prefix = "[SCHEDULED]"
                else:
                    prefix = f"[{status_text}]" if status_text != "Scheduled" else "[TBD]"
                game_text = f"{team1_norm.upper()} vs {team2_norm.upper()}{score_text}"
                game_title = f"{prefix} | {game_text}"
                desc_lines = [
                    f"Status: {status_text}",
                    f"Time: {time_text}",
                    f"Teams: {team1_norm.upper()} vs {team2_norm.upper()}{score_text}",
                    f"Network: {game['network'] or 'TBD'}"
                ]
                program_data = {
                    'start': slot_start,
                    'stop': slot_end,
                    'title': game_title,
                    'desc': "\n".join(desc_lines),
                    'category': self.sport.upper(),
                    'time': time_text,
                    'status': status_text
                }
                program_data['channel'] = team1_channel or team2_channel
                program_data['away_channel'] = team2_channel if team1_channel else None
                programs.append(program_data)
        return programs
        
    def update_selected_info(self):
        """Update info panel based on current selection"""
        try:
            focused_id = self.getFocusId()
            debug_log(f"SportsGrid: update_selected_info called, focused_id={focused_id}", xbmc.LOGINFO)
            
            # Default to channel panel if no focus
            if focused_id == 0:
                focused_id = 100
            
            if focused_id == 100 and self.channel_panel:  # Team stats panel
                # Show team statistics
                selected_pos = self.channel_panel.getSelectedPosition()
                debug_log(f"SportsGrid: Channel panel pos={selected_pos}, size={self.channel_panel.size()}", xbmc.LOGINFO)
                if 0 <= selected_pos < self.channel_panel.size():
                    selected_item = self.channel_panel.getSelectedItem()
                    team_name = selected_item.getLabel()
                    team_record = selected_item.getProperty('record')
                    debug_log(f"SportsGrid: Selected team: {team_name}, record: {team_record}", xbmc.LOGINFO)
                    
                    # Build team stats info
                    stats_info = []
                    if team_record:
                        stats_info.append(f"Record: {team_record}")
                    
                    # Add any additional team stats from properties
                    for prop in ['wins', 'losses', 'pct', 'gb', 'home', 'away', 'l10']:
                        value = selected_item.getProperty(prop)
                        if value:
                            stats_info.append(f"{prop.upper()}: {value}")
                    
                    debug_log(f"SportsGrid: About to set GameTitle and GameInfo properties", xbmc.LOGINFO)
                    self.setProperty('GameTitle', f"[B]{team_name.upper()} Statistics[/B]")
                    self.setProperty('GameInfo', '\n'.join(stats_info) if stats_info else 'No statistics available')
                    debug_log(f"SportsGrid: Set GameTitle property", xbmc.LOGINFO)
                    # Force property update by reading it back
                    _ = self.getProperty('GameTitle')
                    # Force UI refresh
                    xbmc.sleep(10)
                    debug_log(f"SportsGrid: Set GameInfo: {stats_info}", xbmc.LOGINFO)
                    
            elif focused_id == 101 and self.time_panel:  # Games panel
                # Show game information
                selected_pos = self.time_panel.getSelectedPosition()
                debug_log(f"SportsGrid: Time panel pos={selected_pos}, size={self.time_panel.size()}", xbmc.LOGINFO)
                if 0 <= selected_pos < self.time_panel.size():
                    selected_item = self.time_panel.getSelectedItem()
                    game_info_text = selected_item.getProperty('game')
                    
                    if game_info_text:
                        self.setProperty('GameTitle', '[B]Game Information[/B]')
                        self.setProperty('GameInfo', game_info_text)
                    else:
                        self.setProperty('GameTitle', '[B]No Game Selected[/B]')
                        self.setProperty('GameInfo', 'Select a game to view details')
            else:
                # Clear info if no valid selection
                self.setProperty('GameTitle', '')
                self.setProperty('GameInfo', '')
                
        except Exception as e:
            debug_log(f"SportsGrid: update_selected_info error: {str(e)}", xbmc.LOGERROR)
            # Clear properties on error
            self.setProperty('GameTitle', '')
            self.setProperty('GameInfo', '')

    def update_program_info(self, selected_item, channel=None):
        """Update the info panel with sports-specific information"""
        try:
            # Clear properties if no item
            self.setProperty('GameTitle', '')
            self.setProperty('GameInfo', '')
            if not selected_item:
                return
            # Get game title from item or dict
            game = (selected_item.getProperty('game') if isinstance(selected_item, xbmcgui.ListItem)
                   else selected_item.get('title', ''))
            if not game:
                return
            # Build info display
            game_info = []
            self.setProperty('GameTitle', f"[B]{game}[/B]")
            # Add channel/team info if available
            if channel and isinstance(channel, dict):
                record = channel.get('record', '')
                team_name = channel.get('name', '')
                if record and team_name:
                    game_info.append(f"[COLOR yellow]{team_name.upper()}:[/COLOR] {record}")
            # Add program details
            for program in self.programs:
                if program.get('title') == game and program.get('desc'):
                    for line in program['desc'].split('\n'):
                        game_info.append(line)
            self.setProperty('GameInfo', '\n'.join(game_info))
            
        except Exception as e:
            # debug_log(f"SportsGrid: Error updating program info: {str(e)}", xbmc.LOGERROR)
            # Clear properties on error
            self.setProperty('GameTitle', '')
            self.setProperty('GameInfo', '')
    
    def show_loading(self):
        """Show the loading overlay"""
        try:
            # Fix: Check if control 999 exists before trying to use it
            loading_control = self.getControl(999)
            if loading_control:
                loading_control.setVisible(True)
        except:
            # Control 999 doesn't exist in skin - silently ignore
            pass
    
    def hide_loading(self):
        """Hide the loading overlay"""
        try:
            # Fix: Check if control 999 exists before trying to use it
            loading_control = self.getControl(999)
            if loading_control:
                loading_control.setVisible(False)
        except:
            # Control 999 doesn't exist in skin - silently ignore
            pass
    
    def update_loading_progress(self, progress_text="", percent=0):
        """Update loading progress text and bar"""
        try:
            # Update loading text
            if progress_text:
                text_control = self.getControl(998)
                text_control.setLabel(progress_text)
            
            # Update progress bar width (360px max width)
            if 0 <= percent <= 100:
                progress_width = int((percent / 100) * 360)
                progress_control = self.getControl(997)
                progress_control.setWidth(progress_width)
        except Exception as e:
            pass

    def handleGameSelection(self):
            """Handle game selection and stream dialog. Only trigger playback after explicit user selection."""
            # Fix: Use dialog lock to prevent concurrent busydialog crash
            if not self.acquire_dialog_lock('handleGameSelection'):
                debug_log("SportsGrid: Dialog already in use, skipping", xbmc.LOGWARNING)
                return
            
            try:
                item = self.time_panel.getSelectedItem()
                if not item:
                    self.release_dialog_lock('handleGameSelection')
                    return

                game_title = item.getProperty('game')
                if not game_title:
                    self.release_dialog_lock('handleGameSelection')
                    return
                parts = game_title.split('|')
                if len(parts) < 2:
                    self.release_dialog_lock('handleGameSelection')
                    return
                teams_part = parts[1].strip()
                teams = teams_part.split('vs')
                if len(teams) != 2:
                    self.release_dialog_lock('handleGameSelection')
                    return

                team1_full = teams[0].strip()
                team2_full = teams[1].strip()
                # Remove records or extra info in parentheses
                team1_name = re.sub(r"\s*\(.*?\)", "", team1_full).lower().strip()
                team2_name = re.sub(r"\s*\(.*?\)", "", team2_full).lower().strip()
                
                # Try fuzzy matching for team names in stream titles
                def fuzzy_match(title, team1, team2):
                    t = re.sub(r'\[/?[a-z]+.*?\]', '', title.lower())
                    t = re.sub(r'[^a-z0-9 ]', '', t)
                    team1_clean = re.sub(r'[^a-z0-9 ]', '', team1)
                    team2_clean = re.sub(r'[^a-z0-9 ]', '', team2)
                    debug_log(f"[DEBUG] Matching '{team1_clean}' and '{team2_clean}' against '{t}'", xbmc.LOGINFO)
                    result = (team1_clean in t or team1_clean.split()[-1] in t) and (team2_clean in t or team2_clean.split()[-1] in t)
                    debug_log(f"[DEBUG] Result: {result}", xbmc.LOGINFO)
                    return result
                
                # Use lock-aware progress dialog
                progress = xbmcgui.DialogProgress()
                progress.create('Searching', f'Searching for {team1_name} vs {team2_name}...\n{game_title}')
                if "wnba" in self.sport:
                    url = f'https://magnetic.website/MAD_TITAN_SPORTS/SPORTS/LEAGUE/titansports_nba.json'
                elif "mls" in self.sport or "premierleague" in self.sport or "laliga" in self.sport or "bundesliga" in self.sport or "seriea" in self.sport or "ligue1" in self.sport or "championsleague" in self.sport:
                    url = f'https://magnetic.website/MAD_TITAN_SPORTS/SPORTS/LEAGUE/titansports_soccer.json'
                else:
                    url = f'https://magnetic.website/MAD_TITAN_SPORTS/SPORTS/LEAGUE/titansports_{self.sport}.json'
                try:
                    response = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'}, timeout=10)
                    response.raise_for_status()
                    data = response.json()
                except Exception as e:
                    progress.close()
                    xbmcgui.Dialog().ok("Error", f"Failed to load game data: {str(e)}")
                    self.release_dialog_lock('handleGameSelection')
                    return
                progress.update(25, 'Loading game data...')
                stream_links = []
                stream_names = []
                total_items = len(data.get('items', []))
                if total_items == 0:
                    progress.close()
                    xbmcgui.Dialog().ok("Error", "No games found in data source")
                    self.release_dialog_lock('handleGameSelection')
                    return
                for idx, game in enumerate(data['items']):
                    if progress.iscanceled():
                        progress.close()
                        self.release_dialog_lock('handleGameSelection')
                        return
                    progress.update(50 + int((idx / total_items) * 50), f'Checking game {idx + 1} of {total_items}...')
                    if (game.get('type') == 'item' and
                        game.get('title') and
                        fuzzy_match(game.get('title',''), team1_name, team2_name)):
                        
                        if 'link' in game:
                            links = game.get('link', [])
                            for i, link in enumerate(links):
                                clean_url = link
                                stream_name = f"Stream {i + 1}"
                                if '(' in link and ')' in link:
                                    stream_name = link[link.rindex('(')+1:link.rindex(')')].strip()
                                    clean_url = link[:link.rindex('(')].strip()
                                elif '[COLOR' in link and '[/COLOR]' in link:
                                    clean_url = re.sub(r'\[COLOR.*?\](.*?)\[/COLOR\]', r'\1', link).strip()
                                    if '(' in clean_url and ')' in clean_url:
                                        stream_name = clean_url[clean_url.rindex('(')+1:clean_url.rindex(')')].strip()
                                        clean_url = clean_url[:clean_url.rindex('(')].strip()

                                stream_links.append(clean_url)
                                stream_names.append(stream_name)
                progress.close()
                if not stream_links:
                    self.release_dialog_lock('handleGameSelection')
                    return
                dialog = xbmcgui.Dialog()
                choice = dialog.select('Choose Stream', stream_names)
            
                if choice is not None and choice >= 0:
                    selected_url = stream_links[choice]
                    list_item = xbmcgui.ListItem(game_title)
                    list_item.setInfo('video', {'title': game_title})
                    list_item.setProperty('IsPlayable', 'true')
                    try:
                        from youtube_guide import cancel_youtube_playback
                        cancel_youtube_playback()
                    except ImportError:
                        pass
                    xbmc.Player().play(selected_url, list_item)
                # Only trigger playback if a stream was explicitly selected
                self.release_dialog_lock('handleGameSelection')

            except Exception as e:
                xbmcgui.Dialog().ok("Error", f"Failed to play stream: {str(e)}")
                self.release_dialog_lock('handleGameSelection')
    def onClick(self, control_id):
        """Handle click events"""
        # Handle sport selection buttons
        if control_id in range(900, 911):
            self.handle_sport_button(control_id)
            return
        # Handle click on time panel (right side)
        if control_id == 101 and self.time_panel:
            if hasattr(self, 'handleGameSelection') and callable(getattr(self, 'handleGameSelection')):
                self.handleGameSelection()
            elif hasattr(self, 'handleGameSelection__patched') and callable(getattr(self, 'handleGameSelection__patched')):
                self.handleGameSelection__patched()
            else:
                # Try to call as a method of the class (handleGameSelection may be indented inside another method)
                try:
                    type(self).handleGameSelection(self)
                except Exception as e:
                    xbmcgui.Dialog().ok("Error", f"Could not trigger game selection: {str(e)}")
            return
        # Handle click on channel panel (left side, teams)
        if control_id == 100 and self.channel_panel:
            selected_pos = self.channel_panel.getSelectedPosition()
            if 0 <= selected_pos < self.channel_panel.size():
                selected_item = self.channel_panel.getSelectedItem()
                team_name = selected_item.getLabel()
                team_logo = selected_item.getProperty('logo')
                team_record = selected_item.getProperty('record')
                # Get all available stats
                team_wins = selected_item.getProperty('wins')
                team_losses = selected_item.getProperty('losses')
                team_pct = selected_item.getProperty('pct')
                team_gb = selected_item.getProperty('gb')
                team_home = selected_item.getProperty('home')
                team_away = selected_item.getProperty('away')
                team_l10 = selected_item.getProperty('l10')
                team_strk = selected_item.getProperty('strk')
                team_division_record = selected_item.getProperty('division_record')
                team_conference_record = selected_item.getProperty('conference_record')
                team_differential = selected_item.getProperty('differential')
                
                # Get latest game stats for this team
                game_stats = self.get_team_game_stats(team_name)
                
                # Get recent games using date range API
                recent_games_list = self.get_team_recent_games(team_name)
                
                # Check if recent games are actually recent (within last 14 days)
                use_recent_games = False
                if recent_games_list:
                    from datetime import timedelta
                    now = datetime.now()
                    for game in recent_games_list:
                        game_date_str = game.get('date', '')
                        if game_date_str:
                            try:
                                game_date = datetime.strptime(game_date_str[:10], '%Y-%m-%d')
                                days_ago = (now - game_date).days
                                if days_ago <= 14:
                                    use_recent_games = True
                                    break
                            except:
                                pass
                
                if use_recent_games:
                    # Format as: W vs Opponent: score, L vs Opponent: score, etc.
                    recent_games_str = ' | '.join([f"{g['result']} {g['opponent']}: {g['score']}" for g in recent_games_list])
                else:
                    # Fall back to L10 from standings
                    recent_games_str = team_l10 if team_l10 else ''
                
                # Get next upcoming game
                next_game = self.get_next_game(team_name)
                next_game_str = ''
                if next_game:
                    next_game_str = f"vs {next_game.get('opponent', 'TBD')} -\n {next_game.get('time', 'TBD')} ({next_game.get('network', 'TBD')})"
                
                # Get team season leaders/rankings
                debug_log(f"SportsGrid: Getting season leaders for {team_name}", xbmc.LOGINFO)
                season_leaders = self.get_team_season_leaders(team_name)
                debug_log(f"SportsGrid: Season leaders result: {season_leaders}", xbmc.LOGINFO)
                
                # Extract team colors
                team_color = season_leaders.get('team_color', '') if season_leaders else ''
                debug_log(f"SportsGrid: Team color for {team_name}: {team_color}", xbmc.LOGINFO)
                
                # Open team info window with all stats
                TeamInfoWindow.show_team_info(
                    team_name, team_logo, team_record,
                    team_wins, team_losses, team_pct, team_gb,
                    team_home, team_away, team_l10, team_strk,
                    game_stats, self.sport, recent_games_str,
                    team_division_record, team_conference_record,
                    next_game_str, season_leaders, team_differential,
                    team_color
                )
            return

    def switch_sport(self, new_sport):
        """Switch to a different sport by using executebuiltin to properly navigate"""
        try:
            debug_log(f"SportsGrid: Switching to sport: {new_sport}", xbmc.LOGINFO)
            self.close()
            xbmc.executebuiltin('Dialog.Close(all,true)')
            xbmc.sleep(200)
            xbmc.executebuiltin(f'RunPlugin(plugin://plugin.video.jet_guide/?action=sports_grid&sport={new_sport})')
        except Exception as e:
            debug_log(f"SportsGrid: Error in switch_sport: {str(e)}", xbmc.LOGERROR)
            xbmcgui.Dialog().ok("Error", f"Failed to switch sport: {str(e)}")
    
    def show_more_sports(self):
        """Show dialog with more sports options"""
        more_sports = [
            ("CFL", "cfl"),
            ("Bundesliga", "bundesliga"),
            ("Serie A", "seriea"),
            ("Ligue 1", "ligue1"),
            ("Champions League", "championsleague"),
        ]
        
        sport_names = [name for name, _ in more_sports]
        dialog = xbmcgui.Dialog()
        choice = dialog.select('Select Sport', sport_names)
        
        if choice >= 0:
            sport_code = more_sports[choice][1]
            self.switch_sport(sport_code)

    def handle_sport_button(self, control_id):
        """Handle sport selection button clicks"""
        sport_map = {
            900: 'mlb',
            901: 'nba',
            902: 'nhl',
            903: 'nfl',
            904: 'wnba',
            905: 'ncaab',
            906: 'mls',
            907: 'premierleague',
            908: 'laliga',
            909: 'more',
            910: 'exit',
        }
        
        sport = sport_map.get(control_id)
        
        if sport == 'exit':
            self.close()
            xbmc.sleep(100)
            xbmc.executebuiltin('Dialog.Close(all,true)')
            xbmc.sleep(100)
            xbmc.executebuiltin('ActivateWindow(10024)')
            return
        elif sport == 'more':
            self.show_more_sports()
            return
        elif sport:
            if sport == self.sport:
                return
            self.switch_sport(sport)

# --- TeamInfoWindow stub ---
class TeamInfoWindow(xbmcgui.WindowXML):
    def onInit(self):
        debug_log(f"[TeamInfoWindow] onInit called", xbmc.LOGINFO)
        self.setProperty('TeamName', getattr(self, '_team_name', ''))
        self.setProperty('TeamLogo', getattr(self, '_team_logo', ''))
        self.setProperty('TeamRecord', getattr(self, '_team_record', ''))
        # Try to set focus to a control to receive actions
        try:
            self.setFocusId(10)
        except:
            pass

    def onAction(self, action):
        action_id = action.getId()
        debug_log(f"[TeamInfoWindow] onAction: action_id={action_id}", xbmc.LOGINFO)
        if action_id == xbmcgui.ACTION_PREVIOUS_MENU or action_id == xbmcgui.ACTION_NAV_BACK:
            debug_log("[TeamInfoWindow] Closing due to back action", xbmc.LOGINFO)
            xbmc.executebuiltin('Dialog.Close(all,true)')

    @staticmethod
    def show_team_info(team_name, team_logo, team_record, wins='', losses='', pct='', gb='', home='', away='', l10='', strk='', game_stats=None, sport='nba', recent_games='', division_record='', conference_record='', next_game='', season_leaders=None, differential='', team_color=''):
        debug_log(f"[TeamInfoWindow] show_team_info: name={team_name}, sport={sport}, differential={differential}, team_color={team_color}", xbmc.LOGINFO)
        gs = game_stats or {}
        config = get_sport_stats_config(sport)
        stat1_val = gs.get(config['stat_keys'][0], '')
        stat2_val = gs.get(config['stat_keys'][1], '')
        stat3_val = gs.get(config['stat_keys'][2], '')
        stat4_val = gs.get(config['stat_keys'][3], '')
        stat5_val = gs.get(config['stat_keys'][4], '')
        leader1_val = gs.get(config['leader_keys'][0], '')
        leader2_val = gs.get(config['leader_keys'][1], '')
        leader3_val = gs.get(config['leader_keys'][2], '')
        leader4_val = gs.get(config['leader_keys'][3], '') if len(config['leader_keys']) > 3 else ''
        leader5_val = gs.get(config['leader_keys'][4], '') if len(config['leader_keys']) > 4 else ''
        leader1_headshot = gs.get(f"{config['leader_keys'][0]}_headshot", '')
        leader2_headshot = gs.get(f"{config['leader_keys'][1]}_headshot", '')
        leader3_headshot = gs.get(f"{config['leader_keys'][2]}_headshot", '')
        leader4_headshot = gs.get(f"{config['leader_keys'][3]}_headshot", '') if len(config['leader_keys']) > 3 else ''
        leader5_headshot = gs.get(f"{config['leader_keys'][4]}_headshot", '') if len(config['leader_keys']) > 4 else ''
        opponent = gs.get('opponent', '')
        team_score = gs.get('team_score', '')
        opponent_score = gs.get('opponent_score', '')
        
        def safe_arg(val):
            if val is None:
                return ''
            s = str(val)
            # Remove ALL % characters completely
            s = s.replace('%', '')
            # Replace commas with pipe
            return s.replace(',', '|')
        
        num_placeholders = 40
        args = [
            safe_arg(team_name),
            safe_arg(team_logo or ''),
            safe_arg(team_record or ''),
            safe_arg(wins or ''),
            safe_arg(losses or ''),
            safe_arg(pct or ''),
            safe_arg(gb or ''),
            safe_arg(home or ''),
            safe_arg(away or ''),
            safe_arg(l10 or ''),
            safe_arg(strk or ''),
            safe_arg(stat1_val),
            safe_arg(stat2_val),
            safe_arg(stat3_val),
            safe_arg(stat4_val),
            safe_arg(stat5_val),
            safe_arg(leader1_val),
            safe_arg(leader2_val),
            safe_arg(leader3_val),
            safe_arg(leader4_val),
            safe_arg(leader5_val),
            safe_arg(sport or ''),
            safe_arg(opponent),
            safe_arg(team_score),
            safe_arg(opponent_score),
            safe_arg(leader1_headshot),
            safe_arg(leader2_headshot),
            safe_arg(leader3_headshot),
            safe_arg(leader4_headshot),
            safe_arg(leader5_headshot),
            safe_arg(recent_games or ''),
            safe_arg(division_record or ''),
            safe_arg(conference_record or ''),
            safe_arg(next_game or ''),
            safe_arg(str(season_leaders) if season_leaders else ''),
            safe_arg(differential or ''),
            safe_arg(team_color or ''),
            safe_arg(gs.get('leader_era_alt', '')),
            safe_arg(gs.get('leader_strikeouts_alt', '')),
            safe_arg(gs.get('leader_era_alt_headshot', '')),
            safe_arg(gs.get('leader_strikeouts_alt_headshot', ''))
        ]
        debug_log("[TeamInfoWindow] Args: team_name=" + str(team_name) + ", sport=" + str(sport) + ", args_count=" + str(len(args)) + ", season_leaders=" + str(season_leaders)[:200] if season_leaders else "None", xbmc.LOGINFO)
        
        # Build the argument string manually to avoid format issues
        arg_parts = []
        for a in args:
            # Escape any pipe characters in the value (they're our delimiter)
            arg_parts.append(str(a).replace('|', '___PIPE___'))
        
        arg_string = ','.join(arg_parts)
        xbmc.executebuiltin('RunScript(special://home/addons/plugin.video.jet_guide/team_info_window.py,' + arg_string + ')')