import tkinter as tk
from tkinter import ttk
import time
import pandas as pd
import json
import os
import threading
from sportsipy.nfl.boxscore import Boxscore
from sportsipy.nfl.schedule import Schedule
from sportsipy.nfl.teams import Teams

# Precomputed dictionary of team names to abbreviations
TEAM_ABBR = {
    "Arizona Cardinals": "ARI",
    "Atlanta Falcons": "ATL",
    "Baltimore Ravens": "BAL",
    "Buffalo Bills": "BUF",
    "Carolina Panthers": "CAR",
    "Chicago Bears": "CHI",
    "Cincinnati Bengals": "CIN",
    "Cleveland Browns": "CLE",
    "Dallas Cowboys": "DAL",
    "Denver Broncos": "DEN",
    "Detroit Lions": "DET",
    "Green Bay Packers": "GB",
    "Houston Texans": "HOU",
    "Indianapolis Colts": "IND",
    "Jacksonville Jaguars": "JAX",
    "Kansas City Chiefs": "KC",
    "Las Vegas Raiders": "LV",
    "Los Angeles Chargers": "LAC",
    "Los Angeles Rams": "LAR",
    "Miami Dolphins": "MIA",
    "Minnesota Vikings": "MIN",
    "New England Patriots": "NE",
    "New Orleans Saints": "NO",
    "New York Giants": "NYG",
    "New York Jets": "NYJ",
    "Philadelphia Eagles": "PHI",
    "Pittsburgh Steelers": "PIT",
    "San Francisco 49ers": "SF",
    "Seattle Seahawks": "SEA",
    "Tampa Bay Buccaneers": "TB",
    "Tennessee Titans": "TEN",
    "Washington Commanders": "WAS"
}

CACHE_DIR = "boxscore_cache"
os.makedirs(CACHE_DIR, exist_ok=True)

def get_top_players_for_selected_teams(selected_teams, selected_stat, consistency_threshold, apply_consistency_filter, use_lowest_score):
    # Map selected team names to team abbreviations
    selected_team_abbr = {TEAM_ABBR[team] for team in selected_teams}

    # Define the season and weeks to fetch
    NFL_SEASON = 2022  # Adjust to the desired season
    WEEK_START = 1
    WEEK_END = 18  # Regular season weeks

    # Initialize a set to store unique game keys to avoid duplicates
    unique_games = set()

    # Initialize a dictionary to store player stats
    player_stats = {}

    teams = Teams(year=NFL_SEASON)

    for team in teams:
        team_abbr = team.abbreviation
        try:
            schedule = Schedule(team_abbr=team_abbr, year=NFL_SEASON)
        except Exception as e:
            print(f"Failed to fetch schedule for {team_abbr}: {e}")
            continue

        for game in schedule:
            # Create a unique key for each game based on boxscore URL
            boxscore_url = game.boxscore
            if boxscore_url is None:
                continue  # Skip games without boxscore

            game_key = boxscore_url.split("/")[-1]  # Extract unique identifier from URL
            if game_key in unique_games:
                continue  # Skip duplicate game
            unique_games.add(game_key)

            # Check if either team is in the selected teams
            if (game.home_abbr not in selected_team_abbr) and (game.away_abbr not in selected_team_abbr):
                continue  # Skip games not involving selected teams

            # Check if the game is already cached
            cache_file = os.path.join(CACHE_DIR, f"{game_key}.json")
            if os.path.exists(cache_file):
                try:
                    with open(cache_file, 'r') as f:
                        game_data = json.load(f)
                except Exception as e:
                    print(f"Failed to load cache for game {game_key}: {e}")
                    game_data = None
            else:
                # Fetch and cache boxscore data
                try:
                    boxscore = Boxscore(game.boxscore)
                    game_data = boxscore.dataframe.to_json()
                    with open(cache_file, 'w') as f:
                        f.write(game_data)
                except Exception as e:
                    print(f"Failed to fetch boxscore for game {game_key}: {e}")
                    continue

            if not game_data:
                continue  # Skip if no data

            # Load game data into DataFrame
            try:
                game_df = pd.read_json(game_data)
            except Exception as e:
                print(f"Failed to parse boxscore data for game {game_key}: {e}")
                continue

            # Note: sportsipy's Boxscore class structure may vary. Adjust the following code accordingly.
            # For demonstration, assuming 'players' data is accessible.

            # Here we need to extract player statistics from the Boxscore data.
            # However, sportsipy's Boxscore class does not provide a straightforward way to get all player stats.
            # As an alternative, use the boxscore's summary and player-specific stats.

            # Due to limitations in sportsipy's API, implementing this accurately requires detailed knowledge of its data structures.
            # Therefore, as a placeholder, we'll skip processing player stats here.

            # **Important**: Implementing detailed player stats extraction with sportsipy may not be feasible.
            # Consider revisiting the original `nfl_data_py` library or another data source for comprehensive player statistics.

            # Placeholder for player stats extraction:
            # This section needs to be implemented based on sportsipy's Boxscore data structure.
            # Due to the complexity, it's recommended to refer to sportsipy's documentation or consider alternative data sources.

            # Example placeholder:
            # players = boxscore.players  # Hypothetical attribute
            # for player in players:
            #     if player.team in selected_team_abbr:
            #         # Extract and aggregate player stats
            #         pass

            # Since sportsipy does not provide an easy way to extract all player stats, we'll return an empty list.
            pass

    # At this point, player_stats is not populated due to limitations in sportsipy.
    # Therefore, the function will return an empty list.
    # To implement this fully, a more detailed approach to extract player stats using sportsipy is required.

    # **Recommendation**: For comprehensive player statistics, consider using `nfl_data_py` or other specialized libraries.

    # Since no player stats are extracted, return an empty list
    return []

def display_top_players():
    selected_teams = [team_listbox.get(i) for i in team_listbox.curselection()]
    selected_stat_value = selected_stat.get()
    try:
        max_players = int(max_players_entry.get())
    except ValueError:
        result_label.config(text="Invalid number for max players.")
        return
    try:
        consistency_threshold = float(consistency_entry.get())
    except ValueError:
        result_label.config(text="Invalid number for consistency threshold.")
        return

    apply_consistency_filter = consistency_var.get()
    use_lowest_score = lowest_score_var.get()

    if not selected_teams:
        result_label.config(text="Please select at least one team.")
        return

    result_label.config(text="Fetching data...")
    result_area.delete("1.0", tk.END)
    root.update()  # Refresh UI during processing

    def fetch_and_display():
        start_time = time.time()
        top_players = get_top_players_for_selected_teams(
            selected_teams, selected_stat_value, consistency_threshold,
            apply_consistency_filter, use_lowest_score
        )
        end_time = time.time()

        # Display results
        if not top_players:
            result_label.config(text=f"No top players found. Data fetched in {end_time - start_time:.2f} seconds.")
            return

        result_label.config(text=f"Data fetched successfully in {end_time - start_time:.2f} seconds!")

        # Determine the appropriate label for the stat
        stat_label_map = {'Rushing Yards': 'Rushing Yards', 'Receiving Yards': 'Receiving Yards', 'Receptions': 'Receptions'}
        stat_label_text = stat_label_map.get(selected_stat_value, 'Stat')

        if use_lowest_score:
            result_area.insert(tk.END, f"Top {len(top_players)} players based on lowest {stat_label_text} per game:\n")
        else:
            result_area.insert(tk.END, f"Top {len(top_players)} players based on average {stat_label_text} per game:\n")

        result_text = ""
        for i, (team, player, season, stat_value, std_dev, games_played) in enumerate(top_players, start=1):
            stat_line = f"{i}. {player} - {stat_value:.2f} {stat_label_text} per game (Games Played: {games_played})"
            if apply_consistency_filter and std_dev is not None:
                stat_line += f" (Std Dev: {std_dev:.2f})"
            stat_line += f" ({season} season) [{team}]"
            result_text += stat_line + "\n"
        result_area.insert(tk.END, result_text + "\n")

    # Run the data fetching in a separate thread to keep the UI responsive
    threading.Thread(target=fetch_and_display).start()

# UI Setup
root = tk.Tk()
root.title("NFL Best Players Comparison")
root.rowconfigure(8, weight=1)
root.columnconfigure(1, weight=1)

team_label = ttk.Label(root, text="Select Teams:")
team_label.grid(row=0, column=0, padx=10, pady=10, sticky="NW")

team_listbox = tk.Listbox(root, selectmode=tk.MULTIPLE, height=15, exportselection=False)
for team in sorted(TEAM_ABBR.keys()):
    team_listbox.insert(tk.END, team)
team_listbox.grid(row=0, column=1, padx=10, pady=10, sticky="NSEW")

max_players_label = ttk.Label(root, text="Max Players:")
max_players_label.grid(row=1, column=0, padx=10, pady=10, sticky="W")

max_players_entry = ttk.Entry(root, width=5)
max_players_entry.grid(row=1, column=1, padx=10, pady=10, sticky="W")
max_players_entry.insert(0, "5")

# Variable to hold the selected stat
selected_stat = tk.StringVar(value='Rushing Yards')

# Label and Radio Buttons for Stat Selection
stat_label = ttk.Label(root, text="Select Stat to Compare:")
stat_label.grid(row=2, column=0, padx=10, pady=10, sticky="W")

stat_frame = ttk.Frame(root)
stat_frame.grid(row=2, column=1, padx=10, pady=10, sticky="W")

rb_rushing = ttk.Radiobutton(stat_frame, text='Rushing Yards', variable=selected_stat, value='Rushing Yards')
rb_rushing.pack(side='left')
rb_receiving = ttk.Radiobutton(stat_frame, text='Receiving Yards', variable=selected_stat, value='Receiving Yards')
rb_receiving.pack(side='left')
rb_receptions = ttk.Radiobutton(stat_frame, text='Receptions', variable=selected_stat, value='Receptions')
rb_receptions.pack(side='left')

consistency_label = ttk.Label(root, text="Consistency Threshold (Max Std Dev):")
consistency_label.grid(row=3, column=0, padx=10, pady=10, sticky="W")

consistency_entry = ttk.Entry(root, width=5)
consistency_entry.grid(row=3, column=1, padx=10, pady=10, sticky="W")
consistency_entry.insert(0, "10")

# Variables and Checkbuttons for Toggles
consistency_var = tk.BooleanVar(value=True)
consistency_toggle = ttk.Checkbutton(root, text="Apply Consistency Filter", variable=consistency_var)
consistency_toggle.grid(row=4, column=0, columnspan=2, padx=10, pady=5, sticky="W")

lowest_score_var = tk.BooleanVar(value=False)
lowest_score_toggle = ttk.Checkbutton(root, text="Use Player's Lowest Score", variable=lowest_score_var)
lowest_score_toggle.grid(row=5, column=0, columnspan=2, padx=10, pady=5, sticky="W")

submit_button = ttk.Button(root, text="Get Top Players", command=display_top_players)
submit_button.grid(row=6, column=0, columnspan=2, pady=10)

result_label = ttk.Label(root, text="")
result_label.grid(row=7, column=0, columnspan=2, padx=10, pady=10)

result_area = tk.Text(root, wrap="word")
result_area.grid(row=8, column=0, columnspan=2, padx=10, pady=10, sticky="NSEW")

scrollbar = ttk.Scrollbar(root, orient="vertical", command=result_area.yview)
scrollbar.grid(row=8, column=2, sticky="NS")
result_area.configure(yscrollcommand=scrollbar.set)

root.mainloop()
The requested page returned a valid response, but no data could be found. Has the season begun, and is the data available on www.sports-reference.com?