using System.Collections;
using System.Collections.Generic;
using System;
using UnityEngine;
using UnityEngine.Networking;
using TMPro;
using UnityEngine.UI;
using UnityEngine.Video;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.UI;
using UnityEngine.SceneManagement;
using UnityEngine.EventSystems;

public class TrousGameManager : MonoBehaviour
{
    [Header("Configuration")]
    public string configUrl = "";
    
    [Header("UI References")]
    public TextMeshProUGUI questionText;
    public GameObject feedbackPanel;
    public TextMeshProUGUI feedbackText;
    public Transform textContainer;
    public GameObject dropdownPrefab; // Legacy - conservé pour compatibilité
    
    [Header("New UI Components")]
    public GameObject validationButtonPrefab; // Optionnel - peut être créé dynamiquement
    
    [Header("Game Objects")]
    public Camera mainCamera;
    public VideoPlayer backgroundVideo;
    public Image backgroundImage;
    public AudioSource audioSource;
    
    [Header("Text Configuration")]
    public GameObject textPanel;
    public ScrollRect textScrollRect;
    
    [Header("Layout")]
    private TrousGameLayout gameLayout;
    
    // Variables privées
    private int currentQuestionIndex = 0;
    private List<TrousQuestionData> questions = new List<TrousQuestionData>();
    private bool jsonLoaded = false;
    private TrousGameConfig cachedGameConfig;
    private Dictionary<string, Sprite> loadedSprites = new Dictionary<string, Sprite>();
    private Dictionary<string, AudioClip> loadedAudioClips = new Dictionary<string, AudioClip>();
    
    [Header("Feedback Background")]
    private Sprite feedbackSuccessSprite;
    private Sprite feedbackFailureSprite;
    private Image feedbackBackgroundImage;
    
    // Listes pour tracker les ressources créées dynamiquement (nettoyage WebGL)
    private readonly List<Texture2D> dynamicTextures = new List<Texture2D>();
    private readonly List<Sprite> dynamicSprites = new List<Sprite>();
    
    [Header("Font Assets")]
    public TMP_FontAsset customFont;
    
    // Variables pour le système multi-scènes
    private string dBeforeUrl = null;
    private string dSuccessUrl = null;
    private string dFailUrl = null;
    
    // Variables pour les trous (nouvelle ergonomie)
    private List<TrouButton> activeTrouButtons = new List<TrouButton>();
    private ValidationButton validationButton;
    private SelectionPopup currentPopup;
    private TrousQuestionData currentQuestion;
    private bool isInitializing = false; // Flag pour éviter la double initialisation
    
    // Variables pour l'API answers
    private List<(int questionId, int answerOptionId)> lastAnswers = new List<(int, int)>();
    
    // Flag pour indiquer qu'on charge via l'API
    private bool loadingFromApi = false;
    
    void Awake()
    {
        // Créer ou récupérer le layout AVANT tout le reste
        InitializeLayout();
    }
    
    void Start()
    {
        // Si déjà en cours d'initialisation via LoadGameFromURL ou API, ne pas recharger
        if (isInitializing || jsonLoaded || loadingFromApi)
        {
            Debug.Log("[TrousGameManager] Initialisation déjà en cours ou terminée");
            return;
        }
        
        // Lancer la coroutine d'initialisation qui attend les données API si nécessaire
        StartCoroutine(InitializeWithApiCheck());
    }
    
    void InitializeLayout()
    {
        // Chercher ou créer TrousGameLayout
        gameLayout = TrousGameLayout.Instance;
        if (gameLayout == null)
        {
            gameLayout = FindFirstObjectByType<TrousGameLayout>();
        }
        
        if (gameLayout == null)
        {
            Debug.Log("[TrousGameManager] Création de TrousGameLayout");
            GameObject layoutObj = new GameObject("TrousGameLayout");
            gameLayout = layoutObj.AddComponent<TrousGameLayout>();
        }
        else
        {
            Debug.Log("[TrousGameManager] TrousGameLayout trouvé");
        }
    }
    
    void Update()
    {
        // Gérer la touche ESC pour retourner à la scène d'origine
        if (Keyboard.current != null && Keyboard.current.escapeKey.wasPressedThisFrame)
        {
            Debug.Log("[TrousGameManager] 🔙 Touche ESC détectée - Retour à la scène d'origine");
            ReturnToOriginScene();
            return;
        }
    }
    
    /// <summary>
    /// Retourne à la scène d'origine (Menu ou Map selon d'où on vient)
    /// </summary>
    void ReturnToOriginScene()
    {
        string returnToScene = PlayerPrefs.GetString("ReturnToScene", "Menu");
        Debug.Log($"[TrousGameManager] ReturnToScene = {returnToScene}");
        
        // Afficher l'écran de chargement
        if (UnifiedLoadingManager.Instance != null)
        {
            UnifiedLoadingManager.ShowLoading("Retour...", LoadingContext.Menu);
        }
        
        if (returnToScene.ToLower() == "map")
        {
            LevelManager levelManager = FindFirstObjectByType<LevelManager>();
            if (levelManager != null)
            {
                levelManager.ReturnToMap();
            }
            else
            {
                SceneManager.LoadScene("Map");
            }
        }
        else
        {
            // Par défaut, retourner au Menu
            SceneManager.LoadScene("Menu");
        }
    }
    
    IEnumerator InitializeWithApiCheck()
    {
        Debug.Log("=== DEBUG TROUS START ===");
        Debug.Log($"CurrentLevelType: '{PlayerPrefs.GetString("CurrentLevelType")}'");
        Debug.Log($"GameConfigUrl: '{PlayerPrefs.GetString("GameConfigUrl")}'");
        Debug.Log($"GamePhase: '{PlayerPrefs.GetString("GamePhase")}'");
        Debug.Log($"UseApiData: '{PlayerPrefs.GetString("UseApiData")}'");
        Debug.Log("==============================");
        
        // **ATTENDRE QUE LE LAYOUT SOIT COMPLÈTEMENT CHARGÉ** (Fix écran noir)
        Debug.Log("[TrousGameManager] ⏳ Attente du chargement complet du layout...");
        float timeout = 10f;
        float elapsed = 0f;
        while (gameLayout != null && !gameLayout.IsFullyLoaded && elapsed < timeout)
        {
            yield return new WaitForSeconds(0.05f);
            elapsed += 0.05f;
        }
        
        if (gameLayout != null && gameLayout.IsFullyLoaded)
        {
            Debug.Log($"[TrousGameManager] ✅ Layout chargé après {elapsed:F2}s");
        }
        else if (elapsed >= timeout)
        {
            Debug.LogWarning($"[TrousGameManager] ⚠️ Timeout layout après {timeout}s - Continuation quand même");
        }
        
        // Attendre un frame pour que GameDataManager soit initialisé
        yield return null;
        
        // Vérifier si on doit utiliser les données API
        bool useApiData = PlayerPrefs.GetString("UseApiData") == "true";
        
        if (useApiData)
        {
            Debug.Log("[TrousGameManager] 🔍 UseApiData=true, attente de GameDataManager...");
            
            // Attendre que GameDataManager soit prêt (max 3 secondes)
            float apiTimeout = 3f;
            float apiElapsed = 0f;
            while ((GameDataManager.Instance == null || !GameDataManager.Instance.HasData) && apiElapsed < apiTimeout)
            {
                yield return new WaitForSeconds(0.1f);
                apiElapsed += 0.1f;
            }
            
            if (GameDataManager.Instance != null && GameDataManager.Instance.HasData)
            {
                Debug.Log("[TrousGameManager] ✅ Données API disponibles - utilisation");
                LoadGameFromApiData(GameDataManager.Instance.CurrentGameData);
                yield break;
            }
            else
            {
                Debug.LogWarning($"[TrousGameManager] ⚠️ Timeout ({timeout}s) - GameDataManager non disponible");
            }
        }
    
        // ANCIEN SYSTÈME
        string gamePhase = PlayerPrefs.GetString("GamePhase", "");
        string returnToScene = PlayerPrefs.GetString("ReturnToScene", "");
        
        // Vérifier s'il y a un GameLauncher dans la scène qui va gérer le chargement
        GameLauncher gameLauncher = FindFirstObjectByType<GameLauncher>();
        bool shouldWaitForGameLauncher = gameLauncher != null && gamePhase == "Shooting";
        
        if (shouldWaitForGameLauncher)
        {
            Debug.Log("[TrousGameManager] GameLauncher détecté dans la scène, attente de l'appel LoadGameFromURL()");
            yield break;
        }
        
        // Récupérer la config depuis PlayerPrefs
        string gameConfigUrl = PlayerPrefs.GetString("GameConfigUrl");
        if (!string.IsNullOrEmpty(gameConfigUrl))
        {
            configUrl = gameConfigUrl;
            Debug.Log($"[TrousGameManager] Config URL depuis PlayerPrefs: {configUrl}");
        }
        
        // Si pas de config URL, ne pas charger
        if (string.IsNullOrEmpty(configUrl))
        {
            Debug.LogWarning("[TrousGameManager] ⚠️ Pas d'URL de config, attente de LoadGameFromApiData ou LoadGameFromURL");
            yield break;
        }
        
        // Afficher l'écran de chargement
        if (UnifiedLoadingManager.Instance != null)
        {
            UnifiedLoadingManager.ShowLoading("Chargement du jeu de trous...", LoadingContext.Game);
        }
        
        // Charger la configuration du jeu
        isInitializing = true;
        StartCoroutine(LoadGameConfig());
    }
    
    private IEnumerator LoadGameConfig()
    {
        Debug.Log($"[TrousGameManager] Chargement config: {configUrl}");
        
        // CORRECTION : Créer GeneralConfigManager s'il n'existe pas
        if (GeneralConfigManager.Instance == null)
        {
            GameObject configManagerObj = new GameObject("GeneralConfigManager");
            configManagerObj.AddComponent<GeneralConfigManager>();
            Debug.Log("[TrousGameManager] GeneralConfigManager créé automatiquement");
        }

        // Attendre que GeneralConfigManager soit initialisé et chargé
        while (GeneralConfigManager.Instance == null || !GeneralConfigManager.Instance.IsConfigLoaded())
        {
            yield return null;
        }
        
        Debug.Log("[TrousGameManager] ✅ GeneralConfigManager est prêt");
        
        // Convertir STREAMING_ASSETS/ en chemin local file:///
        string fullConfigUrl = configUrl;
        if (configUrl.StartsWith("STREAMING_ASSETS/"))
        {
            string fileName = configUrl.Substring("STREAMING_ASSETS/".Length);
            string streamingAssetsPath = System.IO.Path.Combine(Application.streamingAssetsPath, fileName);
            streamingAssetsPath = streamingAssetsPath.Replace("\\", "/");
            
            #if UNITY_WEBGL && !UNITY_EDITOR
            fullConfigUrl = streamingAssetsPath;
            #else
            fullConfigUrl = "file:///" + streamingAssetsPath;
            #endif
            
            Debug.Log($"[TrousGameManager] URL convertie: {fullConfigUrl}");
        }
        
        using (var www = UnityWebRequest.Get(fullConfigUrl))
        {
            yield return www.SendWebRequest();
            
            if (www.result != UnityWebRequest.Result.Success)
            {
                Debug.LogError($"[TrousGameManager] Erreur chargement config: {www.error}");
                Debug.LogError($"[TrousGameManager] URL utilisée: {fullConfigUrl}");
                yield break;
            }
            
            try
            {
                var wrapper = JsonUtility.FromJson<TrousGameConfigWrapper>(www.downloadHandler.text);
                cachedGameConfig = wrapper.gameConfig;
                Debug.Log("[TrousGameManager] Configuration chargée avec succès");
            }
            catch (Exception e)
            {
                Debug.LogError($"[TrousGameManager] Erreur parsing config: {e.Message}");
                yield break;
            }
        }
        
        // Charger les questions
        yield return StartCoroutine(LoadQuestions());
        
        // S'assurer que la caméra est trouvée avant de charger les assets
        if (mainCamera == null)
        {
            mainCamera = Camera.main;
            if (mainCamera == null)
            {
                mainCamera = FindFirstObjectByType<Camera>();
            }
        }
        
        // Charger les assets
        yield return StartCoroutine(LoadAssets());
        
        // Initialiser le jeu
        InitializeGame();
        
        // Masquer l'écran de chargement
        if (UnifiedLoadingManager.Instance != null)
        {
            UnifiedLoadingManager.HideLoading();
        }
    }
    
    private IEnumerator LoadQuestions()
    {
        // Construire l'URL de l'API depuis la configuration
        string apiUrl = GeneralConfigManager.Instance?.GetTrousQuestionsApiUrl() ?? "";
        
        if (string.IsNullOrEmpty(apiUrl))
        {
            Debug.LogError("[TrousGameManager] ❌ Impossible de construire l'URL de l'API - Vérifiez general-config.json");
            yield break;
        }

        Debug.Log($"[TrousGameManager] 🌐 Appel API: {apiUrl}");

        // Vérifier que l'utilisateur est connecté et a un token
        if (UserDataManager.Instance == null || string.IsNullOrEmpty(UserDataManager.Instance.token))
        {
            Debug.LogError("[TrousGameManager] ❌ Utilisateur non connecté ou token manquant. Impossible de charger les questions depuis l'API.");
            yield break;
        }

        string token = UserDataManager.Instance.token;

        using (UnityWebRequest www = UnityWebRequest.Get(apiUrl))
        {
            // Ajouter l'authentification Bearer Token
            www.SetRequestHeader("Authorization", $"Bearer {token}");
            
            // NOTE: Les headers Cache-Control, Pragma, Expires causent des problèmes CORS
            // Le serveur ne les autorise pas dans Access-Control-Allow-Headers
            // On les retire pour éviter les erreurs CORS en WebGL

            yield return www.SendWebRequest();

            if (www.result != UnityWebRequest.Result.Success)
            {
                Debug.LogError($"[TrousGameManager] ❌ Erreur de chargement des questions depuis l'API : {www.error}");
                Debug.LogError($"URL utilisée : {apiUrl}");
                Debug.LogError($"Code de réponse : {www.responseCode}");
                if (!string.IsNullOrEmpty(www.downloadHandler?.text))
                {
                    Debug.LogError($"Réponse serveur : {www.downloadHandler.text}");
                }
                yield break;
            }

            try
            {
                string jsonData = www.downloadHandler.text;
                Debug.Log($"[TrousGameManager] ✅ Données questions reçues depuis l'API ({jsonData.Length} caractères)");

                // Parser la réponse de l'API (format snake_case)
                APITrousQuestionsResponse apiResponse = JsonUtility.FromJson<APITrousQuestionsResponse>(jsonData);

                if (apiResponse == null || apiResponse.data == null || apiResponse.data.Length == 0)
                {
                    Debug.LogError("[TrousGameManager] ❌ Aucune question trouvée dans la réponse de l'API ou structure invalide");
                    Debug.LogError($"Début du JSON : {jsonData.Substring(0, Mathf.Min(200, jsonData.Length))}...");
                    yield break;
                }

                // Mapper les données de l'API vers le format TrousQuestionData
                questions = new List<TrousQuestionData>();
                
                for (int i = 0; i < apiResponse.data.Length; i++)
                {
                    APITrousQuestionData apiQuestion = apiResponse.data[i];
                    TrousQuestionData question = new TrousQuestionData
                    {
                        id = apiQuestion.id.ToString(), // Convertir int en string
                        text = apiQuestion.text,
                        explanation = apiQuestion.explanation,
                        holes = new List<TrousHole>()
                    };

                    // Mapper les trous
                    if (apiQuestion.holes != null)
                    {
                        foreach (var apiHole in apiQuestion.holes)
                        {
                            // Extraire les options et IDs
                            List<string> optionTexts = new List<string>();
                            List<int> optionIds = new List<int>();
                            string correctAnswerText = "";
                            
                            if (apiHole.options != null)
                            {
                                foreach (var opt in apiHole.options)
                                {
                                    optionTexts.Add(opt.content);
                                    optionIds.Add(opt.id);
                                    if (opt.id == apiHole.correctAnswer)
                                    {
                                        correctAnswerText = opt.content;
                                    }
                                }
                            }
                            
                            TrousHole hole = new TrousHole
                            {
                                id = apiHole.id.ToString(), // Convertir int en string
                                keyword = apiHole.keyword,
                                correctAnswer = correctAnswerText,
                                options = optionTexts,
                                optionIds = optionIds,
                                correctOptionId = apiHole.correctAnswer,
                                position = 0, // Legacy - non utilisé avec le système keyword
                                length = 0    // Legacy - non utilisé avec le système keyword
                            };
                            question.holes.Add(hole);
                        }
                    }

                    questions.Add(question);
                }

                Debug.Log($"[TrousGameManager] ✅ {questions.Count} questions chargées depuis l'API");
            }
            catch (Exception e)
            {
                Debug.LogError($"[TrousGameManager] ❌ Erreur parsing questions: {e.Message}");
                Debug.LogError($"StackTrace: {e.StackTrace}");
            }
        }
    }
    
    private IEnumerator LoadAssets()
    {
        // Charger le fond (vidéo ou image)
        if (cachedGameConfig.background != null)
        {
            string backgroundType = cachedGameConfig.background.type?.ToLower() ?? "video";
            
            if (backgroundType == "image" && !string.IsNullOrEmpty(cachedGameConfig.background.imageUrl))
            {
                // Charger une image de fond
                string imageUrl = GeneralConfigManager.Instance.GetBackgroundImageUrl(cachedGameConfig.background.imageUrl);
                Debug.Log($"[TrousGameManager] Chargement image de fond: {imageUrl}");
                
                yield return StartCoroutine(LoadBackgroundImage(imageUrl));
            }
            else if (backgroundType == "video" && !string.IsNullOrEmpty(cachedGameConfig.background.videoUrl))
            {
                // Charger une vidéo de fond
                string videoUrl = GeneralConfigManager.Instance.GetVideoUrl(cachedGameConfig.background.videoUrl);
                Debug.Log($"[TrousGameManager] Chargement vidéo de fond: {videoUrl}");
                
                // Créer le VideoPlayer si nécessaire
                if (backgroundVideo == null)
                {
                    GameObject videoObj = new GameObject("BackgroundVideo");
                    backgroundVideo = videoObj.AddComponent<VideoPlayer>();
                    if (mainCamera != null)
                    {
                        backgroundVideo.renderMode = VideoRenderMode.CameraNearPlane;
                        backgroundVideo.targetCamera = mainCamera;
                        backgroundVideo.targetCameraAlpha = 1f;
                    }
                    Debug.Log("[TrousGameManager] VideoPlayer créé automatiquement");
                }
                
                backgroundVideo.url = videoUrl;
                backgroundVideo.isLooping = true;
                backgroundVideo.Prepare();
                
                while (!backgroundVideo.isPrepared)
                {
                    yield return null;
                }
                
                backgroundVideo.Play();
                Debug.Log("[TrousGameManager] Vidéo de fond lancée");
            }
            else
            {
                Debug.LogWarning("[TrousGameManager] Aucun fond configuré ou type non reconnu");
            }
        }
        
        // Charger les sons
        if (cachedGameConfig.sounds != null)
        {
            if (!string.IsNullOrEmpty(cachedGameConfig.sounds.background))
            {
                yield return StartCoroutine(LoadAudioClip(cachedGameConfig.sounds.background, "background"));
            }
            if (!string.IsNullOrEmpty(cachedGameConfig.sounds.success))
            {
                yield return StartCoroutine(LoadAudioClip(cachedGameConfig.sounds.success, "success"));
            }
            if (!string.IsNullOrEmpty(cachedGameConfig.sounds.fail))
            {
                yield return StartCoroutine(LoadAudioClip(cachedGameConfig.sounds.fail, "fail"));
            }
        }
        
        // Charger les sprites de feedback (depuis la config du jeu ou les defaults depuis general-config.json)
        // Utiliser GetFeedbackMessagesConfig() qui fait automatiquement le fallback
        var feedbackConfig = GetFeedbackMessagesConfig();
        if (feedbackConfig != null && feedbackConfig.useBackgroundImage)
        {
            string successBgUrl = feedbackConfig.successBackgroundImageUrl;
            string failureBgUrl = feedbackConfig.failureBackgroundImageUrl;
            
            if (!string.IsNullOrEmpty(successBgUrl))
            {
                string successUrl = GeneralConfigManager.Instance.GetUIUrl(successBgUrl);
                yield return StartCoroutine(LoadSprite(successUrl, "success_bg"));
                Debug.Log($"[TrousGameManager] ✅ Image de succès chargée depuis: {successUrl}");
            }
            if (!string.IsNullOrEmpty(failureBgUrl))
            {
                string failureUrl = GeneralConfigManager.Instance.GetUIUrl(failureBgUrl);
                yield return StartCoroutine(LoadSprite(failureUrl, "failure_bg"));
                Debug.Log($"[TrousGameManager] ✅ Image d'échec chargée depuis: {failureUrl}");
            }
        }
        else
        {
            Debug.Log("[TrousGameManager] ⚠️ Pas de configuration feedbackMessages trouvée pour charger les images");
        }
    }
    
    private IEnumerator LoadBackgroundImage(string imageUrl)
    {
        using (var www = UnityWebRequestTexture.GetTexture(imageUrl))
        {
            yield return www.SendWebRequest();
            
            if (www.result == UnityWebRequest.Result.Success)
            {
                // Avec TrousGameLayout, charger le background DANS la zone de jeu via SetGameAreaBackgroundUrl
                if (gameLayout != null && gameLayout.IsInitialized)
                {
                    Debug.Log($"[TrousGameManager] ✅ Chargement du background dans la zone de jeu via TrousGameLayout: {imageUrl}");
                    gameLayout.SetGameAreaBackgroundUrl(imageUrl);
                }
                else
                {
                    Debug.LogWarning("[TrousGameManager] ⚠️ TrousGameLayout non disponible pour charger le background");
                }
            }
            else
            {
                Debug.LogError($"[TrousGameManager] Erreur chargement image de fond: {www.error}");
            }
        }
    }
    
    private IEnumerator LoadAudioClip(string url, string key)
    {
        using (var www = UnityWebRequestMultimedia.GetAudioClip(url, AudioType.MPEG))
        {
            yield return www.SendWebRequest();
            
            if (www.result == UnityWebRequest.Result.Success)
            {
                AudioClip clip = DownloadHandlerAudioClip.GetContent(www);
                loadedAudioClips[key] = clip;
                Debug.Log($"[TrousGameManager] Audio chargé: {key}");
            }
            else
            {
                Debug.LogError($"[TrousGameManager] Erreur chargement audio {key}: {www.error}");
            }
        }
    }
    
    private IEnumerator LoadSprite(string url, string key)
    {
        // 🚀 NOUVEAU : Vérifier d'abord le cache de GameDataManager
        if (GameDataManager.Instance != null)
        {
            Sprite cachedSprite = GameDataManager.Instance.GetCachedSprite(key);
            if (cachedSprite != null)
            {
                Debug.Log($"[TrousGameManager] ⚡ Sprite '{key}' chargé instantanément depuis le cache !");
                loadedSprites[key] = cachedSprite;
                yield break;
            }
        }
        
        using (var www = UnityWebRequestTexture.GetTexture(url))
        {
            yield return www.SendWebRequest();
            
            if (www.result == UnityWebRequest.Result.Success)
            {
                Texture2D texture = DownloadHandlerTexture.GetContent(www);
                Sprite sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
                loadedSprites[key] = sprite;
                Debug.Log($"[TrousGameManager] Sprite chargé: {key}");
            }
            else
            {
                Debug.LogError($"[TrousGameManager] Erreur chargement sprite {key}: {www.error}");
            }
        }
    }
    
    private void InitializeGame()
    {
        Debug.Log("[TrousGameManager] Initialisation du jeu");
        
        // IMPORTANT : Nettoyer les éléments UI des dialogues AVANT d'initialiser le jeu
        Debug.Log("[TrousGameManager] Nettoyage des UI de dialogue avant initialisation du jeu");
        CleanupDialogueUI();
        
        // Trouver la caméra si elle n'est pas assignée
        if (mainCamera == null)
        {
            mainCamera = Camera.main;
            if (mainCamera == null)
            {
                mainCamera = FindFirstObjectByType<Camera>();
            }
            if (mainCamera != null)
            {
                Debug.Log("[TrousGameManager] Caméra trouvée automatiquement");
            }
            else
            {
                Debug.LogWarning("[TrousGameManager] Aucune caméra trouvée dans la scène");
            }
        }
        
        // NE PAS configurer la caméra en SolidColor - ça masque le backdrop du TrousGameLayout
        // Le Canvas est en ScreenSpaceOverlay donc la caméra n'affecte pas l'UI
        if (mainCamera != null)
        {
            // Mettre la caméra en Depth pour qu'elle ne masque rien
            mainCamera.clearFlags = CameraClearFlags.Depth;
            Debug.Log("[TrousGameManager] Caméra configurée en mode Depth (ne masque pas l'UI)");
        }
        
        // DÉSACTIVER le BackgroundVideo s'il existe (peut masquer le backdrop)
        if (backgroundVideo != null && backgroundVideo.gameObject != null)
        {
            backgroundVideo.gameObject.SetActive(false);
            Debug.Log("[TrousGameManager] BackgroundVideo désactivé (utilisation du TrousGameLayout backdrop)");
        }
        
        // DÉSACTIVER le backgroundImage s'il existe (peut masquer le backdrop)
        if (backgroundImage != null && backgroundImage.gameObject != null)
        {
            backgroundImage.gameObject.SetActive(false);
            Debug.Log("[TrousGameManager] BackgroundImage désactivé (utilisation du TrousGameLayout backdrop)");
        }
        
        // Créer l'UI si elle n'existe pas
        if (questionText == null)
        {
            Debug.LogWarning("[TrousGameManager] UI manquante, création de l'UI de base");
            CreateBasicUI();
        }
        
        // Configurer le panneau de texte
        SetupTextPanel();
        
        // Charger la première question
        if (questions.Count > 0)
        {
            LoadQuestion(0);
        }
        else
        {
            Debug.LogError("[TrousGameManager] Aucune question disponible");
        }
        
        // Configurer les dialogues
        SetupDialogues();
        
        jsonLoaded = true;
    }
    
    private void CreateBasicUI()
    {
        Debug.Log("[TrousGameManager] Création de l'UI de base pour affichage du texte");
        
        // TOUJOURS utiliser TrousGameLayout - ne jamais créer de canvas
        if (gameLayout == null || !gameLayout.IsInitialized)
        {
            Debug.LogError("[TrousGameManager] ❌ TrousGameLayout non disponible !");
            return;
        }
        
        if (gameLayout.GameAreaPanel == null)
        {
            Debug.LogError("[TrousGameManager] ❌ GameAreaPanel non disponible !");
            return;
        }
        
        Debug.Log("[TrousGameManager] ✅ Utilisation du TrousGameLayout");
        
        // Créer le textPanel DANS le GameAreaPanel - REMPLIR TOUTE LA ZONE
        GameObject panelObj = new GameObject("TextPanel");
        panelObj.transform.SetParent(gameLayout.GameAreaPanel.transform, false);
        
        RectTransform panelRect = panelObj.AddComponent<RectTransform>();
        // IMPORTANT : Ancrer sur toute la gameArea pour remplir l'espace
        panelRect.anchorMin = Vector2.zero;  // (0, 0)
        panelRect.anchorMax = Vector2.one;   // (1, 1)
        panelRect.pivot = new Vector2(0.5f, 0.5f);  // Centré
        panelRect.sizeDelta = Vector2.zero;  // Pas de taille additionnelle
        panelRect.anchoredPosition = Vector2.zero;  // Pas de décalage
        
        Image panelImage = panelObj.AddComponent<Image>();
        panelImage.color = new Color(0, 0, 0, 0f); // Fond transparent
        
        Debug.Log($"[TrousGameManager] ✅ TextPanel créé dans gameArea - anchorMin={panelRect.anchorMin}, anchorMax={panelRect.anchorMax}, anchoredPos={panelRect.anchoredPosition}, sizeDelta={panelRect.sizeDelta}");
        
        // Créer le texte de la question
        GameObject textObj = new GameObject("QuestionText");
        textObj.transform.SetParent(panelObj.transform, false);
        questionText = textObj.AddComponent<TextMeshProUGUI>();
        questionText.fontSize = 32;
        questionText.color = Color.white;
        questionText.alignment = TextAlignmentOptions.Center;
        RectTransform textRect = textObj.GetComponent<RectTransform>();
        textRect.anchorMin = new Vector2(0.1f, 0.1f);
        textRect.anchorMax = new Vector2(0.9f, 0.9f);
        textRect.sizeDelta = Vector2.zero;
        textRect.anchoredPosition = Vector2.zero;
        
        // Créer un panneau de feedback (sur le canvas principal, au-dessus de tout)
        Canvas mainCanvas = gameLayout.GetComponentInParent<Canvas>();
        GameObject feedbackObj = new GameObject("FeedbackPanel");
        feedbackObj.transform.SetParent(mainCanvas != null ? mainCanvas.transform : panelObj.transform, false);
        feedbackObj.SetActive(false);
        feedbackPanel = feedbackObj;
        
        Image feedbackImage = feedbackObj.AddComponent<Image>();
        feedbackImage.color = new Color(0.2f, 0.8f, 0.2f, 0.9f); // Vert
        RectTransform feedbackRect = feedbackObj.GetComponent<RectTransform>();
        feedbackRect.anchorMin = new Vector2(0.2f, 0.7f);
        feedbackRect.anchorMax = new Vector2(0.8f, 0.9f);
        feedbackRect.sizeDelta = Vector2.zero;
        feedbackRect.anchoredPosition = Vector2.zero;
        
        // Créer le texte du feedback
        GameObject feedbackTextObj = new GameObject("FeedbackText");
        feedbackTextObj.transform.SetParent(feedbackObj.transform, false);
        feedbackText = feedbackTextObj.AddComponent<TextMeshProUGUI>();
        feedbackText.fontSize = 24;
        feedbackText.color = Color.white;
        feedbackText.alignment = TextAlignmentOptions.Center;
        RectTransform feedbackTextRect = feedbackTextObj.GetComponent<RectTransform>();
        feedbackTextRect.anchorMin = new Vector2(0, 0);
        feedbackTextRect.anchorMax = new Vector2(1, 1);
        feedbackTextRect.sizeDelta = Vector2.zero;
        feedbackTextRect.anchoredPosition = Vector2.zero;
        
        textPanel = panelObj;
        
        Debug.Log("[TrousGameManager] UI de base créée");
    }
    
    private IEnumerator VerifyTextPanelPositionDelayed(RectTransform panelRect, float expectedX, float expectedY, float expectedWidth, float expectedHeight)
    {
        // Attendre 1 frame
        yield return null;
        
        // Vérifier si la position a changé
        Vector2 actualPos = panelRect.anchoredPosition;
        Vector2 expectedPos = new Vector2(expectedX, -expectedY);
        
        // Vérifier si la position a été écrasée
        if (Vector2.Distance(actualPos, expectedPos) > 1f)
        {
            Debug.LogError($"[TrousGameManager] Position écrasée après 1 frame ! Réapplication (écart: {Vector2.Distance(actualPos, expectedPos):F1}px)");
            
            // Réappliquer la position
            panelRect.anchoredPosition = expectedPos;
            panelRect.sizeDelta = new Vector2(expectedWidth, expectedHeight);
        }
    }
    
    private void SetupTextPanel()
    {
        if (textPanel == null)
        {
            Debug.LogError("[TrousGameManager] textPanel est NULL !");
            return;
        }
        
        if (cachedGameConfig == null)
        {
            Debug.LogError("[TrousGameManager] cachedGameConfig est NULL !");
            return;
        }
        
        if (cachedGameConfig.textConfig == null)
        {
            Debug.LogError("[TrousGameManager] cachedGameConfig.textConfig est NULL !");
            return;
        }
        
        if (cachedGameConfig.textConfig != null)
        {
            var textConfig = cachedGameConfig.textConfig;
            
            // Position et taille du panneau
            RectTransform panelRect = textPanel.GetComponent<RectTransform>();
            
            if (panelRect == null)
            {
                Debug.LogError("[TrousGameManager] panelRect est NULL !");
                return;
            }
            
            // VÉRIFIER l'état du gameLayout
            if (gameLayout == null)
            {
                Debug.LogError("[TrousGameManager] gameLayout est NULL - Le TextPanel doit être enfant de GameAreaPanel");
                return;
            }
            
            if (!gameLayout.IsInitialized)
            {
                Debug.LogError("[TrousGameManager] gameLayout n'est pas initialisé");
                return;
            }
            
            if (gameLayout.GameAreaPanel == null)
            {
                Debug.LogError("[TrousGameManager] GameAreaPanel est NULL");
                return;
            }
            
            // VÉRIFIER si le textPanel est bien enfant du GameAreaPanel
            if (textPanel.transform.parent != gameLayout.GameAreaPanel.transform)
            {
                Debug.LogWarning($"[TrousGameManager] TextPanel n'est pas enfant du GameAreaPanel - Re-parentage en cours");
                
                try
                {
                    // Sauvegarder l'état actif avant le re-parentage
                    bool wasActive = textPanel.activeSelf;
                    
                    // Re-parentiser avec worldPositionStays=false pour préserver le positionnement local
                    textPanel.transform.SetParent(gameLayout.GameAreaPanel.transform, false);
                    
                    // Restaurer l'état actif
                    if (!wasActive)
                    {
                        textPanel.SetActive(false);
                    }
                }
                catch (System.Exception ex)
                {
                    Debug.LogError($"[TrousGameManager] EXCEPTION lors du re-parentage: {ex.Message}");
                }
            }
            
            // DÉSACTIVER le ScrollRect s'il existe (peut empêcher le repositionnement)
            ScrollRect scrollRect = textPanel.GetComponent<ScrollRect>();
            if (scrollRect != null)
            {
                scrollRect.enabled = false;
            }
            
            // Les coordonnées de l'API sont relatives à la GameArea (1480x960)
            // Origine (0,0) = coin haut-gauche de la GameArea
            float relativeX = textConfig.position.x;
            float relativeY = textConfig.position.y;
            
            // Positionner le TextPanel DANS la GameAreaPanel avec pivot coin haut-gauche
            // Origine (0, 0) en haut à gauche de la GameArea
            panelRect.anchorMin = new Vector2(0, 1); // Haut-gauche du GameArea
            panelRect.anchorMax = new Vector2(0, 1); // Haut-gauche du GameArea
            panelRect.pivot = new Vector2(0, 1); // Pivot: coin haut-gauche
            panelRect.anchoredPosition = new Vector2(relativeX, -relativeY); // Position relative (y négatif car Unity UI)
            panelRect.sizeDelta = new Vector2(textConfig.size.width, textConfig.size.height);
            
            // Supprimer tous les composants de layout qui pourraient interférer
            LayoutElement layoutElement = textPanel.GetComponent<LayoutElement>();
            if (layoutElement != null)
            {
                Destroy(layoutElement);
            }
            
            ContentSizeFitter sizeFitter = textPanel.GetComponent<ContentSizeFitter>();
            if (sizeFitter != null)
            {
                Destroy(sizeFitter);
            }
            
            // Forcer le recalcul immédiat du layout pour que les changements soient appliqués
            UnityEngine.UI.LayoutRebuilder.ForceRebuildLayoutImmediate(panelRect);
            Canvas.ForceUpdateCanvases();
            
            // Vérifier après un frame que la position est toujours correcte
            StartCoroutine(VerifyTextPanelPositionDelayed(panelRect, relativeX, relativeY, textConfig.size.width, textConfig.size.height));
            
            // Configuration du texte
            if (questionText != null)
            {
                questionText.fontSize = textConfig.fontSize;
                questionText.color = ParseColor(textConfig.fontColor);
                questionText.fontStyle = textConfig.fontBold ? FontStyles.Bold : FontStyles.Normal;
                questionText.alignment = ParseTextAlignment(textConfig.alignment);
                questionText.lineSpacing = textConfig.lineSpacing;
                
                // Force update du texte
                questionText.textWrappingMode = TMPro.TextWrappingModes.Normal;
                questionText.overflowMode = TextOverflowModes.Overflow;
                
                if (customFont != null)
                {
                    questionText.font = customFont;
                }
            }
            
            // Configuration du fond - Forcer la transparence (alpha = 0)
            Image panelImage = textPanel.GetComponent<Image>();
            if (panelImage != null)
            {
                // Forcer la transparence complète pour ne voir que le texte
                panelImage.color = new Color(0, 0, 0, 0f);
                Debug.Log("[TrousGameManager] Panneau configuré avec fond transparent (alpha = 0)");
            }
            
            // Configuration du padding
            if (textScrollRect != null)
            {
                RectTransform contentRect = textScrollRect.content.GetComponent<RectTransform>();
                contentRect.offsetMin = new Vector2(textConfig.padding.left, textConfig.padding.bottom);
                contentRect.offsetMax = new Vector2(-textConfig.padding.right, -textConfig.padding.top);
            }
        }
    }
    
    private void LoadQuestion(int questionIndex)
    {
        if (questionIndex >= questions.Count)
        {
            Debug.LogError($"[TrousGameManager] Index de question invalide: {questionIndex}");
            return;
        }
        
        currentQuestionIndex = questionIndex;
        currentQuestion = questions[questionIndex];
        
        Debug.Log($"[TrousGameManager] Chargement question {questionIndex + 1}/{questions.Count}");
        
        // Nettoyer les boutons précédents
        ClearTrouButtons();
        
        // Fermer le popup s'il est ouvert
        CloseCurrentPopup();
        
        // Afficher le texte de la question avec les trous
        DisplayQuestionWithHoles();
        
        // Créer ou mettre à jour le bouton de validation
        SetupValidationButton();
        
        // Masquer le feedback
        if (feedbackPanel != null)
        {
            feedbackPanel.SetActive(false);
        }
    }
    
    private void DisplayQuestionWithHoles()
    {
        ClearTrouButtons();
        
        if (currentQuestion == null)
        {
            Debug.LogError("[TrousGameManager] currentQuestion est null");
            return;
        }
        
        // Cacher le questionText par défaut
        if (questionText != null)
        {
            questionText.gameObject.SetActive(false);
        }
        
        // TOUJOURS utiliser textPanel pour le positionnement correct selon le JSON
        // textContainer peut être un ScrollRect content qui couvre tout l'écran, donc on l'ignore
        Transform parentTransform = textPanel != null ? textPanel.transform : null;
        
        if (parentTransform == null)
        {
            Debug.LogError("[TrousGameManager] textPanel est null, impossible d'afficher le texte");
            return;
        }
        
        // Nettoyer les enfants existants (sauf les éléments de base)
        foreach (Transform child in parentTransform)
        {
            if (child.name != "QuestionText" && child.name != "FeedbackPanel" && child.name != "TextWithHolesContainer")
            {
                Destroy(child.gameObject);
            }
            }
            
        // Supprimer les anciens layouts du parent
        VerticalLayoutGroup vertLayout = parentTransform.GetComponent<VerticalLayoutGroup>();
            if (vertLayout != null) Destroy(vertLayout);
        HorizontalLayoutGroup horizLayout = parentTransform.GetComponent<HorizontalLayoutGroup>();
            if (horizLayout != null) Destroy(horizLayout);
            
            // S'assurer que le Canvas a un GraphicRaycaster
        Canvas canvas = parentTransform.GetComponentInParent<Canvas>();
            if (canvas != null && canvas.GetComponent<UnityEngine.UI.GraphicRaycaster>() == null)
            {
                canvas.gameObject.AddComponent<UnityEngine.UI.GraphicRaycaster>();
                Debug.Log("[TrousGameManager] GraphicRaycaster ajouté au Canvas");
            }
            
            // S'assurer qu'il y a un EventSystem dans la scène
            if (UnityEngine.EventSystems.EventSystem.current == null)
            {
                GameObject eventSystemObj = new GameObject("EventSystem");
                eventSystemObj.AddComponent<UnityEngine.EventSystems.EventSystem>();
                
                // Utiliser InputSystemUIInputModule pour le nouveau Input System
                var inputModule = eventSystemObj.AddComponent<UnityEngine.InputSystem.UI.InputSystemUIInputModule>();
                
                Debug.Log("[TrousGameManager] EventSystem créé avec InputSystemUIInputModule");
            }
            
        // Vérifier que le parent est bien configuré
        RectTransform parentRect = parentTransform.GetComponent<RectTransform>();
        if (parentRect != null)
        {
            Debug.Log($"[TrousGameManager] Parent '{parentTransform.name}' RectTransform: anchorMin={parentRect.anchorMin}, anchorMax={parentRect.anchorMax}, sizeDelta={parentRect.sizeDelta}, anchoredPosition={parentRect.anchoredPosition}");
            Debug.Log($"[TrousGameManager] Parent rect: {parentRect.rect}");
        }
        
        // Construire le texte directement dans textPanel (qui est positionné selon le JSON)
        BuildTextWithTrouButtons(parentTransform);
    }
    
    private void BuildTextWithTrouButtons(Transform parent)
    {
        string fullText = currentQuestion.text ?? "";
        
        if (string.IsNullOrEmpty(fullText))
        {
            Debug.LogWarning("[TrousGameManager] Texte de question vide");
            return;
        }
        
        // Vérifier si on utilise le nouveau système (keyword) ou l'ancien (position/length)
        bool useKeywordSystem = currentQuestion.holes != null && currentQuestion.holes.Count > 0 && !string.IsNullOrEmpty(currentQuestion.holes[0].keyword);
        
        List<TrousHole> sortedHoles = currentQuestion.holes != null ? new List<TrousHole>(currentQuestion.holes) : new List<TrousHole>();
        
        if (useKeywordSystem)
        {
            // Nouveau système : trier par ordre d'apparition dans le texte
            sortedHoles.Sort((a, b) => 
            {
                int indexA = fullText.IndexOf(a.keyword, StringComparison.OrdinalIgnoreCase);
                int indexB = fullText.IndexOf(b.keyword, StringComparison.OrdinalIgnoreCase);
                if (indexA == -1) indexA = int.MaxValue;
                if (indexB == -1) indexB = int.MaxValue;
                return indexA.CompareTo(indexB);
            });
            Debug.Log($"[TrousGameManager] Utilisation du système de mots-clés pour {sortedHoles.Count} trous");
        }
        else
        {
            // Ancien système : trier par position
        sortedHoles.Sort((a, b) => a.position.CompareTo(b.position));
            Debug.Log($"[TrousGameManager] Utilisation du système position/length (legacy) pour {sortedHoles.Count} trous");
        }
        
        // Calculer la largeur disponible (taille du panneau moins les paddings)
        var textConfig = cachedGameConfig.textConfig;
        float availableWidth = textConfig.size.width;
        if (textConfig.padding != null)
        {
            availableWidth -= (textConfig.padding.left + textConfig.padding.right);
        }
        
        // Créer un conteneur avec une largeur fixe pour permettre le word wrapping
        GameObject containerObj = new GameObject("TextWithHolesContainer");
        containerObj.transform.SetParent(parent, false);
        
        RectTransform containerRect = containerObj.AddComponent<RectTransform>();
        
        // Remplir tout l'espace du parent (textPanel ou textContainer)
        // Utiliser anchorMin/anchorMax pour s'étendre sur tout le parent
        containerRect.anchorMin = Vector2.zero;
        containerRect.anchorMax = Vector2.one;
        containerRect.pivot = new Vector2(0, 1);
        
        // Appliquer le padding avec les offsets
        // offsetMin = coin inférieur gauche (left, bottom) en pixels depuis les anchors
        // offsetMax = coin supérieur droit (right, top) en pixels depuis les anchors
        float paddingLeft = textConfig.padding != null ? textConfig.padding.left : 30;
        float paddingRight = textConfig.padding != null ? textConfig.padding.right : 30;
        float paddingTop = textConfig.padding != null ? textConfig.padding.top : 30;
        float paddingBottom = textConfig.padding != null ? textConfig.padding.bottom : 30;
        
        // Les offsets sont relatifs aux anchors
        // offsetMin.x = distance depuis anchorMin.x (left)
        // offsetMin.y = distance depuis anchorMin.y (bottom)
        // offsetMax.x = distance depuis anchorMax.x (right, négatif = vers la gauche)
        // offsetMax.y = distance depuis anchorMax.y (top, négatif = vers le bas)
        containerRect.offsetMin = new Vector2(paddingLeft, paddingBottom);
        containerRect.offsetMax = new Vector2(-paddingRight, -paddingTop);
        
        // Forcer le recalcul du layout
        UnityEngine.UI.LayoutRebuilder.ForceRebuildLayoutImmediate(containerRect);
        
        Debug.Log($"[TrousGameManager] Container positionné avec padding: L={paddingLeft}, R={paddingRight}, T={paddingTop}, B={paddingBottom}");
        Debug.Log($"[TrousGameManager] Container RectTransform: anchorMin={containerRect.anchorMin}, anchorMax={containerRect.anchorMax}, offsetMin={containerRect.offsetMin}, offsetMax={containerRect.offsetMax}");
        Debug.Log($"[TrousGameManager] Container rect: {containerRect.rect}");
        
        // Utiliser un VerticalLayoutGroup pour gérer les lignes
        VerticalLayoutGroup verticalLayout = containerObj.AddComponent<VerticalLayoutGroup>();
        verticalLayout.spacing = 2; // Espacement réduit entre les lignes
        verticalLayout.padding = new RectOffset(0, 0, 0, 0);
        verticalLayout.childControlWidth = true;
        verticalLayout.childControlHeight = false;
        verticalLayout.childForceExpandWidth = true;
        verticalLayout.childForceExpandHeight = false;
        
        // Centrer le contenu verticalement et horizontalement selon la configuration
        TextAlignmentOptions textAlignment = ParseTextAlignment(textConfig.alignment);
        if (textAlignment == TextAlignmentOptions.Center)
        {
            verticalLayout.childAlignment = TextAnchor.MiddleCenter; // Centrer verticalement et horizontalement
        }
        else if (textAlignment == TextAlignmentOptions.Right)
        {
            verticalLayout.childAlignment = TextAnchor.MiddleRight; // Centrer verticalement, aligner à droite
        }
        else
        {
            verticalLayout.childAlignment = TextAnchor.MiddleLeft; // Centrer verticalement, aligner à gauche
        }
        
        // Ajouter ContentSizeFitter pour adapter la hauteur
        ContentSizeFitter containerFitter = containerObj.AddComponent<ContentSizeFitter>();
        containerFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
        containerFitter.horizontalFit = ContentSizeFitter.FitMode.PreferredSize;
        
        // Centrer le conteneur verticalement dans le panneau si le contenu est plus petit
        // Cela sera géré par le VerticalLayoutGroup avec childAlignment
        
        // Créer le système de lignes avec wrapping automatique
        BuildWrappedTextWithHoles(containerObj.transform, fullText, sortedHoles, availableWidth, useKeywordSystem);
        
        Debug.Log($"[TrousGameManager] Texte avec trous créé avec wrapping automatique, largeur disponible: {availableWidth}");
    }
    
    private void BuildWrappedTextWithHoles(Transform container, string fullText, List<TrousHole> sortedHoles, float maxWidth, bool useKeywordSystem = false)
    {
        // Créer une ligne courante
        GameObject currentLine = CreateNewLine(container, maxWidth);
        float currentLineWidth = 0;
        float spacing = 4f; // Espacement réduit pour que les boutons soient plus inline
        int trouIndex = 0;
        
        if (useKeywordSystem)
        {
            // NOUVEAU SYSTÈME : Utiliser les mots-clés
            string remainingText = fullText;
            
            foreach (var hole in sortedHoles)
            {
                if (string.IsNullOrEmpty(hole.keyword))
                {
                    Debug.LogWarning($"[TrousGameManager] Trou {hole.id} n'a pas de mot-clé défini. Ignoré.");
                    continue;
                }
                
                int keywordIndex = remainingText.IndexOf(hole.keyword, StringComparison.OrdinalIgnoreCase);
                if (keywordIndex == -1)
                {
                    Debug.LogWarning($"[TrousGameManager] Mot-clé '{hole.keyword}' non trouvé dans le texte pour le trou {hole.id}. Ignoré.");
                    continue;
                }
                
                // Ajouter le texte avant le mot-clé
                if (keywordIndex > 0)
                {
                    string textSegment = remainingText.Substring(0, keywordIndex);
                    AddTextSegmentToLine(textSegment, ref currentLine, container, ref currentLineWidth, maxWidth);
                }
                
                // Créer le bouton de trou à la place du mot-clé
                // La largeur sera calculée dynamiquement par TrouButton
                float estimatedWidth = EstimateTrouButtonWidth(hole);
                if (currentLineWidth > 0 && currentLineWidth + spacing + estimatedWidth > maxWidth)
                {
                    currentLine = CreateNewLine(container, maxWidth);
                    currentLineWidth = 0;
                }
                
                float actualWidth = CreateTrouButton(hole, trouIndex, currentLine.transform);
                currentLineWidth += actualWidth + spacing;
                trouIndex++;
                
                // Retirer le mot-clé du texte restant
                remainingText = remainingText.Substring(keywordIndex + hole.keyword.Length);
            }
            
            // Ajouter le texte restant
            if (!string.IsNullOrWhiteSpace(remainingText))
            {
                AddTextSegmentToLine(remainingText, ref currentLine, container, ref currentLineWidth, maxWidth);
            }
            }
            else
            {
            // ANCIEN SYSTÈME : Utiliser position et length
            int lastPosition = 0;
            
            for (int i = 0; i < sortedHoles.Count; i++)
            {
                var hole = sortedHoles[i];
                
                // Vérifier que la position est valide
                if (hole.position < 0 || hole.position >= fullText.Length)
                {
                    Debug.LogWarning($"[TrousGameManager] Trou {hole.id} à la position {hole.position} est invalide. Ignoré.");
                    continue;
                }
                
                // Ajouter le texte avant le trou
                if (hole.position > lastPosition)
                {
                    string textSegment = fullText.Substring(lastPosition, hole.position - lastPosition);
                    AddTextSegmentToLine(textSegment, ref currentLine, container, ref currentLineWidth, maxWidth);
                }
                
                // Créer le bouton pour ce trou
                // La largeur sera calculée dynamiquement par TrouButton
                float estimatedWidth = EstimateTrouButtonWidth(hole);
                if (currentLineWidth > 0 && currentLineWidth + spacing + estimatedWidth > maxWidth)
                {
                    currentLine = CreateNewLine(container, maxWidth);
                    currentLineWidth = 0;
                }
                
                float actualWidth = CreateTrouButton(hole, trouIndex, currentLine.transform);
                currentLineWidth += actualWidth + spacing;
                trouIndex++;
                
                lastPosition = hole.position + hole.length;
            }
            
            // Ajouter le texte restant après le dernier trou
            if (lastPosition < fullText.Length)
            {
                string textSegment = fullText.Substring(lastPosition);
                AddTextSegmentToLine(textSegment, ref currentLine, container, ref currentLineWidth, maxWidth);
            }
        }
    }
    
    private void AddTextSegmentToLine(string textSegment, ref GameObject currentLine, Transform container, ref float currentLineWidth, float maxWidth)
    {
        if (string.IsNullOrWhiteSpace(textSegment))
            return;
            
        // Traiter le texte mot par mot pour un meilleur wrapping
        string[] words = textSegment.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
        foreach (string word in words)
        {
            string wordWithSpace = word + " ";
            float wordWidth = EstimateTextWidth(wordWithSpace, cachedGameConfig.textConfig.fontSize);
            
            // Si le mot ne rentre pas dans la ligne actuelle, créer une nouvelle ligne
            if (currentLineWidth > 0 && currentLineWidth + wordWidth > maxWidth)
            {
                currentLine = CreateNewLine(container, maxWidth);
                currentLineWidth = 0;
            }
            
            GameObject textObj = CreateTextSegment(wordWithSpace, currentLine.transform, maxWidth - currentLineWidth);
            UnityEngine.UI.LayoutRebuilder.ForceRebuildLayoutImmediate(textObj.GetComponent<RectTransform>());
            float actualWidth = textObj.GetComponent<RectTransform>().rect.width;
            currentLineWidth += actualWidth;
        }
    }
    
    private GameObject CreateNewLine(Transform parent, float width)
    {
        GameObject lineObj = new GameObject("TextLine");
        lineObj.transform.SetParent(parent, false);
        
        HorizontalLayoutGroup lineLayout = lineObj.AddComponent<HorizontalLayoutGroup>();
        lineLayout.spacing = 8;
        lineLayout.padding = new RectOffset(0, 0, 0, 0);
        lineLayout.childControlWidth = false;
        lineLayout.childControlHeight = false;
        lineLayout.childForceExpandWidth = false;
        lineLayout.childForceExpandHeight = false;
        
        // Aligner les lignes selon la configuration du texte
        // Utiliser MiddleLeft/Center/Right pour que les boutons et texte soient alignés verticalement
        var textConfig = cachedGameConfig.textConfig;
        TextAlignmentOptions textAlignment = ParseTextAlignment(textConfig.alignment);
        if (textAlignment == TextAlignmentOptions.Center)
        {
            lineLayout.childAlignment = TextAnchor.MiddleCenter;
        }
        else if (textAlignment == TextAlignmentOptions.Right)
        {
            lineLayout.childAlignment = TextAnchor.MiddleRight;
        }
        else
        {
            lineLayout.childAlignment = TextAnchor.MiddleLeft;
        }
        
        RectTransform lineRect = lineObj.GetComponent<RectTransform>();
        lineRect.sizeDelta = new Vector2(width, 0);
        
        ContentSizeFitter lineFitter = lineObj.AddComponent<ContentSizeFitter>();
        lineFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
        
        return lineObj;
    }
    
    private float EstimateTextWidth(string text, int fontSize)
    {
        // Estimation approximative de la largeur du texte
        // Utilise la moyenne de 0.6 * fontSize par caractère (approximation pour la plupart des polices)
        return text.Length * fontSize * 0.6f;
    }
    
    private GameObject CreateTextSegment(string text, Transform parent, float maxWidth)
    {
        GameObject textObj = new GameObject("TextSegment");
        textObj.transform.SetParent(parent, false);
        
        RectTransform textRect = textObj.AddComponent<RectTransform>();
        
        TextMeshProUGUI textComp = textObj.AddComponent<TextMeshProUGUI>();
        textComp.text = text;
        
        // Appliquer la configuration du texte depuis le JSON
        var textConfig = cachedGameConfig.textConfig;
        textComp.fontSize = textConfig.fontSize;
        textComp.color = ParseColor(textConfig.fontColor);
        textComp.fontStyle = textConfig.fontBold ? FontStyles.Bold : FontStyles.Normal;
        textComp.alignment = ParseTextAlignment(textConfig.alignment); // Utiliser l'alignement du JSON
        textComp.verticalAlignment = VerticalAlignmentOptions.Middle;
        textComp.lineSpacing = textConfig.lineSpacing;
        
        // Désactiver le word wrapping pour les segments individuels (on gère le wrapping au niveau des lignes)
        textComp.textWrappingMode = TMPro.TextWrappingModes.NoWrap;
        textComp.overflowMode = TextOverflowModes.Overflow;
        
        if (customFont != null)
        {
            textComp.font = customFont;
        }
        
        // Utiliser ContentSizeFitter pour adapter la taille au contenu
        ContentSizeFitter fitter = textObj.AddComponent<ContentSizeFitter>();
        fitter.horizontalFit = ContentSizeFitter.FitMode.PreferredSize;
        fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
        
        // Forcer le recalcul du layout
        UnityEngine.UI.LayoutRebuilder.ForceRebuildLayoutImmediate(textRect);
        
        return textObj;
    }
    
    /// <summary>
    /// Estime la largeur d'un bouton de trou avant sa création (pour le calcul de layout)
    /// </summary>
    private float EstimateTrouButtonWidth(TrousHole hole)
    {
        // Estimation basée sur la plus longue option
        if (hole?.options == null || hole.options.Count == 0)
        {
            return 120f; // Largeur par défaut pour "CHOISIR"
        }
        
        // Trouver la plus longue option
        int maxLength = 0;
        foreach (var option in hole.options)
        {
            if (option.Length > maxLength)
            {
                maxLength = option.Length;
            }
        }
        
        // Estimation approximative : environ 12 pixels par caractère + padding
        var textConfig = cachedGameConfig?.textConfig;
        float fontSize = textConfig?.fontSize ?? 24f;
        float charWidth = fontSize * 0.5f; // Approximation
        float padding = 48f; // Padding horizontal
        
        return Mathf.Max(100f, (maxLength * charWidth) + padding);
    }
    
    /// <summary>
    /// Crée un bouton de trou et retourne sa largeur réelle
    /// </summary>
    private float CreateTrouButton(TrousHole hole, int index, Transform parent)
    {
        GameObject buttonObj = new GameObject($"TrouButton_{index + 1}");
        buttonObj.transform.SetParent(parent, false);
        
        RectTransform buttonRect = buttonObj.AddComponent<RectTransform>();
        
        // La taille initiale sera remplacée par TrouButton.Initialize()
        var textConfig = cachedGameConfig.textConfig;
        buttonRect.sizeDelta = new Vector2(150f, textConfig.fontSize * 1.5f);
        
        TrouButton trouButton = buttonObj.AddComponent<TrouButton>();
        trouButton.Initialize(hole, customFont, OnTrouButtonClicked, textConfig.fontSize);
        
        activeTrouButtons.Add(trouButton);
        
        // Récupérer la largeur réelle calculée par TrouButton
        float actualWidth = trouButton.GetCalculatedWidth();
        
        Debug.Log($"[TrousGameManager] Bouton de trou créé: {hole.id}, largeur calculée={actualWidth}px (options: {hole.options?.Count ?? 0})");
        
        return actualWidth;
    }
    
    private void OnTrouButtonClicked(TrouButton trouButton)
    {
        Debug.Log($"[TrousGameManager] Bouton de trou cliqué: {trouButton.GetHoleData().id}");
        
        // Fermer le popup actuel s'il existe
        CloseCurrentPopup();
        
        // Créer et afficher le popup de sélection
        GameObject popupObj = new GameObject("SelectionPopup");
        currentPopup = popupObj.AddComponent<SelectionPopup>();
        
        currentPopup.Initialize(
            trouButton.GetHoleData(),
            customFont,
            (selectedOption) => OnOptionSelected(trouButton, selectedOption)
        );
        
        currentPopup.Show();
    }
    
    private void OnOptionSelected(TrouButton trouButton, string selectedOption)
    {
        Debug.Log($"[TrousGameManager] Option sélectionnée: {selectedOption}");
        
        // Mettre à jour le bouton avec la réponse sélectionnée
        trouButton.SetSelectedAnswer(selectedOption);
        
        // Fermer le popup
        CloseCurrentPopup();
        
        // Vérifier si tous les trous sont remplis et mettre à jour le bouton de validation
        UpdateValidationButtonState();
    }
    
    private void CloseCurrentPopup()
    {
        if (currentPopup != null)
        {
            currentPopup.Close();
            currentPopup = null;
        }
    }
    
    private void SetupValidationButton()
    {
        // Trouver le Canvas
        Canvas canvas = textContainer != null ? textContainer.GetComponentInParent<Canvas>() : FindFirstObjectByType<Canvas>();
        if (canvas == null)
        {
            Debug.LogError("[TrousGameManager] Canvas introuvable pour créer le bouton de validation");
            return;
        }
        
        // Créer le bouton de validation s'il n'existe pas
        if (validationButton == null)
        {
            GameObject buttonObj = new GameObject("ValidationButton");
            buttonObj.transform.SetParent(canvas.transform, false);
            
            validationButton = buttonObj.AddComponent<ValidationButton>();
            validationButton.Initialize(customFont, OnValidateClicked);
        }
        
        // Mettre à jour l'état initial
        UpdateValidationButtonState();
    }
    
    private void UpdateValidationButtonState()
    {
        if (validationButton == null) return;
        
        // Vérifier si tous les trous sont remplis
        bool allFilled = true;
        foreach (var trouButton in activeTrouButtons)
        {
            if (trouButton == null || !trouButton.IsFilled())
            {
                allFilled = false;
                break;
            }
        }
        
        validationButton.SetActive(allFilled);
    }
    
    private void OnValidateClicked()
    {
        Debug.Log("[TrousGameManager] Validation déclenchée par le bouton");
        CheckAnswers();
    }
    
    // Méthodes obsolètes supprimées - remplacées par la nouvelle ergonomie avec TrouButton et SelectionPopup
    // Legacy methods removed - replaced by new ergonomics with TrouButton and SelectionPopup
    
    private void CheckAnswers()
    {
        Debug.Log("[TrousGameManager] Vérification des réponses");
        
        bool allCorrect = true;
        int correctCount = 0;
        
        // Réinitialiser la liste des réponses pour l'API
        lastAnswers.Clear();
        
        // Trier les trous pour correspondre avec les boutons
        List<TrousHole> sortedHoles = new List<TrousHole>(currentQuestion.holes);
        sortedHoles.Sort((a, b) => a.position.CompareTo(b.position));
        
        // Filtrer les trous valides
        List<TrousHole> validHoles = new List<TrousHole>();
        foreach (var hole in sortedHoles)
        {
            if (hole.position >= 0 && hole.position < currentQuestion.text.Length)
            {
                validHoles.Add(hole);
            }
        }
        
        // Comparer les réponses
        for (int i = 0; i < activeTrouButtons.Count && i < validHoles.Count; i++)
        {
            var trouButton = activeTrouButtons[i];
            var hole = validHoles[i];
            
            string selectedValue = trouButton.GetSelectedAnswer();
            bool isCorrect = selectedValue == hole.correctAnswer;
            
            // Mettre à jour l'état visuel du bouton
            trouButton.SetValidated(isCorrect);
            
            if (isCorrect)
            {
                correctCount++;
            }
            else
            {
                allCorrect = false;
            }
            
            // Stocker les données pour l'API answers
            // Utiliser l'ID de l'option sélectionnée comme answer_option_id
            if (int.TryParse(currentQuestion.id, out int questionId))
            {
                int selectedOptionId = 0;
                
                // Trouver l'ID de l'option sélectionnée
                if (hole.options != null && hole.optionIds != null && hole.optionIds.Count > 0)
                {
                    int selectedIndex = hole.options.IndexOf(selectedValue);
                    if (selectedIndex >= 0 && selectedIndex < hole.optionIds.Count)
                    {
                        selectedOptionId = hole.optionIds[selectedIndex];
                    }
                }
                
                if (selectedOptionId > 0)
                {
                    lastAnswers.Add((questionId, selectedOptionId));
                    Debug.Log($"[TrousGameManager] Réponse stockée pour API: questionId={questionId}, answer_option_id={selectedOptionId} (trou: '{hole.keyword}', valeur sélectionnée: '{selectedValue}')");
                }
                else
                {
                    Debug.LogWarning($"[TrousGameManager] ⚠️ ID de l'option non trouvé pour '{selectedValue}' (options: [{string.Join(", ", hole.options ?? new List<string>())}], optionIds: [{string.Join(", ", hole.optionIds ?? new List<int>())}])");
                }
            }
            else
            {
                Debug.LogWarning($"[TrousGameManager] ⚠️ questionId invalide: '{currentQuestion.id}'");
            }
        }
        
        // Désactiver le bouton de validation
        if (validationButton != null)
        {
            validationButton.SetActive(false);
        }
        
        // Afficher le feedback avec délai configurable (le son est joué APRÈS le délai)
        StartCoroutine(ShowFeedbackWithDelay(allCorrect, correctCount, activeTrouButtons.Count));
    }
    
    IEnumerator ShowFeedbackWithDelay(bool allCorrect, int correctCount, int totalCount)
    {
        // Obtenir le délai depuis la configuration
        float delay = GetFeedbackDisplayDelay();
        
        if (delay > 0)
        {
            Debug.Log($"[TrousGameManager] Attente de {delay}s avant affichage du feedback...");
            yield return new WaitForSeconds(delay);
        }
        
        // Jouer le son approprié AU MOMENT de l'affichage
        PlayFeedbackSound(allCorrect);
        
        StartCoroutine(ShowFeedback(allCorrect, correctCount, totalCount));
    }
    
    float GetFeedbackDisplayDelay()
    {
        // Priorité 1: Config du jeu actuel
        var feedbackConfig = GetFeedbackMessagesConfig();
        if (feedbackConfig != null && feedbackConfig.feedbackDisplayDelay > 0)
        {
            return feedbackConfig.feedbackDisplayDelay;
        }
        
        // Priorité 2: Config globale (general-config.json)
        if (GeneralConfigManager.Instance != null)
        {
            var defaultFeedback = GeneralConfigManager.Instance.GetDefaultFeedbackMessages();
            if (defaultFeedback != null)
            {
                return defaultFeedback.feedbackDisplayDelay;
            }
        }
        
        // Valeur par défaut
        return 0.5f;
    }
    
    /// <summary>
    /// Envoie les réponses des trous au serveur via l'API
    /// Chaque trou fait un appel séparé à l'endpoint
    /// </summary>
    private void SendAnswersToServer()
    {
        // Récupérer le gameId depuis GameDataManager
        int gameId = GameDataManager.Instance?.CurrentGameId ?? 0;
        
        if (gameId <= 0)
        {
            Debug.LogWarning("[TrousGameManager] ⚠️ Impossible d'envoyer les réponses: gameId non disponible");
            return;
        }
        
        if (lastAnswers == null || lastAnswers.Count == 0)
        {
            Debug.LogWarning("[TrousGameManager] ⚠️ Aucune réponse à envoyer");
            return;
        }
        
        // S'assurer que le service existe
        GameAnswerService.EnsureInstance();
        
        // Envoyer chaque réponse de trou individuellement (un appel par trou)
        int totalAnswers = lastAnswers.Count;
        int successCount = 0;
        int errorCount = 0;
        
        Debug.Log($"[TrousGameManager] 📤 Envoi de {totalAnswers} réponse(s) individuellement...");
        
        foreach (var answer in lastAnswers)
        {
            GameAnswerService.Instance.SendTrousAnswer(
                gameId,
                answer.questionId,
                answer.answerOptionId,
                onSuccess: () => 
                {
                    successCount++;
                    Debug.Log($"[TrousGameManager] ✅ Réponse {successCount}/{totalAnswers} envoyée (questionId={answer.questionId}, holeId={answer.answerOptionId})");
                },
                onError: (error) => 
                {
                    errorCount++;
                    Debug.LogWarning($"[TrousGameManager] ⚠️ Erreur envoi réponse {errorCount}: {error}");
                }
            );
        }
    }
    
    private IEnumerator ShowFeedback(bool allCorrect, int correctCount, int totalCount)
    {
        if (feedbackPanel == null || feedbackText == null)
        {
            Debug.LogWarning("[TrousGameManager] Panneau de feedback manquant");
            yield break;
        }
        
        // ==========================================
        // ENVOI DES RÉPONSES AU SERVEUR
        // ==========================================
        SendAnswersToServer();
        
        // 1. ACTIVER LE PANNEAU D'ABORD
        feedbackPanel.SetActive(true);
        
        // 2. La taille du panel sera appliquée dans ApplyFeedbackPanelStyling depuis general-config.json
        // On ne l'applique plus ici pour éviter les conflits
        
        // 3. CONSTRUIRE ET AFFICHER LE TEXTE
        string feedbackContent = BuildFeedbackMessage(allCorrect, correctCount, totalCount);
        feedbackText.text = feedbackContent;
        
        // 4. APPLIQUER LE STYLE DU TEXTE
        ApplyFeedbackTextStyling(allCorrect);
        
        // 5. NETTOYAGE ET STYLING DU PANNEAU
        yield return StartCoroutine(ApplyFeedbackPanelStyling(allCorrect));
        
        // 6. APPLIQUER L'IMAGE DE FOND (désactivé si on utilise le nouveau style)
        var feedbackConfig = GetFeedbackMessagesConfig();
        if (feedbackConfig != null && feedbackConfig.useBackgroundImage)
        {
            ApplyFeedbackBackgroundImage(allCorrect);
        }
        
        // 7. AJOUTER LE TEXTE D'INSTRUCTIONS
        CreateFeedbackInstructionsText();
        
        // 8. CONFIGURER LE CLIC
        SetupFeedbackClick();
    }
    
    /// <summary>
    /// Récupère la configuration feedbackMessages avec fallback vers defaultFeedbackMessages
    /// </summary>
    private FeedbackMessagesConfig GetFeedbackMessagesConfig()
    {
        // Vérifier si feedbackMessages existe ET est vraiment configuré (pas juste un objet vide avec valeurs par défaut)
        if (cachedGameConfig?.feedbackMessages != null && IsFeedbackMessagesConfigActuallyConfigured(cachedGameConfig.feedbackMessages))
        {
            Debug.Log("[TrousGameManager] Utilisation de feedbackMessages depuis le JSON du jeu");
            return cachedGameConfig.feedbackMessages;
        }

        // Sinon, utiliser defaultFeedbackMessages depuis general-config.json
        if (GeneralConfigManager.Instance == null)
        {
            Debug.LogWarning("[TrousGameManager] GeneralConfigManager.Instance est null");
            return null;
        }

        if (!GeneralConfigManager.Instance.IsConfigLoaded())
        {
            Debug.LogWarning("[TrousGameManager] GeneralConfigManager n'est pas encore chargé");
            return null;
        }

        var defaultFeedback = GeneralConfigManager.Instance.GetDefaultFeedbackMessages();
        if (defaultFeedback != null)
        {
            Debug.Log("[TrousGameManager] ✅ Utilisation de defaultFeedbackMessages depuis general-config.json");
            return ConvertDefaultFeedbackToFeedbackConfig(defaultFeedback);
        }

        Debug.LogWarning("[TrousGameManager] ⚠️ Aucune configuration feedbackMessages trouvée");
        return null;
    }

    /// <summary>
    /// Vérifie si un FeedbackMessagesConfig a été réellement configuré dans le JSON
    /// </summary>
    private bool IsFeedbackMessagesConfigActuallyConfigured(FeedbackMessagesConfig config)
    {
        if (config == null) return false;

        // Vérifier si les messages sont différents des valeurs par défaut de la classe
        // Les valeurs par défaut dans FeedbackMessagesConfig sont : 
        // correctAnswerMessage="BONNE RÉPONSE", incorrectAnswerMessage="MAUVAISE RÉPONSE", explanationTextColor="#FFFFFF", explanationTextSize=24
        // Dans general-config.json, les valeurs sont différentes
        
        // Si les messages sont les valeurs par défaut de la classe, c'est probablement un objet vide
        if (config.correctAnswerMessage == "BONNE RÉPONSE" && 
            config.incorrectAnswerMessage == "MAUVAISE RÉPONSE" &&
            config.explanationTextColor == "#FFFFFF" &&
            config.explanationTextSize == 24f)
        {
            // C'est probablement un objet vide avec valeurs par défaut
            return false;
        }

        // Si on arrive ici, l'objet a probablement été configuré
        return true;
    }

    /// <summary>
    /// Convertit DefaultFeedbackMessages en FeedbackMessagesConfig
    /// </summary>
    private FeedbackMessagesConfig ConvertDefaultFeedbackToFeedbackConfig(DefaultFeedbackMessages defaultConfig)
    {
        return new FeedbackMessagesConfig
        {
            correctAnswerMessage = defaultConfig.correctAnswerMessage,
            incorrectAnswerMessage = defaultConfig.incorrectAnswerMessage,
            showResultMessage = defaultConfig.showResultMessage,
            resultMessageInBold = defaultConfig.resultMessageInBold,
            resultMessageColor = defaultConfig.resultMessageColor,
            incorrectMessageColor = defaultConfig.incorrectMessageColor,
            resultMessageSize = defaultConfig.resultMessageSize,
            separator = defaultConfig.separator,
            showSeparatorLine = defaultConfig.showSeparatorLine,
            changeBackgroundColor = defaultConfig.changeBackgroundColor,
            useBackgroundImage = defaultConfig.useBackgroundImage,
            successBackgroundImageUrl = defaultConfig.successBackgroundImageUrl,
            failureBackgroundImageUrl = defaultConfig.failureBackgroundImageUrl,
            backgroundImageAlpha = defaultConfig.backgroundImageAlpha,
            stretchToFitPanel = defaultConfig.stretchToFitPanel,
            useCustomPanelSize = defaultConfig.useCustomPanelSize,
            panelSize = defaultConfig.panelSize,
            explanationTextColor = defaultConfig.explanationTextColor,
            explanationTextSize = defaultConfig.explanationTextSize,
            explanationTextBold = defaultConfig.explanationTextBold,
            centerTextInPanel = defaultConfig.centerTextInPanel,
            explanationTextPaddingLeft = defaultConfig.explanationTextPaddingLeft,
            explanationTextPaddingRight = defaultConfig.explanationTextPaddingRight,
            feedbackInstructionsText = defaultConfig.feedbackInstructionsText,
            feedbackInstructionsTextSize = defaultConfig.feedbackInstructionsTextSize,
            feedbackInstructionsTextColor = defaultConfig.feedbackInstructionsTextColor
        };
    }

    private string BuildFeedbackMessage(bool wasCorrect, int correctCount, int totalCount)
    {
        var feedbackConfig = GetFeedbackMessagesConfig();
        
        // Si pas de config, utiliser un fallback ultime
        if (feedbackConfig == null)
        {
            Debug.LogWarning("[TrousGameManager] ⚠️ Aucune configuration feedbackMessages disponible, utilisation d'un message par défaut");
            string defaultMessage = wasCorrect ? "BONNE RÉPONSE !" : "MAUVAISE RÉPONSE !";
            return $"<color={(wasCorrect ? "#00FF00" : "#FF0000")}><b>{defaultMessage}</b></color>\n\nRésultat: {correctCount}/{totalCount} bonnes réponses\n\n{currentQuestion.explanation}";
        }
        
        string resultMessage = "";
        
        // Message de résultat
        if (feedbackConfig.showResultMessage)
        {
            string baseMessage = wasCorrect ? 
                feedbackConfig.correctAnswerMessage : 
                feedbackConfig.incorrectAnswerMessage;
            
            string messageColor = wasCorrect ? 
                feedbackConfig.resultMessageColor : 
                feedbackConfig.incorrectMessageColor;
            
            string fontOpen = "";
            string fontClose = "";
            if (!string.IsNullOrEmpty(feedbackConfig.resultMessageFont))
            {
                fontOpen = $"<font=\"{feedbackConfig.resultMessageFont}\">";
                fontClose = "</font>";
            }
            
            if (feedbackConfig.resultMessageInBold)
            {
                resultMessage = $"{fontOpen}<color={messageColor}><b><size={feedbackConfig.resultMessageSize}>{baseMessage}</size></b></color>{fontClose}";
            }
            else
            {
                resultMessage = $"{fontOpen}<color={messageColor}><size={feedbackConfig.resultMessageSize}>{baseMessage}</size></color>{fontClose}";
            }
        }
        
        // Ajouter le résultat (X/Y bonnes réponses)
        string resultInfo = $"\n\nRésultat: {correctCount}/{totalCount} bonnes réponses";
        
        // Assemblage final
        string finalMessage = resultMessage + resultInfo;
        
        if (!string.IsNullOrEmpty(feedbackConfig.separator) && !string.IsNullOrEmpty(currentQuestion.explanation))
        {
            finalMessage += feedbackConfig.separator + currentQuestion.explanation;
        }
        else if (!string.IsNullOrEmpty(currentQuestion.explanation))
        {
            finalMessage += "\n\n" + currentQuestion.explanation;
        }
        
        return finalMessage;
    }
    
    private void ApplyFeedbackPanelSize()
    {
        var feedbackConfig = GetFeedbackMessagesConfig();
        if (feedbackConfig == null) return;
        
        // Utiliser la config (qui vient du JSON du jeu ou de general-config.json)
        bool useCustomSize = feedbackConfig.useCustomPanelSize;
        Vector2? panelSize = null;
        
        if (feedbackConfig.panelSize != null)
        {
            panelSize = new Vector2(feedbackConfig.panelSize.x, feedbackConfig.panelSize.y);
        }
        
        if (useCustomSize && panelSize != null)
        {
            RectTransform panelRect = feedbackPanel.GetComponent<RectTransform>();
            if (panelRect != null)
            {
                panelRect.sizeDelta = panelSize.Value;
                Debug.Log($"[TrousGameManager] Taille panneau appliquée: {panelSize.Value.x}x{panelSize.Value.y}");
            }
        }
    }
    
    private IEnumerator ApplyFeedbackPanelStyling(bool isCorrect)
    {
        if (feedbackPanel == null)
        {
            Debug.LogError("[TrousGameManager] feedbackPanel est null dans ApplyFeedbackPanelStyling");
            yield break;
        }

        // Appliquer le style de panel (depuis defaultFeedbackMessages.panelStyle fusionné)
        yield return StartCoroutine(FeedbackPanelStyler.ApplyPanelStyle(feedbackPanel));

        // Créer le picto de bonne/mauvaise réponse
        yield return StartCoroutine(FeedbackPanelStyler.CreateFeedbackPicto(feedbackPanel, isCorrect));

        Debug.Log("[TrousGameManager] Style de panel feedback appliqué");
    }
    
    private void ApplyFeedbackTextStyling(bool wasCorrect)
    {
        var feedbackConfig = GetFeedbackMessagesConfig();
        if (feedbackConfig == null || feedbackText == null) return;
        
        // Taille du texte d'explication
        float textSize = feedbackConfig.explanationTextSize;
        if (textSize > 0)
        {
            feedbackText.fontSize = (int)textSize;
        }
        
        // Couleur du texte d'explication
        string textColor = feedbackConfig.explanationTextColor;
        if (!string.IsNullOrEmpty(textColor))
        {
            feedbackText.color = ParseColor(textColor);
        }
        
        // Style gras
        bool isBold = feedbackConfig.explanationTextBold;
        feedbackText.fontStyle = isBold ? FontStyles.Bold : FontStyles.Normal;
        
        // Aligner le texte - centré horizontalement mais EN HAUT verticalement
        bool centerText = feedbackConfig.centerTextInPanel;
        if (centerText)
        {
            feedbackText.alignment = TextAlignmentOptions.Top;
        }
        else
        {
            feedbackText.alignment = TextAlignmentOptions.TopLeft;
        }

        // Appliquer le padding au texte
        RectTransform textRect = feedbackText.GetComponent<RectTransform>();
        if (textRect != null)
        {
            float paddingLeft = feedbackConfig.explanationTextPaddingLeft > 0 
                ? feedbackConfig.explanationTextPaddingLeft 
                : 20f;
            float paddingRight = feedbackConfig.explanationTextPaddingRight > 0 
                ? feedbackConfig.explanationTextPaddingRight 
                : 20f;
            
            // Position verticale du titre (depuis le haut du panel, en pixels)
            // Utiliser defaultFeedbackMessages si feedbackConfig n'a pas la valeur
            var defaultFeedback = GeneralConfigManager.Instance?.GetDefaultFeedbackMessages();
            float titlePositionY = feedbackConfig.resultMessagePositionY > 0 
                ? feedbackConfig.resultMessagePositionY 
                : (defaultFeedback?.resultMessagePositionY ?? 100f);
            float paddingTop = titlePositionY;
            float paddingBottom = 80f; // Espace pour le bouton

            textRect.anchorMin = Vector2.zero;
            textRect.anchorMax = Vector2.one;
            textRect.offsetMin = new Vector2(paddingLeft, paddingBottom);
            textRect.offsetMax = new Vector2(-paddingRight, -paddingTop);
        }
    }
    
    private void ApplyFeedbackBackgroundImage(bool wasCorrect)
    {
        var feedbackConfig = GetFeedbackMessagesConfig();
        if (feedbackConfig == null) return;
        
        // Utiliser la config (qui vient du JSON du jeu ou de general-config.json)
        bool useBackgroundImage = feedbackConfig.useBackgroundImage;
        
        if (!useBackgroundImage)
        {
            Debug.Log("[TrousGameManager] Pas d'image de fond à appliquer");
            return;
        }
        
        // Choisir le bon sprite selon le résultat
        string spriteKey = wasCorrect ? "success_bg" : "failure_bg";
        Sprite spriteToUse = loadedSprites.ContainsKey(spriteKey) ? loadedSprites[spriteKey] : null;
        
        if (spriteToUse == null)
        {
            Debug.LogWarning($"[TrousGameManager] Sprite {(wasCorrect ? "succès" : "échec")} non chargé");
            return;
        }
        
        // Créer ou réutiliser l'image de fond
        if (feedbackBackgroundImage == null)
        {
            GameObject backgroundObj = new GameObject("FeedbackBackgroundImage");
            backgroundObj.transform.SetParent(feedbackPanel.transform, false);
            backgroundObj.transform.SetSiblingIndex(0);
            
            RectTransform bgRect = backgroundObj.AddComponent<RectTransform>();
            bgRect.anchorMin = Vector2.zero;
            bgRect.anchorMax = Vector2.one;
            bgRect.offsetMin = Vector2.zero;
            bgRect.offsetMax = Vector2.zero;
            
            feedbackBackgroundImage = backgroundObj.AddComponent<Image>();
        }
        
        // Appliquer le sprite approprié
        feedbackBackgroundImage.sprite = spriteToUse;
        
        // Alpha depuis la config
        float alpha = feedbackConfig.backgroundImageAlpha;
        Color imageColor = Color.white;
        imageColor.a = alpha;
        feedbackBackgroundImage.color = imageColor;
        
        // Stretch depuis la config
        bool stretch = feedbackConfig.stretchToFitPanel;
        if (stretch)
        {
            feedbackBackgroundImage.type = Image.Type.Sliced;
            feedbackBackgroundImage.preserveAspect = false;
        }
        else
        {
            feedbackBackgroundImage.type = Image.Type.Simple;
            feedbackBackgroundImage.preserveAspect = true;
        }
        
        feedbackBackgroundImage.gameObject.SetActive(true);
        
        Debug.Log($"[TrousGameManager] Image de fond {(wasCorrect ? "succès" : "échec")} appliquée");
    }
    
    private void SetupFeedbackClick()
    {
        CreateFeedbackBlockingOverlay();
        
        // Supprimer tout bouton existant sur le panel (on ne veut plus de clic sur le panel entier)
        Button panelButton = feedbackPanel.GetComponent<Button>();
        if (panelButton != null)
        {
            panelButton.onClick.RemoveAllListeners();
            Destroy(panelButton);
        }
        
        // Créer le bouton "FERMER" en bas du panel
        CreateFeedbackCloseButton();
    }
    
    private void CreateFeedbackCloseButton()
    {
        // Chercher ou créer le bouton
        Transform existing = feedbackPanel.transform.Find("FeedbackCloseButton");
        GameObject closeButtonObj;
        if (existing != null)
        {
            closeButtonObj = existing.gameObject;
        }
        else
        {
            closeButtonObj = new GameObject("FeedbackCloseButton");
            closeButtonObj.transform.SetParent(feedbackPanel.transform, false);
        }
        
        RectTransform btnRect = closeButtonObj.GetComponent<RectTransform>();
        if (btnRect == null)
        {
            btnRect = closeButtonObj.AddComponent<RectTransform>();
        }
        
        // Positionner en bas au centre du panneau
        btnRect.anchorMin = new Vector2(0.5f, 0f);
        btnRect.anchorMax = new Vector2(0.5f, 0f);
        btnRect.pivot = new Vector2(0.5f, 0.5f);
        btnRect.anchoredPosition = new Vector2(0f, 70f);
        
        // Image du bouton
        Image btnImage = closeButtonObj.GetComponent<Image>();
        if (btnImage == null)
        {
            btnImage = closeButtonObj.AddComponent<Image>();
        }
        
        // Utiliser le style validationDefault
        ButtonStyleConfig buttonStyle = null;
        
        if (GeneralConfigManager.Instance != null)
        {
            buttonStyle = GeneralConfigManager.Instance.GetButtonStyle("validationDefault");
        }
        
        // Dimensions du bouton
        float btnWidth = buttonStyle?.width ?? 300f;
        float btnHeight = buttonStyle?.height ?? 80f;
        float borderRadius = buttonStyle?.borderRadius ?? 35f;
        float borderWidth = buttonStyle?.borderWidth ?? 4f;
        
        btnRect.sizeDelta = new Vector2(btnWidth, btnHeight);
        
        // Couleurs du dégradé et bordure
        Color startColor = ParseColorHex(buttonStyle?.gradient?.startColor ?? "#CE9BFD");
        Color endColor = ParseColorHex(buttonStyle?.gradient?.endColor ?? "#9A2DFF");
        Color borderColor = ParseColorHex(buttonStyle?.borderColor ?? "#f5ece5");
        
        // Créer le sprite avec dégradé à haute résolution (2x)
        int texWidth = (int)(btnWidth * 2);
        int texHeight = (int)(btnHeight * 2);
        float texRadius = borderRadius * 2;
        float texBorderWidth = borderWidth * 2;
        
        Sprite gradientSprite = CreateGradientSpriteWithBorder(
            texWidth, texHeight, texRadius, startColor, endColor, borderColor, texBorderWidth);
        
        btnImage.sprite = gradientSprite;
        btnImage.type = Image.Type.Simple;
        btnImage.color = Color.white;
        btnImage.raycastTarget = true;
        
        // Ajouter l'ombre
        Shadow shadow = closeButtonObj.GetComponent<Shadow>();
        if (shadow == null)
        {
            shadow = closeButtonObj.AddComponent<Shadow>();
        }
        shadow.effectColor = ParseColorHex(buttonStyle?.shadow?.color ?? "#00000040");
        // Respecter le style (comme Intro/validationDefault)
        if (buttonStyle?.shadow != null && buttonStyle.shadow.enabled)
            shadow.effectDistance = new Vector2(buttonStyle.shadow.offsetX, -buttonStyle.shadow.offsetY);
        else
        shadow.effectDistance = new Vector2(0, -6);
        
        Debug.Log($"[TrousGameManager] Bouton FERMER créé - {btnWidth}x{btnHeight}, radius={borderRadius}");
        
        // Composant Button
        Button closeButton = closeButtonObj.GetComponent<Button>();
        if (closeButton == null)
        {
            closeButton = closeButtonObj.AddComponent<Button>();
        }
        closeButton.targetGraphic = btnImage;
        closeButton.transition = Selectable.Transition.ColorTint;
        
        // Améliorer les couleurs de transition
        ColorBlock colors = closeButton.colors;
        colors.normalColor = Color.white;
        colors.highlightedColor = new Color(1.1f, 1.1f, 1.1f, 1f);
        colors.pressedColor = new Color(0.9f, 0.9f, 0.9f, 1f);
        colors.selectedColor = Color.white;
        closeButton.colors = colors;
        
        closeButton.onClick.RemoveAllListeners();
        closeButton.onClick.AddListener(() =>
        {
            Debug.Log("[TrousGameManager] Clic sur le bouton FERMER");
            NextQuestion();
        });
        
        // Texte du bouton - TOUJOURS "FERMER" pour les jeux trous
        TextMeshProUGUI label = closeButtonObj.GetComponentInChildren<TextMeshProUGUI>();
        if (label == null)
        {
            GameObject labelObj = new GameObject("Label");
            labelObj.transform.SetParent(closeButtonObj.transform, false);
            label = labelObj.AddComponent<TextMeshProUGUI>();
            
            RectTransform labelRect = labelObj.GetComponent<RectTransform>();
            labelRect.anchorMin = Vector2.zero;
            labelRect.anchorMax = Vector2.one;
            labelRect.offsetMin = Vector2.zero;
            labelRect.offsetMax = Vector2.zero;
        }
        
        // Texte FERMER (forcé pour les jeux trous)
        label.text = "FERMER";
        label.alignment = TextAlignmentOptions.Center;
        label.verticalAlignment = VerticalAlignmentOptions.Middle;
        
        // Style du texte depuis validationDefault
        float fontSize = buttonStyle?.text?.fontSize ?? 28f;
        label.fontSize = fontSize;
        label.color = ParseColorHex(buttonStyle?.text?.color ?? "#FFFFFF");
        label.fontStyle = buttonStyle?.text?.GetFontStyle() ?? FontStyles.Normal;
        label.raycastTarget = false;
        
        // Charger la police Anton-Regular SDF
        string fontFamily = buttonStyle?.text?.fontFamily ?? "Anton-Regular SDF";
        TMP_FontAsset font = Resources.Load<TMP_FontAsset>($"Fonts/{fontFamily}");
        if (font == null)
        {
            font = Resources.Load<TMP_FontAsset>($"Fonts & Materials/{fontFamily}");
        }
        if (font != null)
        {
            label.font = font;
        }
    }
    
    private Color ParseColorHex(string hex)
    {
        if (string.IsNullOrEmpty(hex)) return Color.white;
        
        hex = hex.TrimStart('#');
        
        if (hex.Length == 6)
        {
            byte r = byte.Parse(hex.Substring(0, 2), System.Globalization.NumberStyles.HexNumber);
            byte g = byte.Parse(hex.Substring(2, 2), System.Globalization.NumberStyles.HexNumber);
            byte b = byte.Parse(hex.Substring(4, 2), System.Globalization.NumberStyles.HexNumber);
            return new Color32(r, g, b, 255);
        }
        else if (hex.Length == 8)
        {
            byte r = byte.Parse(hex.Substring(0, 2), System.Globalization.NumberStyles.HexNumber);
            byte g = byte.Parse(hex.Substring(2, 2), System.Globalization.NumberStyles.HexNumber);
            byte b = byte.Parse(hex.Substring(4, 2), System.Globalization.NumberStyles.HexNumber);
            byte a = byte.Parse(hex.Substring(6, 2), System.Globalization.NumberStyles.HexNumber);
            return new Color32(r, g, b, a);
        }
        
        return Color.white;
    }
    
    private Sprite CreateGradientSpriteWithBorder(int width, int height, float radius, Color startColor, Color endColor, Color borderColor, float borderWidth)
    {
        Texture2D texture = new Texture2D(width, height, TextureFormat.RGBA32, false);
        texture.filterMode = FilterMode.Bilinear;
        texture.wrapMode = TextureWrapMode.Clamp;
        
        Color[] pixels = new Color[width * height];
        
        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                // Calculer la distance au bord le plus proche (pour les coins arrondis)
                float distToBorder = CalculateDistanceToBorder(x, y, width, height, radius);
                
                if (distToBorder < 0)
                {
                    // En dehors de la forme = transparent
                    pixels[y * width + x] = Color.clear;
                }
                else if (distToBorder < borderWidth)
                {
                    // Dans la bordure - avec anti-aliasing sur le bord externe
                    float edgeAlpha = Mathf.Clamp01(distToBorder + 1f);
                    Color border = borderColor;
                    border.a *= edgeAlpha;
                    pixels[y * width + x] = border;
                }
                else
                {
                    // À l'intérieur - dégradé vertical (du bas vers le haut)
                    float t = (float)y / height;
                    Color gradientColor = Color.Lerp(endColor, startColor, t);
                    pixels[y * width + x] = gradientColor;
                }
            }
        }
        
        texture.SetPixels(pixels);
        texture.Apply();
        dynamicTextures.Add(texture);
        
        Sprite sprite = Sprite.Create(texture, new Rect(0, 0, width, height), new Vector2(0.5f, 0.5f), 100f);
        dynamicSprites.Add(sprite);
        return sprite;
    }
    
    private float CalculateDistanceToBorder(int x, int y, int width, int height, float radius)
    {
        // Distance aux bords droits
        float distLeft = x;
        float distRight = width - 1 - x;
        float distBottom = y;
        float distTop = height - 1 - y;
        
        // Pour les coins, calculer la distance au cercle
        // Coin bas-gauche
        if (x < radius && y < radius)
        {
            float dx = radius - x;
            float dy = radius - y;
            float dist = Mathf.Sqrt(dx * dx + dy * dy);
            return radius - dist;
        }
        // Coin bas-droit
        if (x > width - 1 - radius && y < radius)
        {
            float dx = x - (width - 1 - radius);
            float dy = radius - y;
            float dist = Mathf.Sqrt(dx * dx + dy * dy);
            return radius - dist;
        }
        // Coin haut-gauche
        if (x < radius && y > height - 1 - radius)
        {
            float dx = radius - x;
            float dy = y - (height - 1 - radius);
            float dist = Mathf.Sqrt(dx * dx + dy * dy);
            return radius - dist;
        }
        // Coin haut-droit
        if (x > width - 1 - radius && y > height - 1 - radius)
        {
            float dx = x - (width - 1 - radius);
            float dy = y - (height - 1 - radius);
            float dist = Mathf.Sqrt(dx * dx + dy * dy);
            return radius - dist;
        }
        
        // Pour les bords droits, retourner la distance minimale
        return Mathf.Min(distLeft, distRight, distBottom, distTop);
    }
    
    TextMeshProUGUI feedbackInstructionsText;
    
    void CreateFeedbackInstructionsText()
    {
        var feedbackConfig = GetFeedbackMessagesConfig();
        if (feedbackConfig == null || string.IsNullOrEmpty(feedbackConfig.feedbackInstructionsText))
        {
            if (feedbackInstructionsText != null)
            {
                feedbackInstructionsText.gameObject.SetActive(false);
            }
            return;
        }

        if (feedbackInstructionsText == null)
        {
            GameObject instructionsObj = new GameObject("FeedbackInstructions");
            instructionsObj.transform.SetParent(feedbackPanel.transform, false);
            feedbackInstructionsText = instructionsObj.AddComponent<TextMeshProUGUI>();

            RectTransform instructionsRect = instructionsObj.GetComponent<RectTransform>();
            instructionsRect.anchorMin = new Vector2(0, 0);
            instructionsRect.anchorMax = new Vector2(1, 0);
            instructionsRect.pivot = new Vector2(0.5f, 0);
            instructionsRect.anchoredPosition = new Vector2(0, 20);
            instructionsRect.sizeDelta = new Vector2(-40, 40);

            feedbackInstructionsText.alignment = TextAlignmentOptions.Center;
            feedbackInstructionsText.fontStyle = FontStyles.Italic;
        }

        feedbackInstructionsText.gameObject.SetActive(true);
        feedbackInstructionsText.text = feedbackConfig.feedbackInstructionsText;
        
        feedbackInstructionsText.fontSize = feedbackConfig.feedbackInstructionsTextSize > 0 
            ? feedbackConfig.feedbackInstructionsTextSize 
            : 18;
        
        if (!string.IsNullOrEmpty(feedbackConfig.feedbackInstructionsTextColor) && 
            ColorUtility.TryParseHtmlString(feedbackConfig.feedbackInstructionsTextColor, out Color textColor))
        {
            feedbackInstructionsText.color = textColor;
        }
        else
        {
            feedbackInstructionsText.color = new Color(0.5f, 0.5f, 0.5f, 1f);
        }
    }
    
    GameObject feedbackBlockingOverlay;
    Canvas feedbackOverlayCanvas;
    
    void CreateFeedbackBlockingOverlay()
    {
        if (feedbackBlockingOverlay == null)
        {
            GameObject overlayCanvasObj = new GameObject("FeedbackOverlayCanvas");
            feedbackOverlayCanvas = overlayCanvasObj.AddComponent<Canvas>();
            feedbackOverlayCanvas.renderMode = RenderMode.ScreenSpaceOverlay;
            feedbackOverlayCanvas.overrideSorting = true;
            feedbackOverlayCanvas.sortingOrder = 9999;
            
            CanvasScaler scaler = overlayCanvasObj.AddComponent<CanvasScaler>();
            scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
            
            if (cachedGameConfig != null && cachedGameConfig.resolution != null)
            {
                scaler.referenceResolution = new Vector2(cachedGameConfig.resolution.width, cachedGameConfig.resolution.height);
            }
            else
            {
                scaler.referenceResolution = new Vector2(1920, 1080);
            }
            scaler.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight;
            scaler.matchWidthOrHeight = 0.5f;
            
            overlayCanvasObj.AddComponent<GraphicRaycaster>();

            feedbackBlockingOverlay = new GameObject("FeedbackBlockingOverlay");
            feedbackBlockingOverlay.transform.SetParent(overlayCanvasObj.transform, false);

            RectTransform overlayRect = feedbackBlockingOverlay.AddComponent<RectTransform>();
            overlayRect.anchorMin = Vector2.zero;
            overlayRect.anchorMax = Vector2.one;
            overlayRect.sizeDelta = Vector2.zero;
            overlayRect.anchoredPosition = Vector2.zero;

            Image overlayImage = feedbackBlockingOverlay.AddComponent<Image>();
            overlayImage.color = new Color(0, 0, 0, 0f);
            overlayImage.raycastTarget = true;

            Button overlayButton = feedbackBlockingOverlay.AddComponent<Button>();
            overlayButton.transition = Selectable.Transition.None;
            overlayButton.targetGraphic = overlayImage;
            overlayButton.onClick.AddListener(() =>
            {
                NextQuestion();
            });
        }

        if (feedbackOverlayCanvas != null)
        {
            feedbackOverlayCanvas.gameObject.SetActive(true);
        }
        feedbackBlockingOverlay.SetActive(true);
        
        if (feedbackPanel != null && feedbackPanel.transform.parent != feedbackOverlayCanvas.transform)
        {
            RectTransform feedbackRect = feedbackPanel.GetComponent<RectTransform>();
            Vector2 anchoredPos = feedbackRect.anchoredPosition;
            Vector2 sizeDelta = feedbackRect.sizeDelta;
            Vector2 anchorMin = feedbackRect.anchorMin;
            Vector2 anchorMax = feedbackRect.anchorMax;
            Vector3 localScale = feedbackPanel.transform.localScale;
            
            feedbackPanel.transform.SetParent(feedbackOverlayCanvas.transform, false);
            
            feedbackRect.anchoredPosition = anchoredPos;
            feedbackRect.sizeDelta = sizeDelta;
            feedbackRect.anchorMin = anchorMin;
            feedbackRect.anchorMax = anchorMax;
            feedbackPanel.transform.localScale = localScale;
        }
        
        if (feedbackPanel != null)
        {
            feedbackPanel.transform.SetAsLastSibling();
        }
    }
    
    private void NextQuestion()
    {
        if (feedbackPanel != null)
        {
            feedbackPanel.SetActive(false);
        }
        
        if (feedbackBlockingOverlay != null)
        {
            feedbackBlockingOverlay.SetActive(false);
        }
        if (feedbackOverlayCanvas != null)
        {
            feedbackOverlayCanvas.gameObject.SetActive(false);
        }
        
        currentQuestionIndex++;
        
        if (currentQuestionIndex >= questions.Count)
        {
            EndGame();
        }
        else
        {
            LoadQuestion(currentQuestionIndex);
        }
    }
    
    private void PlayFeedbackSound(bool success)
    {
        if (audioSource == null) return;
        
        string soundKey = success ? "success" : "fail";
        if (loadedAudioClips.ContainsKey(soundKey))
        {
            audioSource.PlayOneShot(loadedAudioClips[soundKey]);
        }
    }
    
    /// <summary>
    /// Enregistre le résultat du jeu dans GameResultsManager
    /// </summary>
    void RecordGameResult(bool isSuccess, int correctCount, int totalCount)
    {
        // S'assurer que GameResultsManager existe
        if (GameResultsManager.Instance == null)
        {
            GameObject resultsManagerObj = new GameObject("GameResultsManager");
            resultsManagerObj.AddComponent<GameResultsManager>();
            Debug.Log("[TrousGameManager] GameResultsManager créé automatiquement");
        }
        
        // Identifier le jeu par son configUrl
        string gameId = configUrl;
        if (string.IsNullOrEmpty(gameId))
        {
            gameId = PlayerPrefs.GetString("GameConfigUrl", "unknown");
        }
        
        // Extraire un nom de jeu depuis l'URL
        string gameName = gameId;
        if (!string.IsNullOrEmpty(gameId))
        {
            // Enlever le chemin et l'extension
            int lastSlash = gameId.LastIndexOf('/');
            if (lastSlash >= 0)
            {
                gameName = gameId.Substring(lastSlash + 1);
            }
            int lastDot = gameName.LastIndexOf('.');
            if (lastDot >= 0)
            {
                gameName = gameName.Substring(0, lastDot);
            }
        }
        
        // Enregistrer le résultat
        GameResultsManager.Instance.RecordGameResult(
            gameId: gameId,
            gameName: gameName,
            isSuccess: isSuccess,
            correctAnswers: correctCount,
            totalAnswers: totalCount,
            gameType: "trous"
        );
    }
    
    private void EndGame()
    {
        Debug.Log("[TrousGameManager] Fin du jeu");
        
        // Déterminer le résultat : succès si toutes les réponses sont correctes, échec sinon
        bool allCorrect = true;
        foreach (var trouButton in activeTrouButtons)
        {
            if (trouButton != null)
            {
                var hole = trouButton.GetHoleData();
                if (hole != null && trouButton.GetSelectedAnswer() != hole.correctAnswer)
            {
                allCorrect = false;
                break;
                }
            }
        }
        
        string result = allCorrect ? "success" : "fail";
        Debug.Log($"[TrousGameManager] Résultat du jeu: {result}");
        
        // Compter les réponses correctes et totales
        int correctCount = 0;
        int totalCount = activeTrouButtons.Count;
        foreach (var trouButton in activeTrouButtons)
        {
            if (trouButton != null)
            {
                var hole = trouButton.GetHoleData();
                if (hole != null && trouButton.GetSelectedAnswer() == hole.correctAnswer)
                {
                    correctCount++;
                }
            }
        }
        
        // Enregistrer le résultat dans GameResultsManager
        RecordGameResult(allCorrect, correctCount, totalCount);
        
        // Sauvegarder le résultat pour le dialogue "After"
        PlayerPrefs.SetString("LastGameResult", result);
        
        // Vérifier si on utilise le nouveau système (dialogues groupés) ou l'ancien
        string gameConfigUrl = PlayerPrefs.GetString("GameConfigUrl");
        if (!string.IsNullOrEmpty(gameConfigUrl))
        {
            // Nouveau système avec GameConfigLoader
            Debug.Log("[TrousGameManager] Utilisation du système de dialogues groupés");
            PlayerPrefs.SetString("GamePhase", "After");
            PlayerPrefs.Save();
            SceneManager.LoadScene("Player");
        }
        else
        {
            // Ancien système avec dialogues séparés
            string dialogueUrl = allCorrect ? 
                PlayerPrefs.GetString("DialogueSuccessUrl") : 
                PlayerPrefs.GetString("DialogueFailUrl");
        
        if (!string.IsNullOrEmpty(dialogueUrl))
        {
            PlayerPrefs.SetString("NextDialogueUrl", dialogueUrl);
            PlayerPrefs.SetString("GamePhase", "After");
            SceneManager.LoadScene("Player");
        }
        else
        {
                // Retour direct à la scène d'origine (Menu ou Map)
                ReturnToOriginScene();
            }
        }
    }
    
    private void ClearTrouButtons()
    {
        foreach (var trouButton in activeTrouButtons)
        {
            if (trouButton != null && trouButton.gameObject != null)
            {
                Destroy(trouButton.gameObject);
                }
                }
        activeTrouButtons.Clear();
    }
    
    private void SetupDialogues()
    {
        // Récupérer les URLs de dialogue depuis PlayerPrefs
        dBeforeUrl = PlayerPrefs.GetString("DialogueBeforeUrl");
        dSuccessUrl = PlayerPrefs.GetString("DialogueSuccessUrl");
        dFailUrl = PlayerPrefs.GetString("DialogueFailUrl");
        
        Debug.Log($"[TrousGameManager] Dialogues configurés - Before: {dBeforeUrl}, Success: {dSuccessUrl}, Fail: {dFailUrl}");
    }
    
    // Méthodes utilitaires pour le parsing
    private Color ParseColor(string colorString)
    {
        if (ColorUtility.TryParseHtmlString(colorString, out Color color))
        {
            return color;
        }
        return Color.white;
    }
    
    private Color ParseColorWithAlpha(string colorString, float alpha)
    {
        Color color = ParseColor(colorString);
        color.a = alpha;
        return color;
    }
    
    private TextAlignmentOptions ParseTextAlignment(string alignment)
    {
        switch (alignment.ToLower())
        {
            case "left": return TextAlignmentOptions.Left;
            case "center": return TextAlignmentOptions.Center;
            case "right": return TextAlignmentOptions.Right;
            case "justified": return TextAlignmentOptions.Justified;
            default: return TextAlignmentOptions.Left;
        }
    }
    
    // Méthode publique pour charger depuis une URL (compatible avec GameLauncher)
    public void LoadGameFromURL(string url)
    {
        Debug.Log("═══════════════════════════════════════");
        Debug.Log($"[TrousGameManager] LoadGameFromURL appelé avec: {url}");
        Debug.Log($"Scène actuelle: {SceneManager.GetActiveScene().name}");
        Debug.Log("═══════════════════════════════════════");
        
        // Si déjà en cours d'initialisation, arrêter la coroutine précédente et relancer
        if (isInitializing)
        {
            Debug.LogWarning("[TrousGameManager] Initialisation déjà en cours, arrêt et relance avec nouvelle URL");
            StopAllCoroutines();
            isInitializing = false;
            jsonLoaded = false;
        }
        
        // Vérifier que l'URL n'est pas vide
        if (string.IsNullOrEmpty(url))
        {
            Debug.LogError("[TrousGameManager] ❌ URL vide dans LoadGameFromURL !");
            url = PlayerPrefs.GetString("GameConfigUrl", "");
            if (string.IsNullOrEmpty(url))
            {
                Debug.LogError("[TrousGameManager] ❌ Impossible de récupérer l'URL depuis PlayerPrefs");
                return;
            }
            Debug.LogWarning($"[TrousGameManager] ⚠️ Utilisation de l'URL depuis PlayerPrefs: {url}");
        }
        
        configUrl = url;
        isInitializing = true;
        
        // Afficher l'écran de chargement
        if (UnifiedLoadingManager.Instance != null)
        {
            UnifiedLoadingManager.ShowLoading("Chargement du jeu de trous...", LoadingContext.Game);
        }
        
        Debug.Log($"[TrousGameManager] Démarrage de LoadGameConfig() avec URL: {configUrl}");
        StartCoroutine(LoadGameConfig());
    }
    
    /// <summary>
    /// Charge le jeu depuis les données de l'API (via GameDataManager)
    /// </summary>
    public void LoadGameFromApiData(APIGameData apiData)
    {
        if (apiData == null)
        {
            Debug.LogError("[TrousGameManager] ❌ Données API nulles");
            return;
        }
        
        Debug.Log("[TrousGameManager] ✅ Chargement depuis les données API");
        Debug.Log($"[TrousGameManager] Background: {apiData.background?.type} - {apiData.background?.url}");
        Debug.Log($"[TrousGameManager] Questions: {apiData.questions?.Length ?? 0}");
        
        // Logger la config de zone de texte
        var textAreaConfig = apiData.textArea ?? apiData.textConfig;
        if (textAreaConfig != null)
        {
            Debug.Log($"[TrousGameManager] TextArea: x={textAreaConfig.x}, y={textAreaConfig.y}, w={textAreaConfig.width}, h={textAreaConfig.height}");
        }
        else
        {
            Debug.Log("[TrousGameManager] TextArea: non définie dans l'API");
        }
        
        // Empêcher le chargement automatique
        loadingFromApi = true;
        isInitializing = true;
        jsonLoaded = true;
        
        // Arrêter toute coroutine en cours
        StopAllCoroutines();
        
        // Charger depuis les données API
        StartCoroutine(LoadGameFromApiDataCoroutine(apiData));
    }
    
    private IEnumerator LoadGameFromApiDataCoroutine(APIGameData apiData)
    {
        // Afficher l'écran de chargement
        if (UnifiedLoadingManager.Instance != null)
        {
            UnifiedLoadingManager.ShowLoading("Chargement du jeu de texte...", LoadingContext.Game);
        }
        
        // Attendre que GeneralConfigManager soit prêt
        while (GeneralConfigManager.Instance == null || !GeneralConfigManager.Instance.IsConfigLoaded())
        {
            yield return null;
        }
        
        // ═══════════════════════════════════════════════════════════════════
        // RESET DES RÉPONSES AVANT DE COMMENCER LE JEU
        // ═══════════════════════════════════════════════════════════════════
        int gameId = GameDataManager.Instance?.CurrentGameId ?? 0;
        if (gameId > 0)
        {
            bool resetSuccess = false;
            bool resetCompleted = false;
            string resetError = null;
            
            GameAnswerService.EnsureInstance();
            GameAnswerService.Instance.ResetGameAnswers(
                gameId,
                onSuccess: () => { resetSuccess = true; resetCompleted = true; },
                onError: (error) => { resetError = error; resetCompleted = true; }
            );
            
            // Attendre la fin du reset
            float timeout = 10f;
            float elapsed = 0f;
            while (!resetCompleted && elapsed < timeout)
            {
                elapsed += Time.deltaTime;
                yield return null;
            }
            
            if (!resetCompleted)
            {
                Debug.LogError("[TrousGameManager] ❌ Timeout lors du reset des réponses");
                if (UnifiedLoadingManager.Instance != null)
                {
                    UnifiedLoadingManager.HideLoading();
                }
                yield break;
            }
            
            if (!resetSuccess)
            {
                Debug.LogError($"[TrousGameManager] ❌ Échec du reset des réponses: {resetError}");
                if (UnifiedLoadingManager.Instance != null)
                {
                    UnifiedLoadingManager.HideLoading();
                }
                yield break;
            }
            
            Debug.Log($"[TrousGameManager] ✅ Reset des réponses réussi pour gameId: {gameId}");
        }
        else
        {
            Debug.LogWarning("[TrousGameManager] ⚠️ Pas de gameId disponible, skip du reset des réponses");
        }
        // ═══════════════════════════════════════════════════════════════════
        
        // Charger les sprites de feedback depuis GeneralConfigManager
        var feedbackConfig = GeneralConfigManager.Instance.GetDefaultFeedbackMessages();
        if (feedbackConfig != null && feedbackConfig.useBackgroundImage)
        {
            Debug.Log("[TrousGameManager] Chargement des sprites de feedback...");
            
            if (!string.IsNullOrEmpty(feedbackConfig.successBackgroundImageUrl))
            {
                string successUrl = GeneralConfigManager.Instance.GetUIUrl(feedbackConfig.successBackgroundImageUrl);
                yield return StartCoroutine(LoadSprite(successUrl, "success_bg"));
            }
            
            if (!string.IsNullOrEmpty(feedbackConfig.failureBackgroundImageUrl))
            {
                string failureUrl = GeneralConfigManager.Instance.GetUIUrl(feedbackConfig.failureBackgroundImageUrl);
                yield return StartCoroutine(LoadSprite(failureUrl, "failure_bg"));
            }
        }
        
        // Charger le BACKDROP (image d'habillage à l'extérieur de la zone de jeu)
        if (apiData.backdrop != null && !string.IsNullOrEmpty(apiData.backdrop.url))
        {
            string backdropUrl = apiData.backdrop.url;
            if (!backdropUrl.StartsWith("http"))
            {
                backdropUrl = GeneralConfigManager.Instance.GetBackgroundImageUrl(backdropUrl);
            }
            
            Debug.Log($"[TrousGameManager] 🖼️ Backdrop URL: {backdropUrl}");
            
            // Appliquer le backdrop au layout (fromApi=true pour priorité)
            if (gameLayout != null)
            {
                gameLayout.SetBackdropUrl(backdropUrl, true);
            }
            else if (ShootingGameLayout.Instance != null)
            {
                ShootingGameLayout.Instance.SetBackdropUrl(backdropUrl, true);
            }
            else if (CalculatorGameLayout.Instance != null)
            {
                CalculatorGameLayout.Instance.SetBackdropUrl(backdropUrl, true);
            }
            else
            {
                Debug.LogWarning("[TrousGameManager] ⚠️ Aucun layout disponible pour appliquer le backdrop");
            }
        }
        else
        {
            Debug.Log("[TrousGameManager] ℹ️ Pas de backdrop dans les données API");
        }
        
        // Charger le BACKGROUND (vidéo/image dans la zone de jeu)
        if (apiData.background != null && !string.IsNullOrEmpty(apiData.background.url))
        {
            string bgUrl = apiData.background.url;
            string bgType = apiData.background.type ?? "image";
            
            if (!bgUrl.StartsWith("http"))
            {
                if (bgType == "video")
                {
                    bgUrl = GeneralConfigManager.Instance.GetBackgroundVideoUrl(bgUrl);
                }
                else
                {
                    bgUrl = GeneralConfigManager.Instance.GetBackgroundImageUrl(bgUrl);
                }
            }
            
            Debug.Log($"[TrousGameManager] 🎬 Chargement background ({bgType}): {bgUrl}");
            
            if (bgType == "video")
            {
                // Pour les vidéos, utiliser le VideoPlayer s'il existe
                if (backgroundVideo != null)
                {
                    backgroundVideo.url = bgUrl;
                    backgroundVideo.isLooping = true;
                    backgroundVideo.Play();
                    Debug.Log($"[TrousGameManager] ✅ Vidéo de fond lancée: {bgUrl}");
                }
                else
                {
                    Debug.LogWarning("[TrousGameManager] VideoPlayer non disponible pour la vidéo de fond");
                }
            }
            else
            {
                yield return StartCoroutine(LoadBackgroundImage(bgUrl));
            }
        }
        
        // Convertir les questions API en format TrousQuestionData
        questions.Clear();
        
        Debug.Log($"[TrousGameManager] 📋 Données API reçues:");
        Debug.Log($"[TrousGameManager]   - questions: {(apiData.questions != null ? apiData.questions.Length.ToString() : "null")}");
        
        // Pour les jeux "trous", on ne prend que la PREMIÈRE question
        if (apiData.questions != null && apiData.questions.Length > 0)
        {
            Debug.Log($"[TrousGameManager] ⚠️ Seule la première question sera utilisée (type trous)");
            var apiQ = apiData.questions[0]; // Prendre uniquement la première question
            
            Debug.Log($"[TrousGameManager] 📝 Question API id={apiQ.id}:");
                Debug.Log($"[TrousGameManager]   - question: '{apiQ.question ?? "(null)"}'");
                Debug.Log($"[TrousGameManager]   - text: '{apiQ.text ?? "(null)"}'");
                Debug.Log($"[TrousGameManager]   - explanation: '{apiQ.explanation ?? "(null)"}'");
                Debug.Log($"[TrousGameManager]   - correct_answer: '{apiQ.correct_answer ?? "(null)"}'");
                Debug.Log($"[TrousGameManager]   - options: {(apiQ.options != null ? string.Join(", ", apiQ.options) : "null")}");
                Debug.Log($"[TrousGameManager]   - holes: {(apiQ.holes != null ? apiQ.holes.Length.ToString() : "null")}");
                Debug.Log($"[TrousGameManager]   - blanks: {(apiQ.blanks != null ? apiQ.blanks.Length.ToString() : "null")}");
                Debug.Log($"[TrousGameManager]   - answers: {(apiQ.answers != null ? apiQ.answers.Length.ToString() : "null")}");
                
                // Afficher les détails des holes/blanks
                var holesData = apiQ.holes ?? apiQ.blanks;
                if (holesData != null)
                {
                    foreach (var hole in holesData)
                    {
                        Debug.Log($"[TrousGameManager]     - hole: keyword='{hole.keyword ?? hole.marker}', correctId={hole.correctAnswer}, options=[{(hole.options != null ? string.Join(", ", System.Linq.Enumerable.Select(hole.options, o => $"{{id:{o.id}, content:'{o.content}'}}")) : "null")}]");
                    }
                }
                
                if (apiQ.answers != null)
                {
                    foreach (var ans in apiQ.answers)
                    {
                        Debug.Log($"[TrousGameManager]     - answer: '{ans.text}' (is_correct={ans.is_correct})");
                    }
                }
                
                // Essayer plusieurs champs pour trouver le texte de la question
                string questionText = !string.IsNullOrEmpty(apiQ.question) ? apiQ.question 
                                    : !string.IsNullOrEmpty(apiQ.text) ? apiQ.text 
                                    : apiQ.explanation ?? "";
                
                Debug.Log($"[TrousGameManager]   => Texte utilisé: '{questionText.Substring(0, Math.Min(50, questionText.Length))}'");
                TrousQuestionData trousQ = new TrousQuestionData
                {
                    id = apiQ.id.ToString(),
                    text = questionText,
                    explanation = apiQ.explanation ?? "",
                    holes = new List<TrousHole>()
                };
                
                // Détecter les trous dans le texte (patterns: ___, [TROU_X], {X}, TROU1, TROU2, etc.)
                var holeMatches = System.Text.RegularExpressions.Regex.Matches(questionText, @"___+|\[TROU_\d+\]|\{\d+\}|TROU\d+");
                
                if (holeMatches.Count > 0)
                {
                    // Le texte contient des marqueurs de trous
                    Debug.Log($"[TrousGameManager] {holeMatches.Count} trous détectés dans le texte: {string.Join(", ", System.Linq.Enumerable.Select(System.Linq.Enumerable.Cast<System.Text.RegularExpressions.Match>(holeMatches), m => m.Value))}");
                    
                    // Récupérer les données des trous depuis l'API (holes ou blanks)
                    var apiHolesData = apiQ.holes ?? apiQ.blanks;
                    
                    for (int i = 0; i < holeMatches.Count; i++)
                    {
                        var match = holeMatches[i];
                        string markerValue = match.Value; // "TROU1", "TROU2", etc.
                        
                        // Chercher les options pour ce trou spécifique
                        List<string> holeOptions = new List<string>();
                        string correctAnswer = "";
                        
                        // ID du trou depuis l'API (c'est cet ID qu'on doit envoyer à l'API answers)
                        int holeApiId = 0;
                        
                        // Préparer les listes pour les IDs (legacy, pas utilisé pour l'envoi)
                        List<int> holeOptionIds = new List<int>();
                        int correctOptionIdValue = 0;
                        
                        // D'abord chercher dans les données holes/blanks de l'API par INDEX (car marker peut être vide)
                        if (apiHolesData != null && i < apiHolesData.Length)
                        {
                            var apiHole = apiHolesData[i];
                            holeApiId = apiHole.id; // ← ID du trou depuis l'API (si disponible)
                            
                            // Options avec IDs {id, content}
                            if (apiHole.options != null && apiHole.options.Length > 0)
                            {
                                foreach (var opt in apiHole.options)
                                {
                                    holeOptions.Add(opt.content);
                                    holeOptionIds.Add(opt.id);
                                }
                                // correctAnswer est l'ID de la bonne option
                                correctOptionIdValue = apiHole.correctAnswer;
                                // Trouver le texte de la bonne réponse
                                foreach (var opt in apiHole.options)
                                {
                                    if (opt.id == apiHole.correctAnswer)
                                    {
                                        correctAnswer = opt.content;
                                        break;
                                    }
                                }
                                Debug.Log($"[TrousGameManager] ✅ Trou {i} ({markerValue}): options=[{string.Join(", ", holeOptions)}], IDs=[{string.Join(", ", holeOptionIds)}], correctId={correctOptionIdValue} ('{correctAnswer}')");
                            }
                        }
                        
                        // Si pas trouvé dans holes, essayer answers (format générique)
                        if (holeOptions.Count == 0 && apiQ.answers != null && apiQ.answers.Length > 0)
                        {
                            foreach (var ans in apiQ.answers)
                            {
                                holeOptions.Add(ans.text);
                                holeOptionIds.Add(ans.id);
                                if (ans.is_correct)
                                {
                                    correctAnswer = ans.text;
                                    correctOptionIdValue = ans.id;
                                }
                            }
                        }
                        
                        // Si toujours pas d'options, essayer le champ options global
                        if (holeOptions.Count == 0 && apiQ.options != null && apiQ.options.Length > 0)
                        {
                            holeOptions.AddRange(apiQ.options);
                            correctAnswer = apiQ.correct_answer ?? "";
                            // Pas d'IDs disponibles dans ce cas
                        }
                        
                        // Créer le trou - utiliser l'ID de l'API si disponible, sinon générer un ID local
                        string holeIdStr = holeApiId > 0 ? holeApiId.ToString() : $"hole_{i}";
                        
                        trousQ.holes.Add(new TrousHole
                        {
                            id = holeIdStr,
                            position = match.Index,
                            length = match.Length,
                            keyword = markerValue,
                            correctAnswer = correctAnswer,
                            options = holeOptions.Count > 0 ? holeOptions : null,
                            optionIds = holeOptionIds.Count > 0 ? holeOptionIds : null,
                            correctOptionId = correctOptionIdValue,
                            isTextInput = holeOptions.Count == 0
                        });
                        
                        Debug.Log($"[TrousGameManager] Trou {i} créé: id={holeIdStr}, position={match.Index}, marqueur='{markerValue}', options={holeOptions.Count}");
                    }
                }
                else
                {
                    // Pas de marqueurs de trous détectés - chercher les données de réponse
                    List<string> options = new List<string>();
                    List<int> optionIds = new List<int>();
                    string correctAnswer = "";
                    int correctOptionIdValue = 0;
                    
                    // D'abord essayer avec answers
                    if (apiQ.answers != null && apiQ.answers.Length > 0)
                    {
                        foreach (var ans in apiQ.answers)
                        {
                            options.Add(ans.text);
                            optionIds.Add(ans.id);
                            if (ans.is_correct)
                            {
                                correctAnswer = ans.text;
                                correctOptionIdValue = ans.id;
                            }
                        }
                    }
                    // Sinon essayer avec options et correct_answer
                    else if (apiQ.options != null && apiQ.options.Length > 0)
                    {
                        options.AddRange(apiQ.options);
                        correctAnswer = apiQ.correct_answer ?? (apiQ.options.Length > 0 ? apiQ.options[0] : "");
                        // Pas d'IDs disponibles dans ce cas
                    }
                    
                    if (options.Count > 0)
                    {
                        Debug.Log($"[TrousGameManager] Pas de marqueurs de trous, création d'un trou avec {options.Count} options");
                        
                        // Ajouter un espace et un marqueur à la fin du texte
                        trousQ.text = questionText + " ___";
                        
                        trousQ.holes.Add(new TrousHole
                        {
                            id = "hole_0",
                            position = questionText.Length + 1, // Position après l'espace ajouté
                            length = 3, // Longueur de "___"
                            correctAnswer = correctAnswer,
                            options = options,
                            optionIds = optionIds.Count > 0 ? optionIds : null,
                            correctOptionId = correctOptionIdValue
                        });
                    }
                    else
                    {
                        // Aucune option disponible - afficher le texte tel quel sans trou
                        Debug.LogWarning($"[TrousGameManager] ⚠️ Aucune option trouvée pour la question {apiQ.id}, affichage du texte seul");
                        trousQ.text = questionText;
                    }
                }
                
            questions.Add(trousQ);
            string textPreview = string.IsNullOrEmpty(trousQ.text) ? "(vide)" : trousQ.text.Substring(0, Math.Min(50, trousQ.text.Length));
            Debug.Log($"[TrousGameManager] Question ajoutée: {textPreview}... ({trousQ.holes.Count} trous)");
        }
        
        Debug.Log($"[TrousGameManager] {questions.Count} questions chargées depuis l'API");
        
        // Récupérer la config de zone de texte de l'API
        // Priorité à textConfig si textArea est vide (tous les champs à 0)
        var apiTextArea = apiData.textConfig;
        if (apiTextArea == null || (apiTextArea.GetX() == 0 && apiTextArea.GetY() == 0 && apiTextArea.GetWidth() == 0 && apiTextArea.GetHeight() == 0))
        {
            apiTextArea = apiData.textArea;
        }
        
        // Valeurs par défaut pour le positionnement du texte
        // Ces valeurs correspondent à une zone centrée sur l'écran 1920x1080
        // Ajuster selon la position réelle de la zone papier dans l'image de fond
        const int DEFAULT_TEXT_X = 550;     // Position X du centre de la zone
        const int DEFAULT_TEXT_Y = 400;     // Position Y du centre de la zone  
        const int DEFAULT_TEXT_WIDTH = 500; // Largeur de la zone de texte
        const int DEFAULT_TEXT_HEIGHT = 400; // Hauteur de la zone de texte
        const int DEFAULT_FONT_SIZE = 22;   // Taille de police adaptée
        const int DEFAULT_PADDING = 20;
        
        // IMPORTANT : Pour position/taille, 0 = "non fourni" → utiliser DEFAULT
        // L'API renvoie x=0,y=0,width=0,height=0 quand textConfig n'est pas configuré
        int textX = (apiTextArea != null && apiTextArea.GetX() > 0) ? (int)apiTextArea.GetX() : DEFAULT_TEXT_X;
        int textY = (apiTextArea != null && apiTextArea.GetY() > 0) ? (int)apiTextArea.GetY() : DEFAULT_TEXT_Y;
        int textWidth = (apiTextArea != null && apiTextArea.GetWidth() > 0) ? (int)apiTextArea.GetWidth() : DEFAULT_TEXT_WIDTH;
        int textHeight = (apiTextArea != null && apiTextArea.GetHeight() > 0) ? (int)apiTextArea.GetHeight() : DEFAULT_TEXT_HEIGHT;
        
        Debug.Log($"[TrousGameManager] 🔍 Valeurs finales: textX={textX}, textY={textY}, width={textWidth}, height={textHeight}");
        // Pour les autres valeurs, garder une validation minimale (fontSize, padding, etc.)
        // mais utiliser les valeurs API si elles sont présentes
        int fontSize = (apiTextArea != null && apiTextArea.fontSize > 0) ? (int)apiTextArea.fontSize : DEFAULT_FONT_SIZE;
        string fontColor = !string.IsNullOrEmpty(apiTextArea?.fontColor) ? apiTextArea.fontColor : "#333333";
        bool fontBold = apiTextArea?.fontBold ?? false;
        string alignment = !string.IsNullOrEmpty(apiTextArea?.alignment) ? apiTextArea.alignment : "left";
        float lineSpacing = (apiTextArea != null && apiTextArea.lineSpacing > 0) ? apiTextArea.lineSpacing : 1.5f;
        string bgColor = !string.IsNullOrEmpty(apiTextArea?.backgroundColor) ? apiTextArea.backgroundColor : "#FFFFFF";
        float bgAlpha = (apiTextArea != null && apiTextArea.backgroundAlpha > 0) ? apiTextArea.backgroundAlpha : 0.95f;
        int padTop = (apiTextArea != null && apiTextArea.GetPaddingTop() >= 0) ? (int)apiTextArea.GetPaddingTop() : DEFAULT_PADDING;
        int padBottom = (apiTextArea != null && apiTextArea.GetPaddingBottom() >= 0) ? (int)apiTextArea.GetPaddingBottom() : DEFAULT_PADDING;
        int padLeft = (apiTextArea != null && apiTextArea.GetPaddingLeft() >= 0) ? (int)apiTextArea.GetPaddingLeft() : DEFAULT_PADDING + 20;
        int padRight = (apiTextArea != null && apiTextArea.GetPaddingRight() >= 0) ? (int)apiTextArea.GetPaddingRight() : DEFAULT_PADDING + 20;
        
        Debug.Log($"[TrousGameManager] Config TextArea: pos=({textX}, {textY}), size=({textWidth}x{textHeight}), fontSize={fontSize}, fontBold={fontBold}, alignment={alignment}");
        
        cachedGameConfig = new TrousGameConfig
        {
            textConfig = new TrousTextConfig
            {
                fontSize = fontSize,
                fontColor = fontColor,
                fontBold = fontBold,
                alignment = alignment,
                lineSpacing = lineSpacing,
                backgroundColor = bgColor,
                backgroundAlpha = bgAlpha,
                padding = new PaddingConfig { top = padTop, bottom = padBottom, left = padLeft, right = padRight },
                position = new TrousPositionConfig { x = textX, y = textY },
                size = new TrousSizeConfig { width = textWidth, height = textHeight }
            }
        };
        
        // Initialiser le jeu (InitializeGame appelle déjà LoadQuestion(0))
        jsonLoaded = true;
        isInitializing = false;
        InitializeGame();
        
        // Masquer l'écran de chargement
        if (UnifiedLoadingManager.Instance != null)
        {
            UnifiedLoadingManager.HideLoadingAfterDelay(0.5f);
        }
    }
    
    /// <summary>
    /// Nettoie les éléments UI des dialogues avant d'initialiser le jeu
    /// </summary>
    private void CleanupDialogueUI()
    {
        Debug.Log("[TrousGameManager] Nettoyage des UI de dialogue");
        
        // Forcer le SubtitleManager à nettoyer proprement
        #if UNITY_2023_1_OR_NEWER
            SubtitleManager subtitleManager = FindFirstObjectByType<SubtitleManager>();
        #else
            SubtitleManager subtitleManager = FindObjectOfType<SubtitleManager>();
        #endif
        
        if (subtitleManager != null)
        {
            if (subtitleManager.IsPlayingDialogue())
            {
                Debug.Log("[TrousGameManager] Dialogue SubtitleManager en cours -> skip nettoyage UI");
            }
            else
            {
                Debug.Log("[TrousGameManager] SubtitleManager trouvé - nettoyage forcé");
                subtitleManager.ForceCleanupAndHide();
            }
        }
        
        // Forcer le DialoguePlayer à nettoyer proprement
        #if UNITY_2023_1_OR_NEWER
            DialoguePlayer dialoguePlayer = FindFirstObjectByType<DialoguePlayer>();
        #else
            DialoguePlayer dialoguePlayer = FindObjectOfType<DialoguePlayer>();
        #endif
        
        if (dialoguePlayer != null)
        {
            if (dialoguePlayer.IsPlayingDialogue())
            {
                Debug.Log("[TrousGameManager] Dialogue DialoguePlayer en cours -> skip nettoyage UI");
            }
            else
            {
                Debug.Log("[TrousGameManager] DialoguePlayer trouvé - nettoyage forcé");
                dialoguePlayer.ForceCleanupAndHide();
            }
        }

        // Nettoyer spécifiquement le DialogueBottomBanner (fallback si SubtitleManager n'existe pas)
        GameObject bottomBanner = GameObject.Find("DialogueBottomBanner");
        if (bottomBanner != null)
        {
            Debug.Log("[TrousGameManager] ✅ DialogueBottomBanner trouvé et désactivé");
            bottomBanner.SetActive(false);
        }
    }
    
    void OnDestroy()
    {
        CleanupDynamicResources();
    }
    
    /// <summary>
    /// Nettoie toutes les ressources créées dynamiquement (important pour WebGL)
    /// </summary>
    void CleanupDynamicResources()
    {
        foreach (var sprite in dynamicSprites)
        {
            if (sprite != null) Destroy(sprite);
        }
        dynamicSprites.Clear();
        
        foreach (var texture in dynamicTextures)
        {
            if (texture != null) Destroy(texture);
        }
        dynamicTextures.Clear();
        
        Debug.Log("[TrousGameManager] 🧹 Ressources dynamiques nettoyées");
    }
}

[System.Serializable]
public class TrousGameConfigWrapper
{
    public TrousGameConfig gameConfig;
}

[System.Serializable]
public class TrousGameConfig
{
    public string questionsUrl;
    public Resolution resolution;
    public Background background;
    public TrousSoundsConfig sounds;
    public TrousTextConfig textConfig;
    public TrousDropdownConfig dropdownConfig;
    public FeedbackMessagesConfig feedbackMessages;
    public string dialoguesUrl;
}

[System.Serializable]
public class TrousSoundsConfig
{
    public string background;
    public string success;
    public string fail;
}

[System.Serializable]
public class TrousTextConfig
{
    public TrousPositionConfig position;
    public TrousSizeConfig size;
    public int fontSize;
    public string fontColor;
    public bool fontBold;
    public string alignment;
    public float lineSpacing;
    public string backgroundColor;
    public float backgroundAlpha;
    public PaddingConfig padding;
    
    // Méthodes d'accès qui gèrent la structure imbriquée
    public float GetX() => position != null ? position.x : 0;
    public float GetY() => position != null ? position.y : 0;
    public float GetWidth() => size != null ? size.width : 0;
    public float GetHeight() => size != null ? size.height : 0;
    public float GetPaddingTop() => padding != null ? padding.top : 0;
    public float GetPaddingBottom() => padding != null ? padding.bottom : 0;
    public float GetPaddingLeft() => padding != null ? padding.left : 0;
    public float GetPaddingRight() => padding != null ? padding.right : 0;
}

[System.Serializable]
public class TrousDropdownConfig
{
    public int fontSize;
    public string fontColor;
    public string backgroundColor;
    public string borderColor;
    public int borderWidth;
    public int itemHeight;
    public int maxVisibleItems;
    public float animationDuration;
}

[System.Serializable]
public class TrousPositionConfig
{
    public int x;
    public int y;
}

[System.Serializable]
public class TrousSizeConfig
{
    public int width;
    public int height;
}

[System.Serializable]
public class TrousQuestionsData
{
    public List<TrousQuestionData> questions;
}

[System.Serializable]
public class TrousQuestionData
{
    public string id;
    public string text;
    public List<TrousHole> holes;
    public string explanation;
}

[System.Serializable]
public class TrousHole
{
    public string id;
    public int position; // Legacy - conservé pour compatibilité
    public int length; // Legacy - conservé pour compatibilité
    public string keyword; // Nouveau système : mot-clé à remplacer dans le texte (ex: "TROU")
    public string correctAnswer;
    public List<string> options;
    public List<int> optionIds; // IDs des options pour l'API answers (même ordre que options)
    public int correctOptionId; // ID de l'option correcte pour l'API
    public bool isTextInput; // True si l'utilisateur doit saisir du texte (pas de choix multiples)
}
