# app.py
import gradio as gr
import xgboost as xgb
from xgboost import DMatrix
from huggingface_hub import hf_hub_download
from app_training_df_getter import create_app_user_training_df
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder
from helper import *
from helper import ChampionConverter 
import joblib


# Define champion list for dropdowns
CHAMPIONS = [
    "Aatrox", "Ahri", "Akali", "Akshan", "Alistar", "Amumu", "Anivia", "Annie", "Aphelios", "Ashe",
    "Aurelion Sol", "Azir", "Bard", "Bel'Veth", "Blitzcrank", "Brand", "Braum", "Caitlyn", "Camille",
    "Cassiopeia", "Cho'Gath", "Corki", "Darius", "Diana", "Dr. Mundo", "Draven", "Ekko", "Elise",
    "Evelynn", "Ezreal", "Fiddlesticks", "Fiora", "Fizz", "Galio", "Gangplank", "Garen", "Gnar",
    "Gragas", "Graves", "Gwen", "Hecarim", "Heimerdinger", "Illaoi", "Irelia", "Ivern", "Janna",
    "Jarvan IV", "Jax", "Jayce", "Jhin", "Jinx", "Kai'Sa", "Kalista", "Karma", "Karthus", "Kassadin",
    "Katarina", "Kayle", "Kayn", "Kennen", "Kha'Zix", "Kindred", "Kled", "Kog'Maw", "KSante", "LeBlanc",
    "Lee Sin", "Leona", "Lillia", "Lissandra", "Lucian", "Lulu", "Lux", "Malphite", "Malzahar", "Maokai",
    "Master Yi", "Milio", "Miss Fortune", "Mordekaiser", "Morgana", "Naafiri", "Nami", "Nasus", "Nautilus",
    "Neeko", "Nidalee", "Nilah", "Nocturne", "Nunu & Willump", "Olaf", "Orianna", "Ornn", "Pantheon",
    "Poppy", "Pyke", "Qiyana", "Quinn", "Rakan", "Rammus", "Rek'Sai", "Rell", "Renata Glasc", "Renekton",
    "Rengar", "Riven", "Rumble", "Ryze", "Samira", "Sejuani", "Senna", "Seraphine", "Sett", "Shaco",
    "Shen", "Shyvana", "Singed", "Sion", "Sivir", "Skarner", "Sona", "Soraka", "Swain", "Sylas",
    "Syndra", "Tahm Kench", "Taliyah", "Talon", "Taric", "Teemo", "Thresh", "Tristana", "Trundle",
    "Tryndamere", "Twisted Fate", "Twitch", "Udyr", "Urgot", "Varus", "Vayne", "Veigar", "Vel'Koz",
    "Vex", "Vi", "Viego", "Viktor", "Vladimir", "Volibear", "Warwick", "Wukong", "Xayah", "Xerath",
    "Xin Zhao", "Yasuo", "Yone", "Yorick", "Yuumi", "Zac", "Zed", "Zeri", "Ziggs", "Zilean", "Zoe", "Zyra"
]

try:
    label_encoder = joblib.load('util/label_encoder.joblib')
    n_classes = len(label_encoder.classes_)
    print("Label encoder loaded successfully")
except Exception as e:
    print(f"Error loading label encoder: {e}")
    label_encoder = None

# Load model
try:
    model_path = hf_hub_download(
        repo_id="ivwhy/champion-predictor-model",
        filename="champion_predictor.json"
    )

    # Initialize the model with proper parameters
    model = xgb.XGBClassifier(
        use_label_encoder=False,
        objective='multi:softprob',
        num_class=n_classes
    )

    # Load the model
    model._Booster = xgb.Booster()
    model._Booster.load_model(model_path)

    # Set only n_classes_
    model.n_classes_ = n_classes
except Exception as e:
    print(f"Error loading model: {e}")
    model = None 

# Initialize champion name encoder
# champion_encoder = LabelEncoder()
# champion_encoder.fit(CHAMPIONS)


#==================================== Functions =================================================
def get_user_training_df(player_opgg_url):
    try:
        print("========= Inside get_user_training_df(player_opgg_url) ============= \n") 
        #print("player_opgg_url: ", player_opgg_url, "\n type(player_opgg_url): ", type(player_opgg_url), "\n")

        # Add input validation
        if not player_opgg_url or not isinstance(player_opgg_url, str):
            return "Invalid URL provided"

        training_df = create_app_user_training_df(player_opgg_url)
        return training_df
    except Exception as e:

        # Add more detailed error information
        import traceback
        error_trace = traceback.format_exc()
        print(f"Full error trace:\n{error_trace}")
        return f"Error getting training data: {str(e)}"

        #return f"Error getting training data: {e}"

def show_stats(player_opgg_url):
    """Display player statistics and recent matches"""
    if not player_opgg_url:
        return "Please enter a player link to OPGG", None
    
    try:
        training_features = get_user_training_df(player_opgg_url)

        print("training_features: ", training_features, "\n")
        
        if isinstance(training_features, str):  # Error message
            return training_features, None

        wins = training_features['result'].sum()
        losses = len(training_features) - wins
        winrate = f"{(wins / len(training_features)) * 100:.0f}%"
        favorite_champions = (
            training_features['champion']
            .value_counts()
            .head(3)
            .index.tolist()
        )

        stats_html = f"""
        <div style='padding: 20px; background: #f5f5f5; border-radius: 10px;'>
            <h3>Player's Recent Stats</h3>
            <p>Wins: {wins} | Losses: {losses}</p>
            <p>Winrate: {winrate}</p>
            <p>Favorite Champions: {', '.join(favorite_champions)}</p>
        </div>
        """
        
        return stats_html, None
    except Exception as e:
        return f"Error processing stats: {e}. ", None

def predict_top_5_champion_w_confidence(player_opgg_url, *champions):
    """Make prediction based on selected champions"""
    if not player_opgg_url or None in champions:
        return "Please fill in all fields"
    
    try:
        if model is None:
            return "Model not loaded properly"
        
        if label_encoder is None:
            return "Label encoder not loaded properly" 
            
        # Get and process the data
        training_df = get_user_training_df(player_opgg_url)
        
        if isinstance(training_df, str):
            return training_df

        training_df = convert_df(training_df)
        print("check_datatypes(training_df) BEFORE feature eng:  \n", check_datatypes(training_df), "\n")

        training_df = apply_feature_engineering(training_df)
        print("check_datatypes(training_df) AFTER feature eng: \n", check_datatypes(training_df), "\n")
        
        label_column = training_df['champion']
        predict_column = training_df.drop(columns=['champion', 'region'])

        proba = model.predict_proba(predict_column)

        # Get top 5 indices and probabilities
        top_5_idx = np.argsort(proba, axis=1)[:, -5:][:, ::-1]
        top_5_proba = np.take_along_axis(proba, top_5_idx, axis=1)

        # Initialize results DataFrame
        results = pd.DataFrame()

        champion_converter = ChampionConverter()
        
        # Add true champion - convert numeric label to champion name
        true_numbers = label_column
        results['True_Champion'] = [champion_converter.num_to_champion(int(num)) for num in true_numbers]
        
        # Process each rank separately
        for i in range(5):
            # Convert indices to champion names using the champion converter
            champions = [champion_converter.num_to_champion(int(label_encoder.classes_[idx])) for idx in top_5_idx[:, i]]
            probabilities = top_5_proba[:, i]
            
            # Add to results
            results[f'Rank_{i+1}_Champion'] = champions
            results[f'Rank_{i+1}_Confidence'] = probabilities.round(4)

        try:


            def find_champion_rank(row):
                true_champ = row['True_Champion']
                for i in range(1, 6):
                    if row[f'Rank_{i}_Champion'] == true_champ:
                        return f'Rank_{i}'
                return 'Not in Top 5'
            
            results['Prediction_Rank'] = results.apply(find_champion_rank, axis=1)
            
            return results
            
        except Exception as e:
            print(f"Error mapping champion ID: {e}")
            # return f"Error: Could not map champion ID {decoded_numeric[0]}"
    
    except Exception as e:
        import traceback
        print(f"Full error trace:\n{traceback.format_exc()}")
        return f"Error making prediction: {e}"

# Define your interface
with gr.Blocks() as demo:
    gr.Markdown("# League of Legends Champion Prediction")
    
    with gr.Row():
        player_opgg_url = gr.Textbox(label="OPGG Player URL")
        show_button = gr.Button("Show Player Stats")
    
    with gr.Row():
        stats_output = gr.HTML(label="Player Statistics")
        recent_matches = gr.HTML(label="Recent Matches")
    
    with gr.Row():
        champion_dropdowns = [
            gr.Dropdown(choices=CHAMPIONS, label=f"Champion {i+1}")
            for i in range(9)
        ]
    
    with gr.Row():
        predict_button = gr.Button("Predict")
        prediction_output = gr.Text(label="Prediction")
        
    # Set up event handlers
    show_button.click(
        fn=show_stats,
        inputs=[player_opgg_url],
        outputs=[stats_output, recent_matches]
    )
    
    predict_button.click(
        fn=predict_top_5_champion_w_confidence,
        inputs=[player_opgg_url] + champion_dropdowns,
        outputs=prediction_output
    )

# Optional: Save the champion encoder for future use
#joblib.dump(champion_encoder, 'champion_encoder.joblib')
# Enable queuing
demo.launch(debug=True)

# For local testing
if __name__ == "__main__":
    demo.launch()