using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using TMPro;
using UnityEngine.EventSystems;

/// <summary>
/// Menu principal - Mosaïque de jeux chargée depuis l'API
/// Charge les quêtes depuis {{baseUrl}}/api/ujsa/projects/{{projectSlug}}
/// puis pour chaque quête, charge les détails depuis {{baseUrl}}/api/ujsa/quests/{{questId}}
/// Affiche tous les jeux disponibles (intro, jeux des zones, outro)
/// </summary>
public class MainMenuManager : MonoBehaviour
{
    [Header("UI References")]
    public TextMeshProUGUI titleText;

    [Tooltip("Viewport du ScrollRect (enfant direct de l'objet Scroll View)")]
    public RectTransform viewport;

    [Tooltip("Transform/RectTransform qui contient les cartes (Content du ScrollRect)")]
    public RectTransform levelsContainer;

    [Tooltip("Scroll View (ScrollRect)")]
    public ScrollRect scrollRect;

    [Tooltip("Scrollbar verticale (optionnelle)")]
    public Scrollbar verticalScrollbar;

    [Header("Loading")]
    public GameObject loadingPanel;
    public TextMeshProUGUI loadingText;

    [Header("Layout (Grid)")]
    [SerializeField] private int columns = 6;
    [SerializeField] private Vector2 cellSize = new Vector2(290, 120);
    [SerializeField] private Vector2 spacing = new Vector2(10, 10);
    [SerializeField] private RectOffset padding;
    [SerializeField] private TextAnchor childAlignment = TextAnchor.UpperLeft;

    [Header("Card Style")]
    [SerializeField] private Color shootingColor = new Color(0.39f, 0.28f, 0.5f, 1f);
    [SerializeField] private Color textHoleColor = new Color(0.8f, 0.58f, 0.26f, 1f);
    [SerializeField] private Color calculatorColor = new Color(0.33f, 0.61f, 0.36f, 1f);
    [SerializeField] private Color introOutroColor = new Color(0.2f, 0.5f, 0.8f, 1f); // Bleu pour intro/outro
    [SerializeField] private Color defaultColor = new Color(0.5f, 0.5f, 0.5f, 1f);
    [SerializeField] private int titleFontSize = 18;
    [SerializeField] private int typeFontSize = 14;

    [Header("Debug")]
    public bool debugMode = false;

    // Données internes
    private readonly List<GameEntry> gameEntries = new List<GameEntry>();
    private readonly List<GameObject> gameCards = new List<GameObject>();
    private GridLayoutGroup grid;
    private string csvPath;

    // Gate "anti écran noir" : tant que la config générale n'est pas exploitable, on bloque les clics sur les jeux.
    private Button retryConfigButton;

    void Awake()
    {
        if (padding == null) padding = new RectOffset(20, 20, 20, 20);

        EnsureEventSystem();

        // Chemin du fichier CSV
        // IMPORTANT WebGL: Application.streamingAssetsPath est une URL HTTP. Sur Windows, Path.Combine injecte des '\'
        // qui cassent l'URL (=> UnityWebRequest échoue). On normalise donc toujours en '/'.
        csvPath = Path.Combine(Application.streamingAssetsPath, "json", "jeux.csv").Replace("\\", "/");

        if (levelsContainer == null)
        {
            Debug.LogError("[MainMenuManager] 'levelsContainer' n'est pas assigné.");
            return;
        }

        // Configurer le GridLayoutGroup sur le Content
        grid = levelsContainer.GetComponent<GridLayoutGroup>();
        if (grid == null) grid = levelsContainer.gameObject.AddComponent<GridLayoutGroup>();

        // Configure le Grid
        grid.cellSize = cellSize;
        grid.spacing = spacing;
        grid.padding = padding;
        grid.startCorner = GridLayoutGroup.Corner.UpperLeft;
        grid.startAxis = GridLayoutGroup.Axis.Horizontal;
        grid.childAlignment = childAlignment;
        grid.constraint = GridLayoutGroup.Constraint.FixedColumnCount;
        grid.constraintCount = Mathf.Max(1, columns);

        // Ancrage du Content pour le scroll vertical
        levelsContainer.anchorMin = new Vector2(0f, 1f);
        levelsContainer.anchorMax = new Vector2(1f, 1f);
        levelsContainer.pivot = new Vector2(0.5f, 1f);
        levelsContainer.anchoredPosition = Vector2.zero;
    }

    private void EnsureEventSystem()
    {
        // IMPORTANT: Sans EventSystem, aucun clic UI ne marche (ça ressemble à un "voile" qui bloque tout).
        if (EventSystem.current != null) return;

        GameObject eventSystemObj = new GameObject("EventSystem");
        eventSystemObj.AddComponent<EventSystem>();
#if ENABLE_INPUT_SYSTEM
        eventSystemObj.AddComponent<UnityEngine.InputSystem.UI.InputSystemUIInputModule>();
#else
        eventSystemObj.AddComponent<StandaloneInputModule>();
#endif
        DontDestroyOnLoad(eventSystemObj);
    }

    void Start()
    {
        if (loadingPanel != null)
        {
            loadingPanel.SetActive(true);
            if (loadingText != null) loadingText.text = "Chargement des jeux...";
        }

        SetupScrollRect();
        StartCoroutine(InitializeMenu());
    }

    /// <summary>
    /// Initialise le menu : charge la config, vérifie la connexion, crée le header et charge les jeux
    /// </summary>
    private IEnumerator InitializeMenu()
    {
        // Attendre que GeneralConfigManager soit prêt
        if (GeneralConfigManager.Instance != null)
        {
            yield return GeneralConfigManager.Instance.WaitForConfigLoaded();
        }
        else
        {
            Debug.LogError("[MainMenuManager] GeneralConfigManager.Instance est null!");
            yield break;
        }

        // IMPORTANT: IsConfigLoaded peut être true même si on est tombé sur une config vide (fallback).
        // Dans ce cas, on bloque le menu et on propose un "Réessayer" au lieu de laisser partir vers un écran noir.
        UpdateConfigGateUI();

        // Vérifier le statut de connexion
        yield return StartCoroutine(CheckLoginStatusAndShowPopup());

        // Créer le header
        yield return StartCoroutine(CreateHeader());

        // Charger et afficher les jeux depuis l'API (au lieu du CSV)
        yield return StartCoroutine(LoadGamesFromAPI());

        // Masquer le panneau de chargement seulement si la config est OK
        if (loadingPanel != null && IsGeneralConfigUsable())
        {
            loadingPanel.SetActive(false);
        }
    }

    /// <summary>
    /// Charge les jeux depuis l'API au lieu du CSV
    /// 1. Charge l'API projects pour obtenir la liste des quêtes
    /// 2. Pour chaque quête, charge les détails avec l'API quests/{questId}
    /// 3. Extrait tous les jeux (intro, zones, outro) de chaque quête
    /// </summary>
    private IEnumerator LoadGamesFromAPI()
    {
        Debug.Log("[MainMenuManager] 📄 Chargement des jeux depuis l'API");
        
        // Vérifier que l'utilisateur est connecté
        if (UserDataManager.Instance == null || !UserDataManager.Instance.IsLoggedIn())
        {
            Debug.LogWarning("[MainMenuManager] ⚠️ Utilisateur non connecté, impossible de charger les jeux");
            yield break;
        }
        
        string token = UserDataManager.Instance.token;
        if (string.IsNullOrEmpty(token))
        {
            Debug.LogWarning("[MainMenuManager] ⚠️ Token manquant, impossible de charger les jeux");
            yield break;
        }
        
        // 1. Charger la liste des quêtes depuis l'API projects
        string projectsApiUrl = GeneralConfigManager.Instance?.GetMainSceneConfigApiUrl();
        if (string.IsNullOrEmpty(projectsApiUrl))
        {
            Debug.LogError("[MainMenuManager] ❌ Impossible de construire l'URL de l'API projects");
            yield break;
        }
        
        Debug.Log($"[MainMenuManager] 🌐 Chargement des quêtes depuis: {projectsApiUrl}");
        
        List<ApiMainSceneQuest> quests = new List<ApiMainSceneQuest>();
        
        using (UnityWebRequest www = UnityWebRequest.Get(projectsApiUrl))
        {
            www.SetRequestHeader("Authorization", $"Bearer {token}");
            yield return www.SendWebRequest();
            
            if (www.result != UnityWebRequest.Result.Success)
            {
                Debug.LogError($"[MainMenuManager] ❌ Erreur chargement API projects: {www.error}");
                yield break;
            }
            
            string jsonData = www.downloadHandler.text;
            ApiMainSceneResponse apiResponse = JsonUtility.FromJson<ApiMainSceneResponse>(jsonData);
            
            if (apiResponse != null && apiResponse.status == "success" && apiResponse.data != null && apiResponse.data.quests != null)
            {
                quests = apiResponse.data.quests;
                Debug.Log($"[MainMenuManager] ✅ {quests.Count} quêtes chargées");
            }
            else
            {
                Debug.LogError("[MainMenuManager] ❌ Réponse API projects invalide");
                yield break;
            }
        }
        
        // 2. Pour chaque quête, charger les détails et extraire les jeux
        gameEntries.Clear();
        
        bool isFirstQuest = true;
        
        foreach (var quest in quests)
        {
            // Ne charger que les quêtes accessibles
            if (!quest.has_access)
            {
                Debug.Log($"[MainMenuManager] ⏭️ Quête {quest.id} ({quest.title}) non accessible, ignorée");
                continue;
            }
            
            // Ajouter des séparateurs pour remplir la ligne actuelle et forcer l'intro en début de ligne suivante
            if (!isFirstQuest)
            {
                int currentPosition = gameEntries.Count % columns;
                int separatorsNeeded = (currentPosition > 0) ? (columns - currentPosition) : 0;
                
                Debug.Log($"[MainMenuManager] 📐 Position actuelle: {currentPosition}/{columns}, Séparateurs nécessaires: {separatorsNeeded}");
                
                for (int i = 0; i < separatorsNeeded; i++)
                {
                    GameEntry separator = new GameEntry
                    {
                        id = -1,
                        title = "",
                        type = "separator",
                        isSeparator = true
                    };
                    gameEntries.Add(separator);
                }
                
                if (separatorsNeeded > 0)
                {
                    Debug.Log($"[MainMenuManager] ➕ {separatorsNeeded} séparateur(s) ajouté(s) pour finir la ligne");
                }
            }
            
            yield return StartCoroutine(LoadGamesFromQuest(quest, token));
            isFirstQuest = false;
        }
        
        Debug.Log($"[MainMenuManager] ✅ {gameEntries.Count} jeux chargés depuis toutes les quêtes");
        
        // Générer les cartes
        GenerateGameCards();
        
        // Gate: si config invalide, désactiver les clics et afficher un message
        ApplyCardsInteractivity(IsGeneralConfigUsable());
        UpdateConfigGateUI();
        
        // Mettre à jour le layout
        yield return StartCoroutine(UpdateLayoutWithDelay());
    }
    
    /// <summary>
    /// Charge tous les jeux d'une quête spécifique
    /// </summary>
    private IEnumerator LoadGamesFromQuest(ApiMainSceneQuest quest, string token)
    {
        Debug.Log($"[MainMenuManager] 🔍 Chargement des jeux de la quête {quest.id}: {quest.title}");
        
        string questApiUrl = GeneralConfigManager.Instance?.GetQuestConfigApiUrl(quest.id);
        if (string.IsNullOrEmpty(questApiUrl))
        {
            Debug.LogWarning($"[MainMenuManager] ⚠️ Impossible de construire l'URL pour la quête {quest.id}");
            yield break;
        }
        
        using (UnityWebRequest www = UnityWebRequest.Get(questApiUrl))
        {
            www.SetRequestHeader("Authorization", $"Bearer {token}");
            yield return www.SendWebRequest();
            
            if (www.result != UnityWebRequest.Result.Success)
            {
                Debug.LogWarning($"[MainMenuManager] ⚠️ Erreur chargement quête {quest.id}: {www.error}");
                yield break;
            }
            
            string jsonData = www.downloadHandler.text;
            ApiQuestResponse apiResponse = JsonUtility.FromJson<ApiQuestResponse>(jsonData);
            
            if (apiResponse == null || apiResponse.status != "success" || apiResponse.data == null)
            {
                Debug.LogWarning($"[MainMenuManager] ⚠️ Réponse API invalide pour la quête {quest.id}");
                yield break;
            }
            
            MapConfigData questData = apiResponse.data;
            
            if (questData.steps == null || questData.steps.Count == 0)
            {
                Debug.LogWarning($"[MainMenuManager] ⚠️ Aucun step dans la quête {quest.id}");
                yield break;
            }
            
            // Première passe : compter le nombre total de jeux (zones) dans la quête
            int totalGamesInQuest = 0;
            foreach (var step in questData.steps)
            {
                if (step.type == "zones" && step.zones != null)
                {
                    foreach (var zone in step.zones)
                    {
                        if (zone.gameId > 0)
                        {
                            totalGamesInQuest++;
                        }
                    }
                }
            }
            
            Debug.Log($"[MainMenuManager] 📊 Total de jeux dans la quête {quest.id}: {totalGamesInQuest}");
            
            // Deuxième passe : extraire les jeux de chaque step avec leur position
            int gameCount = 0;
            int currentPosition = 0;
            
            foreach (var step in questData.steps)
            {
                // NE PAS ajouter les intro/outro - seulement les jeux des zones
                
                // Jeux des zones (type = "zones")
                if (step.type == "zones" && step.zones != null)
                {
                    foreach (var zone in step.zones)
                    {
                        if (zone.gameId > 0)
                        {
                            currentPosition++; // Incrémenter la position pour chaque jeu
                            string gameType = DetermineGameType(zone);
                            
                            // Utiliser uniquement le titre de la quête (sans suffixe)
                            string gameTitle = quest.title.ToUpper();
                            
                            GameEntry gameEntry = new GameEntry
                            {
                                id = zone.gameId,
                                title = gameTitle,
                                type = gameType,
                                questId = quest.id,
                                questTitle = quest.title,
                                isIntro = false,
                                isOutro = false,
                                positionInQuest = currentPosition,
                                totalGamesInQuest = totalGamesInQuest,
                                stepId = step.id, // Stocker le step ID pour l'URL admin
                                zoneId = zone.id  // Stocker le zone ID pour l'URL admin
                            };
                            gameEntries.Add(gameEntry);
                            gameCount++;
                        }
                    }
                }
            }
            
            Debug.Log($"[MainMenuManager] ✅ {gameCount} jeux extraits de la quête {quest.id}");
        }
    }
    
    /// <summary>
    /// Détermine le type de jeu à partir d'une zone
    /// </summary>
    private string DetermineGameType(MapZone zone)
    {
        // Si le type est spécifié dans la zone, l'utiliser
        if (!string.IsNullOrEmpty(zone.gameType))
        {
            return zone.gameType.ToLower();
        }
        
        // Sinon, utiliser "shooting" par défaut
        return "shooting";
    }

    /// <summary>
    /// Lance un jeu depuis une carte (appelé par le bouton JOUER)
    /// </summary>
    private void LaunchGameFromCard(GameEntry entry)
    {
        // Sécurité: éviter l'écran noir si la config générale n'est pas exploitable
        if (GeneralConfigManager.Instance == null || !GeneralConfigManager.Instance.HasValidApiUrls())
        {
            Debug.LogError("[MainMenuManager] ❌ general-config.json non chargé ou invalide - lancement ignoré");
            return;
        }

        Debug.Log($"[MainMenuManager] 🎮 Lancement du jeu: {entry.title} (ID: {entry.id})");

        // Définir les paramètres dans PlayerPrefs
        string gameType = entry.GetNormalizedType();
        PlayerPrefs.SetString("CurrentLevelType", gameType);
        PlayerPrefs.SetString("CurrentLevelId", $"game_{entry.id}");
        PlayerPrefs.SetInt("CurrentGameId", entry.id);
        PlayerPrefs.SetString("GamePhase", "Before");
        
        // IMPORTANT : Définir la scène de retour au Menu
        PlayerPrefs.SetString("ReturnToScene", "Menu");
        Debug.Log($"[MainMenuManager] ✅ ReturnToScene défini à 'Menu'");
        
        PlayerPrefs.Save();

        Debug.Log($"[MainMenuManager] Type de jeu: {gameType}");
        Debug.Log($"[MainMenuManager] GameId: {entry.id}");

        // Charger les données du jeu via l'API puis lancer
        StartCoroutine(LoadGameDataAndLaunchCoroutine(entry));
    }

    /// <summary>
    /// Charge les données du jeu depuis l'API puis lance la scène
    /// </summary>
    private IEnumerator LoadGameDataAndLaunchCoroutine(GameEntry entry)
    {
        // Afficher l'écran de chargement
        if (UnifiedLoadingManager.Instance != null)
        {
            UnifiedLoadingManager.ShowLoading($"Chargement de {entry.title}...", LoadingContext.Game);
        }

        bool loadingComplete = false;
        bool loadingSuccess = false;

        // Charger les données du jeu via GameDataManager
        GameDataManager.Instance.LoadGameData(
            entry.id,
            "Débutant", // TODO: Récupérer depuis les paramètres utilisateur
            onSuccess: (gameData) => {
                Debug.Log($"[MainMenuManager] ✅ Données du jeu {entry.id} chargées avec succès");
                loadingComplete = true;
                loadingSuccess = true;
            },
            onError: (error) => {
                Debug.LogError($"[MainMenuManager] ❌ Erreur lors du chargement du jeu {entry.id}: {error}");
                loadingComplete = true;
                loadingSuccess = false;
            }
        );

        // Attendre la fin du chargement
        float timeout = 10f;
        float elapsed = 0f;
        while (!loadingComplete && elapsed < timeout)
        {
            elapsed += Time.deltaTime;
            yield return null;
        }

        if (!loadingComplete)
        {
            Debug.LogError($"[MainMenuManager] ⏰ Timeout lors du chargement du jeu {entry.id}");
        }

        // Définir le flag pour indiquer qu'on utilise les données API
        if (loadingSuccess)
        {
            PlayerPrefs.SetString("UseApiData", "true");
            PlayerPrefs.Save();
            Debug.Log($"[MainMenuManager] ✅ UseApiData=true défini, transition vers la scène Player...");
        }
        else
        {
            PlayerPrefs.SetString("UseApiData", "false");
            PlayerPrefs.Save();
            Debug.LogWarning($"[MainMenuManager] ⚠️ Lancement malgré l'erreur de chargement API (UseApiData=false)");
        }

        // 🧹 CORRECTION OOM WebGL: Nettoyer la mémoire avant de charger la scène Player
#if UNITY_WEBGL && !UNITY_EDITOR
        Debug.Log("[MainMenuManager] 🧹 WebGL - Nettoyage mémoire avant chargement Player...");
        
        // Purger les caches persistants
        try { if (RemoteSpriteCache.GetExisting() != null) RemoteSpriteCache.GetExisting().ClearAll(); } catch { }
        try { if (GameDataManager.Instance != null) GameDataManager.Instance.ClearAssetCache(); } catch { }
        
        // Attendre un frame pour que les Destroy() prennent effet
        yield return null;
        yield return new WaitForEndOfFrame();
        
        // Libérer les assets non référencés
        yield return Resources.UnloadUnusedAssets();
        try { System.GC.Collect(); } catch { }
        
        yield return null;
        Debug.Log("[MainMenuManager] ✅ Nettoyage mémoire WebGL terminé");
#else
        yield return null;
#endif

        UnityEngine.SceneManagement.SceneManager.LoadScene("Player");
    }
    
    /// <summary>
    /// Charge les jeux depuis le fichier CSV (ancienne méthode, conservée pour rétrocompatibilité)
    /// </summary>
    private IEnumerator LoadGamesFromCSV()
    {
        Debug.Log($"[MainMenuManager] 📄 Chargement du CSV: {csvPath}");

        string csvContent = "";

        // Charger le fichier CSV
        if (csvPath.Contains("://") || csvPath.Contains(":///"))
        {
            // Sur Android/WebGL, utiliser UnityWebRequest
            using (UnityWebRequest www = UnityWebRequest.Get(csvPath))
            {
                yield return www.SendWebRequest();

                if (www.result != UnityWebRequest.Result.Success)
                {
                    Debug.LogError($"[MainMenuManager] ❌ Erreur chargement CSV: {www.error}");
                    yield break;
                }

                csvContent = www.downloadHandler.text;
            }
        }
        else
        {
            // Sur desktop, lecture directe
            if (!File.Exists(csvPath))
            {
                Debug.LogError($"[MainMenuManager] ❌ Fichier CSV introuvable: {csvPath}");
                yield break;
            }

            csvContent = File.ReadAllText(csvPath);
        }

        // Parser le CSV
        ParseCSV(csvContent);

        // Générer les cartes
        GenerateGameCards();

        // Gate: si config invalide, désactiver les clics et afficher un message
        ApplyCardsInteractivity(IsGeneralConfigUsable());
        UpdateConfigGateUI();

        // Mettre à jour le layout
        yield return StartCoroutine(UpdateLayoutWithDelay());
    }

    /// <summary>
    /// Parse le contenu CSV et remplit la liste des jeux
    /// Format: id,title,type
    /// </summary>
    private void ParseCSV(string csvContent)
    {
        gameEntries.Clear();

        // Supprimer le BOM UTF-8 si présent
        if (csvContent.Length > 0 && csvContent[0] == '\uFEFF')
        {
            csvContent = csvContent.Substring(1);
            Debug.Log("[MainMenuManager] 📄 BOM UTF-8 supprimé");
        }

        string[] lines = csvContent.Split(new[] { '\r', '\n' }, System.StringSplitOptions.RemoveEmptyEntries);

        Debug.Log($"[MainMenuManager] 📊 {lines.Length} lignes dans le CSV");
        
        // Afficher les premières lignes pour debug
        for (int dbg = 0; dbg < Mathf.Min(3, lines.Length); dbg++)
        {
            Debug.Log($"[MainMenuManager] 📄 Ligne {dbg}: '{lines[dbg]}'");
        }

        for (int i = 0; i < lines.Length; i++)
        {
            string line = lines[i].Trim();
            
            // Ignorer les lignes vides
            if (string.IsNullOrEmpty(line)) continue;

            // Ignorer la ligne d'en-tête
            if (i == 0 && (line.ToLower().Contains("\"id\"") || line.StartsWith("id,") || line.StartsWith("\"id\"")))
            {
                Debug.Log($"[MainMenuManager] ⏭️ Ligne d'en-tête ignorée: {line}");
                continue;
            }

            // Parser la ligne CSV (gestion des guillemets)
            List<string> fields = ParseCSVLine(line);

            if (fields.Count >= 3)
            {
                string idStr = fields[0].Trim().Trim('"');
                
                if (int.TryParse(idStr, out int id))
                {
                    GameEntry entry = new GameEntry
                    {
                        id = id,
                        title = fields[1].Trim('"'),
                        type = fields[2].Trim('"')
                    };

                    gameEntries.Add(entry);

                    // Toujours logger les 3 premiers pour debug
                    if (gameEntries.Count <= 3 || debugMode)
                        Debug.Log($"[MainMenuManager] ✅ Jeu {gameEntries.Count}: ID={entry.id}, '{entry.title}' ({entry.type})");
                }
                else
                {
                    Debug.LogWarning($"[MainMenuManager] ⚠️ ID invalide ligne {i + 1}: '{idStr}' (raw: '{fields[0]}')");
                }
            }
            else
            {
                Debug.LogWarning($"[MainMenuManager] ⚠️ Ligne {i + 1} n'a que {fields.Count} champs: '{line}'");
            }
        }

        Debug.Log($"[MainMenuManager] ✅ {gameEntries.Count} jeux chargés depuis le CSV");
    }

    /// <summary>
    /// Parse une ligne CSV en gérant les guillemets
    /// </summary>
    private List<string> ParseCSVLine(string line)
    {
        List<string> fields = new List<string>();
        bool inQuotes = false;
        string currentField = "";

        for (int i = 0; i < line.Length; i++)
        {
            char c = line[i];

            if (c == '"')
            {
                inQuotes = !inQuotes;
            }
            else if (c == ',' && !inQuotes)
            {
                fields.Add(currentField);
                currentField = "";
            }
            else
            {
                currentField += c;
            }
        }

        // Ajouter le dernier champ
        fields.Add(currentField);

        return fields;
    }

    /// <summary>
    /// Génère les cartes de jeu dans la mosaïque
    /// </summary>
    private void GenerateGameCards()
    {
        ClearGameCards();

        if (levelsContainer == null)
        {
            Debug.LogError("[MainMenuManager] levelsContainer est null !");
            return;
        }

        foreach (var entry in gameEntries)
        {
            if (entry == null) continue;
            
            GameObject cardObj = CreateGameCard(entry);
            if (cardObj != null)
            {
                gameCards.Add(cardObj);
            }
        }

        Debug.Log($"[MainMenuManager] ✅ {gameCards.Count} cartes générées sur {gameEntries.Count} jeux");
    }

    private bool IsGeneralConfigUsable()
    {
        return GeneralConfigManager.Instance != null &&
               GeneralConfigManager.Instance.IsConfigLoaded() &&
               GeneralConfigManager.Instance.HasValidApiUrls();
    }

    private void ApplyCardsInteractivity(bool enabled)
    {
        foreach (var cardObj in gameCards)
        {
            if (cardObj == null) continue;

            var btn = cardObj.GetComponent<Button>();
            if (btn != null) btn.interactable = enabled;

            var cg = cardObj.GetComponent<CanvasGroup>();
            if (cg != null)
            {
                cg.alpha = enabled ? 1f : 0.5f;
                cg.blocksRaycasts = enabled;
                cg.interactable = enabled;
            }
        }
    }

    private void UpdateConfigGateUI()
    {
        if (loadingPanel == null) return;

        bool ok = IsGeneralConfigUsable();
        if (ok)
        {
            if (retryConfigButton != null) retryConfigButton.gameObject.SetActive(false);
            return;
        }

        loadingPanel.SetActive(true);
        if (loadingText != null)
        {
            loadingText.text =
                "Configuration non chargée.\n\n" +
                "Ceci arrive généralement à cause du cache navigateur/serveur.\n" +
                "Clique sur « Réessayer » ou fais un rechargement forcé (Ctrl+F5).";
        }

        EnsureRetryButtonExists();
        if (retryConfigButton != null)
        {
            retryConfigButton.gameObject.SetActive(true);
            retryConfigButton.interactable = true;
        }

        ApplyCardsInteractivity(false);
    }

    private void EnsureRetryButtonExists()
    {
        if (retryConfigButton != null || loadingPanel == null) return;

        GameObject btnObj = new GameObject("RetryConfigButton");
        btnObj.transform.SetParent(loadingPanel.transform, false);

        var rt = btnObj.AddComponent<RectTransform>();
        rt.anchorMin = new Vector2(0.5f, 0.5f);
        rt.anchorMax = new Vector2(0.5f, 0.5f);
        rt.pivot = new Vector2(0.5f, 0.5f);
        rt.sizeDelta = new Vector2(260, 60);
        rt.anchoredPosition = new Vector2(0, -90);

        var img = btnObj.AddComponent<Image>();
        img.color = new Color(0.15f, 0.55f, 0.9f, 1f);

        retryConfigButton = btnObj.AddComponent<Button>();
        retryConfigButton.targetGraphic = img;
        retryConfigButton.transition = Selectable.Transition.ColorTint;

        // Texte
        GameObject labelObj = new GameObject("Label");
        labelObj.transform.SetParent(btnObj.transform, false);
        var labelRt = labelObj.AddComponent<RectTransform>();
        labelRt.anchorMin = Vector2.zero;
        labelRt.anchorMax = Vector2.one;
        labelRt.offsetMin = Vector2.zero;
        labelRt.offsetMax = Vector2.zero;

        var tmp = labelObj.AddComponent<TextMeshProUGUI>();
        tmp.text = "RÉESSAYER";
        tmp.alignment = TextAlignmentOptions.Center;
        tmp.fontSize = 22;
        tmp.color = Color.white;
        // Assurer une police visible même si la config générale n'est pas chargée
        if (tmp.font == null)
        {
            tmp.font = Resources.Load<TMP_FontAsset>("Fonts/Lato-Regular SDF") ?? TMP_Settings.defaultFontAsset;
        }

        retryConfigButton.onClick.RemoveAllListeners();
        retryConfigButton.onClick.AddListener(() =>
        {
            Debug.Log("[MainMenuManager] 🔄 Réessayer: ReloadConfig()");
            retryConfigButton.interactable = false;
            if (loadingText != null) loadingText.text = "Rechargement de la configuration…";

            if (GeneralConfigManager.Instance != null)
            {
                GeneralConfigManager.Instance.ReloadConfig();
                StartCoroutine(WaitForConfigThenEnableMenu());
            }
        });
    }

    private IEnumerator WaitForConfigThenEnableMenu()
    {
        if (GeneralConfigManager.Instance != null)
        {
            yield return GeneralConfigManager.Instance.WaitForConfigLoaded();
        }

        bool ok = IsGeneralConfigUsable();
        if (ok)
        {
            ApplyCardsInteractivity(true);
            if (loadingPanel != null) loadingPanel.SetActive(false);
        }
        else
        {
            UpdateConfigGateUI();
        }
    }

    /// <summary>
    /// Crée une carte de jeu programmatiquement
    /// </summary>
    private GameObject CreateGameCard(GameEntry entry)
    {
        if (entry == null)
        {
            Debug.LogError("[MainMenuManager] ❌ Entry est null dans CreateGameCard!");
            return null;
        }

        try
        {
            // Si c'est un séparateur, créer une case vide et inactive
            if (entry.isSeparator)
            {
                GameObject separatorObj = new GameObject("Separator");
                separatorObj.transform.SetParent(levelsContainer, false);

                RectTransform rt = separatorObj.AddComponent<RectTransform>();
                rt.sizeDelta = cellSize;
                rt.localScale = Vector3.one;

                // Image de fond transparente
                Image separatorBgImage = separatorObj.AddComponent<Image>();
                separatorBgImage.color = new Color(0, 0, 0, 0); // Transparent
                separatorBgImage.raycastTarget = false;

                // CanvasGroup pour désactiver l'interaction
                CanvasGroup separatorCanvasGroup = separatorObj.AddComponent<CanvasGroup>();
                separatorCanvasGroup.alpha = 0.3f;
                separatorCanvasGroup.blocksRaycasts = false;
                separatorCanvasGroup.interactable = false;

                return separatorObj;
            }
            
            // Créer l'objet carte
            GameObject cardObj = new GameObject($"GameCard_{entry.id}");
            cardObj.transform.SetParent(levelsContainer, false);

            // RectTransform
            RectTransform cardRt = cardObj.AddComponent<RectTransform>();
            cardRt.sizeDelta = cellSize;
            cardRt.localScale = Vector3.one;

            // Image de fond (avant CanvasGroup et Button)
            Image bgImage = cardObj.AddComponent<Image>();
            bgImage.color = GetTypeColor(entry);
            // ✅ raycastTarget = false pour ne PAS bloquer le scroll
            // Les boutons JOUER et ADMIN ont leur propre raycastTarget
            bgImage.raycastTarget = false;

            // CanvasGroup pour les effets
            CanvasGroup canvasGroup = cardObj.AddComponent<CanvasGroup>();

            // NE PAS ajouter de Button à la carte entière - les boutons sont maintenant séparés (JOUER et ADMIN)

            // Container pour le contenu
            GameObject contentContainer = new GameObject("Content");
            contentContainer.transform.SetParent(cardObj.transform, false);
            RectTransform contentRt = contentContainer.AddComponent<RectTransform>();
            contentRt.anchorMin = Vector2.zero;
            contentRt.anchorMax = Vector2.one;
            contentRt.offsetMin = new Vector2(5, 5);
            contentRt.offsetMax = new Vector2(-5, -5);

            // Titre du jeu (partie haute)
            GameObject titleObj = new GameObject("Title");
            titleObj.transform.SetParent(contentContainer.transform, false);
            RectTransform titleRt = titleObj.AddComponent<RectTransform>();
            titleRt.anchorMin = new Vector2(0, 0.55f);
            titleRt.anchorMax = new Vector2(1, 1);
            titleRt.offsetMin = Vector2.zero;
            titleRt.offsetMax = Vector2.zero;

            TextMeshProUGUI titleText = titleObj.AddComponent<TextMeshProUGUI>();
            titleText.text = entry.title;
            titleText.fontSize = titleFontSize - 2; // Un peu plus petit pour laisser de la place
            titleText.color = Color.white;
            titleText.alignment = TextAlignmentOptions.Center;
            titleText.textWrappingMode = TextWrappingModes.Normal;
            titleText.overflowMode = TextOverflowModes.Ellipsis;

            // Zone info (position/type/id)
            GameObject typeObj = new GameObject("Info");
            typeObj.transform.SetParent(contentContainer.transform, false);
            RectTransform typeRt = typeObj.AddComponent<RectTransform>();
            typeRt.anchorMin = new Vector2(0, 0.35f);
            typeRt.anchorMax = new Vector2(1, 0.55f);
            typeRt.offsetMin = Vector2.zero;
            typeRt.offsetMax = Vector2.zero;

            TextMeshProUGUI typeText = typeObj.AddComponent<TextMeshProUGUI>();
            
            // Afficher la position pour les jeux normaux, le type pour intro/outro
            if (entry.isIntro || entry.isOutro)
            {
                typeText.text = entry.GetDisplayType();
            }
            else if (entry.positionInQuest > 0 && entry.totalGamesInQuest > 0)
            {
                typeText.text = $"{entry.positionInQuest}/{entry.totalGamesInQuest}";
            }
            else
            {
                typeText.text = entry.GetDisplayType();
            }
            
            typeText.fontSize = typeFontSize - 2;
            typeText.color = new Color(1f, 1f, 1f, 0.7f);
            typeText.alignment = TextAlignmentOptions.Center;

            // Container pour les boutons (partie basse)
            GameObject buttonsContainer = new GameObject("ButtonsContainer");
            buttonsContainer.transform.SetParent(contentContainer.transform, false);
            RectTransform buttonsRt = buttonsContainer.AddComponent<RectTransform>();
            buttonsRt.anchorMin = new Vector2(0, 0);
            buttonsRt.anchorMax = new Vector2(1, 0.35f);
            buttonsRt.offsetMin = Vector2.zero;
            buttonsRt.offsetMax = Vector2.zero;

            // Bouton JOUER (à gauche)
            GameObject playButtonObj = new GameObject("PlayButton");
            playButtonObj.transform.SetParent(buttonsContainer.transform, false);
            RectTransform playBtnRt = playButtonObj.AddComponent<RectTransform>();
            playBtnRt.anchorMin = new Vector2(0, 0);
            playBtnRt.anchorMax = new Vector2(0.48f, 1);
            playBtnRt.offsetMin = Vector2.zero;
            playBtnRt.offsetMax = Vector2.zero;

            Image playBtnImg = playButtonObj.AddComponent<Image>();
            playBtnImg.color = new Color(1f, 1f, 1f, 0.1f); // Transparent avec légère teinte blanche

            // Ajouter une bordure blanche
            Outline playOutline = playButtonObj.AddComponent<Outline>();
            playOutline.effectColor = Color.white;
            playOutline.effectDistance = new Vector2(2, -2);

            Button playBtn = playButtonObj.AddComponent<Button>();
            playBtn.targetGraphic = playBtnImg;
            
            // Configurer les transitions du bouton
            ColorBlock playColors = playBtn.colors;
            playColors.normalColor = new Color(1f, 1f, 1f, 0.1f);
            playColors.highlightedColor = new Color(1f, 1f, 1f, 0.3f); // Plus visible au survol
            playColors.pressedColor = new Color(1f, 1f, 1f, 0.5f);
            playBtn.colors = playColors;
            
            playBtn.onClick.AddListener(() => {
                // Lancer le jeu (même logique que GameCard.OnCardClicked)
                if (entry != null && entry.id > 0)
                {
                    LaunchGameFromCard(entry);
                }
            });

            GameObject playLabelObj = new GameObject("Label");
            playLabelObj.transform.SetParent(playButtonObj.transform, false);
            RectTransform playLabelRt = playLabelObj.AddComponent<RectTransform>();
            playLabelRt.anchorMin = Vector2.zero;
            playLabelRt.anchorMax = Vector2.one;
            playLabelRt.offsetMin = Vector2.zero;
            playLabelRt.offsetMax = Vector2.zero;

            TextMeshProUGUI playLabel = playLabelObj.AddComponent<TextMeshProUGUI>();
            playLabel.text = "JOUER";
            playLabel.fontSize = 14;
            playLabel.color = Color.white;
            playLabel.alignment = TextAlignmentOptions.Center;
            playLabel.fontStyle = FontStyles.Bold;

            // Bouton ADMIN (à droite)
            GameObject adminButtonObj = new GameObject("AdminButton");
            adminButtonObj.transform.SetParent(buttonsContainer.transform, false);
            RectTransform adminBtnRt = adminButtonObj.AddComponent<RectTransform>();
            adminBtnRt.anchorMin = new Vector2(0.52f, 0);
            adminBtnRt.anchorMax = new Vector2(1, 1);
            adminBtnRt.offsetMin = Vector2.zero;
            adminBtnRt.offsetMax = Vector2.zero;

            Image adminBtnImg = adminButtonObj.AddComponent<Image>();
            adminBtnImg.color = new Color(1f, 1f, 1f, 0.1f); // Transparent avec légère teinte blanche

            // Ajouter une bordure blanche
            Outline adminOutline = adminButtonObj.AddComponent<Outline>();
            adminOutline.effectColor = Color.white;
            adminOutline.effectDistance = new Vector2(2, -2);

            Button adminBtn = adminButtonObj.AddComponent<Button>();
            adminBtn.targetGraphic = adminBtnImg;
            
            // Configurer les transitions du bouton
            ColorBlock adminColors = adminBtn.colors;
            adminColors.normalColor = new Color(1f, 1f, 1f, 0.1f);
            adminColors.highlightedColor = new Color(1f, 1f, 1f, 0.3f); // Plus visible au survol
            adminColors.pressedColor = new Color(1f, 1f, 1f, 0.5f);
            adminBtn.colors = adminColors;
            
            adminBtn.onClick.AddListener(() => {
                // Ouvrir l'URL admin
                if (entry != null && entry.id > 0)
                {
                    string adminUrl = $"https://www.newsassurancespro.com/administration/annie/ujsa/ujsa-default/quests/{entry.questId}/steps/{entry.stepId}/zones/{entry.zoneId}/game";
                    Debug.Log($"[MainMenuManager] Ouverture URL admin: {adminUrl}");
                    Application.OpenURL(adminUrl);
                }
            });

            GameObject adminLabelObj = new GameObject("Label");
            adminLabelObj.transform.SetParent(adminButtonObj.transform, false);
            RectTransform adminLabelRt = adminLabelObj.AddComponent<RectTransform>();
            adminLabelRt.anchorMin = Vector2.zero;
            adminLabelRt.anchorMax = Vector2.one;
            adminLabelRt.offsetMin = Vector2.zero;
            adminLabelRt.offsetMax = Vector2.zero;

            TextMeshProUGUI adminLabel = adminLabelObj.AddComponent<TextMeshProUGUI>();
            adminLabel.text = "ADMIN";
            adminLabel.fontSize = 14;
            adminLabel.color = Color.white;
            adminLabel.alignment = TextAlignmentOptions.Center;
            adminLabel.fontStyle = FontStyles.Bold;

            // Ajouter le composant GameCard EN DERNIER (uniquement pour les effets visuels)
            GameCard gameCard = cardObj.AddComponent<GameCard>();
            gameCard.titleText = titleText;
            gameCard.typeText = typeText;
            gameCard.backgroundImage = bgImage;
            gameCard.cardButton = null; // Pas de bouton sur la carte entière
            gameCard.shootingColor = shootingColor;
            gameCard.textHoleColor = textHoleColor;
            gameCard.calculatorColor = calculatorColor;
            gameCard.introOutroColor = introOutroColor;
            gameCard.defaultColor = defaultColor;

            // Initialiser la carte avec les données
            gameCard.Initialize(entry);

            return cardObj;
        }
        catch (System.Exception e)
        {
            Debug.LogError($"[MainMenuManager] ❌ Erreur création carte pour {entry?.title}: {e.Message}");
            Debug.LogError($"[MainMenuManager] Stack: {e.StackTrace}");
            return null;
        }
    }

    /// <summary>
    /// Retourne la couleur basée sur la quête et le type de jeu
    /// </summary>
    private Color GetTypeColor(GameEntry entry)
    {
        // Obtenir la couleur de base pour cette quête
        Color baseColor = GetQuestBaseColor(entry.questId);
        
        // Appliquer une variante selon le type de jeu
        return GetColorVariant(baseColor, entry.type);
    }
    
    /// <summary>
    /// Retourne une couleur de base pour une quête donnée
    /// </summary>
    private Color GetQuestBaseColor(int questId)
    {
        // Palette de 6 couleurs de base
        Color[] palette = new Color[]
        {
            new Color(0.45f, 0.30f, 0.60f, 1f), // Violet
            new Color(0.25f, 0.55f, 0.70f, 1f), // Bleu
            new Color(0.35f, 0.65f, 0.40f, 1f), // Vert
            new Color(0.85f, 0.50f, 0.25f, 1f), // Orange
            new Color(0.70f, 0.30f, 0.45f, 1f), // Rose/Rouge
            new Color(0.55f, 0.60f, 0.35f, 1f)  // Vert olive
        };
        
        int colorIndex = questId % palette.Length;
        return palette[colorIndex];
    }
    
    /// <summary>
    /// Applique une variante à une couleur de base selon le type de jeu
    /// </summary>
    private Color GetColorVariant(Color baseColor, string type)
    {
        float factor = 1f;
        
        switch (type?.ToLower())
        {
            case "shooting":
                factor = 1.0f; // Couleur normale (moyenne)
                break;
                
            case "text_hole":
            case "trous":
                factor = 1.2f; // Plus clair
                break;
                
            case "calculator":
                factor = 0.8f; // Plus foncé
                break;
                
            default:
                factor = 1.0f;
                break;
        }
        
        return new Color(
            Mathf.Clamp01(baseColor.r * factor),
            Mathf.Clamp01(baseColor.g * factor),
            Mathf.Clamp01(baseColor.b * factor),
            1f
        );
    }

    /// <summary>
    /// Supprime toutes les cartes existantes
    /// </summary>
    private void ClearGameCards()
    {
        foreach (var card in gameCards)
        {
            if (card != null) Destroy(card);
        }
        gameCards.Clear();
    }

    // ========================================
    // SCROLL & LAYOUT
    // ========================================

    void SetupScrollRect()
    {
        if (scrollRect == null)
        {
            Debug.LogError("[MainMenuManager] ScrollRect non assigné.");
            return;
        }

        scrollRect.content = levelsContainer;
        if (viewport != null) scrollRect.viewport = viewport;

        if (viewport != null && viewport.GetComponent<RectMask2D>() == null)
            viewport.gameObject.AddComponent<RectMask2D>();

        scrollRect.horizontal = false;
        scrollRect.vertical = true;
        scrollRect.movementType = ScrollRect.MovementType.Clamped;
        scrollRect.inertia = true;
        
        // 🖱️ Sensibilité augmentée en WebGL pour un meilleur support du trackpad Mac
#if UNITY_WEBGL && !UNITY_EDITOR
        scrollRect.scrollSensitivity = 50f; // Double sensibilité pour trackpad
        scrollRect.inertia = true; // Garder l'inertie pour une sensation fluide
#else
        scrollRect.scrollSensitivity = 25f;
#endif
        scrollRect.decelerationRate = 0.135f;

        if (verticalScrollbar != null)
        {
            scrollRect.verticalScrollbar = verticalScrollbar;
#if UNITY_2021_1_OR_NEWER
            scrollRect.verticalScrollbarVisibility = ScrollRect.ScrollbarVisibility.AutoHideAndExpandViewport;
#endif
        }
        
        // 🖱️ IMPORTANT : Ajouter une Image sur le viewport pour capturer les événements de scroll
        // Sans cela, le scroll ne fonctionne pas car les cartes enfants bloquent les événements
        if (viewport != null)
        {
            Image viewportImage = viewport.GetComponent<Image>();
            if (viewportImage == null)
            {
                viewportImage = viewport.gameObject.AddComponent<Image>();
            }
            viewportImage.color = new Color(0, 0, 0, 0); // Transparent
            viewportImage.raycastTarget = true; // ✅ CRUCIAL pour capturer le scroll
            
            Debug.Log("[MainMenuManager] ✅ Viewport Image ajoutée pour capturer le scroll (molette + trackpad)");
        }
        
        // 🖱️ WEBGL SCROLL FIX: Ajouter le handler JavaScript pour capturer le scroll en WebGL
#if UNITY_WEBGL && !UNITY_EDITOR
        if (scrollRect.GetComponent<WebGLScrollHandler>() == null)
        {
            var handler = scrollRect.gameObject.AddComponent<WebGLScrollHandler>();
            handler.scrollSpeed = 1000f; // Sensibilité élevée pour un scroll fluide (plus c'est haut, plus c'est lent)
            handler.debugLogs = false; // Désactiver les logs maintenant que ça fonctionne
            Debug.Log("[MainMenuManager] 🖱️ WebGLScrollHandler ajouté (scroll fluide activé)");
        }
#endif
        
        Debug.Log($"[MainMenuManager] ScrollRect configuré - Sensibilité: {scrollRect.scrollSensitivity}");
    }

    IEnumerator UpdateLayoutWithDelay()
    {
        yield return new WaitForEndOfFrame();
        yield return new WaitForEndOfFrame();

        UpdateContentHeight();
        ScrollToTop();

        LayoutRebuilder.ForceRebuildLayoutImmediate(levelsContainer);
        Canvas.ForceUpdateCanvases();
    }

    void UpdateContentHeight()
    {
        if (grid == null || levelsContainer == null) return;

        int items = gameCards.Count;
        if (items == 0)
        {
            levelsContainer.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 100f);
            return;
        }

        int cols = Mathf.Max(1, columns);
        int rows = Mathf.CeilToInt(items / (float)cols);

        float totalHeight = padding.top
                          + rows * cellSize.y
                          + Mathf.Max(0, rows - 1) * spacing.y
                          + padding.bottom;

        float viewportHeight = viewport ? viewport.rect.height : 600f;
        float minHeight = Mathf.Max(totalHeight, viewportHeight + 50f);

        levelsContainer.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, minHeight);
        LayoutRebuilder.ForceRebuildLayoutImmediate(levelsContainer);

        if (scrollRect != null)
        {
            scrollRect.Rebuild(CanvasUpdate.PostLayout);
        }
    }

    void ScrollToTop()
    {
        if (scrollRect != null)
        {
            scrollRect.verticalNormalizedPosition = 1f;
            scrollRect.velocity = Vector2.zero;
        }
    }

    // ========================================
    // HEADER & LOGIN
    // ========================================

    private IEnumerator CreateHeader()
    {
        Canvas canvas = FindFirstObjectByType<Canvas>();
        if (canvas == null)
        {
            Debug.LogError("[MainMenuManager] Canvas introuvable pour créer le header");
            yield break;
        }

        DefaultHeaderConfig headerConfig = GeneralConfigManager.Instance.GetDefaultHeaderConfig();
        if (headerConfig == null)
        {
            Debug.LogError("[MainMenuManager] Configuration header introuvable");
            yield break;
        }

        if (UnityEngine.EventSystems.EventSystem.current == null)
        {
            GameObject eventSystemObj = new GameObject("EventSystem");
            eventSystemObj.AddComponent<UnityEngine.EventSystems.EventSystem>();
#if ENABLE_INPUT_SYSTEM
            eventSystemObj.AddComponent<UnityEngine.InputSystem.UI.InputSystemUIInputModule>();
#else
            eventSystemObj.AddComponent<UnityEngine.EventSystems.StandaloneInputModule>();
#endif
        }

        if (canvas.GetComponent<GraphicRaycaster>() == null)
        {
            canvas.gameObject.AddComponent<GraphicRaycaster>();
        }

        // Pas de callback pour le header dans la scène MENU (pas de bouton paramètres)

        GameObject headerPanel = new GameObject("HeaderPanel");
        headerPanel.transform.SetParent(canvas.transform, false);

        RectTransform headerRect = headerPanel.AddComponent<RectTransform>();
        headerRect.anchorMin = new Vector2(0f, 1f);
        headerRect.anchorMax = new Vector2(1f, 1f);
        headerRect.pivot = new Vector2(0.5f, 1f);
        headerRect.sizeDelta = new Vector2(0f, headerConfig.height);
        headerRect.anchoredPosition = Vector2.zero;

        Image headerBg = headerPanel.AddComponent<Image>();
        if (ColorUtility.TryParseHtmlString(headerConfig.backgroundColor, out Color bgColor))
        {
            headerBg.color = bgColor;
        }
        headerBg.raycastTarget = false;

        // NE PAS créer le bouton des paramètres dans la scène MENU
        Debug.Log("[MainMenuManager] Header créé sans bouton paramètres");
    }

    private IEnumerator WaitForSettingsManagerAndOpen()
    {
        yield return null;
        if (SettingsManager.Instance != null)
        {
            SettingsManager.Instance.OpenSettings();
        }
    }

    private IEnumerator CheckLoginStatusAndShowPopup()
    {
        yield return new WaitForSeconds(0.5f);

        // Si l'utilisateur vient de cliquer "Se déconnecter", on FORCE l'affichage de la popup
        // même en iframe (sinon un token parent résiduel peut supprimer la popup).
        if (PlayerPrefs.GetInt("ForceShowLoginPopup", 0) == 1)
        {
            PlayerPrefs.DeleteKey("ForceShowLoginPopup");
            PlayerPrefs.Save();
            Debug.Log("[MainMenuManager] 🔐 ForceShowLoginPopup=1 -> affichage popup");
            ShowLoginPopup();
            yield break;
        }
        
        // Si on est dans une iframe, attendre le token et l'authentification (max 5 secondes)
        if (PostMessageBridge.Instance != null && PostMessageBridge.Instance.IsInIframe)
        {
            Debug.Log("[MainMenuManager] 📡 Dans une iframe - attente du token parent...");
            float timeout = 5f;
            float elapsed = 0f;
            
            while (elapsed < timeout)
            {
                if (UserDataManager.Instance != null && UserDataManager.Instance.IsLoggedIn())
                {
                    Debug.Log($"[MainMenuManager] ✓ Utilisateur connecté après {elapsed:F1}s");
                    break;
                }
                
                if (IframeAuthManager.Instance != null && IframeAuthManager.Instance.IsAuthenticatedViaIframe)
                {
                    Debug.Log($"[MainMenuManager] ✓ Authentifié via iframe après {elapsed:F1}s");
                    break;
                }
                
                bool hasToken = PostMessageBridge.Instance.HasToken || PostMessageBridge.Instance.HasPendingToken;
                bool isAuthenticating = IframeAuthManager.Instance != null && IframeAuthManager.Instance.IsAuthenticating;
                
                if (!hasToken && !isAuthenticating && elapsed > 2f)
                {
                    Debug.Log($"[MainMenuManager] ⚠ Aucun token reçu après {elapsed:F1}s - affichage popup");
                    break;
                }
                
                yield return new WaitForSeconds(0.1f);
                elapsed += 0.1f;
            }
        }

        if (UserDataManager.Instance == null || !UserDataManager.Instance.IsLoggedIn())
        {
            if (IframeAuthManager.Instance != null && IframeAuthManager.Instance.IsAuthenticatedViaIframe)
            {
                Debug.Log("[MainMenuManager] ✓ Utilisateur authentifié via iframe - popup non affichée");
            }
            // Si on est dans une iframe et qu'on a un token (même si la vérification a échoué),
            // ne pas afficher la popup - le token pourrait être valide mais le serveur a un problème temporaire
            else if (PostMessageBridge.Instance != null && PostMessageBridge.Instance.IsInIframe && 
                     (PostMessageBridge.Instance.HasToken || PostMessageBridge.Instance.HasPendingToken))
            {
                Debug.Log("[MainMenuManager] ⚠ Token présent mais vérification échouée - popup non affichée (erreur serveur possible)");
            }
            else
            {
                Debug.Log("[MainMenuManager] ⚠️ Utilisateur non connecté - Affichage du panneau d'identification");
                ShowLoginPopup();
            }
        }
        else
        {
            Debug.Log("[MainMenuManager] ✅ Utilisateur déjà connecté");
        }
    }

    private void ShowLoginPopup()
    {
        LoginPopup existingPopup = FindFirstObjectByType<LoginPopup>();
        if (existingPopup != null) return;

        GameObject popupObj = new GameObject("LoginPopup");
        LoginPopup loginPopup = popupObj.AddComponent<LoginPopup>();
        loginPopup.Initialize(
            onSuccess: () => Debug.Log("[MainMenuManager] ✅ Connexion réussie"),
            onCloseCallback: () => Debug.Log("[MainMenuManager] Popup fermée")
        );
        loginPopup.Show();
    }

    // ========================================
    // PUBLIC METHODS
    // ========================================

    public void RefreshMenu()
    {
        StartCoroutine(LoadGamesFromAPI());
    }

    public void SetColumns(int col)
    {
        columns = Mathf.Max(1, col);
        if (grid != null) grid.constraintCount = columns;
        GenerateGameCards();
        StartCoroutine(UpdateLayoutWithDelay());
    }

    public void SetCellSize(float w, float h)
    {
        cellSize = new Vector2(w, h);
        if (grid != null) grid.cellSize = cellSize;
        GenerateGameCards();
        StartCoroutine(UpdateLayoutWithDelay());
    }

    // ========================================
    // INPUT
    // ========================================

    void Update()
    {
        var k = UnityEngine.InputSystem.Keyboard.current;
        if (k == null) return;

        // ESC pour quitter
        if (k.escapeKey.wasPressedThisFrame)
        {
            Debug.Log("[MainMenuManager] 🚪 ESC - Quitter le jeu");
            QuitApplication();
            return;
        }

        // Debug keys
        if (!debugMode) return;

        if (k.rKey.wasPressedThisFrame) RefreshMenu();
        if (k.digit1Key.wasPressedThisFrame) SetColumns(1);
        if (k.digit2Key.wasPressedThisFrame) SetColumns(2);
        if (k.digit3Key.wasPressedThisFrame) SetColumns(3);
        if (k.digit4Key.wasPressedThisFrame) SetColumns(4);
        if (k.digit5Key.wasPressedThisFrame) SetColumns(5);
        if (k.digit6Key.wasPressedThisFrame) SetColumns(6);
    }

    void QuitApplication()
    {
#if UNITY_EDITOR
        UnityEditor.EditorApplication.isPlaying = false;
#else
        Application.Quit();
#endif
    }
}
