using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using TMPro;
using UnityEngine.UI;
using UnityEngine.Video;
using UnityEngine.InputSystem;
using System;
using UnityEngine.SceneManagement;
using System.Linq; // ← AJOUTER CETTE LIGNE


public class GameManager : MonoBehaviour
{
    /*
    // Global defaults (poussés par GameLauncher depuis level-config.json)
    [Header("Global Dialogue Defaults")]
    [SerializeField] private string gVideoRoot = null;
    [SerializeField] private string gImageRoot = null;
    [SerializeField] private DialogueConfig gDialogueDefault = null;

    // Fallback local (capturé depuis le JSON du jeu si présent)
    private DialogueConfig dConfig = null;
*/


    [Header("Configuration")]
    public string configUrl = ""; // Si vide, utilise la valeur par défaut de general-config.json

    public bool useLocalConfig = false;

    [Header("Video Override (optionnel)")]
    [Tooltip("Si rempli, cette URL sera utilisée au lieu de celle du JSON")]
    public string customVideoUrl = "";

    [Header("UI References")]
    public TextMeshProUGUI questionText;
    public GameObject feedbackPanel;
    public TextMeshProUGUI feedbackText;

    // PROTECTION GLOBALE ANTI "CLIC QUI TRAVERSE"
    // Quand le panneau de feedback est fermé par un clic UI natif (EventSystem),
    // le même clic peut être aussi vu par :
    //  - le pipeline WebGL (WebGLClickReceiver)
    //  - et/ou l'Input System (Mouse.current / Touchscreen.current)
    // Ces flags permettent d'ignorer CE clic précis sur tous les chemins.
    public static bool ignoreNextWebGLClickAfterFeedback = false;
    public static bool ignoreNextInputAfterFeedback = false;
    public static bool isHandlingFeedbackClickFromWebGL = false;
    public Transform ledContainer;
    public GameObject ledPrefab;

    [Header("Hover System - POSITION FIXE EN BAS")]
    public GameObject hoverPanel;
    public TextMeshProUGUI hoverText;

    [Header("Game Objects")]
    public Camera mainCamera;
    public VideoPlayer backgroundVideo;


    [Header("Audio")]
    public AudioSource audioSource;

    [Header("Impact Effect")]
    public GameObject impactEffectPrefab;

    [Header("Crosshair System")]
    public CrosshairManager crosshairManager;

    [Header("Gun Sprite System")]
    public GunSpriteManager gunSpriteManager;

    [Header("Debug Visual Zones")]
    public bool showDebugZones = false;

    [Header("LED Management")]
    private GameObject[] leds;
    private Sprite ledOffSprite, ledGreenSprite, ledRedSprite;
    private List<bool> answeredCorrectly = new List<bool>();
    private bool dKeyWasPressedLastFrame = false;

    [Header("Question Display")]
    private TextMeshProUGUI topBandQuestionText;

    [Header("Hover System")]
    private GameObject fixedHoverPanel;
    private TMPro.TextMeshProUGUI fixedHoverText;
    private UnityEngine.UI.Image fixedHoverBackground;

    [Header("UI Bands")]
    private GameObject topBand;
    private GameObject bottomBand;

    [Header("Feedback Background")]
    private Sprite feedbackSuccessSprite;
    private Sprite feedbackFailureSprite;
    private Image feedbackBackgroundImage;


    [Header("Dialogue System")]
    public DialoguePlayer dialoguePlayer;

    // Dialogue URLs cached from config JSON (if present)
    private string dBeforeUrl = null;
    private string dSuccessUrl = null;
    private string dFailUrl = null;



    // Optional per-game dialogue config from game JSON
    private DialogueConfig dConfig = null;

    // Globals injected from GameLauncher (level-config.json)
    private string globalVideoRoot = null;
    private string globalImageRoot = null;
    private DialogueConfig globalDialogueConfig = null;


    // Defaults globaux (provenant de level-config.json)
    private string gVideoRoot = "";
    private string gImageRoot = "";


    private DialogueConfig gDialogueDefault = null;

    private static string GetDirectoryUrl(string url)
    {
        if (string.IsNullOrEmpty(url)) return null;
        int i = url.LastIndexOf('/');
        return (i >= 0) ? url.Substring(0, i + 1) : null;
    }



    // Allow GameLauncher to inject global defaults
    public void SetGlobalDialogueDefaults(string videoRoot, string imageRoot, DialogueConfig cfg)
    {
        gVideoRoot = videoRoot;
        gImageRoot = imageRoot;
        gDialogueDefault = cfg;

        Debug.Log($"[GM] Global defaults set: videoRoot='{gVideoRoot}', imageRoot='{gImageRoot}', cfg={(gDialogueDefault != null)}");

        // Propager tout de suite au DialoguePlayer si déjà instancié
        if (dialoguePlayer != null)
        {
            try { if (!string.IsNullOrEmpty(gImageRoot)) dialoguePlayer.SetMediaRoots(gImageRoot); } catch { }
            try { if (!string.IsNullOrEmpty(gVideoRoot)) dialoguePlayer.SetVideoRoot(gVideoRoot); } catch { }
        }
    }

    // Helpers
    private string CombineUrl(string root, string leaf)
    {
        if (string.IsNullOrEmpty(root) || string.IsNullOrEmpty(leaf)) return null;
        return root.EndsWith("/") ? (root + leaf) : (root + "/" + leaf);
    }

    private bool HasBeforeDialogue() => !string.IsNullOrEmpty(dBeforeUrl);
    private bool HasSuccessDialogue() => !string.IsNullOrEmpty(dSuccessUrl);
    private bool HasFailDialogue() => !string.IsNullOrEmpty(dFailUrl);

    // Minimal models to parse only the dialogue fields from the main config JSON
    [Serializable]
    private class _MiniWrapper { public _MiniGameConfig gameConfig; }
    [Serializable]
    private class _MiniGameConfig
    {
        public string dialogueBeforeUrl;
        public string dialogueSuccessUrl;
        public string dialogueFailUrl;
        public DialogueConfig dialogueConfig;
    }

    private void CaptureDialogueFromConfigJson(string json, string sourceLabel)
    {
        bool keysPresent = json.Contains("dialogueBeforeUrl") || json.Contains("dialogueSuccessUrl") || json.Contains("dialogueFailUrl");
        var prevB = dBeforeUrl; var prevS = dSuccessUrl; var prevF = dFailUrl;
        try
        {
            var mini = JsonUtility.FromJson<_MiniWrapper>(json);
            if (mini != null && mini.gameConfig != null)
            {
                if (!string.IsNullOrEmpty(mini.gameConfig.dialogueBeforeUrl)) dBeforeUrl = mini.gameConfig.dialogueBeforeUrl;
                if (!string.IsNullOrEmpty(mini.gameConfig.dialogueSuccessUrl)) dSuccessUrl = mini.gameConfig.dialogueSuccessUrl;
                if (!string.IsNullOrEmpty(mini.gameConfig.dialogueFailUrl)) dFailUrl = mini.gameConfig.dialogueFailUrl;
                if (mini.gameConfig.dialogueConfig != null) dConfig = mini.gameConfig.dialogueConfig;
            }
        }
        catch (Exception e)
        {
            Debug.LogWarning($"[Dialogue JSON] parse error from {sourceLabel}: {e.Message}");
        }

        string b = string.IsNullOrEmpty(dBeforeUrl) ? "(vide)" : dBeforeUrl;
        string s = string.IsNullOrEmpty(dSuccessUrl) ? "(vide)" : dSuccessUrl;
        string f = string.IsNullOrEmpty(dFailUrl) ? "(vide)" : dFailUrl;
        Debug.Log($"[Dialogue JSON] before='{b}', success='{s}', fail='{f}', hasConfig={(dConfig != null)}, keysPresentInJson={keysPresent} (src:{sourceLabel})");
        if (!keysPresent)
        {
            Debug.LogWarning("[Dialogue JSON] Aucun champ dialogue*Url trouvé dans le JSON pointé par configUrl. Si attendu, vérifie l'URL ou la structure.");
        }
    }

    // Wrapper propre côté GameManager → appelle le DialoguePlayer avec overrideConfig
    // Appel unique et robuste au DialoguePlayer (avec override de config)
    // ─────────────────────────────────────────────────────────────
    // PlayDialogueUrl — surcharges
    // ─────────────────────────────────────────────────────────────
    // Dans GameManager.cs
    // private bool shooterUiWasVisible = true;

    private void SetShooterUIVisible(bool visible)
    {
        // Crosshair
        if (crosshairManager != null)
        {
            // Si ton CrosshairManager a une API dédiée, utilise-la (ex: Show/Hide/SetVisible)
            // Sinon, fallback:
            crosshairManager.gameObject.SetActive(visible);
        }

        // Gun sprite
        if (gunSpriteManager != null)
        {
            // Idem: si tu as ShowGun/HideGun/SetVisible, privilégie ça.
            // Fallback:
            gunSpriteManager.gameObject.SetActive(visible);
        }

        // (optionnel) autre HUD shooter (canvas, reticules, etc.)
        // if (shooterHudCanvas != null) shooterHudCanvas.enabled = visible;
    }

    //private bool gameplayUiPrev = true;

    private void SetGameplayUIVisible(bool visible)
    {
        // Crosshair + Gun
        if (crosshairManager) crosshairManager.gameObject.SetActive(visible);
        if (gunSpriteManager) gunSpriteManager.gameObject.SetActive(visible);

        // Bandeaux + question + LEDs
        if (topBand) topBand.SetActive(visible);

        // Si on utilise le panneau de hover CENTRÉ, on masque le bandeau du bas
        var uiCfg = GetUIConfig();
        bool hideBottomBandForCenteredHover = uiCfg != null && uiCfg.useCenteredHoverPanel;
        if (bottomBand) bottomBand.SetActive(visible && !hideBottomBandForCenteredHover);
        if (ledContainer) ledContainer.gameObject.SetActive(visible);

        // Ancien champ de question si utilisé
        if (questionText) questionText.gameObject.SetActive(visible);

        // Hover panel & Feedback
        if (hoverPanel) hoverPanel.SetActive(visible);
        if (feedbackPanel) feedbackPanel.SetActive(false); // jamais par-dessus un dialogue
    }


    // Convertit DefaultDialogueConfig en DialogueConfig
    private DialogueConfig ConvertDefaultDialogueToConfig(DefaultDialogueConfig def)
    {
        if (def == null) return new DialogueConfig();
        
        return new DialogueConfig
        {
            // Paramètres de texte
            dialogueTextSize = def.dialogueTextSize > 0 ? def.dialogueTextSize : 28f,
            dialogueTextColor = !string.IsNullOrEmpty(def.dialogueTextColor) ? def.dialogueTextColor : "#4a4a4a",
            speakerTextSize = def.speakerTextSize > 0 ? def.speakerTextSize : 32f,
            speakerTextColor = !string.IsNullOrEmpty(def.speakerTextColor) ? def.speakerTextColor : "#64477f",
            speakerTextBold = def.speakerTextBold,
            speakerMarginBottom = def.speakerMarginBottom > 0 ? def.speakerMarginBottom : 15f,
            dialogueTextAlignment = !string.IsNullOrEmpty(def.dialogueTextAlignment) ? def.dialogueTextAlignment : "left",
            backgroundDimming = def.backgroundDimming,
            
            // Mode cadre centré
            useFrameMode = def.useFrameMode,
            frameWidth = def.frameWidth > 0 ? def.frameWidth : 1230f,
            frameHeight = def.frameHeight > 0 ? def.frameHeight : 340f,
            frameRadius = def.frameRadius > 0 ? def.frameRadius : 20f,
            frameBottomMargin = def.frameBottomMargin > 0 ? def.frameBottomMargin : 40f,
            frameBackgroundColor = !string.IsNullOrEmpty(def.frameBackgroundColor) ? def.frameBackgroundColor : "#f5ece5",
            framePaddingLeft = def.framePaddingLeft > 0 ? def.framePaddingLeft : 40f,
            framePaddingRight = def.framePaddingRight > 0 ? def.framePaddingRight : 40f,
            framePaddingTop = def.framePaddingTop > 0 ? def.framePaddingTop : 30f,
            framePaddingBottom = def.framePaddingBottom > 0 ? def.framePaddingBottom : 30f,
            
            // Padding intérieur du conteneur titre+sous-titre
            textContentPaddingLeft = def.textContentPaddingLeft,
            textContentPaddingRight = def.textContentPaddingRight,
            textContentPaddingTop = def.textContentPaddingTop,
            textContentPaddingBottom = def.textContentPaddingBottom,
            
            // Indicateur de continuation
            showContinueIndicator = def.showContinueIndicator,
            continueIndicatorColor = !string.IsNullOrEmpty(def.continueIndicatorColor) ? def.continueIndicatorColor : "#64477f",
            continueIndicatorSize = def.continueIndicatorSize > 0 ? def.continueIndicatorSize : 24f,
            continueIndicatorBottomMargin = def.continueIndicatorBottomMargin > 0 ? def.continueIndicatorBottomMargin : 20f,
            
            // Mode bandeau legacy
            bottomBarHeightRatio = def.bottomBarHeightRatio > 0 ? def.bottomBarHeightRatio : 0.32f,
            bottomBarColor = !string.IsNullOrEmpty(def.bottomBarColor) ? def.bottomBarColor : "#00000099",
            paddingLeft = def.paddingLeft,
            paddingRight = def.paddingRight,
            
            // Instructions
            instructionsText = !string.IsNullOrEmpty(def.instructionsText) ? def.instructionsText : "Cliquez pour continuer"
        };
    }

    // v1 — compat : un seul paramètre (redirige vers v2 avec config = null)
    private IEnumerator PlayDialogueUrl(string url)
    {
        yield return StartCoroutine(PlayDialogueUrl(url, null));

        // Coupe les UI vides potentielles (ex: RawImage vidéo sans texture)
        KillStrayDialogueUI();

        // Et on rallume le HUD du shooter
        SetGameplayUIVisible(true);

    }

    // v2 — avec config : priorité overrideConfig > dConfig > gameConfig.dialogueConfig > gDialogueDefault > general-config.json
    private IEnumerator PlayDialogueUrl(string url, DialogueConfig overrideConfig)
    {
        if (dialoguePlayer == null || string.IsNullOrEmpty(url))
            yield break;

        bool done = false;
        DialogueConfig cfg = overrideConfig ?? dConfig ?? GetDialogueConfigFromGameConfig() ?? gDialogueDefault;
        
        // Fallback vers general-config.json si toujours null
        if (cfg == null && GeneralConfigManager.Instance != null)
        {
            var defDialogue = GeneralConfigManager.Instance.GetDefaultDialogueConfig();
            if (defDialogue != null)
            {
                cfg = ConvertDefaultDialogueToConfig(defDialogue);
                Debug.Log("[GameManager] ✅ Utilisation de defaultDialogueConfig depuis general-config.json");
            }
        }
        
        // Si toujours null, utiliser la config par défaut de DialogueConfig
        if (cfg == null)
        {
            cfg = new DialogueConfig();
            Debug.LogWarning("[GameManager] ⚠️ Aucune config dialogue trouvée, utilisation des valeurs par défaut");
        }

        dialoguePlayer.PlayDialogueFromUrl(url, cfg, () => done = true);
        
        // FIX MAC : Nettoyer le voile blanc en continu pendant le dialogue
        StartCoroutine(ContinuouslyKillWhiteVeil(() => done));
        
        // FIX MAC : Corriger la vidéo du dialogue (utiliser CameraNearPlane comme shooting)
        if (Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.OSXPlayer)
        {
            StartCoroutine(FixDialogueVideoOnMac());
        }
        
        yield return new WaitUntil(() => done);

        // NOUVEAU : Nettoyage forcé après dialogue
        yield return new WaitForSeconds(0.1f); // Petit délai pour laisser les animations finir
        dialoguePlayer.ForceCleanupAndHide();
        KillStrayDialogueUI();

        // S'assurer que le gun/crosshair repassent au premier plan
        SetGameplayUIVisible(true);
        BringGameplayToFront();
    }
    
    // FIX MAC : Coroutine qui nettoie le voile blanc toutes les 0.5 secondes pendant le dialogue
    private IEnumerator ContinuouslyKillWhiteVeil(System.Func<bool> isDialogueFinished)
    {
        bool isMac = Application.platform == RuntimePlatform.OSXEditor || 
                     Application.platform == RuntimePlatform.OSXPlayer;
        
        if (!isMac) yield break; // Seulement sur Mac
        
        Debug.Log("[MAC FIX] 🍎 Démarrage nettoyage continu du voile blanc...");
        
        while (!isDialogueFinished())
        {
            // Chercher et tuer les overlays blancs
            foreach (var img in FindObjectsByType<UnityEngine.UI.Image>(FindObjectsSortMode.None))
            {
                if (img.sprite == null && img.color.r > 0.8f && img.color.g > 0.8f && img.color.b > 0.8f)
                {
                    Canvas parentCanvas = img.GetComponentInParent<Canvas>();
                    if (parentCanvas != null && parentCanvas.sortingOrder >= 40000)
                    {
                        Color c = img.color;
                        if (c.a > 0.1f) // Seulement si pas déjà transparent
                        {
                            c.a = 0f;
                            img.color = c;
                            Debug.Log($"[MAC FIX] ✅ Voile blanc éliminé: {img.name}");
                        }
                    }
                }
            }
            
            yield return new WaitForSeconds(0.5f); // Vérifier toutes les 0.5 secondes
        }
        
        Debug.Log("[MAC FIX] 🍎 Nettoyage continu terminé");
    }
    
    // FIX MAC : Coroutine pour corriger la vidéo du DialoguePlayer
    private IEnumerator FixDialogueVideoOnMac()
    {
        Debug.Log("[GameManager/MAC] 🎬 Fix vidéo dialogue - En attente...");
        
        // Attendre que le VideoPlayer du dialogue soit créé
        float timeout = 5f;
        float elapsed = 0f;
        
        while (elapsed < timeout)
        {
            VideoPlayer[] allPlayers = FindObjectsByType<VideoPlayer>(FindObjectsSortMode.None);
            
            foreach (VideoPlayer vp in allPlayers)
            {
                // Chercher un VideoPlayer qui joue une URL (c'est le dialogue)
                if ((vp.isPlaying || vp.source == VideoSource.Url) && vp != backgroundVideo)
                {
                    Debug.Log($"[GameManager/MAC] ✅ VideoPlayer dialogue trouvé: {vp.gameObject.name}");
                    
                    try
                    {
                        // Appliquer les mêmes paramètres que backgroundVideo (qui fonctionne bien)
                        vp.renderMode = VideoRenderMode.CameraNearPlane;
                        vp.targetCamera = mainCamera;
                        vp.targetCameraAlpha = 1f;
                        
                        Debug.Log("[GameManager/MAC] ✅ Render mode: CameraNearPlane");
                        Debug.Log("[GameManager/MAC] ✅ Vidéo corrigée!");
                        
                        // Aussi nettoyer les overlays blancs
                        FixDialogueUIOverlays();
                    }
                    catch (System.Exception ex)
                    {
                        Debug.LogError($"[GameManager/MAC] ❌ Erreur: {ex.Message}");
                    }
                    
                    yield break; // Arrêter après avoir trouvé
                }
            }
            
            elapsed += Time.deltaTime;
            yield return new WaitForSeconds(0.1f);
        }
        
        Debug.Log("[GameManager/MAC] ⏱️ Timeout - VideoPlayer dialogue non trouvé");
    }
    
    // FIX MAC : Nettoyer les overlays blancs du DialoguePlayer
    private void FixDialogueUIOverlays()
    {
        Canvas[] allCanvases = FindObjectsByType<Canvas>(FindObjectsSortMode.None);
        int cleanedCount = 0;
        
        foreach (Canvas canvas in allCanvases)
        {
            // Canvas de dialogue (sortingOrder élevé)
            if (canvas.sortingOrder >= 40000)
            {
                Image[] images = canvas.GetComponentsInChildren<Image>();
                
                foreach (Image img in images)
                {
                    // Image blanche sans sprite = overlay du dialogue
                    if (img.sprite == null && 
                        img.color.r > 0.8f && img.color.g > 0.8f && img.color.b > 0.8f && 
                        img.color.a > 0.1f)
                    {
                        Color c = img.color;
                        c.a = 0f;
                        img.color = c;
                        cleanedCount++;
                        
                        Debug.Log($"[GameManager/MAC] 🎨 Overlay blanc nettoyé: {img.name}");
                    }
                }
            }
        }
        
        if (cleanedCount > 0)
        {
            Debug.Log($"[GameManager/MAC] 🎨 {cleanedCount} overlay(s) nettoyé(s)");
        }
    }
    
    // ... existing code ...



    // Plays a dialogue JSON and temporarily overrides the backgroundVideo with the dialogue video (if present)
    private IEnumerator PlayDialogueFromUrl(string url, Action onComplete)
    {
        if (dialoguePlayer == null || string.IsNullOrEmpty(url))
        {
            onComplete?.Invoke();
            yield break;
        }

        DialogueWrapper wrapper = null;

        // 1) Download JSON (no yield inside try)
        using (UnityWebRequest www = UnityWebRequest.Get(url))
        {
            // NOTE: Headers Cache-Control, Pragma, Expires retirés pour éviter les problèmes CORS en WebGL
            // Ces headers ne sont pas autorisés par certains serveurs dans Access-Control-Allow-Headers

            yield return www.SendWebRequest();

#if UNITY_2020_3_OR_NEWER
            if (www.result != UnityWebRequest.Result.Success)
#else
            if (www.isNetworkError || www.isHttpError)
#endif
            {
                Debug.LogError($"Erreur de chargement du dialogue: {www.error} ({url})");
                onComplete?.Invoke();
                yield break;
            }

            try
            {
                string json = www.downloadHandler.text;
                wrapper = JsonUtility.FromJson<DialogueWrapper>(json);
            }
            catch (Exception e)
            {
                Debug.LogError($"Parse dialogue JSON error: {e.Message}");
                onComplete?.Invoke();
                yield break;
            }
        }

        // 2) Validate
        if (wrapper == null || wrapper.dialogue == null || wrapper.dialogue.lines == null || wrapper.dialogue.lines.Count == 0)
        {
            Debug.LogError($"Structure JSON de dialogue invalide pour {url}");
            onComplete?.Invoke();
            yield break;
        }

        // 3) Determine media roots
        string imgRoot = !string.IsNullOrEmpty(wrapper.imageRoot) ? wrapper.imageRoot : globalImageRoot;
        if (!string.IsNullOrEmpty(imgRoot))
        {
            try { dialoguePlayer.SetMediaRoots(imgRoot); } catch { }
        }

        // Save current backgroundVideo state
        string prevUrl = null; bool prevLoop = false; bool prevWasPlaying = false;
        VideoRenderMode prevMode = 0; Camera prevCam = null; float prevAlpha = 1f;
        if (backgroundVideo != null)
        {
            prevUrl = backgroundVideo.url;
            prevLoop = backgroundVideo.isLooping;
            prevWasPlaying = backgroundVideo.isPlaying;
            prevMode = backgroundVideo.renderMode;
            prevCam = backgroundVideo.targetCamera;
            prevAlpha = backgroundVideo.targetCameraAlpha;
        }

        // 4) Play dialogue background video if provided
        string vroot = !string.IsNullOrEmpty(wrapper.videoRoot) ? wrapper.videoRoot : globalVideoRoot;
        if (backgroundVideo != null && !string.IsNullOrEmpty(vroot) && !string.IsNullOrEmpty(wrapper.dialogue.video))
        {
            string vurl = CombineUrl(vroot, wrapper.dialogue.video);
            Debug.Log("[DIALOGUE] Background video (dialogue): " + vurl);
            yield return StartCoroutine(PlayDialogueBG(vurl));
        }

        // 5) One-shot callbacks that also restore background video
        void Clean()
        {
            dialoguePlayer.OnDialogueComplete -= OnDone;
            dialoguePlayer.OnDialogueSkipped -= OnDone;
        }
        IEnumerator RestoreBG()
        {
            if (backgroundVideo != null)
            {
                backgroundVideo.Stop();
                // restore previous mode/state
                backgroundVideo.renderMode = prevMode;
                backgroundVideo.targetCamera = prevCam;
                backgroundVideo.targetCameraAlpha = prevAlpha;

                if (!string.IsNullOrEmpty(prevUrl))
                {
                    backgroundVideo.source = VideoSource.Url;
                    backgroundVideo.url = prevUrl;
                    backgroundVideo.isLooping = prevLoop;

                    backgroundVideo.Prepare();
                    while (!backgroundVideo.isPrepared) yield return null;

                    if (prevWasPlaying) backgroundVideo.Play();
                    else backgroundVideo.Pause();
                }
            }
        }
        void OnDone()
        {
            Clean();
            StartCoroutine(RestoreBG());
            onComplete?.Invoke();
        }

        dialoguePlayer.OnDialogueComplete += OnDone;
        dialoguePlayer.OnDialogueSkipped += OnDone;

        // 6) Choose config: local > GM.dConfig > global > default
        DialogueConfig cfgToUse = wrapper.dialogueConfig ?? dConfig ?? globalDialogueConfig ?? new DialogueConfig();

        Debug.Log($"[DIALOGUE CFG] srcJson={(wrapper.dialogueConfig != null)} srcGM={(dConfig != null)} srcGlobal={(globalDialogueConfig != null)} " +
                  $"next='{cfgToUse.nextKey}' prev='{cfgToUse.prevKey}' skip='{cfgToUse.skipDialogueKey}' " +
                  $"requireInput={cfgToUse.requireInputToAdvance}");

        Debug.Log($"[DIALOGUE] JSON OK: title='{wrapper.dialogue.title}', lines={wrapper.dialogue.lines?.Count ?? 0}");
        dialoguePlayer.PlayDialogue(wrapper.dialogue, cfgToUse);
    }

    // Use camera near-plane to display the dialogue video full-screen
    private IEnumerator PlayDialogueBG(string videoUrl)
    {
        if (backgroundVideo == null || mainCamera == null || string.IsNullOrEmpty(videoUrl))
            yield break;

        backgroundVideo.Stop();
        backgroundVideo.source = VideoSource.Url;
        backgroundVideo.url = videoUrl;
        backgroundVideo.isLooping = false;
        backgroundVideo.renderMode = VideoRenderMode.CameraNearPlane;
        backgroundVideo.targetCamera = mainCamera;
        backgroundVideo.targetCameraAlpha = 1f;

        backgroundVideo.Prepare();
        while (!backgroundVideo.isPrepared)
            yield return null;

        backgroundVideo.Play();
    }

    private IEnumerator HandlePostGameDialogue()
    {
        // NOUVEAU : Utiliser la configuration de réussite
        bool success = CheckSuccess();

        string url = success ? dSuccessUrl : dFailUrl;
        if (!string.IsNullOrEmpty(url))
            SetGameplayUIVisible(false);       // 👈 coupe tout le HUD
        yield return StartCoroutine(PlayDialogueUrl(url));

        // Coupe les UI vides potentielles (ex: RawImage vidéo sans texture)
        KillStrayDialogueUI();

        // Et on rallume le HUD du shooter
        SetGameplayUIVisible(true);


    }
    // Classes pour la structure JSON séparée (définies UNE SEULE FOIS)
    [System.Serializable]
    public class GameConfigWrapper
    {
        public GameConfigData gameConfig;
    }

    // QuestionsWrapper est maintenant définie dans GameConfig.cs

    // Variables pour les données chargées (définies UNE SEULE FOIS)
    private GameConfigData gameConfig;
    private Question[] questions;
    private SuccessConfig successConfig;

    // Variables privées du jeu
    private int currentQuestionIndex = 0;
    private int score = 0;
    private int correctAnswers = 0;
    private int totalAnswers = 0;
    private List<GameObject> targetZones = new List<GameObject>(); // ← CETTE LIGNE

    // Variable pour bloquer les inputs pendant la séquence
    private bool isProcessingAnswer = false;
    private bool lastAnswerWasCorrect = false;
    
    // Variables pour l'API answers
    private int lastSelectedAnswerId = 0;
    private int lastQuestionId = 0;

    // Garde-fou pour n'initialiser le jeu qu'une seule fois
    private bool gameInitialized = false;

    // Assets chargés
    private Dictionary<string, Sprite> loadedSprites = new Dictionary<string, Sprite>();
    private Dictionary<string, AudioClip> loadedAudioClips = new Dictionary<string, AudioClip>();

    // ==========================================
    // MÉTHODES HELPER POUR RÉCUPÉRER LES CONFIGS AVEC FALLBACK
    // ==========================================

    /// <summary>
    /// Récupère la configuration UI avec fallback vers defaultUIConfig
    /// </summary>
    private UIConfig GetUIConfig()
    {
        // Objectif : TOUTES les valeurs UI viennent de general-config.json,
        // sauf si on réintroduit plus tard des overrides explicites par jeu.

        // On ignore volontairement gameConfig.uiConfig pour l'instant,
        // car les sections uiConfig ont été supprimées des JSON de jeu.

        if (GeneralConfigManager.Instance == null)
        {
            Debug.LogWarning("[GameManager] GeneralConfigManager.Instance est null, création...");
            GameObject configManagerObj = new GameObject("GeneralConfigManager");
            configManagerObj.AddComponent<GeneralConfigManager>();
        }

        // Attendre que la config soit chargée si nécessaire
        if (!GeneralConfigManager.Instance.IsConfigLoaded())
        {
            Debug.LogWarning("[GameManager] GeneralConfigManager n'est pas encore chargé, utilisation d'une UIConfig par défaut temporaire");
            return new UIConfig();
        }

        var defaultUIConfig = GeneralConfigManager.Instance.GetDefaultUIConfig();
        if (defaultUIConfig != null)
        {
            Debug.Log("[GameManager] ✅ Utilisation FORCÉE de defaultUIConfig depuis general-config.json (uiConfig des jeux ignoré)");
            return ConvertDefaultUIConfigToUIConfig(defaultUIConfig);
        }

        // Fallback ultime : retourner une config par défaut
        Debug.LogWarning("[GameManager] ⚠️ Aucune configuration UI trouvée dans general-config.json, utilisation des valeurs par défaut de UIConfig");
        return new UIConfig();
    }

    /// <summary>
    /// Récupère la configuration feedbackMessages avec fallback vers defaultFeedbackMessages
    /// </summary>
    private FeedbackMessagesConfig GetFeedbackMessagesConfig()
    {
        // Objectif : TOUT le feedback vient de general-config.json,
        // sauf si on réintroduit plus tard des overrides explicites par jeu.

        // On ignore volontairement gameConfig.feedbackMessages pour l'instant,
        // car les sections feedbackMessages ont été supprimées des JSON de jeu.

        if (GeneralConfigManager.Instance == null)
        {
            Debug.LogWarning("[GameManager] GeneralConfigManager.Instance est null, création...");
            GameObject configManagerObj = new GameObject("GeneralConfigManager");
            configManagerObj.AddComponent<GeneralConfigManager>();
        }

        // Attendre que la config soit chargée si nécessaire
        if (!GeneralConfigManager.Instance.IsConfigLoaded())
        {
            Debug.LogWarning("[GameManager] GeneralConfigManager n'est pas encore chargé, utilisation d'une FeedbackMessagesConfig par défaut temporaire");
            return new FeedbackMessagesConfig();
        }

        var defaultFeedback = GeneralConfigManager.Instance.GetDefaultFeedbackMessages();
        if (defaultFeedback != null)
        {
            Debug.Log("[GameManager] ✅ Utilisation FORCÉE de defaultFeedbackMessages depuis general-config.json (feedbackMessages des jeux ignoré)");
            Debug.Log($"[GameManager] Message correct: '{defaultFeedback.correctAnswerMessage}', Message incorrect: '{defaultFeedback.incorrectAnswerMessage}'");
            return ConvertDefaultFeedbackToFeedbackConfig(defaultFeedback);
        }

        // Fallback ultime : retourner une config par défaut
        Debug.LogWarning("[GameManager] ⚠️ Aucune configuration feedbackMessages trouvée dans general-config.json, utilisation des valeurs par défaut de FeedbackMessagesConfig");
        return new FeedbackMessagesConfig();
    }

    /// <summary>
    /// Convertit DefaultUIConfig en UIConfig
    /// </summary>
    private UIConfig ConvertDefaultUIConfigToUIConfig(DefaultUIConfig defaultConfig)
    {
        var uiConfig = new UIConfig();

        // Hover text
        uiConfig.hoverTextSize = defaultConfig.hoverTextSize;
        uiConfig.hoverTextColor = defaultConfig.hoverTextColor;
        uiConfig.hoverTextBold = defaultConfig.hoverTextBold;

        // Hover panel
        if (defaultConfig.hoverPanelSize != null)
        {
            uiConfig.hoverPanelSize = new HoverPanelSize
            {
                width = defaultConfig.hoverPanelSize.width,
                height = defaultConfig.hoverPanelSize.height
            };
        }
        uiConfig.hoverBackgroundColor = defaultConfig.hoverBackgroundColor;
        uiConfig.hoverBackgroundAlpha = defaultConfig.hoverBackgroundAlpha;
        uiConfig.useCustomPosition = defaultConfig.useCustomPosition;
        uiConfig.customPosition = defaultConfig.customPosition;
        uiConfig.useCenteredHoverPanel = defaultConfig.useCenteredHoverPanel;
        uiConfig.centeredHoverPaddingX = defaultConfig.centeredHoverPaddingX;
        uiConfig.centeredHoverPaddingY = defaultConfig.centeredHoverPaddingY;
        uiConfig.centeredHoverAlpha    = defaultConfig.centeredHoverAlpha;

        // Bands
        if (defaultConfig.bands != null)
        {
            uiConfig.bands = new UIBandsConfig
            {
                showBands = defaultConfig.bands.showBands,
                bandHeight = defaultConfig.bands.bandHeight,
                bandColor = defaultConfig.bands.bandColor,
                bandAlpha = defaultConfig.bands.bandAlpha,
                sortingOrder = defaultConfig.bands.sortingOrder
            };
        }

        // LED Config
        if (defaultConfig.ledConfig != null)
        {
            var ledConfig = new LEDConfig
            {
                ledSize = defaultConfig.ledConfig.ledSize,
                ledSpacing = defaultConfig.ledConfig.ledSpacing,
                marginLeft = defaultConfig.ledConfig.marginLeft,
                verticalOffset = defaultConfig.ledConfig.verticalOffset,
                useCustomSprites = defaultConfig.ledConfig.useCustomSprites,
                enableLEDAnimation = defaultConfig.ledConfig.enableLEDAnimation
            };

            // Propriétés optionnelles (couleurs)
            if (defaultConfig.ledConfig.defaultOffColor != null)
                ledConfig.defaultOffColor = defaultConfig.ledConfig.defaultOffColor.ToColor();
            if (defaultConfig.ledConfig.defaultGreenColor != null)
                ledConfig.defaultGreenColor = defaultConfig.ledConfig.defaultGreenColor.ToColor();
            if (defaultConfig.ledConfig.defaultRedColor != null)
                ledConfig.defaultRedColor = defaultConfig.ledConfig.defaultRedColor.ToColor();
            if (defaultConfig.ledConfig.animationSpeed > 0)
                ledConfig.animationSpeed = defaultConfig.ledConfig.animationSpeed;
            if (!string.IsNullOrEmpty(defaultConfig.ledConfig.animationType))
                ledConfig.animationType = ParseAnimationType(defaultConfig.ledConfig.animationType);

            uiConfig.ledConfig = ledConfig;
        }

        // Question Display
        if (defaultConfig.questionDisplay != null)
        {
            uiConfig.questionDisplay = new QuestionDisplayConfig
            {
                fontSize = defaultConfig.questionDisplay.fontSize,
                fontColor = defaultConfig.questionDisplay.fontColor,
                fontBold = defaultConfig.questionDisplay.fontBold,
                alignment = ParseTextAlignment(defaultConfig.questionDisplay.alignment),
                marginTop = defaultConfig.questionDisplay.marginTop,
                marginLeft = defaultConfig.questionDisplay.marginLeft,
                marginRight = defaultConfig.questionDisplay.marginRight,
                sortingOrder = defaultConfig.questionDisplay.sortingOrder,
                useCustomPosition = defaultConfig.questionDisplay.useCustomPosition,
                adaptToScreenSize = defaultConfig.questionDisplay.adaptToScreenSize,
                minFontSize = defaultConfig.questionDisplay.minFontSize,
                maxFontSize = defaultConfig.questionDisplay.maxFontSize
            };
        }

        return uiConfig;
    }

    /// <summary>
    /// Convertit DefaultFeedbackMessages en FeedbackMessagesConfig
    /// </summary>
    private FeedbackMessagesConfig ConvertDefaultFeedbackToFeedbackConfig(DefaultFeedbackMessages defaultConfig)
    {
        var feedbackConfig = new FeedbackMessagesConfig
        {
            correctAnswerMessage = defaultConfig.correctAnswerMessage,
            incorrectAnswerMessage = defaultConfig.incorrectAnswerMessage,
            showResultMessage = defaultConfig.showResultMessage,
            resultMessageInBold = defaultConfig.resultMessageInBold,
            resultMessageColor = defaultConfig.resultMessageColor,
            incorrectMessageColor = defaultConfig.incorrectMessageColor,
            resultMessageSize = defaultConfig.resultMessageSize,
            resultMessageFont = defaultConfig.resultMessageFont,
            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,
            feedbackNextButtonText = defaultConfig.feedbackNextButtonText,
            feedbackLastButtonText = defaultConfig.feedbackLastButtonText,
            feedbackButtonBackgroundColor = defaultConfig.feedbackButtonBackgroundColor,
            feedbackButtonStyle = defaultConfig.feedbackButtonStyle
        };

        return feedbackConfig;
    }

    /// <summary>
    /// Parse une chaîne d'alignement en TextAlignmentOptions
    /// </summary>
    private TextAlignmentOptions ParseTextAlignment(string alignment)
    {
        if (string.IsNullOrEmpty(alignment)) return TextAlignmentOptions.Center;

        switch (alignment.ToLower())
        {
            case "left": return TextAlignmentOptions.Left;
            case "right": return TextAlignmentOptions.Right;
            case "center": return TextAlignmentOptions.Center;
            case "justified": return TextAlignmentOptions.Justified;
            default: return TextAlignmentOptions.Center;
        }
    }

    /// <summary>
    /// Parse une chaîne animationType en AnimationType enum
    /// </summary>
    private AnimationType ParseAnimationType(string animationType)
    {
        if (string.IsNullOrEmpty(animationType)) return AnimationType.None;

        switch (animationType.ToLower())
        {
            case "fade": return AnimationType.Fade;
            case "scale": return AnimationType.Scale;
            case "pulse": return AnimationType.Pulse;
            case "none":
            default: return AnimationType.None;
        }
    }

    /// <summary>
    /// Vérifie si un UIConfig a été réellement configuré dans le JSON (pas juste un objet vide avec valeurs par défaut)
    /// </summary>
    private bool IsUIConfigActuallyConfigured(UIConfig config)
    {
        if (config == null) return false;

        // Les valeurs par défaut dans UIConfig sont : hoverTextSize=24, hoverTextColor="#FFFFFF", hoverPanelSize=(500,50), hoverBackgroundColor="#000000", hoverBackgroundAlpha=0.9
        // Dans general-config.json, les valeurs sont : hoverTextSize=36, hoverTextColor="#64477f", hoverPanelSize=(1920,80), hoverBackgroundColor="#f5ece5", hoverBackgroundAlpha=0
        
        // Vérifier hoverTextSize (propriété la plus importante et la plus différente)
        // Si hoverTextSize = 24 (valeur par défaut de la classe), c'est probablement un objet vide
        if (Mathf.Approximately(config.hoverTextSize, 24f))
        {
            // Vérifier aussi hoverTextColor pour être sûr
            if (config.hoverTextColor == "#FFFFFF" || string.IsNullOrEmpty(config.hoverTextColor))
            {
                // Vérifier hoverPanelSize
                if (config.hoverPanelSize == null || 
                    (Mathf.Approximately(config.hoverPanelSize.width, 500f) && 
                     Mathf.Approximately(config.hoverPanelSize.height, 50f)))
                {
                    // Vérifier hoverBackgroundColor
                    if (config.hoverBackgroundColor == "#000000" || string.IsNullOrEmpty(config.hoverBackgroundColor))
                    {
                        Debug.Log("[GameManager] UIConfig détecté comme objet vide (toutes les valeurs sont aux défauts de la classe)");
                        return false;
                    }
                }
            }
        }

        // Si au moins une propriété est différente, l'objet a probablement été configuré
        Debug.Log($"[GameManager] UIConfig détecté comme configuré (hoverTextSize={config.hoverTextSize}, hoverTextColor={config.hoverTextColor}, hoverPanelSize=({config.hoverPanelSize?.width}, {config.hoverPanelSize?.height}), hoverBackgroundColor={config.hoverBackgroundColor})");
        return true;
    }

    /// <summary>
    /// Vérifie si un FeedbackMessagesConfig a été réellement configuré dans le JSON (pas juste un objet vide avec valeurs par défaut)
    /// </summary>
    private bool IsFeedbackMessagesConfigActuallyConfigured(FeedbackMessagesConfig config)
    {
        if (config == null) return false;

        // Les valeurs par défaut dans FeedbackMessagesConfig sont : 
        // correctAnswerMessage="BONNE RÉPONSE" (sans \n\n), incorrectAnswerMessage="MAUVAISE RÉPONSE" (sans \n\n),
        // explanationTextColor="#FFFFFF", explanationTextSize=24, resultMessageColor="#00FF00", incorrectMessageColor="#FF0000", resultMessageSize=32, useBackgroundImage=false
        // Dans general-config.json, les valeurs sont : 
        // correctAnswerMessage="\n\nBONNE RÉPONSE !!", incorrectAnswerMessage="\n\nMAUVAISE RÉPONSE !!!",
        // explanationTextColor="#64477f", explanationTextSize=28, resultMessageColor="#539d38", incorrectMessageColor="#d13e48", resultMessageSize=36, useBackgroundImage=true
        
        // Vérifier useBackgroundImage (valeur par défaut : false, dans general-config.json : true) - c'est la propriété la plus différente
        if (config.useBackgroundImage == false)
        {
            // Vérifier les messages (sans les \n\n)
            if ((config.correctAnswerMessage == "BONNE RÉPONSE" || string.IsNullOrEmpty(config.correctAnswerMessage)) && 
                (config.incorrectAnswerMessage == "MAUVAISE RÉPONSE" || string.IsNullOrEmpty(config.incorrectAnswerMessage)))
            {
                // Vérifier explanationTextSize (valeur par défaut : 24, dans general-config.json : 28)
                if (Mathf.Approximately(config.explanationTextSize, 24f))
                {
                    // Vérifier explanationTextColor (valeur par défaut : "#FFFFFF", dans general-config.json : "#64477f")
                    if (config.explanationTextColor == "#FFFFFF" || string.IsNullOrEmpty(config.explanationTextColor))
                    {
                        Debug.Log("[GameManager] FeedbackMessagesConfig détecté comme objet vide (toutes les valeurs sont aux défauts de la classe)");
                        return false;
                    }
                }
            }
        }

        // Si au moins une propriété est différente, l'objet a probablement été configuré
        Debug.Log($"[GameManager] FeedbackMessagesConfig détecté comme configuré (correctMessage='{config.correctAnswerMessage}', incorrectMessage='{config.incorrectAnswerMessage}', explanationColor={config.explanationTextColor}, explanationSize={config.explanationTextSize}, useBackgroundImage={config.useBackgroundImage})");
        return true;
    }




    private void InitializeGameOnce()
    {
        if (gameInitialized) return;
        gameInitialized = true;
        InitializeGame(); // ← ta méthode existante
    }




    void Start()
    {
        // IMPORTANT : Ne pas s'activer si on est dans la scène "trous" ou "calculator"
        // Ces scènes ont leurs propres managers (TrousGameManager, CalculatorGameManager)
        string currentScene = UnityEngine.SceneManagement.SceneManager.GetActiveScene().name;
        string levelType = PlayerPrefs.GetString("CurrentLevelType", "");
        
        if (currentScene.ToLowerInvariant() == "trous" && levelType.ToLowerInvariant() == "trous")
        {
            Debug.LogWarning($"[GameManager] ⚠️ GameManager désactivé dans la scène 'trous' - TrousGameManager doit gérer cette scène");
            Debug.LogWarning($"[GameManager] ⚠️ Ce GameManager '{gameObject.name}' ne devrait pas être dans la scène 'trous'");
            this.enabled = false; // Désactiver ce composant
            return;
        }
        
        if (currentScene.ToLowerInvariant() == "calculator" && levelType.ToLowerInvariant() == "calculator")
        {
            Debug.LogWarning($"[GameManager] ⚠️ GameManager désactivé dans la scène 'calculator' - CalculatorGameManager doit gérer cette scène");
            this.enabled = false; // Désactiver ce composant
            return;
        }
        
        if (targetZones == null)
        {
            targetZones = new List<GameObject>();
        }

        // Config hover : selon le mode (bandeau bas ou panneau centré), on crée ou non l'ancien hoverPanel
        var uiConfigAtStart = GetUIConfig();
        bool useCenteredHoverAtStart = uiConfigAtStart != null && uiConfigAtStart.useCenteredHoverPanel;

        if (hoverPanel != null)
        {
            // Toujours désactiver au départ
            hoverPanel.SetActive(false);
        }

        // Si on n'utilise PAS le mode centré, on garde l'ancien hoverPanel en bas d'écran
        if (!useCenteredHoverAtStart)
        {
            if (hoverPanel == null || hoverText == null)
            {
                SetupHoverUIAutomatically();
            }
        }

        StartCoroutine(LoadGameDirectly());
    }

    IEnumerator LoadGameDirectly()
    {
        if (UnifiedLoadingManager.Instance != null)
        {
            UnifiedLoadingManager.ShowLoading("Chargement de la configuration du jeu...", LoadingContext.Game);
        }

        // CORRECTION : Créer GeneralConfigManager s'il n'existe pas
        if (GeneralConfigManager.Instance == null)
        {
            GameObject configManagerObj = new GameObject("GeneralConfigManager");
            configManagerObj.AddComponent<GeneralConfigManager>();
            Debug.Log("[GameManager] GeneralConfigManager créé automatiquement");
        }

        // Attendre que GeneralConfigManager soit initialisé et chargé
        while (GeneralConfigManager.Instance == null || !GeneralConfigManager.Instance.IsConfigLoaded())
        {
            yield return null;
        }
        
        // NOUVEAU : Vérifier si les données API sont disponibles via GameDataManager
        if (GameDataManager.Instance != null && GameDataManager.Instance.HasData)
        {
            Debug.Log("[GameManager] ✅ Données API disponibles dans GameDataManager, utilisation de LoadGameConfigurationFromApi()");
            yield return StartCoroutine(LoadGameConfigurationFromApi());
            yield break;
        }
        
        Debug.Log("[GameManager] ⚠️ Pas de données API, tentative de chargement depuis URL...");

        string gameConfigUrl = PlayerPrefs.GetString("GameConfigUrl", configUrl);
        
        // Si vide, utiliser la valeur par défaut de general-config.json
        if (string.IsNullOrEmpty(gameConfigUrl))
        {
            gameConfigUrl = GeneralConfigManager.Instance?.GetDefaultGameConfigUrl("shooting") ?? "";
            if (!string.IsNullOrEmpty(gameConfigUrl))
            {
                Debug.Log($"[GameManager] Utilisation de la config par défaut: {gameConfigUrl}");
            }
        }

        if (string.IsNullOrEmpty(gameConfigUrl))
        {
            Debug.LogError("[GameManager] GameConfigUrl introuvable et aucune valeur par défaut disponible");
            if (UnifiedLoadingManager.Instance != null)
            {
                UnifiedLoadingManager.HideLoading();
            }
            yield break;
        }

        using (UnityWebRequest request = UnityWebRequest.Get(gameConfigUrl))
        {
            request.timeout = 30;
            yield return request.SendWebRequest();

            if (request.result == UnityWebRequest.Result.Success)
            {
                GameConfigWrapper wrapper = JsonUtility.FromJson<GameConfigWrapper>(request.downloadHandler.text);
                if (wrapper != null && wrapper.gameConfig != null)
                {
                    gameConfig = wrapper.gameConfig;
                }
                else
                {
                    Debug.LogError("[GameManager] Structure JSON invalide");
                    yield break;
                }

                yield return StartCoroutine(LoadQuestionsFromUrl());

                if (questions == null || questions.Length == 0)
                {
                    Debug.LogError("[GameManager] Aucune question chargée");
                    yield break;
                }

                yield return StartCoroutine(LoadAllAssets());
                InitializeGame();
                
                
                if (UnifiedLoadingManager.Instance != null)
                {
                    UnifiedLoadingManager.HideLoadingAfterDelay(0.5f);
                }
            }
            else
            {
                Debug.LogError($"[GameManager] Erreur chargement: {request.error}");
                if (UnifiedLoadingManager.Instance != null)
                {
                    UnifiedLoadingManager.HideLoading();
                }
            }
        }
    }







    IEnumerator LoadGameConfiguration()
    {
        Debug.Log("Chargement de la configuration...");

        // CORRECTION : Créer GeneralConfigManager s'il n'existe pas
        if (GeneralConfigManager.Instance == null)
        {
            GameObject configManagerObj = new GameObject("GeneralConfigManager");
            configManagerObj.AddComponent<GeneralConfigManager>();
            Debug.Log("[GameManager] GeneralConfigManager créé automatiquement");
        }

        // Attendre que GeneralConfigManager soit initialisé et chargé
        while (GeneralConfigManager.Instance == null || !GeneralConfigManager.Instance.IsConfigLoaded())
        {
            yield return null;
        }
        
        // NOUVEAU : Vérifier si les données API sont disponibles via GameDataManager
        if (GameDataManager.Instance != null && GameDataManager.Instance.HasData)
        {
            Debug.Log("[GameManager] ✅ Utilisation des données API depuis GameDataManager");
            yield return StartCoroutine(LoadGameConfigurationFromApi());
            yield break;
        }

        string gameConfigUrl = PlayerPrefs.GetString("GameConfigUrl", configUrl);
        Debug.Log($"[GameManager] URL de configuration: {gameConfigUrl}");

        using (UnityWebRequest www = UnityWebRequest.Get(gameConfigUrl))
        {
            // NOTE: Headers Cache-Control, Pragma, Expires retirés pour éviter les problèmes CORS en WebGL
            // Ces headers ne sont pas autorisés par certains serveurs dans Access-Control-Allow-Headers

            yield return www.SendWebRequest();

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

            string jsonData = www.downloadHandler.text;
            Debug.Log($"JSON reçu ({jsonData.Length} caractères)");

            GameConfigWrapper wrapper = JsonUtility.FromJson<GameConfigWrapper>(jsonData);

            if (wrapper?.gameConfig != null)
            {
                gameConfig = wrapper.gameConfig;
                Debug.Log($"Configuration parsée - questionsUrl: '{gameConfig.questionsUrl}'");

                yield return StartCoroutine(LoadQuestionsFromUrl());

                if (questions != null && questions.Length > 0)
                {
                    Debug.Log($"{questions.Length} questions chargées");
                    yield return StartCoroutine(LoadAllAssets());
                }
                else
                {
                    Debug.LogError("Aucune question chargée");
                }
            }
            else
            {
                Debug.LogError("Structure JSON invalide");
            }
        }
    }
    
    /// <summary>
    /// Charge la configuration du jeu depuis les données API (GameDataManager)
    /// </summary>
    IEnumerator LoadGameConfigurationFromApi()
    {
        APIGameData apiData = GameDataManager.Instance.CurrentGameData;
        
        if (apiData == null)
        {
            Debug.LogError("[GameManager] ❌ CurrentGameData est null !");
            if (UnifiedLoadingManager.Instance != null)
            {
                UnifiedLoadingManager.HideLoading();
            }
            yield break;
        }
        
        // ═══════════════════════════════════════════════════════════════════
        // 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("[GameManager] ❌ Timeout lors du reset des réponses");
                if (UnifiedLoadingManager.Instance != null)
                {
                    UnifiedLoadingManager.HideLoading();
                }
                yield break;
            }
            
            if (!resetSuccess)
            {
                Debug.LogError($"[GameManager] ❌ Échec du reset des réponses: {resetError}");
                if (UnifiedLoadingManager.Instance != null)
                {
                    UnifiedLoadingManager.HideLoading();
                }
                yield break;
            }
            
            Debug.Log($"[GameManager] ✅ Reset des réponses réussi pour gameId: {gameId}");
        }
        else
        {
            Debug.LogWarning("[GameManager] ⚠️ Pas de gameId disponible, skip du reset des réponses");
        }
        // ═══════════════════════════════════════════════════════════════════
        
        Debug.Log($"[GameManager] 📊 Données API:");
        Debug.Log($"[GameManager] - Background: {apiData.background?.type} - {apiData.background?.url}");
        Debug.Log($"[GameManager] - Questions (apiData.questions): {(apiData.questions != null ? apiData.questions.Length.ToString() : "NULL")}");
        Debug.Log($"[GameManager] - Zones: {apiData.zones?.Length ?? 0}");
        
        // Convertir les données API vers GameConfigData
        gameConfig = ConvertApiDataToGameConfig(apiData);
        
        // Charger les questions depuis l'API
        questions = GameDataManager.Instance.GetQuestionsForGame();
        
        Debug.Log($"[GameManager] 📊 Questions converties: {(questions != null ? questions.Length.ToString() : "NULL")}");
        
        if (questions != null && questions.Length > 0)
        {
            Debug.Log($"[GameManager] ✅ {questions.Length} questions chargées depuis l'API");
            
            // Valider les questions
            ValidateQuestions();
            
            // Charger les assets
            yield return StartCoroutine(LoadAllAssets());
            
            // Initialiser le jeu
            InitializeGame();
            
            // Masquer l'écran de chargement
            if (UnifiedLoadingManager.Instance != null)
            {
                UnifiedLoadingManager.HideLoadingAfterDelay(0.5f);
            }
        }
        else
        {
            Debug.LogError("[GameManager] ❌ Aucune question dans les données API");
            Debug.LogError($"[GameManager] apiData.questions est {(apiData.questions == null ? "NULL" : "vide")}");
            
            // Essayer de créer des questions factices pour permettre le jeu de continuer (mode debug)
            // Si les zones existent, on peut créer une question par zone
            if (apiData.zones != null && apiData.zones.Length > 0)
            {
                Debug.LogWarning($"[GameManager] ⚠️ Tentative de création de questions depuis les zones ({apiData.zones.Length} zones)");
                questions = CreateQuestionsFromZones(apiData.zones);
                
                if (questions != null && questions.Length > 0)
                {
                    Debug.Log($"[GameManager] ✅ {questions.Length} questions créées depuis les zones");
                    ValidateQuestions();
                    yield return StartCoroutine(LoadAllAssets());
                    InitializeGame();
                    
                    if (UnifiedLoadingManager.Instance != null)
                    {
                        UnifiedLoadingManager.HideLoadingAfterDelay(0.5f);
                    }
                    yield break;
                }
            }
            
            // Masquer l'écran de chargement même en cas d'erreur
            if (UnifiedLoadingManager.Instance != null)
            {
                UnifiedLoadingManager.HideLoading();
            }
        }
    }
    
    /// <summary>
    /// Crée des questions basées sur les zones si les questions ne sont pas disponibles
    /// </summary>
    private Question[] CreateQuestionsFromZones(APIGameZone[] zones)
    {
        if (zones == null || zones.Length == 0) return null;
        
        // Créer une seule question avec toutes les zones comme réponses possibles
        Question q = new Question
        {
            id = 1,
            question = "Trouvez la bonne réponse",
            explanation = "",
            points = 1,
            answerCount = zones.Length,
            answers = new System.Collections.Generic.List<Answer>()
        };
        
        for (int i = 0; i < zones.Length; i++)
        {
            var zone = zones[i];
            q.answers.Add(new Answer
            {
                id = zone.id,
                text = zone.label ?? $"Zone {i + 1}",
                isCorrect = zone.is_correct,
                zoneId = zone.id.ToString(),
                choiceIndex = i
            });
        }
        
        return new Question[] { q };
    }
    
    /// <summary>
    /// Convertit les données API vers le format GameConfigData utilisé par le jeu
    /// </summary>
    private GameConfigData ConvertApiDataToGameConfig(APIGameData apiData)
    {
        GameConfigData config = new GameConfigData();
        
        // Background
        config.background = new Background();
        if (apiData.background != null)
        {
            config.background.type = apiData.background.type ?? "video";
            if (config.background.type == "video")
            {
                config.background.videoUrl = apiData.background.url;
            }
            else
            {
                config.background.imageUrl = apiData.background.url;
            }
        }
        
        // Crosshair config
        if (apiData.crosshairConfig != null)
        {
            config.crosshairConfig = new CrosshairConfig
            {
                defaultSize = apiData.crosshairConfig.defaultSize,
                minSize = apiData.crosshairConfig.minSize,
                maxSize = apiData.crosshairConfig.maxSize,
                allowPlayerResize = apiData.crosshairConfig.allowPlayerResize,
                alwaysShowCrosshair = apiData.crosshairConfig.alwaysShowCrosshair,
                onTargetSizeMultiplier = apiData.crosshairConfig.onTargetSizeMultiplier,
                offTargetSizeMultiplier = apiData.crosshairConfig.offTargetSizeMultiplier,
                onTargetAlpha = apiData.crosshairConfig.onTargetAlpha,
                offTargetAlpha = apiData.crosshairConfig.offTargetAlpha,
                sizeTransitionSpeed = apiData.crosshairConfig.sizeTransitionSpeed,
                alphaTransitionSpeed = apiData.crosshairConfig.alphaTransitionSpeed,
                smoothTransitions = apiData.crosshairConfig.smoothTransitions
            };
        }
        
        // Assets
        if (apiData.assets != null)
        {
            config.assets = new GameAssets
            {
                gun = apiData.assets.gun,
                crosshair = apiData.assets.crosshair,
                impact = apiData.assets.impact
            };
        }
        
        // Sounds
        if (apiData.sounds != null)
        {
            config.sounds = new Sounds
            {
                shoot = apiData.sounds.shoot,
                impact = apiData.sounds.impact,
                success = apiData.sounds.success,
                fail = apiData.sounds.fail
            };
        }
        
        // Gun config
        if (apiData.gunConfig != null)
        {
            config.gunConfig = new GunConfig
            {
                offsetFromCursor = apiData.gunConfig.offsetFromCursor?.ToVector2() ?? Vector2.zero,
                size = apiData.gunConfig.size,
                gunSize = apiData.gunConfig.gunSize?.ToVector2() ?? new Vector2(900, 466),
                flipHorizontally = apiData.gunConfig.flipHorizontally,
                enableRotation = apiData.gunConfig.enableRotation,
                rotationOffset = apiData.gunConfig.rotationOffset,
                alwaysVisible = apiData.gunConfig.alwaysVisible,
                fadeSpeed = apiData.gunConfig.fadeSpeed,
                hideDuringFeedback = apiData.gunConfig.hideDuringFeedback
            };
            
            if (apiData.gunConfig.boundary != null)
            {
                config.gunConfig.boundary = new GunBoundaryConfig
                {
                    enableBoundaryConstraints = apiData.gunConfig.boundary.enableBoundaryConstraints,
                    useAutomaticMargins = apiData.gunConfig.boundary.useAutomaticMargins,
                    marginLeft = apiData.gunConfig.boundary.marginLeft,
                    marginRight = apiData.gunConfig.boundary.marginRight,
                    marginTop = apiData.gunConfig.boundary.marginTop,
                    marginBottom = apiData.gunConfig.boundary.marginBottom
                };
            }
        }
        
        // Zones (convertir vers targetZones ET zoneTemplates)
        if (apiData.zones != null && apiData.zones.Length > 0)
        {
            config.targetZones = new System.Collections.Generic.List<TargetZoneData>();
            for (int i = 0; i < apiData.zones.Length; i++)
            {
                var zone = apiData.zones[i];
                config.targetZones.Add(new TargetZoneData
                {
                    id = $"zone{i + 1}",
                    x = (int)zone.x,
                    y = (int)zone.y,
                    width = (int)zone.width,
                    height = (int)zone.height
                });
            }
            
            // Créer les zoneTemplates à partir des zones de l'API
            // L'API retourne les zones dans l'ordre (zone1, zone2, zone3, zone4)
            config.zoneTemplates = new ZoneTemplates();
            
            // fourChoices utilise les 4 zones
            if (config.targetZones.Count >= 4)
            {
                config.zoneTemplates.fourChoices = new System.Collections.Generic.List<TargetZoneData>
                {
                    config.targetZones[0],
                    config.targetZones[1],
                    config.targetZones[2],
                    config.targetZones[3]
                };
            }
            
            // threeChoices utilise les 3 premières zones
            if (config.targetZones.Count >= 3)
            {
                config.zoneTemplates.threeChoices = new System.Collections.Generic.List<TargetZoneData>
                {
                    config.targetZones[0],
                    config.targetZones[1],
                    config.targetZones[2]
                };
            }
            
            // twoChoices utilise les 2 premières zones
            if (config.targetZones.Count >= 2)
            {
                config.zoneTemplates.twoChoices = new System.Collections.Generic.List<TargetZoneData>
                {
                    config.targetZones[0],
                    config.targetZones[1]
                };
            }
            
            Debug.Log($"[GameManager] ✅ {config.targetZones.Count} zones configurées depuis l'API (zoneTemplates créés)");
        }
        
        return config;
    }



    IEnumerator LoadGameConfigurationWithTimeout(System.Action<bool, Exception> onComplete)
    {
        bool success = false;
        Exception error = null;


        yield return StartCoroutine(LoadGameConfiguration());
        success = true;

        onComplete?.Invoke(success, error);
    }






    IEnumerator LoadQuestionsFromUrl()
    {
        // NOUVEAU : Vérifier si des questions sont disponibles via GameDataManager (API)
        if (GameDataManager.Instance != null && GameDataManager.Instance.HasData)
        {
            Question[] apiQuestions = GameDataManager.Instance.GetQuestionsForGame();
            if (apiQuestions != null && apiQuestions.Length > 0)
            {
                Debug.Log($"[GameManager] ✅ Utilisation des questions de l'API ({apiQuestions.Length} questions)");
                questions = apiQuestions;
                
                // Log des questions pour debug
                for (int i = 0; i < questions.Length; i++)
                {
                    Debug.Log($"[GameManager] Question {i + 1}: {questions[i].question} ({questions[i].answers?.Count ?? 0} réponses)");
                }
                
                ValidateQuestions();
                yield break;
            }
            else
            {
                Debug.LogWarning("[GameManager] ⚠️ GameDataManager a des données mais pas de questions - Fallback sur l'API questions");
            }
        }
        
        string questionsUrl = gameConfig.questionsUrl;


        // Extraire le nom du fichier original pour déterminer le nombre de réponses
        string originalFileName = questionsUrl;
        if (string.IsNullOrEmpty(questionsUrl))
        {
            Debug.LogError("🔥 URL QUESTIONS VIDE ! Génération automatique...");

            // Fallback: construire l'URL questions à partir de configUrl
            if (!string.IsNullOrEmpty(configUrl))
            {
                originalFileName = configUrl.Replace(".json", "_questions.json");
            }
            else
            {
                Debug.LogError("🔥 configUrl également vide !");
                yield break;
            }
        }

        // Déterminer le nombre de réponses à partir du nom du fichier
        int answersCount = 4; // Par défaut pour questions.json
        string fileNameLower = originalFileName.ToLower();
        
        if (fileNameLower.Contains("questions_2_choix"))
        {
            answersCount = 2;
        }
        else if (fileNameLower.Contains("questions_3_choix"))
        {
            answersCount = 3;
        }
        else if (fileNameLower.Contains("questions.json") || fileNameLower.Contains("questions"))
        {
            // questions.json ou questions -> 4 réponses par défaut
            answersCount = 4;
        }


        // Construire l'URL de l'API depuis la configuration
        string apiUrl = GeneralConfigManager.Instance?.GetQuestionsApiUrl(answersCount, 3) ?? "";
        
        if (string.IsNullOrEmpty(apiUrl))
        {
            Debug.LogError("[GameManager] ❌ Impossible de construire l'URL de l'API - Vérifiez general-config.json");
            yield break;
        }


        // Vérifier que l'utilisateur est connecté et a un token
        if (UserDataManager.Instance == null || string.IsNullOrEmpty(UserDataManager.Instance.token))
        {
            Debug.LogError("[GameManager] ❌ 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($"[GameManager] ❌ 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;

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

                if (apiResponse == null || apiResponse.data == null || apiResponse.data.Length == 0)
                {
                    Debug.LogError("[GameManager] ❌ 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 QuestionsWrapper
                Question[] mappedQuestions = new Question[apiResponse.data.Length];
                
                for (int i = 0; i < apiResponse.data.Length; i++)
                {
                    APIQuestionData apiQuestion = apiResponse.data[i];
                    Question question = new Question
                    {
                        id = apiQuestion.id,
                        question = apiQuestion.question,
                        explanation = apiQuestion.explanation,
                        answers = new List<Answer>()
                    };

                    // Mapper les réponses (convertir is_correct en isCorrect)
                    if (apiQuestion.answers != null)
                    {
                        foreach (var apiAnswer in apiQuestion.answers)
                        {
                            Answer answer = new Answer
                            {
                                id = apiAnswer.id,  // ID de l'option pour l'API answers
                                text = apiAnswer.text,
                                isCorrect = apiAnswer.is_correct,  // Conversion snake_case -> camelCase
                                zoneId = apiAnswer.zoneId
                            };
                            question.answers.Add(answer);
                        }
                    }

                    mappedQuestions[i] = question;
                }

                // Récupérer la configuration depuis general-config.json
                ShootingGameConfig shootingConfig = GeneralConfigManager.Instance?.GetConfig()?.shootingGame;
                int minCorrectPercentage = 75; // Valeur par défaut
                bool usePercentage = true;
                string description = "Il faut obtenir au moins 75% de bonnes réponses pour réussir";

                if (shootingConfig != null)
                {
                    minCorrectPercentage = shootingConfig.minCorrectPercentage;
                    usePercentage = shootingConfig.usePercentage;
                    description = shootingConfig.description ?? description;
                }
                else
                {
                    Debug.LogWarning("[GameManager] ⚠️ Configuration shootingGame non trouvée dans general-config.json, utilisation des valeurs par défaut");
                }

                // Créer le SuccessConfig depuis la configuration
                successConfig = new SuccessConfig
                {
                    minCorrectPercentage = minCorrectPercentage,
                    usePercentage = usePercentage,
                    description = description,
                    minCorrectAnswers = 0  // Non utilisé quand usePercentage = true
                };

                questions = mappedQuestions;

                Debug.Log($"  - Description: {successConfig.description}");
                Debug.Log($"  - usePercentage: {successConfig.usePercentage}");
                Debug.Log($"  - minCorrectPercentage: {successConfig.minCorrectPercentage}%");

                // Validation des questions
                ValidateQuestions();
            }
            catch (System.Exception e)
            {
                Debug.LogError($"[GameManager] ❌ Erreur de parsing JSON des questions : {e.Message}");
                Debug.LogError($"Stack trace : {e.StackTrace}");
            }
        }
    }



    void ValidateQuestions()
    {
        Debug.Log("Validation des questions...");

        for (int i = 0; i < questions.Length; i++)
        {
            Question q = questions[i];

            if (string.IsNullOrEmpty(q.question))
            {
                Debug.LogWarning($"Question {i + 1} : texte de question vide");
            }

            // CORRECTION: .Count au lieu de .Length pour List<Answer>
            if (q.answers == null || q.answers.Count == 0)
            {
                Debug.LogError($"Question {i + 1} : aucune réponse définie");
                continue;
            }

            bool hasCorrectAnswer = false;
            // CORRECTION: .Count au lieu de .Length pour List<Answer>
            for (int j = 0; j < q.answers.Count; j++)
            {
                Answer answer = q.answers[j];

                if (string.IsNullOrEmpty(answer.text))
                {
                    Debug.LogWarning($"Question {i + 1}, Réponse {j + 1} : texte vide");
                }

                if (string.IsNullOrEmpty(answer.zoneId))
                {
                    Debug.LogWarning($"Question {i + 1}, Réponse {j + 1} : zoneId manquant");
                }

                if (answer.isCorrect)
                {
                    hasCorrectAnswer = true;
                }
            }

            if (!hasCorrectAnswer)
            {
                Debug.LogError($"Question {i + 1} : aucune bonne réponse définie");
            }

            if (string.IsNullOrEmpty(q.explanation))
            {
                Debug.LogWarning($"Question {i + 1} : explication manquante");
            }
        }

        Debug.Log("Validation des questions terminée");
    }

    IEnumerator LoadAllAssets()
    {

        if (gameConfig.assets.impactEffect != null && gameConfig.assets.impactEffect.type == "animated")
        {
            Debug.Log($"Chargement animation impact avec {gameConfig.assets.impactEffect.frames.Count} frames");
            for (int i = 0; i < gameConfig.assets.impactEffect.frames.Count; i++)
            {
                // Construire l'URL complète pour chaque frame
                string frameFileName = gameConfig.assets.impactEffect.frames[i];
                
                // Si le fichier n'a pas déjà "impacts/" dans son nom, l'ajouter
                if (!frameFileName.StartsWith("impacts/") && !frameFileName.StartsWith("http://") && !frameFileName.StartsWith("https://"))
                {
                    frameFileName = "impacts/" + frameFileName;
                }
                
                // Utiliser GetGameAssetsUrl pour construire l'URL complète
                string frameUrl = GeneralConfigManager.Instance?.GetGameAssetsUrl(frameFileName) ?? frameFileName;
                
                Debug.Log($"Frame {i}: fichier='{gameConfig.assets.impactEffect.frames[i]}', URL complète='{frameUrl}'");
                yield return StartCoroutine(LoadSprite($"impact_frame_{i}", frameUrl));
                Debug.Log($"Frame {i} chargée depuis: {frameUrl}");
            }
        }

        Debug.Log("Chargement des assets...");

        // AJOUTER CES LIGNES ICI ↓
        yield return StartCoroutine(LoadQuestionsFromUrl());

        if (questions == null || questions.Length == 0)
        {
            Debug.LogError("ÉCHEC: Aucune question chargée !");
            yield break;
        }



        // FIN AJOUT ↑




        // Utiliser GetGameAssetsUrl pour l'impact (dans /images/impacts/)
        string impactUrl = !string.IsNullOrEmpty(gameConfig.assets.impact) ? 
            GeneralConfigManager.Instance.GetGameAssetsUrl(gameConfig.assets.impact) : null;
        yield return StartCoroutine(LoadSprite("impact", impactUrl));
        
        // Charger les sprites LEDs (avec defaults depuis general-config si non fournis) - utiliser GetUIUrl pour les LEDs (dans /UI/)
        var defaultAssets = GeneralConfigManager.Instance?.GetDefaultAssets();
        string ledOffUrl = !string.IsNullOrEmpty(gameConfig.assets.ledOff) ? 
            GeneralConfigManager.Instance.GetUIUrl(gameConfig.assets.ledOff) : 
            (defaultAssets != null ? GeneralConfigManager.Instance.GetUIUrl(defaultAssets.ledOff) : null);
        string ledGreenUrl = !string.IsNullOrEmpty(gameConfig.assets.ledGreen) ? 
            GeneralConfigManager.Instance.GetUIUrl(gameConfig.assets.ledGreen) : 
            (defaultAssets != null ? GeneralConfigManager.Instance.GetUIUrl(defaultAssets.ledGreen) : null);
        string ledRedUrl = !string.IsNullOrEmpty(gameConfig.assets.ledRed) ? 
            GeneralConfigManager.Instance.GetUIUrl(gameConfig.assets.ledRed) : 
            (defaultAssets != null ? GeneralConfigManager.Instance.GetUIUrl(defaultAssets.ledRed) : null);

        
        yield return StartCoroutine(LoadSprite("ledOff", ledOffUrl));
        yield return StartCoroutine(LoadSprite("ledGreen", ledGreenUrl));
        yield return StartCoroutine(LoadSprite("ledRed", ledRedUrl));
        
        // DÉSACTIVÉ : Chargement des sons désactivé pour accélérer le chargement

        // Charger les images de fond du feedback si définies
        var feedbackConfig = GetFeedbackMessagesConfig();
        if (feedbackConfig != null && 
            feedbackConfig.useBackgroundImage &&
            (!string.IsNullOrEmpty(feedbackConfig.successBackgroundImageUrl) ||
             !string.IsNullOrEmpty(feedbackConfig.failureBackgroundImageUrl)))
        {
            yield return StartCoroutine(LoadFeedbackBackground());
        }

        // CORRECTION : Vérifier que gameConfig.assets existe avant d'accéder à ses propriétés
        if (gameConfig.assets == null)
        {
            Debug.LogError("[GameManager] ❌ gameConfig.assets est NULL ! Impossible de charger crosshair et gun.");
            yield break;
        }

        
        if (!string.IsNullOrEmpty(gameConfig.assets.crosshair))
        {
            if (crosshairManager != null)
            {
                // Utiliser GetCrosshairUrl pour le crosshair (dans /images/crosshair/)
                string crosshairUrl = GeneralConfigManager.Instance.GetCrosshairUrl(gameConfig.assets.crosshair);
                
                // Vérifier que l'URL n'est pas vide avant de charger
                if (string.IsNullOrEmpty(crosshairUrl))
                {
                    // Si l'URL est vide (probablement à cause d'une URL placeholder ignorée), 
                    // essayer d'utiliser un nom de fichier par défaut basé sur l'ID du jeu extrait de l'URL
                    Debug.LogWarning($"[GameManager] ⚠️ URL crosshair vide après traitement. gameConfig.assets.crosshair='{gameConfig.assets.crosshair}'. Tentative avec nom de fichier par défaut.");
                    
                    // Extraire l'ID du jeu depuis l'URL de configuration (ex: "Q0J1" depuis "Q0J1.json")
                    string gameId = "";
                    string gameConfigUrl = PlayerPrefs.GetString("GameConfigUrl", configUrl);
                    if (!string.IsNullOrEmpty(gameConfigUrl))
                    {
                        // Extraire le nom du fichier depuis l'URL
                        int lastSlash = gameConfigUrl.LastIndexOf('/');
                        string fileName = lastSlash >= 0 ? gameConfigUrl.Substring(lastSlash + 1) : gameConfigUrl;
                        // Enlever l'extension .json
                        if (fileName.EndsWith(".json"))
                        {
                            gameId = fileName.Substring(0, fileName.Length - 5);
                        }
                    }
                    
                    // Utiliser un nom de fichier par défaut basé sur l'ID du jeu
                    string defaultFileName = "curseur_sucre.png"; // Nom par défaut
                    if (!string.IsNullOrEmpty(gameId))
                    {
                        // Essayer de construire un nom de fichier basé sur l'ID du jeu (ex: "Q0J1" -> "curseur_q0j1.png")
                        defaultFileName = $"curseur_{gameId.ToLower()}.png";
                    }
                    
                    crosshairUrl = GeneralConfigManager.Instance.GetCrosshairUrl(defaultFileName);
                    
                    if (string.IsNullOrEmpty(crosshairUrl))
                    {
                        Debug.LogError($"[GameManager] ❌ Impossible de construire l'URL crosshair même avec le nom par défaut ! crosshairPath='{GeneralConfigManager.Instance.GetConfig()?.assetsPaths?.crosshairPath}'");
                    }
                }
                
                if (!string.IsNullOrEmpty(crosshairUrl))
                {
                    crosshairManager.LoadCrosshairFromURL(crosshairUrl);
                }

                // NOUVEAU : Appliquer la configuration du viseur
                if (gameConfig.crosshairConfig != null)
                {
                    crosshairManager.ApplyCrosshairConfig(gameConfig.crosshairConfig);
                }

                
                // CORRECTION : Réactiver le crosshair après le chargement
                var crosshairCanvasGroup = crosshairManager.GetComponent<CanvasGroup>();
                if (crosshairCanvasGroup != null)
                {
                    crosshairCanvasGroup.alpha = 1f;
                }
            }
            else
            {
                Debug.LogError("[GameManager] ❌ CrosshairManager non assigné ! Le viseur ne sera pas affiché.");
            }
        }
        else
        {
            Debug.LogWarning("[GameManager] ⚠️ URL du viseur non définie dans le JSON !");
        }

        
        if (!string.IsNullOrEmpty(gameConfig.assets.gun))
        {
            if (gunSpriteManager != null)
            {
                // Utiliser GetGameAssetsUrl pour le gun (dans /images/)
                string gunUrl = GeneralConfigManager.Instance.GetGameAssetsUrl(gameConfig.assets.gun);
                gunSpriteManager.LoadGunFromURL(gunUrl);
                ApplyGunConfigFromJSON();
                
                // CORRECTION : Réactiver le gun après le chargement
                var gunCanvasGroup = gunSpriteManager.GetComponent<CanvasGroup>();
                if (gunCanvasGroup != null)
                {
                    gunCanvasGroup.alpha = 1f;
                }
            }
            else
            {
                Debug.LogError("[GameManager] ❌ GunSpriteManager non assigné ! Le pistolet ne sera pas affiché.");
            }
        }
        else
        {
            Debug.LogWarning("[GameManager] ⚠️ URL du pistolet non définie dans le JSON !");
        }

        yield return StartCoroutine(LoadBackgroundVideo());


        // Dialogue "Before" if provided
        /*
        if (dialoguePlayer != null && HasBeforeDialogue())
        {
            //yield return StartCoroutine(PlayDialogueUrl(dBeforeUrl));
            yield return StartCoroutine(PlayDialogueUrl(dBeforeUrl, gDialogueDefault));
        }
        */
        //InitializeGame();
        //yield return StartCoroutine(StartGameFlow());
        // On bascule maintenant sur le flux de jeu (attend le BEFORE si présent)
        StartCoroutine(StartGameFlow());
        yield break;
    }

    IEnumerator LoadFeedbackBackground()
    {
        var feedbackConfig = GetFeedbackMessagesConfig();

        // Charger l'image de succès
        if (!string.IsNullOrEmpty(feedbackConfig.successBackgroundImageUrl))
        {
            // Construire l'URL complète via GetUIUrl (les backgrounds de feedback sont dans /UI/)
            string successUrl = GeneralConfigManager.Instance.GetUIUrl(feedbackConfig.successBackgroundImageUrl);
            Debug.Log($"Chargement image succès: {successUrl}");

            using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(successUrl))
            {
                yield return www.SendWebRequest();

                if (www.result == UnityWebRequest.Result.Success)
                {
                    Texture2D texture = ((DownloadHandlerTexture)www.downloadHandler).texture;
                    feedbackSuccessSprite = Sprite.Create(texture,
                        new Rect(0, 0, texture.width, texture.height),
                        new Vector2(0.5f, 0.5f));
                    Debug.Log("Image succès chargée");
                }
                else
                {
                    Debug.LogError($"Erreur image succès: {www.error}");
                }
            }
        }

        // Charger l'image d'échec
        if (!string.IsNullOrEmpty(feedbackConfig.failureBackgroundImageUrl))
        {
            // Construire l'URL complète via GetUIUrl (les backgrounds de feedback sont dans /UI/)
            string failureUrl = GeneralConfigManager.Instance.GetUIUrl(feedbackConfig.failureBackgroundImageUrl);
            Debug.Log($"Chargement image échec: {failureUrl}");

            using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(failureUrl))
            {
                yield return www.SendWebRequest();

                if (www.result == UnityWebRequest.Result.Success)
                {
                    Texture2D texture = ((DownloadHandlerTexture)www.downloadHandler).texture;
                    feedbackFailureSprite = Sprite.Create(texture,
                        new Rect(0, 0, texture.width, texture.height),
                        new Vector2(0.5f, 0.5f));
                    Debug.Log("Image échec chargée");
                }
                else
                {
                    Debug.LogError($"Erreur image échec: {www.error}");
                }
            }
        }
    }

    // Reste du code de GameManager...


    IEnumerator LoadSprite(string key, string url)
    {
        
        if (string.IsNullOrEmpty(url))
        {
            Debug.LogError($"[GameManager] ❌ URL vide pour sprite '{key}'");
            yield break;
        }
        
        using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(url))
        {
            yield return www.SendWebRequest();

            if (www.result == UnityWebRequest.Result.Success)
            {
                Texture2D texture = ((DownloadHandlerTexture)www.downloadHandler).texture;
                Sprite sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
                loadedSprites[key] = sprite;
            }
            else
            {
                Debug.LogError($"[GameManager] ❌ Erreur chargement sprite '{key}' depuis {url}: {www.error}");
                
                // CORRECTION : Charger le sprite d'impact par défaut si le chargement échoue
                if (key == "impact")
                {
                    var defaultAssets = GeneralConfigManager.Instance?.GetDefaultAssets();
                    if (defaultAssets != null && !string.IsNullOrEmpty(defaultAssets.impactEffect))
                    {
                        // Essayer de charger le sprite d'impact par défaut (dans /images/impacts/)
                        // Le fichier est dans impacts/, donc on ajoute le chemin
                        string impactFileName = defaultAssets.impactEffect;
                        if (!impactFileName.StartsWith("impacts/"))
                        {
                            impactFileName = "impacts/" + impactFileName;
                        }
                        string defaultImpactUrl = GeneralConfigManager.Instance.GetGameAssetsUrl(impactFileName);
                        Debug.LogWarning($"[GameManager] ⚠️ Tentative de chargement du sprite d'impact par défaut: {defaultImpactUrl}");
                        
                        // Charger le sprite par défaut directement
                        using (UnityWebRequest fallbackRequest = UnityWebRequestTexture.GetTexture(defaultImpactUrl))
                        {
                            yield return fallbackRequest.SendWebRequest();
                            
                            if (fallbackRequest.result == UnityWebRequest.Result.Success)
                            {
                                Texture2D fallbackTexture = ((DownloadHandlerTexture)fallbackRequest.downloadHandler).texture;
                                Sprite fallbackSprite = Sprite.Create(fallbackTexture, new Rect(0, 0, fallbackTexture.width, fallbackTexture.height), new Vector2(0.5f, 0.5f));
                                loadedSprites[key] = fallbackSprite;
                            }
                            else
                            {
                                // Créer un sprite de fallback simple si le chargement par défaut échoue aussi
                                Texture2D simpleFallback = new Texture2D(64, 64);
                                Color[] pixels = new Color[64 * 64];
                                for (int i = 0; i < pixels.Length; i++)
                                {
                                    pixels[i] = Color.white;
                                }
                                simpleFallback.SetPixels(pixels);
                                simpleFallback.Apply();
                                
                                Sprite simpleSprite = Sprite.Create(simpleFallback, new Rect(0, 0, 64, 64), new Vector2(0.5f, 0.5f));
                                loadedSprites[key] = simpleSprite;
                                Debug.LogWarning($"[GameManager] ⚠️ Sprite de fallback simple créé pour '{key}' (cercle blanc)");
                            }
                        }
                    }
                    else
                    {
                        // Créer un sprite de fallback simple si aucun défaut n'est disponible
                        Texture2D fallbackTexture = new Texture2D(64, 64);
                        Color[] pixels = new Color[64 * 64];
                        for (int i = 0; i < pixels.Length; i++)
                        {
                            pixels[i] = Color.white;
                        }
                        fallbackTexture.SetPixels(pixels);
                        fallbackTexture.Apply();
                        
                        Sprite fallbackSprite = Sprite.Create(fallbackTexture, new Rect(0, 0, 64, 64), new Vector2(0.5f, 0.5f));
                        loadedSprites[key] = fallbackSprite;
                        Debug.LogWarning($"[GameManager] ⚠️ Sprite de fallback créé pour '{key}' (cercle blanc)");
                    }
                }
            }
        }
    }

    IEnumerator LoadAudioClip(string key, string url)
    {
        if (string.IsNullOrEmpty(url))
        {
            Debug.LogWarning($"[GameManager] ⚠️ URL vide pour audio '{key}'");
            yield break;
        }
        
        using (UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip(url, AudioType.WAV))
        {
            yield return www.SendWebRequest();

            if (www.result == UnityWebRequest.Result.Success)
            {
                AudioClip audioClip = DownloadHandlerAudioClip.GetContent(www);
                loadedAudioClips[key] = audioClip;
            }
            else
            {
                Debug.LogError($"[GameManager] ❌ Erreur chargement audio '{key}' depuis {url}: {www.error}");
            }
        }
    }

    IEnumerator LoadTextureFromURL(string url, System.Action<Texture2D> onComplete)
    {
        using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(url))
        {
            yield return www.SendWebRequest();

            if (www.result == UnityWebRequest.Result.Success)
            {
                Texture2D texture = ((DownloadHandlerTexture)www.downloadHandler).texture;
                onComplete?.Invoke(texture);
            }
            else
            {
                Debug.LogError($"Erreur de chargement de la texture depuis {url}: {www.error}");
                onComplete?.Invoke(null);
            }
        }
    }

    IEnumerator LoadBackgroundVideo()
    {
        
        if (backgroundVideo == null)
        {
            Debug.LogError("[GameManager] ❌ backgroundVideo est NULL !");
            yield return null;
            yield break;
        }
        
        if (gameConfig == null)
        {
            Debug.LogError("[GameManager] ❌ gameConfig est NULL !");
            yield return null;
            yield break;
        }
        
        if (gameConfig.background == null)
        {
            Debug.LogError("[GameManager] ❌ gameConfig.background est NULL !");
            yield return null;
            yield break;
        }

        string videoUrl;
        if (!string.IsNullOrEmpty(customVideoUrl))
        {
            videoUrl = customVideoUrl;
            Debug.Log($"[GameManager] Utilisation customVideoUrl: {videoUrl}");
        }
        else
        {
            // Construire l'URL complète via GetBackgroundVideoUrl (les vidéos de fond sont dans /videos/)
            string videoFileName = gameConfig.background.videoUrl;
            
            if (string.IsNullOrEmpty(videoFileName))
            {
                Debug.LogError("[GameManager] ❌ videoFileName est vide !");
                yield return null;
                yield break;
            }
            
            Debug.Log($"[GameManager] videoFileName brut: {videoFileName}");
            videoUrl = GeneralConfigManager.Instance.GetBackgroundVideoUrl(videoFileName);
            Debug.Log($"[GameManager] videoUrl complet: {videoUrl}");
        }

        // CORRECTION : Réactiver le GameObject de la vidéo s'il était caché
        if (backgroundVideo.gameObject != null && !backgroundVideo.gameObject.activeSelf)
        {
            backgroundVideo.gameObject.SetActive(true);
        }
        
        // IMPORTANT: Attendre que le ShootingGameLayout soit complètement chargé
        ShootingGameLayout layout = ShootingGameLayout.Instance;
        if (layout != null && !layout.IsFullyLoaded)
        {
            Debug.Log("[GameManager] ⏳ Attente du chargement complet du ShootingGameLayout pour la vidéo...");
            float timeout = 10f;
            float elapsed = 0f;
            while (!layout.IsFullyLoaded && elapsed < timeout)
            {
                yield return new WaitForSeconds(0.1f);
                elapsed += 0.1f;
            }
            
            if (elapsed >= timeout)
            {
                Debug.LogWarning("[GameManager] ⏱️ Timeout attente layout pour vidéo, on continue quand même...");
            }
            else
            {
                Debug.Log("[GameManager] ✅ ShootingGameLayout prêt, configuration de la vidéo...");
            }
        }
        
        Debug.Log($"[GameManager] ShootingGameLayout.Instance: {(layout != null ? "trouvé" : "NULL")}");
        
        if (layout != null)
        {
            Debug.Log($"[GameManager] Layout.VideoRenderTexture: {(layout.VideoRenderTexture != null ? "présente" : "NULL")}");
        }
        
        if (layout != null && layout.VideoRenderTexture != null)
        {
            // IMPORTANT: Utiliser UNIQUEMENT RenderTexture - pas de fallback CameraNearPlane !
            backgroundVideo.renderMode = VideoRenderMode.RenderTexture;
            backgroundVideo.targetTexture = layout.VideoRenderTexture;
            backgroundVideo.targetCamera = null; // S'assurer qu'on n'utilise pas la caméra
            backgroundVideo.targetCameraAlpha = 0f; // Désactiver complètement le rendu caméra
            
            // IMPORTANT: Désactiver tout Renderer attaché au VideoPlayer qui pourrait afficher la vidéo ailleurs
            Renderer videoRenderer = backgroundVideo.GetComponent<Renderer>();
            if (videoRenderer != null)
            {
                videoRenderer.enabled = false;
                Debug.Log("[GameManager] ⚠️ Renderer du VideoPlayer désactivé");
            }
            
            // S'assurer que la caméra ne montre pas un fond vidéo
            if (mainCamera != null)
            {
                mainCamera.clearFlags = CameraClearFlags.Depth; // Ne pas effacer le color buffer
                Debug.Log("[GameManager] Caméra configurée en ClearFlags.Depth");
            }
            
            Debug.Log($"[GameManager] ✅ Vidéo configurée pour RenderTexture ({layout.VideoRenderTexture.width}x{layout.VideoRenderTexture.height})");
        }
        else
        {
            // IMPORTANT: Ne PAS utiliser CameraNearPlane car ça prend tout l'écran !
            // Si on n'a pas de RenderTexture, on ne peut pas afficher la vidéo dans la zone de jeu
            Debug.LogError("[GameManager] ❌ Pas de RenderTexture disponible - la vidéo ne sera pas confinée à la zone de jeu !");
            
            // Configurer quand même en RenderTexture avec une texture temporaire pour éviter le plein écran
            RenderTexture tempRT = new RenderTexture(1480, 835, 0);
            tempRT.Create();
            backgroundVideo.renderMode = VideoRenderMode.RenderTexture;
            backgroundVideo.targetTexture = tempRT;
            backgroundVideo.targetCamera = null;
            Debug.Log("[GameManager] ⚠️ RenderTexture temporaire créée pour éviter le plein écran");
        }
        
        backgroundVideo.source = VideoSource.Url;
        backgroundVideo.url = videoUrl;
        backgroundVideo.isLooping = true;
        backgroundVideo.playOnAwake = false;
        
        // Préparer la vidéo avant de la jouer
        backgroundVideo.Prepare();
        Debug.Log("[GameManager] Préparation de la vidéo...");
        
        // Attendre que la vidéo soit prête
        float videoTimeout = 10f;
        float videoElapsed = 0f;
        while (!backgroundVideo.isPrepared && videoElapsed < videoTimeout)
        {
            videoElapsed += Time.deltaTime;
            yield return null;
        }
        
        if (backgroundVideo.isPrepared)
        {
            backgroundVideo.Play();
            Debug.Log("[GameManager] ✅ Vidéo lancée avec succès !");
        }
        else
        {
            Debug.LogError("[GameManager] ❌ Timeout: la vidéo n'a pas pu être préparée");
        }

        yield return null;
    }


    private IEnumerator StartGameFlow()
    {
        // ANCIEN SYSTÈME - À DÉSACTIVER
        /*
        if (!EnsureDialoguePlayer())
            Debug.LogError("[FLOW] Pas de DialoguePlayer : le BEFORE ne pourra pas s'afficher.");

        try { dialoguePlayer.BringToFront(50000); } catch {}

        if (!string.IsNullOrEmpty(dBeforeUrl))
        {
            Debug.Log("[FLOW] Playing BEFORE dialogue...");
            gameplayUiPrev = true;
            SetGameplayUIVisible(false);
            yield return StartCoroutine(PlayDialogueUrl(dBeforeUrl, gDialogueDefault));
            Debug.Log("[FLOW] BEFORE dialogue finished.");
            KillStrayDialogueUI();
            SetGameplayUIVisible(true);
        }
        */

        // NOUVEAU SYSTÈME - Le dialogue BEFORE a déjà été joué dans la scène Player
        Debug.Log("[FLOW] Démarrage direct du jeu (dialogues gérés par le système multi-scènes)");

        InitializeGameOnce(); // Lancer directement le jeu

        // Masquer l'écran de chargement après un court délai
        if (UnifiedLoadingManager.Instance != null)
        {
            UnifiedLoadingManager.HideLoadingAfterDelay(0.5f);
        }

        // IMPORTANT : yield break pour terminer la coroutine
        yield break;
    }







    void InitializeGame()
    {
        // IMPORTANT : Nettoyer les éléments UI des dialogues AVANT d'initialiser le jeu
        Debug.Log("[GameManager] Nettoyage des UI de dialogue avant initialisation du jeu");
        KillStrayDialogueUI();
        
        // NOUVEAU : Si on utilise ShootingGameLayout, attendre qu'il soit complètement chargé
        if (ShootingGameLayout.Instance != null)
        {
            StartCoroutine(WaitForLayoutAndInitialize());
            return;
        }
        
        // Sinon, initialiser normalement
        InitializeGameInternal();
    }
    
    IEnumerator WaitForLayoutAndInitialize()
    {
        Debug.Log("[GameManager] ⏳ Attente du chargement complet du ShootingGameLayout...");
        
        float timeout = 10f;
        float elapsed = 0f;
        
        while (ShootingGameLayout.Instance != null && !ShootingGameLayout.Instance.IsFullyLoaded && elapsed < timeout)
        {
            yield return new WaitForSeconds(0.1f);
            elapsed += 0.1f;
        }
        
        if (elapsed >= timeout)
        {
            Debug.LogWarning("[GameManager] ⏱️ Timeout atteint lors de l'attente du layout, initialisation quand même...");
        }
        else
        {
            Debug.Log("[GameManager] ✅ ShootingGameLayout complètement chargé, initialisation du jeu...");
        }
        
        InitializeGameInternal();
    }
    
    void InitializeGameInternal()
    {
        // ORDRE IMPORTANT pour les ordres de rendu :
        CreateUIBands();              // 1. Bandes en arrière-plan (ordre 5)
        CreateTopBandContent();       // 2. Contenu du bandeau supérieur (LEDs + Question)
        
        // IMPORTANT : Configurer la résolution de référence pour la détection des zones
        // Cela corrige le problème de mapping des zones sur différentes résolutions/ratios d'écran
        if (gameConfig != null && gameConfig.resolution != null)
        {
            int refWidth = gameConfig.resolution.width;
            int refHeight = gameConfig.resolution.height;
            
            TargetZone.SetReferenceResolution(refWidth, refHeight);
            
            
            float refAspect = (float)refWidth / refHeight;
            float screenAspect = (float)Screen.width / Screen.height;
            Debug.Log($"[GameManager] Ratio de référence: {refAspect:F3} ({refWidth}:{refHeight})");
            Debug.Log($"[GameManager] Ratio écran: {screenAspect:F3} ({Screen.width}:{Screen.height})");
            
            if (Mathf.Abs(refAspect - screenAspect) > 0.01f)
            {
                Debug.LogWarning($"[GameManager] ⚠️ Ratios différents ! Letterbars/Pillarboxes attendues.");
            }
        }
        else
        {
            Debug.LogWarning("[GameManager] ⚠️ Résolution de référence non définie dans le JSON, utilisation de 1920x1080 par défaut");
            TargetZone.SetReferenceResolution(1920, 1080);
        }
        
        SetupCurrentQuestion();
        SetupFullscreenVideo();

        Debug.Log("Jeu initialisé avec succès - Système de clic direct activé");

        if (crosshairManager != null)
        {
            Debug.Log("Système de viseur conditionnel activé");
        }

        Debug.Log("Jeu initialisé avec succès - Système de clic direct activé");

        // NOUVEAU : Forcer l'affichage des éléments de gameplay
        ForceShowGameplayElements();


    }


    void ForceShowGameplayElements()
    {
        Debug.Log("[GameManager] Affichage forcé des éléments de gameplay");

        // Forcer le crosshair à être visible
        if (crosshairManager != null)
        {
            crosshairManager.gameObject.SetActive(true);
            var canvas = crosshairManager.GetComponent<Canvas>();
            if (canvas == null) canvas = crosshairManager.gameObject.AddComponent<Canvas>();
            canvas.overrideSorting = true;
            canvas.sortingOrder = 60000; // Au-dessus de tout
            Debug.Log("[GameManager] Crosshair forcé visible");
        }

        // Forcer le gun à être visible
        if (gunSpriteManager != null)
        {
            gunSpriteManager.gameObject.SetActive(true);
            var canvas = gunSpriteManager.GetComponent<Canvas>();
            if (canvas == null) canvas = gunSpriteManager.gameObject.AddComponent<Canvas>();
            canvas.overrideSorting = true;
            canvas.sortingOrder = 60000;
            Debug.Log("[GameManager] Gun forcé visible");
        }

        if (fixedHoverPanel != null)
        {
            var canvas = fixedHoverPanel.GetComponent<Canvas>();
            if (canvas == null) canvas = fixedHoverPanel.AddComponent<Canvas>();
            canvas.overrideSorting = true;
            canvas.sortingOrder = 65000; // Au-dessus de tout
            Debug.Log("[GameManager] Hover panel forcé au premier plan");
        }



        // S'assurer que l'UI de gameplay est visible
        SetGameplayUIVisible(true);
    }

    /// <summary>
    /// Crée le contenu du bandeau supérieur : LEDs à gauche, question au centre
    /// </summary>
    void CreateTopBandContent()
    {
        if (topBand == null)
        {
            Debug.LogError("Impossible de créer le contenu : topBand n'existe pas");
            return;
        }

        CreateLEDsInTopBand();
        CreateQuestionInTopBand();

        Debug.Log("Contenu du bandeau supérieur créé (LEDs + Question)");
    }

    /// <summary>
    /// Crée les LEDs de progression dans le bandeau supérieur
    /// </summary>
    void CreateLEDsInTopBand()
    {
        if (gameConfig == null || questions == null)
        {
            Debug.LogError("[GameManager] ERREUR : gameConfig ou questions null");
            return;
        }

        if (topBand == null)
        {
            Debug.LogError("[GameManager] ERREUR : topBand null");
            return;
        }

        // Nettoyer les LEDs existantes
        if (leds != null)
        {
            foreach (var led in leds)
            {
                if (led != null) DestroyImmediate(led);
            }
        }

        int totalQuestions = questions.Length;
        leds = new GameObject[totalQuestions];

        // Configuration des LEDs depuis le JSON ou defaults
        float ledSize = 40f;
        float ledSpacing = 50f;
        float startX = 30f;

        var uiConfig = GetUIConfig();
        if (uiConfig?.ledConfig != null)
        {
            var ledConfig = uiConfig.ledConfig;
            ledSize = ledConfig.ledSize;
            ledSpacing = ledConfig.ledSpacing;
            startX = ledConfig.marginLeft;
        }
        else
        {
            // Si pas de config dans le JSON, utiliser les defaults depuis general-config
            var defaultUIConfig = GeneralConfigManager.Instance?.GetDefaultUIConfig();
            if (defaultUIConfig != null && defaultUIConfig.ledConfig != null)
            {
                ledSize = defaultUIConfig.ledConfig.ledSize;
                ledSpacing = defaultUIConfig.ledConfig.ledSpacing;
                startX = defaultUIConfig.ledConfig.marginLeft;
            }
        }

        // Déterminer le parent des LEDs (ledContainer si disponible, sinon topBand)
        Transform ledParent = (ledContainer != null) ? ledContainer : topBand.transform;
        bool useLayoutGroup = (ledContainer != null && ledContainer.GetComponent<HorizontalLayoutGroup>() != null);

        for (int i = 0; i < totalQuestions; i++)
        {
            GameObject ledObj = new GameObject($"LED_{i}");
            ledObj.transform.SetParent(ledParent, false);

            RectTransform ledRect = ledObj.AddComponent<RectTransform>();

            // Si on utilise un LayoutGroup, pas besoin de positionner manuellement
            if (!useLayoutGroup)
            {
                // Position dans le bandeau supérieur
                ledRect.anchorMin = new Vector2(0f, 0.5f);
                ledRect.anchorMax = new Vector2(0f, 0.5f);
                ledRect.pivot = new Vector2(0f, 0.5f);
                ledRect.anchoredPosition = new Vector2(startX + (i * ledSpacing), 0f);
            }

            ledRect.sizeDelta = new Vector2(ledSize, ledSize);

            Image ledImage = ledObj.AddComponent<Image>();

            leds[i] = ledObj;
        }

        // Appliquer immédiatement un sprite OFF pour rendre les LEDs visibles
        // Récupérer la couleur OFF depuis general-config.json
        Color defaultOffColor = Color.gray;
        if (uiConfig?.ledConfig != null && uiConfig.ledConfig.defaultOffColor != default(Color))
        {
            defaultOffColor = uiConfig.ledConfig.defaultOffColor;
        }

        if (loadedSprites.ContainsKey("ledOff") && loadedSprites["ledOff"] != null)
        {
            for (int i = 0; i < leds.Length; i++)
            {
                var img = leds[i].GetComponent<Image>();
                if (img != null)
                {
                    img.sprite = loadedSprites["ledOff"];
                    img.color = Color.white;
                }
            }
        }
        else
        {
            for (int i = 0; i < leds.Length; i++)
            {
                var img = leds[i].GetComponent<Image>();
                if (img != null)
                {
                    img.sprite = null;
                    img.color = defaultOffColor;
                }
            }
        }

        ApplyLEDSprites();
    }

    /// <summary>
    /// Crée l'affichage de la question dans le bandeau supérieur
    /// </summary>
    void CreateQuestionInTopBand()
    {
        // Si le texte est déjà assigné manuellement, ne pas le recréer
        if (topBandQuestionText != null)
        {
            Debug.Log("Texte de question déjà assigné manuellement");
            ApplyQuestionStyling();
            return;
        }


        if (topBand == null)
        {
            Debug.LogError("Impossible de créer la question : topBand null");
            return;
        }

        GameObject questionObj = new GameObject("QuestionText");
        questionObj.transform.SetParent(topBand.transform, false);

        RectTransform questionRect = questionObj.AddComponent<RectTransform>();

        // Position absolument centrée dans le bandeau
        questionRect.anchorMin = new Vector2(0.5f, 0.5f);  // Centre
        questionRect.anchorMax = new Vector2(0.5f, 0.5f);  // Centre
        questionRect.pivot = new Vector2(0.5f, 0.5f);      // Centre

        // Taille fixe et position centrée
        questionRect.sizeDelta = new Vector2(1200f, 60f);  // Largeur 1200px
        questionRect.anchoredPosition = Vector2.zero;       // Position (0,0) = centre parfait

        topBandQuestionText = questionObj.AddComponent<TextMeshProUGUI>();

        topBandQuestionText.text = "Question en cours de chargement...";
        
        // Appliquer strictement le style depuis general-config.json (via GetUIConfig())
        ApplyQuestionStyling();
        
        // Forcer la mise à jour pour s'assurer que la couleur est appliquée
        topBandQuestionText.ForceMeshUpdate();

        Debug.Log($"[GameManager] Question créée - couleur actuelle: {topBandQuestionText.color}");
    }

    /// <summary>
    /// Applique le style de la question depuis la configuration JSON
    /// Utilise GetUIConfig() qui fait automatiquement le fallback vers general-config.json
    /// </summary>
    void ApplyQuestionStyling()
    {
        if (topBandQuestionText == null) return;

        // Utiliser GetUIConfig() qui fait automatiquement le fallback vers defaultUIConfig
        var uiConfig = GetUIConfig();
        if (uiConfig?.questionDisplay != null)
        {
            var questionConfig = uiConfig.questionDisplay;

            // Taille de police
            topBandQuestionText.fontSize = questionConfig.fontSize;

            // Couleur
            string colorString = questionConfig.fontColor;
            if (!colorString.StartsWith("#"))
            {
                colorString = "#" + colorString;
            }
            if (ColorUtility.TryParseHtmlString(colorString, out Color textColor))
            {
                topBandQuestionText.color = textColor;
                Debug.Log($"[GameManager] ✅ Couleur appliquée: {colorString} -> {textColor}");
            }
            else
            {
                Debug.LogWarning($"[GameManager] ⚠️ Impossible de parser la couleur: {colorString}");
            }

            // Gras
            topBandQuestionText.fontStyle = questionConfig.fontBold ? FontStyles.Bold : FontStyles.Normal;
            
            // Alignement
            topBandQuestionText.alignment = questionConfig.alignment;
            
            // Auto-sizing si activé
            if (questionConfig.adaptToScreenSize)
            {
                topBandQuestionText.enableAutoSizing = true;
                topBandQuestionText.fontSizeMin = questionConfig.minFontSize;
                topBandQuestionText.fontSizeMax = questionConfig.maxFontSize;
            }
            else
            {
                topBandQuestionText.enableAutoSizing = false;
            }
            
            // Configuration du wrapping pour éviter la troncature
            topBandQuestionText.textWrappingMode = TextWrappingModes.Normal;
            topBandQuestionText.overflowMode = TextOverflowModes.Overflow;

        }
        else
        {
            Debug.LogWarning("[GameManager] ⚠️ Aucune configuration questionDisplay trouvée");
        }
    }
    
    /// <summary>
    /// Charge les sprites des LEDs depuis les URLs
    /// </summary>
    IEnumerator LoadLEDSprites()
    {
        if (gameConfig?.assets == null) yield break; // CHANGÉ: supprimé .gameConfig

        var assets = gameConfig.assets; // CHANGÉ: supprimé .gameConfig

        // Chargement séquentiel pour éviter les problèmes de timing
        yield return StartCoroutine(LoadTextureFromURL(assets.ledOff, (texture) =>
        {
            if (texture != null)
            {
                ledOffSprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
                Debug.Log("Sprite ledOff chargé avec succès");
            }
            else
            {
                Debug.LogError("Échec du chargement ledOff");
            }
        }));

        yield return StartCoroutine(LoadTextureFromURL(assets.ledGreen, (texture) =>
        {
            if (texture != null)
            {
                ledGreenSprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
                Debug.Log("Sprite ledGreen chargé avec succès");
            }
            else
            {
                Debug.LogError("Échec du chargement ledGreen");
            }
        }));

        yield return StartCoroutine(LoadTextureFromURL(assets.ledRed, (texture) =>
        {
            if (texture != null)
            {
                ledRedSprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
                Debug.Log("Sprite ledRed chargé avec succès");
            }
            else
            {
                Debug.LogError("Échec du chargement ledRed");
            }
        }));

        // Appliquer les sprites une fois tous chargés
        ApplyLEDSprites();
        Debug.Log("Tous les sprites LEDs traités, application terminée");
    }

    /// <summary>
    /// Applique les sprites chargés aux LEDs
    /// </summary>
    void ApplyLEDSprites()
    {
        if (leds == null) return;

        // Récupérer la configuration LED depuis general-config.json
        var uiConfig = GetUIConfig();
        Color defaultGreenColor = Color.green;
        Color defaultRedColor = Color.red;
        Color defaultOffColor = Color.gray;

        if (uiConfig?.ledConfig != null)
        {
            if (uiConfig.ledConfig.defaultGreenColor != default(Color))
                defaultGreenColor = uiConfig.ledConfig.defaultGreenColor;
            if (uiConfig.ledConfig.defaultRedColor != default(Color))
                defaultRedColor = uiConfig.ledConfig.defaultRedColor;
            if (uiConfig.ledConfig.defaultOffColor != default(Color))
                defaultOffColor = uiConfig.ledConfig.defaultOffColor;
        }

        for (int i = 0; i < leds.Length; i++)
        {
            if (leds[i] != null)
            {
                Image ledImage = leds[i].GetComponent<Image>();
                if (ledImage != null)
                {
                    // État passé (déjà répondu)
                    if (i < currentQuestionIndex)
                    {
                        bool wasCorrect = (i < answeredCorrectly.Count && answeredCorrectly[i]);
                        if (wasCorrect)
                        {
                            if (loadedSprites.ContainsKey("ledGreen") && loadedSprites["ledGreen"] != null)
                            {
                                ledImage.sprite = loadedSprites["ledGreen"];
                                ledImage.color = Color.white;
                            }
                            else
                            {
                                ledImage.sprite = null;
                                ledImage.color = defaultGreenColor;
                            }
                        }
                        else
                        {
                            if (loadedSprites.ContainsKey("ledRed") && loadedSprites["ledRed"] != null)
                            {
                                ledImage.sprite = loadedSprites["ledRed"];
                                ledImage.color = Color.white;
                            }
                            else
                            {
                                ledImage.sprite = null;
                                ledImage.color = defaultRedColor;
                            }
                        }
                    }
                    // État futur (pas encore répondu)
                    else
                    {
                        if (loadedSprites.ContainsKey("ledOff") && loadedSprites["ledOff"] != null)
                        {
                            ledImage.sprite = loadedSprites["ledOff"];
                            ledImage.color = Color.white;
                        }
                        else
                        {
                            ledImage.sprite = null;
                            ledImage.color = defaultOffColor;
                        }
                    }
                }
            }
        }
    }

    void SetupFullscreenVideo()
    {
        GameObject videoBackground = GameObject.Find("VideoBackground");

        if (videoBackground != null && mainCamera != null)
        {
            float cameraHeight = mainCamera.orthographicSize * 2f;
            float cameraWidth = cameraHeight * mainCamera.aspect;

            videoBackground.transform.localScale = new Vector3(cameraWidth, cameraHeight, 1f);
            videoBackground.transform.position = new Vector3(0, 0, 5f);
        }
    }

    void SetupCurrentQuestion()
    {
        // CORRECTION: .Length au lieu de .Count pour Question[]
        if (currentQuestionIndex >= questions.Length)
        {
            EndGame();
            return;
        }

        Question currentQuestion = questions[currentQuestionIndex];

        // Mettre à jour le texte dans le bandeau supérieur
        if (topBandQuestionText != null)
        {
            topBandQuestionText.text = currentQuestion.question;
            // Réappliquer le style pour s'assurer que la couleur est correcte
            ApplyQuestionStyling();
            topBandQuestionText.ForceMeshUpdate();
            Debug.Log($"[GameManager] Question affichée - couleur: {topBandQuestionText.color}");
        }

        // Garder l'ancien système comme fallback
        if (questionText != null)
        {
            questionText.gameObject.SetActive(false);
            Debug.Log("Question centrale désactivée - utilisation du bandeau uniquement");
        }

        foreach (GameObject zone in targetZones)
        {
            if (zone != null)
                Destroy(zone);
        }
        targetZones.Clear();

        CreateTargetZones(currentQuestion);

        if (feedbackPanel != null)
        {
            feedbackPanel.SetActive(false);
        }

        HideHover();

        // CORRECTION: .Length au lieu de .Count pour Question[]
        Debug.Log($"Question {currentQuestionIndex + 1}/{questions.Length} affichée: {currentQuestion.question}");
    }

    void CreateTargetZones(Question question)
    {
        Debug.Log($"[GameManager] Création zones pour question: {question.question}");

        // Vérification de sécurité
        if (targetZones == null)
        {
            targetZones = new List<GameObject>();
            Debug.LogWarning("targetZones était null, réinitialisée");
        }

        // Nettoyer les zones existantes
        foreach (GameObject zone in targetZones)
        {
            if (zone != null) Destroy(zone);
        }
        targetZones.Clear();

        // Déterminer le template à utiliser
        List<TargetZoneData> template = GetZoneTemplate(question.answerCount > 0 ? question.answerCount : question.answers.Count);
        if (template == null)
        {
            Debug.LogError($"[GameManager] TEMPLATE NULL !");
            return;
        }

        // Définir la résolution de référence pour TargetZone (multi-résolution)
        if (gameConfig != null && gameConfig.resolution != null)
        {
            TargetZone.SetReferenceResolution(gameConfig.resolution.width, gameConfig.resolution.height);
        }

        // Créer les nouvelles zones
        for (int i = 0; i < question.answers.Count; i++)
        {
            Answer answer = question.answers[i];
            int zoneIndex = i; // Utilise l'index de boucle

            if (zoneIndex >= template.Count)
            {
                Debug.LogError($"[GameManager] Index {zoneIndex} trop grand pour template de {template.Count} zones");
                continue;
            }

            TargetZoneData zoneData = template[zoneIndex];

            // Créer la zone GameObject
            GameObject zoneObj = new GameObject("TargetZone_" + answer.text);
            TargetZone targetZone = zoneObj.AddComponent<TargetZone>();

            AnswerWithZone enrichedAnswer = new AnswerWithZone
            {
                id = answer.id,  // ID de l'option pour l'API answers
                text = answer.text,
                isCorrect = answer.isCorrect,
                zoneId = zoneData.id,
                choiceIndex = answer.choiceIndex,
                zone = new Zone
                {
                    x = zoneData.x,
                    y = zoneData.y,
                    width = zoneData.width,
                    height = zoneData.height
                }
            };

            targetZone.Initialize(enrichedAnswer);

            // DEBUG ZONES - Affichage simplifié : uniquement basé sur showDebugZones (touche D)
            if (showDebugZones)
            {
                Debug.Log($"[DEBUG] Création zone visuelle pour '{answer.text}' - showDebugZones={showDebugZones}");
                CreateVisualDebugZone(zoneObj, enrichedAnswer.zone, answer);
            }

            targetZones.Add(zoneObj);
        }
    }

    List<TargetZoneData> GetZoneTemplate(int answerCount)
    {
        if (gameConfig?.zoneTemplates == null)
        {
            // Fallback vers targetZones si zoneTemplates n'est pas défini
            if (gameConfig?.targetZones != null && gameConfig.targetZones.Count > 0)
            {
                Debug.LogWarning($"[GameManager] ⚠️ zoneTemplates non défini, utilisation de targetZones ({gameConfig.targetZones.Count} zones)");
                
                // Retourner les N premières zones selon answerCount
                int count = Mathf.Min(answerCount, gameConfig.targetZones.Count);
                return gameConfig.targetZones.GetRange(0, count);
            }
            Debug.LogError("[GameManager] ❌ Ni zoneTemplates ni targetZones ne sont définis dans la configuration");
            return null;
        }

        switch (answerCount)
        {
            case 2:
                if (gameConfig.zoneTemplates.twoChoices != null && gameConfig.zoneTemplates.twoChoices.Count > 0)
                    return gameConfig.zoneTemplates.twoChoices;
                break;
            case 3:
                if (gameConfig.zoneTemplates.threeChoices != null && gameConfig.zoneTemplates.threeChoices.Count > 0)
                    return gameConfig.zoneTemplates.threeChoices;
                break;
            case 4:
                if (gameConfig.zoneTemplates.fourChoices != null && gameConfig.zoneTemplates.fourChoices.Count > 0)
                    return gameConfig.zoneTemplates.fourChoices;
                break;
        }
        
        // Fallback si le template spécifique n'existe pas
        Debug.LogWarning($"[GameManager] ⚠️ Template pour {answerCount} réponses non trouvé, fallback vers targetZones");
        if (gameConfig?.targetZones != null && gameConfig.targetZones.Count > 0)
        {
            int count = Mathf.Min(answerCount, gameConfig.targetZones.Count);
            return gameConfig.targetZones.GetRange(0, count);
        }
        
        Debug.LogError($"[GameManager] ❌ Aucun template ni targetZones disponible pour {answerCount} réponses");
        return null;
    }

    void CreateVisualDebugZone(GameObject zoneObj, Zone zoneData, Answer answer)
    {
        // Utiliser un Canvas UI au lieu de SpriteRenderer pour être au-dessus de la vidéo
        // Chercher ou créer un Canvas pour les zones de debug
        Canvas debugCanvas = GameObject.Find("DebugZonesCanvas")?.GetComponent<Canvas>();
        if (debugCanvas == null)
        {
            GameObject canvasObj = new GameObject("DebugZonesCanvas");
            debugCanvas = canvasObj.AddComponent<Canvas>();
            debugCanvas.renderMode = RenderMode.ScreenSpaceOverlay;
            debugCanvas.sortingOrder = 10000; // Très élevé pour être au-dessus de tout
            debugCanvas.overrideSorting = true;
            
            // PAS de CanvasScaler - on utilise des coordonnées écran directes
            // pour que les zones soient positionnées correctement dans la zone de jeu
            
            canvasObj.AddComponent<UnityEngine.UI.GraphicRaycaster>();
            Debug.Log($"[GameManager] Canvas de debug créé - sortingOrder={debugCanvas.sortingOrder}, renderMode={debugCanvas.renderMode}, pas de scaler (coordonnées écran)");
        }
        
        debugCanvas.gameObject.SetActive(true);

        // Créer un GameObject enfant dans le Canvas
        GameObject uiZone = new GameObject($"DebugZone_{answer.text}");
        uiZone.transform.SetParent(debugCanvas.transform, false);

        // Ajouter un RectTransform
        RectTransform rectTransform = uiZone.AddComponent<RectTransform>();
        
        ApplyDebugZoneTransform(rectTransform, zoneData);
        
        Debug.Log($"[GameManager] Zone UI positionnée (scalée): pos={rectTransform.anchoredPosition}, size={rectTransform.sizeDelta}, Screen={Screen.width}x{Screen.height}");

        // Créer la texture pour la zone
        Texture2D tex = new Texture2D(8, 8);
        Color[] colors = new Color[8 * 8];

        Color zoneColor = answer.isCorrect ? new Color(0, 1, 0, 0.7f) : new Color(1, 0, 0, 0.7f);

        for (int i = 0; i < colors.Length; i++) colors[i] = zoneColor;

        Color borderColor = new Color(zoneColor.r, zoneColor.g, zoneColor.b, 1f);

        for (int x = 0; x < 8; x++)
        {
            for (int y = 0; y < 8; y++)
            {
                if (x == 0 || x == 7 || y == 0 || y == 7)
                {
                    colors[y * 8 + x] = borderColor;
                }
            }
        }

        tex.SetPixels(colors);
        tex.Apply();

        // Créer le sprite
        Sprite sprite = Sprite.Create(tex, new Rect(0, 0, 8, 8), new Vector2(0.5f, 0.5f));

        // Ajouter une Image UI
        UnityEngine.UI.Image image = uiZone.AddComponent<UnityEngine.UI.Image>();
        image.sprite = sprite;
        image.color = Color.white;
        image.raycastTarget = false; // Ne pas bloquer les clics
        image.type = UnityEngine.UI.Image.Type.Sliced; // Permet le redimensionnement

        // Garder l'élément UI dans le Canvas (ne pas le déplacer vers zoneObj)
        uiZone.name = $"DebugZone_{answer.text}";
        uiZone.SetActive(true);

        Debug.Log($"[GameManager] Zone visuelle UI créée pour '{answer.text}': pos={rectTransform.anchoredPosition}, size={rectTransform.sizeDelta}, color={(answer.isCorrect ? "VERT" : "ROUGE")}, image.enabled={image.enabled}, uiZone.active={uiZone.activeSelf}, canvas={debugCanvas.name}");
    }

    void ApplyDebugZoneTransform(RectTransform rectTransform, Zone zoneData)
    {
        if (rectTransform == null || zoneData == null) return;

        // Vérifier si on utilise le nouveau layout
        ShootingGameLayout layout = ShootingGameLayout.Instance;
        if (layout == null)
        {
            layout = FindFirstObjectByType<ShootingGameLayout>();
        }
        
        if (layout != null)
        {
            // Obtenir les limites de la zone de jeu en coordonnées écran
            Rect gameAreaScreen = layout.GetGameAreaScreenBounds();
            
            // Normaliser les coordonnées JSON (0-1920, 0-1080) vers (0-1)
            float normalizedX = (float)zoneData.x / layout.ScreenWidth;
            float normalizedY = (float)zoneData.y / layout.ScreenHeight;
            float normalizedW = (float)zoneData.width / layout.ScreenWidth;
            float normalizedH = (float)zoneData.height / layout.ScreenHeight;
            
            // Convertir vers coordonnées écran de la zone de jeu
            // JSON origine haut-gauche -> écran origine bas-gauche
            float width = normalizedW * gameAreaScreen.width;
            float height = normalizedH * gameAreaScreen.height;
            float screenX = gameAreaScreen.x + normalizedX * gameAreaScreen.width;
            float screenY = gameAreaScreen.y + (1f - normalizedY - normalizedH) * gameAreaScreen.height;
            
            rectTransform.anchorMin = Vector2.zero;
            rectTransform.anchorMax = Vector2.zero;
            rectTransform.pivot = new Vector2(0f, 0f);
            rectTransform.anchoredPosition = new Vector2(screenX, screenY);
            rectTransform.sizeDelta = new Vector2(width, height);
            
            Debug.Log($"[GameManager] Zone debug: JSON({zoneData.x},{zoneData.y},{zoneData.width},{zoneData.height}) -> Screen({screenX:F1},{screenY:F1},{width:F1},{height:F1})");
            return;
        }
        
        // FALLBACK : Ancien système (plein écran)
        ComputeZoneScreenRect(zoneData, out float screenXFallback, out float screenYFallback, out float widthFallback, out float heightFallback);

        rectTransform.anchorMin = Vector2.zero;
        rectTransform.anchorMax = Vector2.zero;
        rectTransform.pivot = new Vector2(0f, 0f);
        rectTransform.anchoredPosition = new Vector2(screenXFallback, screenYFallback);
        rectTransform.sizeDelta = new Vector2(widthFallback, heightFallback);
    }

    void ComputeZoneScreenRect(Zone zoneData, out float screenX, out float screenY, out float width, out float height)
    {
        // NOUVEAU : Utiliser la zone de jeu (1480x835) au lieu de l'écran complet
        ShootingGameLayout layout = ShootingGameLayout.Instance;
        if (layout == null)
        {
            layout = FindFirstObjectByType<ShootingGameLayout>();
        }
        
        if (layout != null)
        {
            // Obtenir les limites de la zone de jeu
            Rect gameAreaBounds = layout.GetGameAreaBoundsInCanvas();
            
            TargetZone.GetReferenceResolution(out int refWidth, out int refHeight);
            if (refWidth <= 0) refWidth = 1920;
            if (refHeight <= 0) refHeight = 1080;
            
            // Convertir les coordonnées JSON (0-1920, 0-1080) vers zone de jeu (0-1480, 0-835)
            float scaleX = 1480f / (float)refWidth;
            float scaleY = 835f / (float)refHeight;
            
            // Taille de la zone dans l'espace zone de jeu
            width = zoneData.width * scaleX;
            height = zoneData.height * scaleY;
            
            // Position dans l'espace zone de jeu (origine haut-gauche en JSON)
            // Convertir en coordonnées zone de jeu (bas-gauche)
            float gameAreaX = zoneData.x * scaleX;
            float gameAreaY = (refHeight - zoneData.y - zoneData.height) * scaleY; // Inverser Y
            
            // Convertir en coordonnées Canvas centrées
            // gameAreaBounds est en coordonnées centrées (x, y = coin bas-gauche relatif au centre)
            screenX = gameAreaBounds.x + gameAreaX;
            screenY = gameAreaBounds.y + gameAreaY;
            
            Debug.Log($"[GameManager] Zone debug positionnée dans zone de jeu: JSON({zoneData.x},{zoneData.y}) -> GameArea({gameAreaX:F1},{gameAreaY:F1}) -> Canvas({screenX:F1},{screenY:F1})");
            return;
        }
        
        // FALLBACK : Ancien système (plein écran) si pas de layout
        TargetZone.GetReferenceResolution(out int refWidthFallback, out int refHeightFallback);
        if (refWidthFallback <= 0) refWidthFallback = 1920;
        if (refHeightFallback <= 0) refHeightFallback = 1080;

        float currentAspect = (float)Screen.width / Screen.height;
        float referenceAspect = (float)refWidthFallback / refHeightFallback;
        float scaleXFallback, scaleYFallback;
        float offsetX = 0f, offsetY = 0f;

        if (currentAspect > referenceAspect)
        {
            scaleYFallback = (float)Screen.height / refHeightFallback;
            scaleXFallback = scaleYFallback;
            float scaledWidth = refWidthFallback * scaleXFallback;
            offsetX = (Screen.width - scaledWidth) * 0.5f;
        }
        else if (currentAspect < referenceAspect)
        {
            scaleXFallback = (float)Screen.width / refWidthFallback;
            scaleYFallback = scaleXFallback;
            float scaledHeight = refHeightFallback * scaleYFallback;
            offsetY = (Screen.height - scaledHeight) * 0.5f;
        }
        else
        {
            scaleXFallback = (float)Screen.width / refWidthFallback;
            scaleYFallback = (float)Screen.height / refHeightFallback;
        }

        width = zoneData.width * scaleXFallback;
        height = zoneData.height * scaleYFallback;

        float topLeftX = offsetX + zoneData.x * scaleXFallback;
        float topLeftY = offsetY + zoneData.y * scaleYFallback;

        screenX = topLeftX;
        screenY = Screen.height - topLeftY - height;
    }


    void HandleCrosshairResizeControls()
    {
        // Vérifier si le redimensionnement est autorisé
        if (gameConfig?.crosshairConfig?.allowPlayerResize != true) return; // CHANGÉ: supprimé .gameConfig

        if (Keyboard.current != null)
        {
            // Augmenter la taille avec + ou =
            if (Keyboard.current.equalsKey.wasPressedThisFrame)
            {
                if (crosshairManager != null)
                {
                    float newSize = crosshairManager.crosshairSize + 10f;
                    crosshairManager.SetCrosshairSize(newSize);
                    Debug.Log($"Taille viseur augmentée : {crosshairManager.crosshairSize}");
                }
            }

            // Diminuer la taille avec -
            if (Keyboard.current.minusKey.wasPressedThisFrame)
            {
                if (crosshairManager != null)
                {
                    float newSize = crosshairManager.crosshairSize - 10f;
                    crosshairManager.SetCrosshairSize(newSize);
                    Debug.Log($"Taille viseur diminuée : {crosshairManager.crosshairSize}");
                }
            }

            // Alternative : utiliser les touches numériques pour des tailles prédéfinies
            if (Keyboard.current.digit1Key.wasPressedThisFrame)
            {
                if (crosshairManager != null)
                {
                    crosshairManager.SetCrosshairSize(30f);
                    Debug.Log("Taille viseur : Petite");
                }
            }

            if (Keyboard.current.digit2Key.wasPressedThisFrame)
            {
                if (crosshairManager != null)
                {
                    crosshairManager.SetCrosshairSize(60f);
                    Debug.Log("Taille viseur : Moyenne");
                }
            }

            if (Keyboard.current.digit3Key.wasPressedThisFrame)
            {
                if (crosshairManager != null)
                {
                    crosshairManager.SetCrosshairSize(120f);
                    Debug.Log("Taille viseur : Grande");
                }
            }
        }
    }

    void HandleInputSystem()
    {
        // Si un panneau de feedback vient tout juste d'être fermé par un clic UI natif,
        // on ignore totalement les entrées de CETTE frame (InputSystem + WebGL fallback).
        if (ignoreNextInputAfterFeedback)
        {
            if (GeneralConfigManager.Instance?.GetConfig()?.debug?.enableDebugLogs == true)
            {
                Debug.Log("[GameManager] ignoreNextInputAfterFeedback actif → entrées ignorées dans HandleInputSystem");
            }
            ignoreNextInputAfterFeedback = false;
            return;
        }

        if (feedbackPanel != null && feedbackPanel.activeInHierarchy)
        {
            return;
        }

        if (isProcessingAnswer)
        {
            return;
        }

        bool inputDetected = false;
        Vector3 inputPosition = Vector3.zero;

        if (Mouse.current != null && Mouse.current.leftButton.wasPressedThisFrame)
        {
            inputPosition = Mouse.current.position.ReadValue();
            inputDetected = true;
        }

        if (Touchscreen.current != null && Touchscreen.current.primaryTouch.press.wasPressedThisFrame)
        {
            inputPosition = Touchscreen.current.primaryTouch.position.ReadValue();
            inputDetected = true;
        }

        // WebGL fallback: vérifier si un clic a été reçu depuis JavaScript
        #if UNITY_WEBGL && !UNITY_EDITOR
        if (!inputDetected && WebGLClickReceiver.WasClickedThisFrame())
        {
            // PROTECTION ANTI "CLIC QUI TRAVERSE" :
            // Si un panneau de feedback vient tout juste d'être fermé par un clic,
            // le clic WebGL brut correspondant doit être totalement ignoré ici.
            if (ignoreNextWebGLClickAfterFeedback)
            {
                ignoreNextWebGLClickAfterFeedback = false;
                Debug.Log("[GameManager] Clic WebGL ignoré juste après fermeture du feedback (HandleInputSystem)");
            }
            else
            {
                inputPosition = WebGLClickReceiver.GetLastClickPosition();
                inputDetected = true;
                Debug.Log($"[GameManager] Clic WebGL détecté dans HandleInput: ({inputPosition.x:F0}, {inputPosition.y:F0})");
            }
        }
        #endif

        if (inputDetected)
        {
            HandleZoneClick(inputPosition);
        }
    }



    void Update()
    {
        // Gérer la touche ESC pour retourner à la scène d'origine
        if (Keyboard.current != null && Keyboard.current.escapeKey.wasPressedThisFrame)
        {
            ReturnToOriginScene();
            return;
        }
        
        // Gérer les touches pour fermer le panneau de feedback (Space, Enter, etc.)
        if (IsFeedbackPanelActive() && Keyboard.current != null)
        {
            if (Keyboard.current.spaceKey.wasPressedThisFrame || 
                Keyboard.current.enterKey.wasPressedThisFrame ||
                Keyboard.current.numpadEnterKey.wasPressedThisFrame)
            {
                Debug.Log("[GameManager] Touche clavier détectée - fermeture du panneau feedback");
                OnFeedbackClick();
                return;
            }
        }
        
        // FIX MAC : Nettoyer le voile blanc en continu (indépendamment du chemin de dialogue)
        if (Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.OSXPlayer)
        {
            CleanMacWhiteVeilInUpdate();
        }
        
        HandleInputSystem(); // CHANGÉ: renommé de HandleInput vers HandleInputSystem
        HandleHover();
        HandleDebugKeys(); // Gère la touche D pour les zones de debug
        HandleCrosshairResizeControls();

        // Dans GameManager.cs, dans Update()
        if (Keyboard.current != null && Keyboard.current.cKey.wasPressedThisFrame)
        {
            if (dialoguePlayer != null) dialoguePlayer.ForceCleanupAndHide();
            KillStrayDialogueUI();
            BringGameplayToFront();
        }

        // DEBUG : Touche H pour tester l'affichage du hover
        if (Keyboard.current != null && Keyboard.current.hKey.wasPressedThisFrame)
        {
            Debug.Log("DEBUG: Test hover (touche H)");
            ShowHover("Test de hover - ceci devrait apparaître en bas");
        }

        // DEBUG : Touche J pour cacher le hover
        if (Keyboard.current != null && Keyboard.current.jKey.wasPressedThisFrame)
        {
            Debug.Log("DEBUG: Cache hover (touche J)");
            HideHover();
        }

        // TEST : Touche Y pour forcer l'affichage du hover
        if (Keyboard.current != null && Keyboard.current.yKey.wasPressedThisFrame)
        {
            if (fixedHoverPanel == null) CreateFixedHoverPanel();
            fixedHoverPanel.SetActive(true);
            fixedHoverText.text = "TEST MANUEL - HOVER FORCÉ";
            Debug.Log($"[TEST] Hover forcé - actif: {fixedHoverPanel.activeInHierarchy}");
        }




    }

    void HandleDebugKeys()
    {
        if (Keyboard.current == null)
        {
            // Log une seule fois toutes les 60 frames pour éviter le spam
            if (Time.frameCount % 60 == 0)
            {
                Debug.LogWarning("[GameManager] ⚠️ Keyboard.current est null - touches de debug non disponibles");
            }
            return;
        }

        if (Keyboard.current.f1Key.wasPressedThisFrame)
        {
            if (questions != null && questions.Length > 0) // CHANGÉ: .Count -> .Length
            {
                lastAnswerWasCorrect = true;
                StartCoroutine(ShowFeedback(questions[0]));
                Debug.Log("[DEBUG] Test feedback BONNE RÉPONSE");
            }
        }

        if (Keyboard.current != null && Keyboard.current.f2Key.wasPressedThisFrame)
        {
            if (questions != null && questions.Length > 0) // CHANGÉ: .Count -> .Length
            {
                lastAnswerWasCorrect = false;
                StartCoroutine(ShowFeedback(questions[0]));
                Debug.Log("[DEBUG] Test feedback MAUVAISE RÉPONSE");
            }
        }

        // Autres touches de debug...
        if (Keyboard.current != null && Keyboard.current.tKey.wasPressedThisFrame)
        {
            if (crosshairManager != null)
            {
                Debug.Log("Test de visibilité du viseur (touche T pressée)");
                Debug.Log($"Viseur actuellement visible: {crosshairManager.IsVisible()}");

                Vector2 mousePos = Mouse.current != null ? Mouse.current.position.ReadValue() : Vector2.zero;
                bool overZone = IsPointOverTargetZone(mousePos);
                Debug.Log($"Survole zone cible: {overZone}");
            }
        }

        if (Keyboard.current != null && Keyboard.current.eKey.wasPressedThisFrame)
        {
            if (crosshairManager != null)
            {
                crosshairManager.DebugCursorState();
            }
        }

        // NOUVEAU : Changement de niveau
        if (Keyboard.current != null && Keyboard.current.digit1Key.wasPressedThisFrame)
        {
            LevelManager levelManager = FindFirstObjectByType<LevelManager>();
            if (levelManager != null)
                levelManager.LoadLevel("level-1");
        }

        if (Keyboard.current != null && Keyboard.current.digit2Key.wasPressedThisFrame)
        {
            LevelManager levelManager = FindFirstObjectByType<LevelManager>();
            if (levelManager != null)
                levelManager.LoadLevel("level-2");
        }

        // Toggle des zones de debug avec la touche D
        // Utiliser isPressed avec un flag pour détecter l'appui
        if (Keyboard.current.dKey.isPressed)
        {
            if (!dKeyWasPressedLastFrame)
            {
                Debug.Log("[GameManager] 🔑 Touche D détectée - ToggleDebugZones()");
                ToggleDebugZones();
                dKeyWasPressedLastFrame = true;
            }
        }
        else
        {
            dKeyWasPressedLastFrame = false;
        }
    }

    void ToggleDebugZones()
    {
        Debug.Log("[GameManager] ToggleDebugZones DEBUT");
        
        showDebugZones = !showDebugZones;
        
        int zoneCount = targetZones != null ? targetZones.Count : 0;
        Debug.Log($"[GameManager] ToggleDebugZones: showDebugZones={showDebugZones}, targetZones.Count={zoneCount}");
        
        if (targetZones == null || targetZones.Count == 0)
        {
            Debug.LogWarning("[GameManager] ⚠️ Aucune zone de tir disponible - impossible d'afficher les zones de debug");
            Debug.Log("[GameManager] ToggleDebugZones FIN (pas de zones)");
            return;
        }
        
        // Si on active le debug, créer les zones visuelles si elles n'existent pas
        if (showDebugZones)
        {
            Debug.Log("[GameManager] ✅ Activation des zones de debug");
            CreateDebugZonesForCurrentQuestion();
        }
        else
        {
            Debug.Log("[GameManager] ❌ Désactivation des zones de debug");
            // Désactiver toutes les zones visuelles dans le Canvas de debug
            Canvas debugCanvas = GameObject.Find("DebugZonesCanvas")?.GetComponent<Canvas>();
            if (debugCanvas != null)
            {
                foreach (Transform child in debugCanvas.transform)
                {
                    if (child.name.StartsWith("DebugZone_"))
                    {
                        child.gameObject.SetActive(false);
                    }
                }
            }
        }
    }

    void CreateDebugZonesForCurrentQuestion()
    {
        if (questions == null || questions.Length == 0)
        {
            Debug.LogWarning("[GameManager] CreateDebugZonesForCurrentQuestion: questions est null ou vide");
            return;
        }

        if (currentQuestionIndex >= questions.Length)
        {
            Debug.LogWarning($"[GameManager] CreateDebugZonesForCurrentQuestion: currentQuestionIndex ({currentQuestionIndex}) >= questions.Length ({questions.Length})");
            return;
        }

        Question currentQuestion = questions[currentQuestionIndex];
        if (currentQuestion == null || currentQuestion.answers == null)
        {
            Debug.LogWarning("[GameManager] CreateDebugZonesForCurrentQuestion: currentQuestion ou answers est null");
            return;
        }

        if (targetZones == null || targetZones.Count == 0)
        {
            Debug.LogWarning("[GameManager] CreateDebugZonesForCurrentQuestion: targetZones est null ou vide");
            return;
        }

        Debug.Log($"[GameManager] CreateDebugZonesForCurrentQuestion: {targetZones.Count} zones à traiter, {currentQuestion.answers.Count} réponses");

        // Parcourir toutes les zones et créer les zones visuelles
        for (int i = 0; i < targetZones.Count && i < currentQuestion.answers.Count; i++)
        {
            GameObject zoneObj = targetZones[i];
            if (zoneObj == null)
            {
                Debug.LogWarning($"[GameManager] CreateDebugZonesForCurrentQuestion: zoneObj[{i}] est null");
                continue;
            }

            TargetZone targetZone = zoneObj.GetComponent<TargetZone>();
            if (targetZone == null)
            {
                Debug.LogWarning($"[GameManager] CreateDebugZonesForCurrentQuestion: TargetZone manquant sur zoneObj[{i}]");
                continue;
            }

            AnswerWithZone answerWithZone = targetZone.GetAnswer();
            if (answerWithZone == null)
            {
                Debug.LogWarning($"[GameManager] CreateDebugZonesForCurrentQuestion: AnswerWithZone null pour zoneObj[{i}]");
                continue;
            }

            // Chercher le Canvas de debug (une seule déclaration pour toute la méthode)
            Canvas debugCanvas = GameObject.Find("DebugZonesCanvas")?.GetComponent<Canvas>();
            
            // Vérifier si une zone visuelle existe déjà (chercher dans le Canvas de debug)
            Transform existingVisual = null;
            if (debugCanvas != null)
            {
                existingVisual = debugCanvas.transform.Find($"DebugZone_{answerWithZone.text}");
            }
            
            if (existingVisual == null)
            {
                Debug.Log($"[GameManager] Création zone visuelle pour '{answerWithZone.text}' (correct: {answerWithZone.isCorrect})");
                // Créer la zone visuelle avec la bonne couleur (vert pour correct, rouge pour incorrect)
                // AnswerWithZone hérite de Answer, donc on peut le passer directement
                CreateVisualDebugZone(zoneObj, answerWithZone.zone, answerWithZone);
            }
            else
            {
                Debug.Log($"[GameManager] Réactivation zone visuelle existante pour '{answerWithZone.text}'");
                // Réactiver et repositionner la zone visuelle existante
                RectTransform existingRect = existingVisual.GetComponent<RectTransform>();
                if (existingRect != null)
                {
                    ApplyDebugZoneTransform(existingRect, answerWithZone.zone);
                }
                existingVisual.gameObject.SetActive(true);
                zoneObj.SetActive(true);
            }
            
            // Réactiver le Canvas de debug
            if (debugCanvas != null)
            {
                debugCanvas.gameObject.SetActive(true);
            }
        }
    }

    /// <summary>
    /// Appelé par WebGLClickReceiver quand un clic est reçu depuis JavaScript (pour les jeux shooting)
    /// </summary>
    public void OnWebGLClick(Vector2 clickPosition)
    {
        #if UNITY_WEBGL && !UNITY_EDITOR
        Application.ExternalCall("console.log", $"[Unity] GameManager.OnWebGLClick: ({clickPosition.x:F0}, {clickPosition.y:F0})");
        #endif
        
        // IMPORTANT : Si le panneau de feedback est actif, ne pas traiter les clics sur les zones
        // Le clic sera géré par le système UI (Button) du feedback panel
        if (IsFeedbackPanelActive())
        {
            Debug.Log("[GameManager] Panneau de feedback actif, clic ignoré (géré par le système UI)");
            return;
        }
        
        // Vérifier si on est dans un jeu shooting actif
        if (targetZones == null || targetZones.Count == 0)
        {
            Debug.Log("[GameManager] Aucune zone de tir disponible, clic ignoré");
            return;
        }
        
        // Traiter le clic comme un clic normal
        HandleZoneClick(clickPosition);
    }
    
    void HandleZoneClick(Vector2 clickPosition)
    {
        var __cfg = GeneralConfigManager.Instance != null ? GeneralConfigManager.Instance.GetConfig() : null;
        if (__cfg == null || __cfg.debug == null || __cfg.debug.enableDebugLogs)
        {
            Debug.Log($"Clic détecté à la position: {clickPosition}");
        }

        TargetZone clickedZone = null;

        foreach (GameObject zoneObj in targetZones)
        {
            if (zoneObj != null)
            {
                TargetZone targetZone = zoneObj.GetComponent<TargetZone>();
                if (targetZone != null && targetZone.IsPointInScreenZone(clickPosition))
                {
                    clickedZone = targetZone;
                    Debug.Log($"Zone cliquée: {targetZone.GetAnswerText()}");
                    break;
                }
            }
        }

        if (clickedZone != null)
        {
            HideHover();
            StartCoroutine(ShowImpactThenFeedback(clickedZone, clickPosition));
        }
        else
        {
            Debug.Log("Clic dans le vide - aucune zone touchée");
        }
    }

    IEnumerator ShowImpactThenFeedback(TargetZone clickedZone, Vector2 clickPosition)
    {
        AnswerWithZone answer = clickedZone.GetAnswer();
        if (answer == null) yield break;

        isProcessingAnswer = true;

        Debug.Log($"Début séquence pour '{answer.text}' - Impact à la position du clic: {clickPosition}");

        GameObject impactEffect = ShowImpactEffect(clickPosition);

        yield return new WaitForSeconds(1f);

        if (impactEffect != null)
        {
            Destroy(impactEffect);
            Debug.Log("Impact masqué");
        }

        // NOTE: On garde isProcessingAnswer = true jusqu'à ce que le feedback soit affiché
        // pour éviter que le hover réapparaisse pendant le délai du feedback

        Debug.Log("Affichage du feedback");
        ProcessAnswer(clickedZone);
    }

    GameObject ShowImpactEffect(Vector2 screenPosition)
    {
        GameObject impact = null;
        
        // CORRECTION : Créer l'effet d'impact dans le Canvas UI pour qu'il soit visible au-dessus de tout
        Canvas canvas = FindFirstObjectByType<Canvas>();
        if (canvas == null)
        {
            Debug.LogError("[GameManager] ❌ Aucun Canvas trouvé dans la scène !");
            return null;
        }

        Debug.Log($"[GameManager] Création effet d'impact UI à la position écran: ({screenPosition.x}, {screenPosition.y})");

        UnityEngine.UI.Image imgComponent = null;

        if (impactEffectPrefab != null)
        {
            impact = Instantiate(impactEffectPrefab, canvas.transform);
            imgComponent = impact.GetComponent<UnityEngine.UI.Image>();
        }
        else
        {
            // CORRECTION : Créer l'objet dans le Canvas UI si le prefab n'existe pas
            Debug.LogWarning("[GameManager] ⚠️ impactEffectPrefab est null, création manuelle de l'objet d'impact dans Canvas UI");
            impact = new GameObject("ImpactEffect");
            impact.transform.SetParent(canvas.transform, false);
            impact.SetActive(true);
            
            // Ajouter une Image UI au lieu d'un SpriteRenderer
            imgComponent = impact.AddComponent<UnityEngine.UI.Image>();
            imgComponent.raycastTarget = false; // Ne pas bloquer les clics
        }

        // Configurer le RectTransform pour positionner l'image à la position du clic
        RectTransform rectTransform = impact.GetComponent<RectTransform>();
        if (rectTransform == null)
        {
            rectTransform = impact.AddComponent<RectTransform>();
        }

        // Utiliser la résolution de référence du CanvasScaler pour positionner correctement
        CanvasScaler scaler = canvas.GetComponent<CanvasScaler>();
        Vector2 referenceResolution;
        
        if (scaler != null && scaler.uiScaleMode == CanvasScaler.ScaleMode.ScaleWithScreenSize)
        {
            referenceResolution = scaler.referenceResolution;
        }
        else
        {
            referenceResolution = new Vector2(Screen.width, Screen.height);
        }
        
        // Convertir les coordonnées écran vers l'espace Canvas de référence (ancres au centre)
        float canvasX = (screenPosition.x / Screen.width - 0.5f) * referenceResolution.x;
        float canvasY = (screenPosition.y / Screen.height - 0.5f) * referenceResolution.y;

        rectTransform.anchoredPosition = new Vector2(canvasX, canvasY);
        rectTransform.pivot = new Vector2(0.5f, 0.5f);
        rectTransform.anchorMin = new Vector2(0.5f, 0.5f);
        rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
        
        // Taille par défaut (sera ajustée selon le sprite)
        rectTransform.sizeDelta = new Vector2(256, 292);

        // S'assurer qu'on a une Image UI (au cas où le prefab n'en aurait pas)
        if (imgComponent == null)
        {
            imgComponent = impact.GetComponent<UnityEngine.UI.Image>();
            if (imgComponent == null)
            {
                imgComponent = impact.AddComponent<UnityEngine.UI.Image>();
            }
        }
        
        // S'assurer que l'Image UI est visible
        impact.SetActive(true);
        imgComponent.enabled = true;
        imgComponent.color = Color.white;
        imgComponent.raycastTarget = false; // Ne pas bloquer les clics
        
        Debug.Log($"[GameManager] Image UI configurée: enabled={imgComponent.enabled}, color={imgComponent.color}, active={impact.activeSelf}");

        // Vérifier s'il y a une configuration d'animation
        if (gameConfig.assets.impactEffect != null && gameConfig.assets.impactEffect.type == "animated")
        {
            // Appliquer la première frame immédiatement pour éviter le carré blanc
            if (loadedSprites.ContainsKey("impact_frame_0") && loadedSprites["impact_frame_0"] != null)
            {
                imgComponent.sprite = loadedSprites["impact_frame_0"];
                imgComponent.enabled = true;
                imgComponent.color = Color.white;
                
                // Ajuster la taille du RectTransform selon la taille du sprite
                if (rectTransform != null)
                {
                    float baseWidth = loadedSprites["impact_frame_0"].texture.width;
                    float baseHeight = loadedSprites["impact_frame_0"].texture.height;
                    
                    // Si on est dans la zone de jeu, réduire la taille (sprites conçus pour 1920x1080)
                    ShootingGameLayout layout = ShootingGameLayout.Instance;
                    if (layout == null) layout = FindFirstObjectByType<ShootingGameLayout>();
                    
                    if (layout != null)
                    {
                        float scaleX = layout.GameAreaWidth / layout.ScreenWidth;  // 1480/1920
                        float scaleY = layout.GameAreaHeight / layout.ScreenHeight; // 835/1080
                        baseWidth *= scaleX;
                        baseHeight *= scaleY;
                    }
                    
                    rectTransform.sizeDelta = new Vector2(baseWidth, baseHeight);
                }
                
                Debug.Log($"[GameManager] Première frame appliquée immédiatement: {loadedSprites["impact_frame_0"].name}");
            }
            else
            {
                Debug.LogWarning("[GameManager] ⚠️ Première frame (impact_frame_0) non trouvée, animation peut ne pas fonctionner");
            }
            
            // Démarrer l'animation frame par frame (commence à la frame 1 car la 0 est déjà affichée)
            StartCoroutine(PlayAnimatedImpact(impact, gameConfig.assets.impactEffect));
            Debug.Log($"Effet d'impact animé créé à {screenPosition} avec {gameConfig.assets.impactEffect.frames.Count} frames");
            
            // Détruire l'objet après la durée de l'animation
            float animationDuration = gameConfig.assets.impactEffect.duration > 0 ? 
                gameConfig.assets.impactEffect.duration : 
                (gameConfig.assets.impactEffect.frames.Count / gameConfig.assets.impactEffect.frameRate);
            Destroy(impact, animationDuration + 0.1f); // +0.1f pour la marge
        }
        else
        {
            // Fallback vers image statique
            if (loadedSprites.ContainsKey("impact") && loadedSprites["impact"] != null)
            {
                imgComponent.sprite = loadedSprites["impact"];
                
                float baseWidth = loadedSprites["impact"].texture.width;
                float baseHeight = loadedSprites["impact"].texture.height;
                
                // Si on est dans la zone de jeu, réduire la taille (sprites conçus pour 1920x1080)
                ShootingGameLayout layout = ShootingGameLayout.Instance;
                if (layout == null) layout = FindFirstObjectByType<ShootingGameLayout>();
                
                if (layout != null)
                {
                    float scaleX = layout.GameAreaWidth / layout.ScreenWidth;
                    float scaleY = layout.GameAreaHeight / layout.ScreenHeight;
                    baseWidth *= scaleX;
                    baseHeight *= scaleY;
                }
                
                rectTransform.sizeDelta = new Vector2(baseWidth, baseHeight);
            }
            else
            {
                // CORRECTION : Créer un sprite de fallback si l'impact n'est pas chargé
                Debug.LogWarning("[GameManager] ⚠️ Sprite d'impact non chargé, création d'un fallback");
                Texture2D fallbackTexture = new Texture2D(256, 256); // Plus grand pour être visible
                Color[] pixels = new Color[256 * 256];
                for (int i = 0; i < pixels.Length; i++)
                {
                    pixels[i] = Color.white;
                }
                fallbackTexture.SetPixels(pixels);
                fallbackTexture.Apply();
                
                Sprite fallbackSprite = Sprite.Create(fallbackTexture, new Rect(0, 0, 256, 256), new Vector2(0.5f, 0.5f));
                imgComponent.sprite = fallbackSprite;
                rectTransform.sizeDelta = new Vector2(256, 256);
                loadedSprites["impact"] = fallbackSprite; // Stocker pour les prochaines fois
            }

            // CORRECTION : Appliquer une taille selon la config
            if (gameConfig.assets.impactEffect != null)
            {
                var config = gameConfig.assets.impactEffect;
                if (config.width > 0 && config.height > 0)
                {
                    rectTransform.sizeDelta = new Vector2(config.width, config.height);
                    Debug.Log($"Taille fixe appliquée: {config.width}x{config.height}px");
                }
                else if (config.scale != 1f)
                {
                    Vector2 currentSize = rectTransform.sizeDelta;
                    rectTransform.sizeDelta = currentSize * config.scale;
                    Debug.Log($"Scale multiplier appliqué: {config.scale}, nouvelle taille={rectTransform.sizeDelta}");
                }
            }
        
            Debug.Log($"[GameManager] Impact final: position={rectTransform.anchoredPosition}, size={rectTransform.sizeDelta}, sprite={imgComponent.sprite?.name ?? "null"}, enabled={imgComponent.enabled}, color={imgComponent.color}");

            Destroy(impact, 1f);
            Debug.Log($"Effet d'impact statique créé à {screenPosition}");
        }

        PlaySound("impact");

        return impact;
    }

    // Nouvelle méthode pour gérer l'animation frame par frame
    IEnumerator PlayAnimatedImpact(GameObject impactObj, ImpactEffect config)
    {
        Debug.Log($"Début animation impact - {config.frames.Count} frames à {config.frameRate} fps");

        if (impactObj == null || config == null || config.frames == null) yield break;

        // CORRECTION : Utiliser Image UI au lieu de SpriteRenderer car l'impact est créé dans le Canvas UI
        UnityEngine.UI.Image imgComponent = impactObj.GetComponent<UnityEngine.UI.Image>();
        if (imgComponent == null)
        {
            Debug.LogError("[GameManager] ❌ Image UI introuvable sur l'objet impact pour l'animation");
            yield break;
        }

        float frameTime = 1f / config.frameRate;

        // Attendre un peu avant de commencer l'animation (la frame 0 est déjà affichée)
        yield return new WaitForSeconds(frameTime);

        // Jouer chaque frame à partir de la frame 1 (la frame 0 est déjà appliquée)
        for (int i = 1; i < config.frames.Count; i++)
        {
            if (impactObj == null || imgComponent == null) yield break; // Sécurité si l'objet est détruit

            if (loadedSprites.ContainsKey($"impact_frame_{i}") && loadedSprites[$"impact_frame_{i}"] != null)
            {
                imgComponent.sprite = loadedSprites[$"impact_frame_{i}"];
                imgComponent.enabled = true;
                imgComponent.color = Color.white;
                
                // Ajuster la taille du RectTransform selon la taille du sprite
                RectTransform rectTransform = impactObj.GetComponent<RectTransform>();
                if (rectTransform != null && loadedSprites[$"impact_frame_{i}"] != null)
                {
                    rectTransform.sizeDelta = new Vector2(
                        loadedSprites[$"impact_frame_{i}"].texture.width,
                        loadedSprites[$"impact_frame_{i}"].texture.height
                    );
                }
                
                Debug.Log($"[GameManager] Frame {i}/{config.frames.Count} appliquée: {loadedSprites[$"impact_frame_{i}"].name}");
            }
            else
            {
                Debug.LogWarning($"[GameManager] ⚠️ Frame {i} non trouvée dans loadedSprites");
            }

            yield return new WaitForSeconds(frameTime);
        }
        Debug.Log("Animation impact terminée");
        // Détruire l'objet à la fin de l'animation
        if (impactObj != null)
        {
            Destroy(impactObj);
        }
    }

    void ProcessAnswer(TargetZone targetZone)
    {
        AnswerWithZone answer = targetZone.GetAnswer();
        if (answer == null)
        {
            Debug.LogError("Answer est null!");
            return;
        }

        Debug.Log($"Traitement de la réponse: '{answer.text}', Correcte: {answer.isCorrect}");

        Question currentQuestion = questions[currentQuestionIndex];
        if (currentQuestion == null)
        {
            Debug.LogError("Question actuelle est null!");
            return;
        }

        lastAnswerWasCorrect = answer.isCorrect;
        lastSelectedAnswerId = answer.id;  // Stocker l'ID de la réponse sélectionnée pour l'API
        lastQuestionId = currentQuestion.id;  // Stocker l'ID de la question pour l'API
        Debug.Log($"Résultat stocké pour feedback: {lastAnswerWasCorrect} (questionId: {lastQuestionId}, answerId: {lastSelectedAnswerId})");

        // Compter les réponses
        totalAnswers++;
        if (answer.isCorrect)
        {
            correctAnswers++;
            Debug.Log("[GameManager] Bonne réponse");
            OnCorrectAnswer();
        }
        else
        {
            Debug.Log("[GameManager] Mauvaise réponse");
            OnWrongAnswer();
        }

        Debug.Log($"Progression: {correctAnswers}/{totalAnswers} bonnes réponses");

        // Lancer le feedback avec délai configurable
        StartCoroutine(ShowFeedbackWithDelay(currentQuestion));
    }
    
    IEnumerator ShowFeedbackWithDelay(Question question)
    {
        // Obtenir le délai depuis la configuration
        float delay = GetFeedbackDisplayDelay();
        
        if (delay > 0)
        {
            Debug.Log($"[GameManager] Attente de {delay}s avant affichage du feedback...");
            yield return new WaitForSeconds(delay);
        }
        
        // Remettre isProcessingAnswer à false maintenant que le feedback va s'afficher
        isProcessingAnswer = false;
        
        StartCoroutine(ShowFeedback(question));
    }
    
    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 la réponse du joueur au serveur via l'API
    /// </summary>
    void SendAnswerToServer()
    {
        // Récupérer le gameId depuis GameDataManager
        int gameId = GameDataManager.Instance?.CurrentGameId ?? 0;
        
        if (gameId <= 0)
        {
            Debug.LogWarning("[GameManager] ⚠️ Impossible d'envoyer la réponse: gameId non disponible");
            return;
        }
        
        if (lastQuestionId <= 0 || lastSelectedAnswerId <= 0)
        {
            Debug.LogWarning($"[GameManager] ⚠️ Impossible d'envoyer la réponse: questionId={lastQuestionId}, answerId={lastSelectedAnswerId}");
            return;
        }
        
        // S'assurer que le service existe
        GameAnswerService.EnsureInstance();
        
        // Envoyer la réponse QCM au serveur
        GameAnswerService.Instance.SendQCMAnswer(
            gameId,
            lastQuestionId,
            lastSelectedAnswerId,
            onSuccess: () => Debug.Log($"[GameManager] ✅ Réponse envoyée avec succès"),
            onError: (error) => Debug.LogWarning($"[GameManager] ⚠️ Erreur envoi réponse: {error}")
        );
    }

    /// <summary>
    /// Enregistre le résultat du jeu dans GameResultsManager
    /// </summary>
    void RecordGameResult(bool isSuccess)
    {
        // S'assurer que GameResultsManager existe
        if (GameResultsManager.Instance == null)
        {
            GameObject resultsManagerObj = new GameObject("GameResultsManager");
            resultsManagerObj.AddComponent<GameResultsManager>();
            Debug.Log("[GameManager] 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 (ex: "quete02_shooting_zombies.json" -> "quete02_shooting_zombies")
        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: correctAnswers,
            totalAnswers: totalAnswers,
            gameType: "shooting"
        );
    }
    
    /// <summary>
    /// Vérifie si le joueur a réussi selon la configuration de réussite
    /// </summary>
    bool CheckSuccess()
    {
        Debug.Log($"[GameManager] CheckSuccess() - correctAnswers={correctAnswers}, totalAnswers={totalAnswers}");
        
        if (successConfig == null)
        {
            Debug.LogWarning("[GameManager] Configuration de réussite manquante, utilisation du seuil par défaut (60%)");
            // Seuil par défaut : 60% de bonnes réponses
            if (totalAnswers == 0)
            {
                Debug.LogWarning("[GameManager] totalAnswers est 0, impossible de calculer. Retour false.");
                return false;
            }
            float percentage = (float)correctAnswers / totalAnswers * 100f;
            bool success = percentage >= 60f;
            Debug.Log($"[GameManager] Résultat (défaut): {success} ({correctAnswers}/{totalAnswers} = {percentage:F1}% >= 60%)");
            return success;
        }
        
        Debug.Log($"[GameManager] successConfig.usePercentage: {successConfig.usePercentage}");
        Debug.Log($"[GameManager] successConfig.minCorrectPercentage: {successConfig.minCorrectPercentage}");
        Debug.Log($"[GameManager] successConfig.minCorrectAnswers: {successConfig.minCorrectAnswers}");
        Debug.Log($"[GameManager] successConfig.description: {successConfig.description}");

        if (successConfig.usePercentage)
        {
            // Protection contre la division par zéro
            if (totalAnswers == 0)
            {
                Debug.LogWarning("[GameManager] totalAnswers est 0, impossible de calculer le pourcentage. Retour false.");
                return false;
            }
            
            float percentage = (float)correctAnswers / totalAnswers * 100f;
            bool success = percentage >= successConfig.minCorrectPercentage;
            Debug.Log($"[GameManager] Vérification réussite (pourcentage): {correctAnswers}/{totalAnswers} = {percentage:F1}% >= {successConfig.minCorrectPercentage}% = {success}");
            return success;
        }
        else
        {
            bool success = correctAnswers >= successConfig.minCorrectAnswers;
            Debug.Log($"[GameManager] Vérification réussite (nombre): {correctAnswers} >= {successConfig.minCorrectAnswers} = {success}");
            return success;
        }
    }

    void HandleHover()
    {
        if (feedbackPanel != null && feedbackPanel.activeInHierarchy)
        {
            HideHover();
            return;
        }

        if (isProcessingAnswer)
        {
            HideHover();
            return;
        }

        Vector3 mousePosition = Vector3.zero;
        bool hasInput = false;

        if (Mouse.current != null)
        {
            mousePosition = Mouse.current.position.ReadValue();
            hasInput = true;
        }

        if (!hasInput && Touchscreen.current != null && Touchscreen.current.primaryTouch.press.isPressed)
        {
            mousePosition = Touchscreen.current.primaryTouch.position.ReadValue();
            hasInput = true;
        }

        if (!hasInput)
        {
            HideHover();
            return;
        }

        string hoveredText = "";

        foreach (GameObject zoneObj in targetZones)
        {
            if (zoneObj != null)
            {
                TargetZone targetZone = zoneObj.GetComponent<TargetZone>();
                if (targetZone != null && targetZone.IsPointInScreenZone(mousePosition))
                {
                    hoveredText = targetZone.GetAnswerText();
                    break;
                }
            }
        }

        if (!string.IsNullOrEmpty(hoveredText))
        {
            ShowHover(hoveredText);
        }
        else
        {
            HideHover();
        }
    }

    /// <summary>
    /// Affiche le texte de survol en bas de l'écran avec configuration JSON
    /// </summary>
    void ShowHover(string answerText)
    {
        //Debug.Log($"[HOVER] ShowHover appelé avec: '{answerText}'");

        if (string.IsNullOrEmpty(answerText)) return;

        if (fixedHoverPanel == null)
        {
            //Debug.Log("[HOVER] Création du panneau hover");
            CreateFixedHoverPanel();
        }

        if (fixedHoverPanel == null || fixedHoverText == null)
        {
            //Debug.LogError("[HOVER] Impossible d'afficher le hover : panneau ou texte manquant");
            return;
        }

        // NOUVEAU : Forcer l'activation explicitement
        if (!fixedHoverPanel.activeSelf)
        {
            //Debug.Log("[HOVER] Activation forcée du panneau");
            fixedHoverPanel.SetActive(true);
        }

        // Appliquer la configuration depuis le JSON si disponible
        ApplyHoverConfiguration();

        // Mettre à jour le texte
        fixedHoverText.text = answerText;

        // En mode panneau CENTRÉ, ajuster la taille du cadre pour qu'il entoure le texte avec un padding
        var uiConfig = GetUIConfig();
        if (uiConfig != null && uiConfig.useCenteredHoverPanel)
        {
            // S'assurer que le mesh est à jour pour obtenir la taille préférée
            fixedHoverText.ForceMeshUpdate();

            float preferredWidth  = fixedHoverText.preferredWidth;
            float preferredHeight = fixedHoverText.preferredHeight;

            // Padding configurable depuis general-config (valeurs par défaut : 40 et 20)
            float paddingX = uiConfig.centeredHoverPaddingX > 0f ? uiConfig.centeredHoverPaddingX : 40f;
            float paddingY = uiConfig.centeredHoverPaddingY > 0f ? uiConfig.centeredHoverPaddingY : 20f;

            float panelWidth  = preferredWidth  + paddingX;
            float panelHeight = preferredHeight + paddingY;

            RectTransform panelRect = fixedHoverPanel.GetComponent<RectTransform>();
            if (panelRect != null)
            {
                panelRect.sizeDelta = new Vector2(panelWidth, panelHeight);
            }
        }

        // DOUBLE VÉRIFICATION : S'assurer que le panneau est bien actif
        if (!fixedHoverPanel.activeInHierarchy)
        {
            //Debug.LogError("[HOVER] Le panneau reste inactif malgré SetActive(true) !");

            // Essayer de forcer par le parent
            Transform parent = fixedHoverPanel.transform.parent;
            if (parent != null)
            {
                Debug.Log("[HOVER] Tentative d'activation via le parent");
                parent.gameObject.SetActive(true);
                fixedHoverPanel.SetActive(true);
            }
        }

        // Configurer l'ordre de rendu pour être au-dessus des bandes
        Canvas hoverCanvas = fixedHoverPanel.GetComponent<Canvas>();
        if (hoverCanvas == null)
        {
            // Le canvas devrait être sur le parent (HoverCanvas)
            hoverCanvas = fixedHoverPanel.transform.parent.GetComponent<Canvas>();
        }

        if (hoverCanvas != null)
        {
            hoverCanvas.overrideSorting = true;
            hoverCanvas.sortingOrder = 70000; // Au-dessus de tout
        }

        /*
        Debug.Log($"[HOVER] État final - Panel actif: {fixedHoverPanel.activeInHierarchy}");
        Debug.Log($"[HOVER] Texte affiché: '{answerText}'");

        // DEBUG : Logs détaillés pour comprendre pourquoi ce n'est pas visible
        Debug.Log($"[HOVER DEBUG] Panel actif: {fixedHoverPanel.activeInHierarchy}");
        Debug.Log($"[HOVER DEBUG] Panel position: {fixedHoverPanel.transform.position}");
        Debug.Log($"[HOVER DEBUG] Panel localPosition: {fixedHoverPanel.transform.localPosition}");
        */
        RectTransform rt = fixedHoverPanel.GetComponent<RectTransform>();
        if (rt != null)
        {
            /*
            Debug.Log($"[HOVER DEBUG] RectTransform sizeDelta: {rt.sizeDelta}");
            Debug.Log($"[HOVER DEBUG] RectTransform anchoredPosition: {rt.anchoredPosition}");
            */
        }

        Canvas canvas = fixedHoverPanel.GetComponent<Canvas>();
        if (canvas != null)
        {
            /*
            Debug.Log($"[HOVER DEBUG] Canvas sortingOrder: {canvas.sortingOrder}");
            Debug.Log($"[HOVER DEBUG] Canvas overrideSorting: {canvas.overrideSorting}");
            */
        }
        else
        {
            Canvas parentCanvas = fixedHoverPanel.transform.parent.GetComponent<Canvas>();
            if (parentCanvas != null)
            {
                /*
                Debug.Log($"[HOVER DEBUG] Parent Canvas sortingOrder: {parentCanvas.sortingOrder}");
                Debug.Log($"[HOVER DEBUG] Parent Canvas overrideSorting: {parentCanvas.overrideSorting}");
                */
            }
        }

        if (fixedHoverText != null)
        {
            /*
            Debug.Log($"[HOVER DEBUG] Text couleur: {fixedHoverText.color}");
            Debug.Log($"[HOVER DEBUG] Text fontSize: {fixedHoverText.fontSize}");
            Debug.Log($"[HOVER DEBUG] Text actif: {fixedHoverText.gameObject.activeInHierarchy}");
            */
        }

        if (fixedHoverBackground != null)
        {
            /*
            Debug.Log($"[HOVER DEBUG] Background couleur: {fixedHoverBackground.color}");
            Debug.Log($"[HOVER DEBUG] Background actif: {fixedHoverBackground.gameObject.activeInHierarchy}");
            */
        }

        // DEBUG : Vérifier toute la hiérarchie
        Transform current = fixedHoverPanel.transform;
        while (current != null)
        {
            //Debug.Log($"[HOVER HIERARCHY] {current.name}: active={current.gameObject.activeSelf}, activeInHierarchy={current.gameObject.activeInHierarchy}");
            current = current.parent;
        }
    }

    /// <summary>
    /// Crée le panneau de hover fixe en bas de l'écran
    /// </summary>
    void CreateFixedHoverPanel()
    {
        if (fixedHoverPanel != null)
        {
            //Debug.Log("[HOVER] Panel déjà existant, nettoyage...");
            Destroy(fixedHoverPanel.transform.parent.gameObject); // Détruire le canvas aussi
        }

        // Créer un Canvas dédié au hover
        GameObject hoverCanvasObj = new GameObject("HoverCanvas");
        Canvas hoverCanvas = hoverCanvasObj.AddComponent<Canvas>();
        hoverCanvas.renderMode = RenderMode.ScreenSpaceOverlay;
        hoverCanvas.overrideSorting = true;
        hoverCanvas.sortingOrder = 70000;

        // Ajouter CanvasScaler et GraphicRaycaster
        CanvasScaler canvasScaler = hoverCanvasObj.AddComponent<CanvasScaler>();
        canvasScaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
        // Utiliser la résolution de référence du jeu si disponible
        if (gameConfig != null && gameConfig.resolution != null && gameConfig.resolution.width > 0 && gameConfig.resolution.height > 0)
        {
            canvasScaler.referenceResolution = new Vector2(gameConfig.resolution.width, gameConfig.resolution.height);
        }
        else
        {
            canvasScaler.referenceResolution = new Vector2(1920, 1080);
        }
        canvasScaler.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight;
        canvasScaler.matchWidthOrHeight = 0.5f;

        hoverCanvasObj.AddComponent<GraphicRaycaster>();

        //Debug.Log($"[HOVER] Canvas dédié créé: {hoverCanvasObj.activeInHierarchy}");

        // Créer le panneau hover
        fixedHoverPanel = new GameObject("FixedHoverPanel");
        fixedHoverPanel.transform.SetParent(hoverCanvasObj.transform, false);

        RectTransform panelRect = fixedHoverPanel.AddComponent<RectTransform>();

        // Utiliser GetUIConfig() pour récupérer les valeurs depuis general-config.json
        var uiConfigForPanel = GetUIConfig();

        bool useCenteredHover = uiConfigForPanel != null && uiConfigForPanel.useCenteredHoverPanel;

        float panelY = 15f;
        float panelHeight = 80f;
        float panelWidth = 0f; // 0 → pleine largeur quand ancré horizontalement

        if (uiConfigForPanel != null)
        {
            if (uiConfigForPanel.hoverPanelSize != null)
            {
                panelHeight = uiConfigForPanel.hoverPanelSize.height;
                panelWidth = uiConfigForPanel.hoverPanelSize.width;
            }
            if (uiConfigForPanel.useCustomPosition && uiConfigForPanel.customPosition != null)
            {
                panelY = uiConfigForPanel.customPosition.y;
            }
        }

        if (useCenteredHover)
        {
            // Panneau centré au milieu de l'écran (largeur configurable)
            panelRect.anchorMin = new Vector2(0.5f, 0.5f);
            panelRect.anchorMax = new Vector2(0.5f, 0.5f);
            panelRect.pivot     = new Vector2(0.5f, 0.5f);

            if (panelWidth <= 0f) panelWidth = 800f; // largeur par défaut si non définie

            panelRect.anchoredPosition = new Vector2(0f, 0f);
            panelRect.sizeDelta        = new Vector2(panelWidth, panelHeight);

            Debug.Log($"[GameManager] Hover panel en mode CENTRÉ: size=({panelWidth},{panelHeight})");
        }
        else
        {
            // Panneau en bas d'écran, centré et en pleine largeur (comportement actuel)
            panelRect.anchorMin = new Vector2(0f, 0f);      // Bas gauche
            panelRect.anchorMax = new Vector2(1f, 0f);      // Bas droite (largeur complète)
            panelRect.pivot     = new Vector2(0.5f, 0f);    // Pivot en bas centre

            panelRect.anchoredPosition = new Vector2(0f, panelY);
            panelRect.sizeDelta        = new Vector2(0f, panelHeight);

            Debug.Log($"[GameManager] Hover panel en mode BANDEAU BAS: y={panelY}, height={panelHeight}");
        }

        // Image de fond du panneau
        fixedHoverBackground = fixedHoverPanel.AddComponent<Image>();
        
        // Couleur depuis le JSON (via GetUIConfig)
        if (uiConfigForPanel != null && !string.IsNullOrEmpty(uiConfigForPanel.hoverBackgroundColor))
        {
            if (ColorUtility.TryParseHtmlString(uiConfigForPanel.hoverBackgroundColor, out Color bgColor))
            {
                bgColor.a = uiConfigForPanel.hoverBackgroundAlpha;
                fixedHoverBackground.color = bgColor;
                Debug.Log($"[GameManager] Hover background depuis JSON: {uiConfigForPanel.hoverBackgroundColor}, alpha={uiConfigForPanel.hoverBackgroundAlpha}");
            }
        }
        else
        {
            // Fallback par défaut
            fixedHoverBackground.color = new Color(0f, 0f, 0f, 0.8f);
        }
        fixedHoverBackground.raycastTarget = false; // Pas d'interaction
        
        // Ajouter les coins arrondis (radius 10px)
        RoundedCornersImage roundedCorners = fixedHoverPanel.AddComponent<RoundedCornersImage>();
        roundedCorners.cornerRadius = 10f;
        Debug.Log("[GameManager] Coins arrondis ajoutés au hover panel (radius=10px)");

        // Texte du hover
        GameObject textObj = new GameObject("HoverText");
        textObj.transform.SetParent(fixedHoverPanel.transform, false);

        RectTransform textRect = textObj.AddComponent<RectTransform>();
        textRect.anchorMin = Vector2.zero;
        textRect.anchorMax = Vector2.one;
        textRect.offsetMin = new Vector2(20f, 5f);    // Marges intérieures
        textRect.offsetMax = new Vector2(-20f, -5f);

        fixedHoverText = textObj.AddComponent<TextMeshProUGUI>();
        fixedHoverText.text = "";
        
        // Utiliser GetUIConfig() pour récupérer les valeurs depuis general-config.json
        var uiConfig = GetUIConfig();
        if (uiConfig != null)
        {
            fixedHoverText.fontSize = uiConfig.hoverTextSize;
            // Couleur du texte depuis le JSON
            if (!string.IsNullOrEmpty(uiConfig.hoverTextColor) && ColorUtility.TryParseHtmlString(uiConfig.hoverTextColor, out Color textColor))
            {
                fixedHoverText.color = textColor;
            }
            else
            {
                fixedHoverText.color = Color.white;
            }
            fixedHoverText.fontStyle = uiConfig.hoverTextBold ? FontStyles.Bold : FontStyles.Normal;
            fixedHoverText.alignment = TextAlignmentOptions.Center;
            Debug.Log($"[GameManager] Hover text configuré depuis JSON: fontSize={uiConfig.hoverTextSize}, color={uiConfig.hoverTextColor}, bold={uiConfig.hoverTextBold}");
        }
        else
        {
            // Fallback si pas de config
            fixedHoverText.fontSize = 36f;
            fixedHoverText.color = Color.white;
            fixedHoverText.alignment = TextAlignmentOptions.Center;
            fixedHoverText.fontStyle = FontStyles.Normal;
        }

        // Activer le panneau
        fixedHoverPanel.SetActive(true);

        // Vérifications finales
        //Debug.Log($"[HOVER] Panel créé et activé");
        //Debug.Log($"[HOVER] Canvas parent actif: {hoverCanvasObj.activeInHierarchy}");
        //Debug.Log($"[HOVER] Panel actif: {fixedHoverPanel.activeInHierarchy}");
        //Debug.Log($"[HOVER] Sorting order: {hoverCanvas.sortingOrder}");
    }

    /// <summary>
    /// Applique la configuration JSON au panneau de hover
    /// </summary>
    void ApplyHoverConfiguration()
    {
        var uiConfig = GetUIConfig();
        if (uiConfig == null) return;

        bool useCenteredHover = uiConfig.useCenteredHoverPanel;

        if (fixedHoverText != null)
        {
            fixedHoverText.fontSize = uiConfig.hoverTextSize;
            
            // Couleur du texte depuis le JSON
            if (!string.IsNullOrEmpty(uiConfig.hoverTextColor) && ColorUtility.TryParseHtmlString(uiConfig.hoverTextColor, out Color textColor))
            {
                fixedHoverText.color = textColor;
            }

            fixedHoverText.fontStyle = uiConfig.hoverTextBold ? TMPro.FontStyles.Bold : TMPro.FontStyles.Normal;
        }

        if (fixedHoverPanel != null)
        {
            RectTransform panelRect = fixedHoverPanel.GetComponent<RectTransform>();
            if (panelRect != null)
            {
                // Mettre à jour la taille du panneau
                if (uiConfig.hoverPanelSize != null)
                {
                    Vector2 size = uiConfig.hoverPanelSize.ToVector2();

                    if (useCenteredHover)
                    {
                        // En mode centré, on garde un panneau plus compact au milieu
                        panelRect.sizeDelta = size;
                    }
                    else
                    {
                        // En mode bandeau bas, on garde la largeur plein écran (sizeDelta.x ignoré dans ce cas)
                        panelRect.sizeDelta = new Vector2(0f, size.y);
                    }
                }

                // Position personnalisée uniquement en mode non centré
                if (!useCenteredHover && uiConfig.useCustomPosition)
                {
                    panelRect.anchoredPosition = uiConfig.customPosition;
                }
            }
        }

        if (fixedHoverBackground != null)
        {
            if (ColorUtility.TryParseHtmlString(uiConfig.hoverBackgroundColor, out Color backgroundColor))
            {
                // Utiliser hoverBackgroundAlpha comme valeur principale
                float alpha = uiConfig.hoverBackgroundAlpha;

                // ⚠️ DÉPRÉCIÉ: centeredHoverAlpha était utilisé pour le mode centré
                // Maintenant on utilise toujours hoverBackgroundAlpha pour la cohérence
                // Si centeredHoverAlpha est défini ET plus grand, on l'utilise (rétro-compatibilité)
                if (useCenteredHover && uiConfig.centeredHoverAlpha > alpha)
                {
                    Debug.LogWarning("[GameManager] ⚠️ centeredHoverAlpha est déprécié, utilisez hoverBackgroundAlpha à la place");
                    alpha = uiConfig.centeredHoverAlpha;
                }
                
                // Fallback si alpha toujours à 0
                if (alpha <= 0f)
                {
                    alpha = 0.9f;
                    Debug.LogWarning("[GameManager] ⚠️ hoverBackgroundAlpha est 0, utilisation de 0.9 par défaut");
                }

                backgroundColor.a = alpha;
                fixedHoverBackground.color = backgroundColor;
                Debug.Log($"[GameManager] Hover background appliqué: {uiConfig.hoverBackgroundColor}, alpha={alpha}");
            }
        }
    }

    /// <summary>
    /// Cache le panneau de hover
    /// </summary>
    void HideHover()
    {
        if (fixedHoverPanel != null)
        {
            fixedHoverPanel.SetActive(false);
        }
    }



    void OnCorrectAnswer()
    {
        // Mettre à jour la LED correspondante
        if (currentQuestionIndex < leds.Length)
        {
            GameObject ledObj = leds[currentQuestionIndex];
            Image ledImage = ledObj.GetComponent<Image>();

            // Récupérer la couleur verte depuis general-config.json
            var uiConfig = GetUIConfig();
            Color defaultGreenColor = Color.green;
            if (uiConfig?.ledConfig != null && uiConfig.ledConfig.defaultGreenColor != default(Color))
            {
                defaultGreenColor = uiConfig.ledConfig.defaultGreenColor;
            }

            if (loadedSprites.ContainsKey("ledGreen") && loadedSprites["ledGreen"] != null)
            {
                ledImage.sprite = loadedSprites["ledGreen"];
                ledImage.color = Color.white;
            }
            else
            {
                ledImage.color = defaultGreenColor;
            }
        }

        // Ajouter à la liste des réponses correctes
        while (answeredCorrectly.Count <= currentQuestionIndex)
        {
            answeredCorrectly.Add(false);
        }
        answeredCorrectly[currentQuestionIndex] = true;

        score += questions[currentQuestionIndex].points;
        PlaySound("success");

        Debug.Log($"LED {currentQuestionIndex} mise à jour pour bonne réponse");
    }

    void OnWrongAnswer()
    {
        // Mettre à jour la LED correspondante
        if (currentQuestionIndex < leds.Length)
        {
            GameObject ledObj = leds[currentQuestionIndex];
            Image ledImage = ledObj.GetComponent<Image>();

            // Récupérer la couleur rouge depuis general-config.json
            var uiConfig = GetUIConfig();
            Color defaultRedColor = Color.red;
            if (uiConfig?.ledConfig != null && uiConfig.ledConfig.defaultRedColor != default(Color))
            {
                defaultRedColor = uiConfig.ledConfig.defaultRedColor;
            }

            if (loadedSprites.ContainsKey("ledRed") && loadedSprites["ledRed"] != null)
            {
                ledImage.sprite = loadedSprites["ledRed"];
                ledImage.color = Color.white;
            }
            else
            {
                ledImage.color = defaultRedColor;
            }
        }

        // Ajouter à la liste des réponses
        while (answeredCorrectly.Count <= currentQuestionIndex)
        {
            answeredCorrectly.Add(false);
        }
        answeredCorrectly[currentQuestionIndex] = false;

        PlaySound("fail");

        Debug.Log($"LED {currentQuestionIndex} mise à jour pour mauvaise réponse");
    }

    IEnumerator ShowFeedback(Question question)
    {
        if (question == null || feedbackPanel == null || feedbackText == null)
        {
            Debug.LogError("ShowFeedback: Paramètres manquants - question, feedbackPanel ou feedbackText est null");
            yield break;
        }

        // ==========================================
        // ENVOI DE LA RÉPONSE AU SERVEUR
        // ==========================================
        SendAnswerToServer();

        if (feedbackPanel != null)
        {
            Debug.Log($"[GameManager] FeedbackPanel enfants: {feedbackPanel.transform.childCount}");

            Image panelImg = feedbackPanel.GetComponent<Image>();
            if (panelImg != null)
            {
                Debug.Log($"Panel principal Image couleur: {panelImg.color}");
            }
        }

        Debug.Log($"[GameManager] Afficher feedback: {(lastAnswerWasCorrect ? "CORRECT" : "INCORRECT")}");

        // La taille du panel sera appliquée dans ApplyFeedbackPanelStyling depuis general-config.json
        // On ne l'applique plus ici pour éviter les conflits

        string feedbackContent = "";

        // Utiliser GetFeedbackMessagesConfig() qui fait automatiquement le fallback
        var feedbackConfig = GetFeedbackMessagesConfig();
        if (feedbackConfig != null)
        {
            // Créer un GameConfigData temporaire avec la config feedback pour GameConfigUtils
            var tempConfig = new GameConfigData { feedbackMessages = feedbackConfig };
            feedbackContent = GameConfigUtils.BuildFeedbackMessage(tempConfig, lastAnswerWasCorrect, question.explanation);
        }
        else
        {
            feedbackContent = question.explanation;
            Debug.LogWarning("Pas de configuration feedbackMessages trouvée, utilisation de l'explication simple");
        }

        feedbackPanel.SetActive(true);
        feedbackText.text = feedbackContent;

        // NETTOYAGE ET APPLICATION DU STYLE DE PANEL D'ABORD (pour avoir la bonne taille)
        yield return StartCoroutine(ApplyFeedbackPanelStyling(lastAnswerWasCorrect));

        // Appliquer le style du texte d'explication APRÈS le panel (pour utiliser la bonne taille)
        ApplyFeedbackTextStyling();

        // Appliquer l'image de fond si disponible (désactivé si on utilise le nouveau style)
        if (feedbackConfig != null && feedbackConfig.useBackgroundImage)
        {
            ApplyFeedbackBackgroundImage();
        }

        // SYSTÈME DE CLIC : bouton explicite en bas du panneau
        SetupFeedbackClick();
    }


    void ConfigureFeedbackPanelClick()
    {
        // Retirer l'ancien système de bouton s'il existe
        Button feedbackButton = feedbackPanel.GetComponent<Button>();
        if (feedbackButton != null)
        {
            feedbackButton.onClick.RemoveAllListeners();
            Destroy(feedbackButton);
        }

        // Ajouter un nouveau bouton qui couvre tout le panel
        Button newButton = feedbackPanel.GetComponent<Button>();
        if (newButton == null)
        {
            newButton = feedbackPanel.AddComponent<Button>();
        }

        // Configurer le bouton pour être invisible mais cliquable
        newButton.transition = Selectable.Transition.None; // Pas d'effet visuel
        newButton.onClick.RemoveAllListeners();
        newButton.onClick.AddListener(OnFeedbackClick);

        Debug.Log("Panel feedback configuré pour clic sur toute la surface");
    }

    void SetupFeedbackClick()
    {
        // 1. CRÉER / ACTIVER L'OVERLAY QUI BLOQUE LES CLICS EN DESSOUS
        CreateFeedbackBlockingOverlay();

        if (feedbackPanel == null)
        {
            Debug.LogWarning("[GameManager] SetupFeedbackClick appelé sans feedbackPanel");
            return;
        }

        // 2. S'assurer que le panneau a une Image pour bloquer les clics (mais SANS bouton dessus)
        Image panelImage = feedbackPanel.GetComponent<Image>();
        if (panelImage == null)
        {
            panelImage = feedbackPanel.AddComponent<Image>();
            panelImage.color = Color.white; // Fond neutre par défaut
        }
        panelImage.raycastTarget = true; // Le panneau bloque les clics vers le fond

        // Supprimer tout bouton directement sur le panneau principal
        Button panelButton = feedbackPanel.GetComponent<Button>();
        if (panelButton != null)
        {
            panelButton.onClick.RemoveAllListeners();
            Destroy(panelButton);
        }

        // 3. CRÉER OU RÉCUPÉRER LE BOUTON DE FERMETURE EXPLICITE EN BAS DU PANNEAU
        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>();
        }
        // Positionné en bas au centre du panneau (remonté de 20px)
        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>();
        }

        // Vérifier si un style de bouton est configuré
        var feedbackConfigForStyle = GetFeedbackMessagesConfig();
        string buttonStyleName = feedbackConfigForStyle?.feedbackButtonStyle;
        ButtonStyleConfig buttonStyle = null;
        
        if (!string.IsNullOrEmpty(buttonStyleName) && GeneralConfigManager.Instance != null)
        {
            var generalConfig = GeneralConfigManager.Instance.GetConfig();
            buttonStyle = generalConfig?.buttonStyles?.GetStyle(buttonStyleName);
        }
        
        if (buttonStyle != null)
        {
            // Utiliser le style configuré avec dégradé et bordure
            btnRect.sizeDelta = new Vector2(buttonStyle.width, buttonStyle.height);
            
            Color startColor = HexToColor(buttonStyle.gradient?.startColor ?? "#b87aff");
            Color endColor = HexToColor(buttonStyle.gradient?.endColor ?? "#7b4fbf");
            Color borderColor = HexToColor(buttonStyle.borderColor ?? "#d8c4b4");
            float borderWidth = buttonStyle.borderWidth;
            float borderRadius = buttonStyle.borderRadius > 0 ? buttonStyle.borderRadius : 20;
            
            Sprite gradientSprite = CreateGradientSpriteWithBorder(
                (int)buttonStyle.width, (int)buttonStyle.height, 
                borderRadius, startColor, endColor, borderColor, borderWidth);
            
            btnImage.sprite = gradientSprite;
            btnImage.type = Image.Type.Simple;
            btnImage.color = Color.white;
            
            // Ajouter l'ombre si configurée
            if (buttonStyle.shadow != null && buttonStyle.shadow.enabled)
            {
                UnityEngine.UI.Shadow shadow = closeButtonObj.GetComponent<UnityEngine.UI.Shadow>();
                if (shadow == null)
                {
                    shadow = closeButtonObj.AddComponent<UnityEngine.UI.Shadow>();
                }
                shadow.effectColor = HexToColor(buttonStyle.shadow.color ?? "#00000040");
                shadow.effectDistance = new Vector2(buttonStyle.shadow.offsetX, -buttonStyle.shadow.offsetY);
            }
            
            Debug.Log($"[GameManager] Bouton feedback créé avec style '{buttonStyleName}'");
        }
        else
        {
            // Fallback : couleur simple
            btnRect.sizeDelta = new Vector2(260f, 60f);
            Color buttonColor = new Color(0.15f, 0.55f, 0.9f, 1f); // bleu soutenu par défaut
            if (!string.IsNullOrEmpty(feedbackConfigForStyle?.feedbackButtonBackgroundColor) &&
                ColorUtility.TryParseHtmlString(feedbackConfigForStyle.feedbackButtonBackgroundColor, out Color parsed))
            {
                buttonColor = parsed;
            }
            btnImage.color = buttonColor;
        }
        btnImage.raycastTarget = true;

        // Composant Button
        Button closeButton = closeButtonObj.GetComponent<Button>();
        if (closeButton == null)
        {
            closeButton = closeButtonObj.AddComponent<Button>();
        }
        closeButton.targetGraphic = btnImage;
        closeButton.transition = Selectable.Transition.ColorTint;
        closeButton.onClick.RemoveAllListeners();
        closeButton.onClick.AddListener(() =>
        {
            Debug.Log("[GameManager] Clic sur le bouton de fermeture du feedback");
            OnFeedbackClick();
        });

        // 4. TEXTE DU BOUTON ("Continuer" ou texte depuis la config)
        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;

            label.alignment = TextAlignmentOptions.Center;
            label.fontStyle = FontStyles.Bold;
        }

        var feedbackConfig = GetFeedbackMessagesConfig();

        // Choix du texte du bouton :
        // - si ce n'est PAS la dernière question → texte "next"
        // - si c'est la dernière → texte "last"
        bool isLastQuestion = (questions != null && questions.Length > 0 &&
                               currentQuestionIndex >= questions.Length - 1);

        string buttonText = null;
        if (!isLastQuestion)
        {
            // Bouton pour passer à la question suivante
            buttonText = feedbackConfig?.feedbackNextButtonText;
        }
        else
        {
            // Bouton pour fermer le dernier feedback
            buttonText = feedbackConfig?.feedbackLastButtonText;
        }

        if (string.IsNullOrEmpty(buttonText))
        {
            buttonText = isLastQuestion ? "Fermer" : "Question suivante";
        }
        label.text = buttonText;

        // Appliquer le style de texte depuis le buttonStyle si disponible
        if (buttonStyle?.text != null)
        {
            // Utiliser les propriétés de texte du style de bouton
            label.fontSize = buttonStyle.text.fontSize > 0 ? buttonStyle.text.fontSize : 22f;
            
            if (!string.IsNullOrEmpty(buttonStyle.text.color))
            {
                label.color = HexToColor(buttonStyle.text.color);
            }
            else
            {
                label.color = Color.white;
            }
            
            // Appliquer le style de police (fontWeight)
            if (!string.IsNullOrEmpty(buttonStyle.text.fontWeight))
            {
                label.fontStyle = buttonStyle.text.fontWeight.ToLower() == "bold" ? FontStyles.Bold : FontStyles.Normal;
            }
            else
            {
                label.fontStyle = FontStyles.Normal;
            }
            
            // Appliquer la police (fontFamily)
            if (!string.IsNullOrEmpty(buttonStyle.text.fontFamily))
            {
                TMP_FontAsset font = Resources.Load<TMP_FontAsset>($"Fonts/{buttonStyle.text.fontFamily}");
                if (font != null)
                {
                    label.font = font;
                    Debug.Log($"[GameManager] Police feedback bouton chargée: {buttonStyle.text.fontFamily}");
                }
                else
                {
                    Debug.LogWarning($"[GameManager] Police non trouvée: {buttonStyle.text.fontFamily}");
                }
            }
        }
        else
        {
            // Fallback : valeurs par défaut
            label.fontSize = 22f;
            label.color = Color.white;
        }

        // Le texte ne doit pas bloquer les clics du bouton
        label.raycastTarget = false;

        Debug.Log("[GameManager] Panneau feedback configuré avec un bouton de fermeture explicite");
    }

    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.raycastTarget = false; // Le texte ne doit pas bloquer les clics
        }

        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 (gameConfig != null && gameConfig.resolution != null)
            {
                scaler.referenceResolution = new Vector2(gameConfig.resolution.width, gameConfig.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;

            // IMPORTANT : L'overlay ne doit PAS avoir de bouton
            // Il sert uniquement à BLOQUER les clics vers les zones de tir en dessous
            // Seul le bouton d'instructions en bas du panneau peut fermer le panneau
        }

        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)
        {
            // IMPORTANT : Le panneau doit être AU-DESSUS de l'overlay pour recevoir les clics
            // SetAsLastSibling place le panneau après l'overlay dans la hiérarchie
            feedbackPanel.transform.SetAsLastSibling();
            
            // S'assurer que le panneau a un GraphicRaycaster pour intercepter les clics
            Canvas panelCanvas = feedbackPanel.GetComponentInParent<Canvas>();
            if (panelCanvas != null && panelCanvas.GetComponent<GraphicRaycaster>() == null)
            {
                panelCanvas.gameObject.AddComponent<GraphicRaycaster>();
            }
        }
    }


    void ApplyFeedbackPanelSize()
    {
        var feedbackConfig = GetFeedbackMessagesConfig();
        if (feedbackConfig == null || !feedbackConfig.useCustomPanelSize)
        {
            return;
        }
        RectTransform panelRect = feedbackPanel.GetComponent<RectTransform>();

        if (panelRect != null)
        {
            panelRect.sizeDelta = feedbackConfig.panelSize;
            Debug.Log($"Taille du panneau feedback appliquée: {feedbackConfig.panelSize}");
        }
    }

    void ApplyFeedbackTextStyling()
    {
        if (feedbackText == null)
        {
            return;
        }

        var feedbackConfig = GetFeedbackMessagesConfig();
        if (feedbackConfig == null)
        {
            return;
        }

        // Appliquer la couleur du texte d'explication
        if (!string.IsNullOrEmpty(feedbackConfig.explanationTextColor))
        {
            if (ColorUtility.TryParseHtmlString(feedbackConfig.explanationTextColor, out Color textColor))
            {
                feedbackText.color = textColor;
                Debug.Log($"Couleur texte feedback appliquée: {feedbackConfig.explanationTextColor}");
            }
        }

        // Appliquer la taille du texte
        if (feedbackConfig.explanationTextSize > 0)
        {
            feedbackText.fontSize = feedbackConfig.explanationTextSize;
            Debug.Log($"Taille texte feedback appliquée: {feedbackConfig.explanationTextSize}");
        }

        // Appliquer le style gras
        if (feedbackConfig.explanationTextBold)
        {
            feedbackText.fontStyle = FontStyles.Bold;
        }
        else
        {
            feedbackText.fontStyle = FontStyles.Normal;
        }

        // Récupérer les valeurs de padding depuis la config ou les defaults
        var defaultFeedback = GeneralConfigManager.Instance?.GetDefaultFeedbackMessages();
        float paddingLeft = feedbackConfig.explanationTextPaddingLeft > 0 
            ? feedbackConfig.explanationTextPaddingLeft 
            : (defaultFeedback?.explanationTextPaddingLeft ?? 20f);
        float paddingRight = feedbackConfig.explanationTextPaddingRight > 0 
            ? feedbackConfig.explanationTextPaddingRight 
            : (defaultFeedback?.explanationTextPaddingRight ?? 20f);
        
        // Position verticale du titre (depuis le haut du panel, en pixels)
        // Valeur positive = depuis le haut (ex: 100 = texte à 100px du haut)
        float titlePositionY = defaultFeedback?.resultMessagePositionY ?? 100f;
        // Position verticale de l'explication (offset depuis le titre, en pixels)
        float explanationOffsetY = defaultFeedback?.explanationTextPositionY ?? 0f;
        
        Debug.Log($"[GameManager] 📊 Config lue - resultMessagePositionY: {titlePositionY}, explanationTextPositionY: {explanationOffsetY}");
        
        // Récupérer la taille du panel (utiliser sizeDelta car rect.height peut être 0)
        RectTransform panelRect = feedbackPanel?.GetComponent<RectTransform>();
        float panelHeight = panelRect != null ? panelRect.sizeDelta.y : 500f;
        if (panelHeight <= 0) panelHeight = 500f; // Fallback si sizeDelta est aussi 0
        
        // Calculer le paddingTop pour positionner le titre
        // paddingTop = espace depuis le haut du panel jusqu'au début du texte
        float paddingTop = titlePositionY;
        
        // Le paddingBottom reste fixe pour l'instant
        float paddingBottom = 80f; // Laisser de la place pour le bouton
        
        Debug.Log($"[GameManager] 📊 Position texte - Title Y: {titlePositionY}, PaddingTop: {paddingTop}, PaddingBottom: {paddingBottom}, Panel height: {panelHeight}");

        // Appliquer le padding au RectTransform du texte
        RectTransform textRect = feedbackText.GetComponent<RectTransform>();
        if (textRect != null)
        {
            textRect.anchorMin = Vector2.zero;
            textRect.anchorMax = Vector2.one;
            textRect.offsetMin = new Vector2(paddingLeft, paddingBottom);
            textRect.offsetMax = new Vector2(-paddingRight, -paddingTop);
            
            Debug.Log($"[GameManager] Position texte - Title Y: {titlePositionY}, Explanation offset: {explanationOffsetY}, PaddingTop: {paddingTop}");
        }

        // Aligner le texte - centré horizontalement mais EN HAUT verticalement
        // pour que paddingTop contrôle réellement la position verticale du texte
        if (feedbackConfig.centerTextInPanel)
        {
            // Centré horizontalement, aligné en haut verticalement
            feedbackText.alignment = TextAlignmentOptions.Top;
            Debug.Log($"Texte aligné en haut avec padding: gauche={paddingLeft}, droite={paddingRight}, haut={paddingTop}");
        }
        else
        {
            feedbackText.alignment = TextAlignmentOptions.TopLeft;
            Debug.Log($"Texte aligné en haut-gauche avec padding: gauche={paddingLeft}, droite={paddingRight}, haut={paddingTop}");
        }

        Debug.Log("Style du texte d'explication appliqué");
    }

    void ApplyFeedbackBackgroundImage()
    {
        var feedbackConfig = GetFeedbackMessagesConfig();
        if (feedbackConfig == null || !feedbackConfig.useBackgroundImage)
        {
            Debug.Log("Pas d'image de fond à appliquer");
            return;
        }

        // Choisir le bon sprite selon le résultat
        Sprite spriteToUse = lastAnswerWasCorrect ? feedbackSuccessSprite : feedbackFailureSprite;

        if (spriteToUse == null)
        {
            Debug.LogWarning($"Sprite {(lastAnswerWasCorrect ? "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;

        Color imageColor = Color.white;
        imageColor.a = feedbackConfig.backgroundImageAlpha;
        feedbackBackgroundImage.color = imageColor;

        if (feedbackConfig.stretchToFitPanel)
        {
            feedbackBackgroundImage.type = Image.Type.Sliced;
            feedbackBackgroundImage.preserveAspect = false;
        }
        else
        {
            feedbackBackgroundImage.type = Image.Type.Simple;
            feedbackBackgroundImage.preserveAspect = true;
        }

        feedbackBackgroundImage.gameObject.SetActive(true);

        Debug.Log($"Image de fond {(lastAnswerWasCorrect ? "succès" : "échec")} appliquée");
    }

    void PlaySound(string soundKey)
    {
        if (loadedAudioClips.ContainsKey(soundKey) && audioSource != null)
        {
            audioSource.clip = loadedAudioClips[soundKey];
            audioSource.Play();
        }
    }

    public void OnFeedbackClick()
    {
        // Si ce clic de fermeture vient du système UI natif (EventSystem) et NON de WebGLClickReceiver,
        // on arme une protection pour ignorer le prochain clic d'entrée (InputSystem + WebGL brut).
        // Cela évite qu'un même clic ferme le panneau ET déclenche une zone de tir en dessous.
        if (!isHandlingFeedbackClickFromWebGL)
        {
            ignoreNextInputAfterFeedback = true;
            ignoreNextWebGLClickAfterFeedback = true;
        }

        if (feedbackPanel != null)
        {
            feedbackPanel.SetActive(false);
        }
        
        // Désactiver l'overlay bloquant
        if (feedbackBlockingOverlay != null)
        {
            feedbackBlockingOverlay.SetActive(false);
        }
        if (feedbackOverlayCanvas != null)
        {
            feedbackOverlayCanvas.gameObject.SetActive(false);
        }

        if (gameConfig?.feedbackMessages?.changeBackgroundColor == true) // CHANGÉ: supprimé .gameConfig
        {
            Image panelBackground = feedbackPanel.GetComponent<Image>();
            if (panelBackground != null)
            {
                panelBackground.color = Color.white;
            }
        }

        currentQuestionIndex++;

        if (currentQuestionIndex >= questions.Length) // CHANGÉ: .Count -> .Length
        {
            EndGame();
        }
        else
        {
            SetupCurrentQuestion();
        }
    }



    void EndGame()
    {
        Debug.Log("Fin du jeu ! Score final : " + score);
        Debug.Log($"Résumé: {correctAnswers}/{totalAnswers} bonnes réponses");

        // NOUVEAU SYSTÈME : Déterminer success/fail avec la configuration
        bool success = CheckSuccess();
        string result = success ? "success" : "fail";
        
        Debug.Log($"[GameManager] Résultat du jeu: {result} (correctAnswers={correctAnswers}, totalAnswers={totalAnswers})");
        
        // Enregistrer le résultat dans GameResultsManager
        RecordGameResult(success);
        
        // IMPORTANT : Sauvegarder le résultat pour le dialogue "After" (compatible avec le nouveau système)
        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 dialogues groupés
            Debug.Log("[GameManager] Utilisation du système de dialogues groupés");
            PlayerPrefs.SetString("GamePhase", "After");
            PlayerPrefs.Save();
            SceneTransitionManager.LoadSceneWithTransition("Player");
        }
        else
        {
            // Ancien système avec dialogues séparés
            string nextDialogue = success ?
                PlayerPrefs.GetString("DialogueSuccessUrl") :
                PlayerPrefs.GetString("DialogueFailUrl");

            PlayerPrefs.SetString("NextDialogueUrl", nextDialogue);
            PlayerPrefs.SetString("GamePhase", "After");

            Debug.Log($"Transition vers dialogue {(success ? "SUCCESS" : "FAIL")}: {nextDialogue}");

            // NOUVEAU SYSTÈME
            SceneTransitionManager.LoadSceneWithTransition("Player");
        }
    }





    void SetupHoverUIAutomatically()
    {
        if (hoverPanel != null && hoverText != null) return;

        Canvas canvas = FindFirstObjectByType<Canvas>();
        if (canvas == null) return;

        GameObject hoverPanelObj = new GameObject("HoverPanel");
        hoverPanelObj.transform.SetParent(canvas.transform, false);

        RectTransform hoverRect = hoverPanelObj.AddComponent<RectTransform>();
        hoverRect.anchorMin = new Vector2(0.5f, 0f);
        hoverRect.anchorMax = new Vector2(0.5f, 0f);
        hoverRect.pivot = new Vector2(0.5f, 0f);

        Vector2 panelSize = new Vector2(500, 50);
        var uiConfig = GetUIConfig();
        if (uiConfig != null && uiConfig.hoverPanelSize != null)
        {
            panelSize = uiConfig.hoverPanelSize.ToVector2();
            Debug.Log($"Taille panneau hover depuis config: {panelSize}");
        }

        hoverRect.sizeDelta = panelSize;
        hoverRect.anchoredPosition = new Vector2(0, 0);

        GameObject backgroundObj = new GameObject("Background");
        backgroundObj.transform.SetParent(hoverPanelObj.transform, false);

        RectTransform bgRect = backgroundObj.AddComponent<RectTransform>();
        bgRect.anchorMin = Vector2.zero;
        bgRect.anchorMax = Vector2.one;
        bgRect.sizeDelta = Vector2.zero;
        bgRect.offsetMin = Vector2.zero;
        bgRect.offsetMax = Vector2.zero;

        Image bgImage = backgroundObj.AddComponent<Image>();

        Color backgroundColor = new Color(0, 0, 0, 0.9f);
        // Réutiliser uiConfig déjà déclaré plus haut (ligne 4005)
        if (uiConfig != null)
        {
            if (ColorUtility.TryParseHtmlString(uiConfig.hoverBackgroundColor, out Color bgColor))
            {
                bgColor.a = uiConfig.hoverBackgroundAlpha;
                backgroundColor = bgColor;
            }
        }
        bgImage.color = backgroundColor;

        GameObject textObj = new GameObject("HoverText");
        textObj.transform.SetParent(hoverPanelObj.transform, false);

        RectTransform textRect = textObj.AddComponent<RectTransform>();
        textRect.anchorMin = Vector2.zero;
        textRect.anchorMax = Vector2.one;
        textRect.sizeDelta = Vector2.zero;
        textRect.offsetMin = new Vector2(10, 5);
        textRect.offsetMax = new Vector2(-10, -5);

        TextMeshProUGUI textComp = textObj.AddComponent<TextMeshProUGUI>();
        textComp.text = "";

        float fontSize = 20;
        Color textColor = Color.white;
        TMPro.FontStyles fontStyle = TMPro.FontStyles.Bold;

        // Réutiliser uiConfig déjà déclaré plus haut
        if (uiConfig != null)
        {
            fontSize = uiConfig.hoverTextSize;

            if (ColorUtility.TryParseHtmlString(uiConfig.hoverTextColor, out Color txtColor))
            {
                textColor = txtColor;
            }

            fontStyle = uiConfig.hoverTextBold ? TMPro.FontStyles.Bold : TMPro.FontStyles.Normal;

            Debug.Log($"Config texte hover appliquée: Taille={fontSize}, Couleur={uiConfig.hoverTextColor}, Gras={uiConfig.hoverTextBold}");
        }

        textComp.fontSize = fontSize;
        textComp.color = textColor;
        textComp.alignment = TextAlignmentOptions.Center;
        textComp.fontStyle = fontStyle;

        hoverPanel = hoverPanelObj;
        hoverText = textComp;
        hoverPanel.SetActive(false);
    }
    public bool IsFeedbackPanelActive()
    {
        return feedbackPanel != null && feedbackPanel.activeInHierarchy;
    }

    public bool IsPointOverTargetZone(Vector2 screenPosition)
    {
        foreach (GameObject zoneObj in targetZones)
        {
            if (zoneObj != null && zoneObj.activeInHierarchy)
            {
                TargetZone targetZone = zoneObj.GetComponent<TargetZone>();
                if (targetZone != null && targetZone.IsPointInScreenZone(screenPosition))
                {
                    return true;
                }
            }
        }

        return false;
    }

    public TargetZone GetHoveredTargetZone(Vector2 screenPosition)
    {
        foreach (GameObject zoneObj in targetZones)
        {
            if (zoneObj != null && zoneObj.activeInHierarchy)
            {
                TargetZone targetZone = zoneObj.GetComponent<TargetZone>();
                if (targetZone != null && targetZone.IsPointInScreenZone(screenPosition))
                {
                    return targetZone;
                }
            }
        }

        return null;
    }

    public void SetConditionalCrosshair(bool enabled)
    {
        if (crosshairManager != null)
        {
            Debug.Log($"Viseur conditionnel: {(enabled ? "ACTIVÉ" : "DÉSACTIVÉ")}");
        }
        else
        {
            Debug.LogWarning("CrosshairManager non assigné !");
        }
    }

    public string GetZoneDebugInfo()
    {
        string info = $"Zones actives: {targetZones.Count}\n";

        for (int i = 0; i < targetZones.Count; i++)
        {
            GameObject zoneObj = targetZones[i];
            if (zoneObj != null)
            {
                TargetZone targetZone = zoneObj.GetComponent<TargetZone>();
                if (targetZone != null)
                {
                    AnswerWithZone answer = targetZone.GetAnswer();
                    if (answer != null)
                    {
                        info += $"Zone {i + 1}: '{answer.text}' - {answer.zone}\n";
                    }
                }
            }
        }

        return info;
    }

    public void EnableCrosshairDebug(bool enable)
    {
        if (crosshairManager != null)
        {
            crosshairManager.showDebugInfo = enable;
            Debug.Log($"Debug viseur: {(enable ? "ACTIVÉ" : "DÉSACTIVÉ")}");
        }
    }

    public void ConfigureCrosshair(float size, Color color)
    {
        if (crosshairManager != null)
        {
            crosshairManager.SetCrosshairSize(size);
            crosshairManager.SetCrosshairColor(color);

            Debug.Log($"Viseur configuré: Taille={size}, Couleur={color}");
        }
    }

    void ApplyGunConfigFromJSON()
    {

        if (gameConfig?.gunConfig == null) // ✅ CORRIGÉ
        {
            Debug.LogError("Configuration pistolet NULL dans le JSON !");
            return;
        }

        if (gunSpriteManager == null)
        {
            Debug.LogError("GunSpriteManager NULL !");
            return;
        }

        GunConfig gunConfigData = gameConfig.gunConfig; // ✅ CORRIGÉ
        Debug.Log($"JSON gunConfig trouvé: alwaysVisible={gunConfigData.alwaysVisible}");

        gunSpriteManager.SetGunOffset(gunConfigData.offsetFromCursor);
        Debug.Log($"Offset pistolet appliqué depuis JSON: {gunConfigData.offsetFromCursor}");

        bool sizeApplied = false;

        var gunSizeField = gunConfigData.GetType().GetField("gunSize");
        if (gunSizeField != null)
        {
            Vector2 jsonGunSize = (Vector2)gunSizeField.GetValue(gunConfigData);
            Debug.Log($"gunSize trouvé dans JSON: {jsonGunSize}");

            if (jsonGunSize != Vector2.zero)
            {
                gunSpriteManager.SetGunSize(jsonGunSize);
                Debug.Log($"Taille pistolet appliquée (gunSize Vector2): {jsonGunSize}");
                sizeApplied = true;
            }
            else
            {
                Debug.Log("gunSize est Vector2.zero, passage au fallback");
            }
        }
        else
        {
            Debug.Log("Propriété gunSize non trouvée dans le JSON");
        }

        if (!sizeApplied)
        {
            float sizeValue = gunConfigData.size;
            Debug.Log($"Utilisation du fallback size: {sizeValue}");

            if (sizeValue > 0)
            {
                Vector2 squareSize = new Vector2(sizeValue, sizeValue);
                gunSpriteManager.SetGunSize(squareSize);
                Debug.Log($"Taille pistolet appliquée (size fallback, carré): {squareSize}");
                sizeApplied = true;
            }
            else
            {
                Debug.LogWarning("size est également <= 0, utilisation de la taille par défaut");
            }
        }

        if (!sizeApplied)
        {
            Vector2 defaultSize = new Vector2(200f, 150f);
            gunSpriteManager.SetGunSize(defaultSize);
            Debug.Log($"Taille pistolet par défaut appliquée: {defaultSize}");
        }

        gunSpriteManager.flipGunHorizontally = gunConfigData.flipHorizontally;
        gunSpriteManager.enableRotation = gunConfigData.enableRotation;
        gunSpriteManager.rotationOffset = gunConfigData.rotationOffset;
        gunSpriteManager.alwaysVisible = gunConfigData.alwaysVisible;
        gunSpriteManager.fadeSpeed = gunConfigData.fadeSpeed;

        Debug.Log($"Propriétés appliquées: Flip={gunConfigData.flipHorizontally}, Rotation={gunConfigData.enableRotation}, AlwaysVisible={gunConfigData.alwaysVisible}");

        var hideFeedbackField = gunConfigData.GetType().GetField("hideDuringFeedback");
        if (hideFeedbackField != null)
        {
            gunSpriteManager.hideDuringFeedback = (bool)hideFeedbackField.GetValue(gunConfigData);
            Debug.Log($"hideDuringFeedback appliqué: {gunSpriteManager.hideDuringFeedback}");
        }
        else
        {
            gunSpriteManager.hideDuringFeedback = true;
            Debug.Log("hideDuringFeedback défini par défaut à true");
        }

        gunSpriteManager.SetAlwaysVisible(gunConfigData.alwaysVisible);
        Debug.Log($"SetAlwaysVisible({gunConfigData.alwaysVisible}) appelé");

        if (gunConfigData.boundary != null)
        {
            gunSpriteManager.SetBoundaryConfiguration(gunConfigData.boundary);
            Debug.Log("Contraintes de mouvement appliquées depuis le JSON");
        }
        else
        {
            Debug.Log("Aucune configuration de contraintes trouvée dans le JSON");
        }







        // NOUVEAU : Debug de la config crosshair
        if (gameConfig?.crosshairConfig != null)
        {
            var crosshairConfig = gameConfig.crosshairConfig;

            // Vérifier les propriétés de base
            Debug.Log($"Config crosshair trouvée - defaultSize: {crosshairConfig.defaultSize}");

            // Vérifier les nouvelles propriétés avec réflexion
            System.Type configType = crosshairConfig.GetType();
            var alwaysShowField = configType.GetField("alwaysShowCrosshair");
            if (alwaysShowField != null)
            {
                Debug.Log($"alwaysShowCrosshair trouvé: {alwaysShowField.GetValue(crosshairConfig)}");
            }
            else
            {
                Debug.LogWarning("alwaysShowCrosshair NON TROUVÉ dans le JSON");
            }

            var onTargetSizeField = configType.GetField("onTargetSizeMultiplier");
            if (onTargetSizeField != null)
            {
                Debug.Log($"onTargetSizeMultiplier trouvé: {onTargetSizeField.GetValue(crosshairConfig)}");
            }
            else
            {
                Debug.LogWarning("onTargetSizeMultiplier NON TROUVÉ dans le JSON");
            }

        }
        else
        {
            Debug.LogError("gameConfig.crosshairConfig est NULL !");
        }



    }

    public void LogCrosshairStats()
    {
        if (crosshairManager != null)
        {
            Debug.Log($"Visible: {crosshairManager.IsVisible()}");
            Debug.Log($"Taille actuelle: {crosshairManager.crosshairSize}");
            Debug.Log("Propriétés avancées disponibles dans l'Inspector du CrosshairManager");
        }
        else
        {
            Debug.LogWarning("CrosshairManager non assigné !");
        }
    }

    IEnumerator ApplyFeedbackPanelStyling(bool isCorrect)
    {
        if (feedbackPanel == null)
        {
            Debug.LogError("[GameManager] 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("[GameManager] Style de panel feedback appliqué");
    }

    /// <summary>
    /// Crée les bandes en haut et en bas de l'écran
    /// OU utilise le ShootingGameLayout s'il existe
    /// </summary>
    void CreateUIBands()
    {
        // === VÉRIFIER SI LE NOUVEAU LAYOUT EXISTE ===
        // Si ShootingGameLayout est présent, il gère les bandes UI à notre place
        ShootingGameLayout shootingLayout = ShootingGameLayout.Instance;
        if (shootingLayout == null)
        {
            shootingLayout = FindFirstObjectByType<ShootingGameLayout>();
        }
        
        if (shootingLayout != null && shootingLayout.IsInitialized)
        {
            Debug.Log("[GameManager] ShootingGameLayout détecté et initialisé - connexion immédiate");
            
            // Connecter les références IMMÉDIATEMENT (pas de coroutine)
            ConnectToShootingLayout(shootingLayout);
            return;
        }
        
        // === COMPORTEMENT ORIGINAL SI PAS DE NOUVEAU LAYOUT ===
        // Utiliser GetUIConfig() qui fait automatiquement le fallback vers defaultUIConfig
        var uiConfig = GetUIConfig();
        var bandsConfig = uiConfig?.bands;
        
        // Si pas de config de bandes, ne pas créer
        if (bandsConfig == null || !bandsConfig.showBands)
        {
            Debug.Log("[GameManager] Pas de bandes UI à créer (showBands=false ou config manquante)");
            return;
        }
        

        if (bandsConfig == null)
        {
            Debug.Log("Pas de config de bandes UI disponible");
            return;
        }

        int bandSortingOrder = bandsConfig.sortingOrder > 0 ? bandsConfig.sortingOrder : 5;

        Canvas canvas = FindFirstObjectByType<Canvas>();
        if (canvas == null)
        {
            Debug.LogError("Pas de Canvas trouvé pour créer les bandes UI");
            return;
        }

        Color bandColor = Color.white;
        if (ColorUtility.TryParseHtmlString(bandsConfig.bandColor, out Color parsedColor))
        {
            parsedColor.a = bandsConfig.bandAlpha;
            bandColor = parsedColor;
        }

        // Récupérer la config shadow depuis GeneralConfigManager
        BandShadowConfig shadowConfig = null;
        if (GeneralConfigManager.Instance != null)
        {
            var defaultUIConfig = GeneralConfigManager.Instance.GetDefaultUIConfig();
            shadowConfig = defaultUIConfig?.bands?.shadow;
        }
        
        CreateBand("TopBand", canvas, bandsConfig.bandHeight, bandColor, bandsConfig.sortingOrder, true, shadowConfig);
        CreateBand("BottomBand", canvas, bandsConfig.bandHeight, bandColor, bandsConfig.sortingOrder, false, shadowConfig);

        Debug.Log($"Bandes UI créées - Hauteur: {bandsConfig.bandHeight}px, Couleur: {bandsConfig.bandColor}, Shadow: {(shadowConfig?.enabled ?? false)}");
    }
    
    /// <summary>
    /// Connecte immédiatement les références au ShootingGameLayout
    /// </summary>
    private void ConnectToShootingLayout(ShootingGameLayout layout)
    {
        Debug.Log("=== [GameManager] CONNEXION AU SHOOTING LAYOUT ===");
        
        // Connecter les références du layout
        if (layout.HeaderPanel != null)
        {
            topBand = layout.HeaderPanel;
            Debug.Log($"[GameManager] ✅ TopBand connecté: {topBand.name}");
        }
        else
        {
            Debug.LogError("[GameManager] ❌ HeaderPanel est NULL dans le layout!");
        }
        
        if (layout.GameAreaPanel != null)
        {
            bottomBand = layout.GameAreaPanel;
            Debug.Log($"[GameManager] ✅ BottomBand connecté: {bottomBand.name}");
        }
        else
        {
            Debug.LogError("[GameManager] ❌ GameAreaPanel est NULL dans le layout!");
        }
        
        if (layout.LedContainer != null)
        {
            ledContainer = layout.LedContainer;
            Debug.Log($"[GameManager] ✅ LEDContainer connecté: {ledContainer.name}");
        }
        else
        {
            Debug.LogWarning("[GameManager] ⚠️ LedContainer est NULL, les LEDs iront dans topBand");
        }
        
        if (layout.QuestionText != null)
        {
            topBandQuestionText = layout.QuestionText;
            Debug.Log($"[GameManager] ✅ QuestionText connecté: {topBandQuestionText.name}");
        }
        else
        {
            Debug.LogError("[GameManager] ❌ QuestionText est NULL dans le layout!");
        }
        
        // NOTE: La vidéo utilise CameraNearPlane, pas besoin de la déplacer
        // Elle sera visible à travers la zone de jeu transparente
        
        // Configurer le VideoPlayer pour utiliser la RenderTexture du layout
        // La vidéo sera affichée UNIQUEMENT dans la zone de jeu
        if (backgroundVideo != null)
        {
            layout.ConfigureVideoPlayer(backgroundVideo);
            Debug.Log("[GameManager] ✅ VideoPlayer configuré pour RenderTexture");
        }
        
        // Configurer le sorting order du feedback panel pour qu'il soit au-dessus du layout
        ConfigureFeedbackPanelSorting();
        
        Debug.Log("=== [GameManager] FIN CONNEXION LAYOUT ===");
    }
    
    /// <summary>
    /// Configure le sorting order du feedback panel pour qu'il soit visible au-dessus du layout
    /// </summary>
    private void ConfigureFeedbackPanelSorting()
    {
        // === FEEDBACK PANEL ===
        if (feedbackPanel != null)
        {
            Canvas feedbackCanvas = feedbackPanel.GetComponent<Canvas>();
            if (feedbackCanvas == null)
            {
                feedbackCanvas = feedbackPanel.AddComponent<Canvas>();
            }
            feedbackCanvas.overrideSorting = true;
            feedbackCanvas.sortingOrder = 200; // Au-dessus du header (100) mais en dessous du crosshair (1000)
            
            if (feedbackPanel.GetComponent<UnityEngine.UI.GraphicRaycaster>() == null)
            {
                feedbackPanel.AddComponent<UnityEngine.UI.GraphicRaycaster>();
            }
            
            Debug.Log("[GameManager] ✅ FeedbackPanel configuré avec sortingOrder=200");
        }
        
        // === HOVER PANEL ===
        if (hoverPanel != null)
        {
            Canvas hoverCanvas = hoverPanel.GetComponent<Canvas>();
            if (hoverCanvas == null)
            {
                hoverCanvas = hoverPanel.AddComponent<Canvas>();
            }
            hoverCanvas.overrideSorting = true;
            hoverCanvas.sortingOrder = 150; // Au-dessus du header mais en dessous du feedback
            
            if (hoverPanel.GetComponent<UnityEngine.UI.GraphicRaycaster>() == null)
            {
                hoverPanel.AddComponent<UnityEngine.UI.GraphicRaycaster>();
            }
            
            Debug.Log("[GameManager] ✅ HoverPanel configuré avec sortingOrder=150");
        }
    }

    /// <summary>
    /// Crée une bande individuelle
    /// </summary>
    void CreateBand(string name, Canvas canvas, float height, Color color, int sortingOrder, bool isTop, BandShadowConfig shadowConfig = null)
    {
        GameObject bandObj = new GameObject(name);
        bandObj.transform.SetParent(canvas.transform, false);

        RectTransform bandRect = bandObj.AddComponent<RectTransform>();

        if (isTop)
        {
            bandRect.anchorMin = new Vector2(0f, 1f);
            bandRect.anchorMax = new Vector2(1f, 1f);
            bandRect.pivot = new Vector2(0.5f, 1f);
            bandRect.sizeDelta = new Vector2(0f, height);
            bandRect.anchoredPosition = Vector2.zero;
            topBand = bandObj;
        }
        else
        {
            bandRect.anchorMin = new Vector2(0f, 0f);
            bandRect.anchorMax = new Vector2(1f, 0f);
            bandRect.pivot = new Vector2(0.5f, 0f);
            bandRect.sizeDelta = new Vector2(0f, height);
            bandRect.anchoredPosition = Vector2.zero;
            bottomBand = bandObj;
        }

        Image bandImage = bandObj.AddComponent<Image>();
        bandImage.color = color;

        Canvas bandCanvas = bandObj.AddComponent<Canvas>();
        bandCanvas.overrideSorting = true;
        bandCanvas.sortingOrder = sortingOrder;
        
        // Ajouter l'ombre si configurée
        if (shadowConfig != null && shadowConfig.enabled)
        {
            Shadow shadow = bandObj.AddComponent<Shadow>();
            
            // Parser la couleur de l'ombre
            Color shadowColor = new Color(0, 0, 0, 0.25f);
            if (ColorUtility.TryParseHtmlString(shadowConfig.color, out Color parsedShadowColor))
            {
                shadowColor = parsedShadowColor;
            }
            
            shadow.effectColor = shadowColor;
            
            // Pour le bandeau du haut, l'ombre va vers le bas
            // Pour le bandeau du bas, l'ombre va vers le haut
            float yOffset = isTop ? -shadowConfig.offsetY : shadowConfig.offsetY;
            shadow.effectDistance = new Vector2(shadowConfig.offsetX, yOffset);
            
            Debug.Log($"[GameManager] Shadow ajoutée à {name}: color={shadowConfig.color}, offset=({shadowConfig.offsetX}, {yOffset})");
        }

        Debug.Log($"Bande {name} créée - Position: {(isTop ? "Haut" : "Bas")}, Hauteur: {height}px, Ordre: {sortingOrder}");
    }

    /// <summary>
    /// Met à jour les bandes selon la configuration JSON
    /// </summary>
    public void UpdateUIBands()
    {
        var uiConfig = GetUIConfig();
        if (uiConfig?.bands == null)
        {
            return;
        }

        var bandsConfig = uiConfig.bands;

        if (topBand != null) DestroyImmediate(topBand);
        if (bottomBand != null) DestroyImmediate(bottomBand);

        if (bandsConfig.showBands)
        {
            CreateUIBands();
            CreateTopBandContent(); // Recréer le contenu du bandeau
        }
    }

    /// <summary>
    /// Met à jour l'affichage des LEDs selon l'état actuel
    /// </summary>
    public void RefreshLEDDisplay()
    {
        ApplyLEDSprites();
        Debug.Log("Affichage des LEDs mis à jour");
    }

    /// <summary>
    /// Met à jour l'affichage de la question dans le bandeau
    /// </summary>
    public void RefreshQuestionDisplay()
    {
        if (topBandQuestionText != null && currentQuestionIndex < questions.Length) // ✅ CORRIGÉ
        {
            topBandQuestionText.text = questions[currentQuestionIndex].question;
            // Réappliquer le style pour s'assurer que la couleur est correcte
            ApplyQuestionStyling();
            topBandQuestionText.ForceMeshUpdate();
            Debug.Log($"[GameManager] RefreshQuestionDisplay - couleur: {topBandQuestionText.color}");
        }
    }

    /// <summary>
    /// Méthode publique pour tester l'affichage du feedback
    /// </summary>
    public void TestFeedbackMessage(bool correctAnswer)
    {
        if (questions == null || questions.Length == 0) // CHANGÉ: .Count -> .Length
        {
            Debug.LogError("Pas de questions disponibles pour le test");
            return;
        }

        lastAnswerWasCorrect = correctAnswer;
        ShowFeedback(questions[0]);

        Debug.Log($"Test feedback exécuté - Résultat simulé: {(correctAnswer ? "CORRECT" : "INCORRECT")}");
    }

    /// <summary>
    /// Méthodes publiques pour debug et configuration
    /// </summary>
    public void ForceUpdateTopBandDisplay()
    {
        if (topBand != null)
        {
            // Forcer la mise à jour du contenu du bandeau
            CreateTopBandContent();
            Debug.Log("Bandeau supérieur forcé à se mettre à jour");
        }
    }

    public void LogTopBandStatus()
    {
        Debug.Log($"TopBand existe: {topBand != null}");
        Debug.Log($"Nombre de LEDs: {leds?.Length ?? 0}");
        Debug.Log($"Question text existe: {topBandQuestionText != null}");

        if (topBandQuestionText != null)
        {
            Debug.Log($"Texte actuel: {topBandQuestionText.text}");
        }

    }

    /// <summary>
    /// Méthode de nettoyage pour redémarrer le jeu
    /// </summary>
    public void RestartGame()
    {
        // Réinitialiser les variables
        currentQuestionIndex = 0;
        score = 0;
        correctAnswers = 0;
        totalAnswers = 0;
        answeredCorrectly.Clear();
        lastAnswerWasCorrect = false;
        isProcessingAnswer = false;

        // Nettoyer les zones
        foreach (GameObject zone in targetZones)
        {
            if (zone != null) Destroy(zone);
        }
        targetZones.Clear();

        // Cacher les panneaux
        if (feedbackPanel != null) feedbackPanel.SetActive(false);
        HideHover();

        // Relancer le jeu
        SetupCurrentQuestion();
        RefreshLEDDisplay();

        Debug.Log("Jeu redémarré");
    }


    public void LoadGameFromURL(string jsonUrl)
    {
        configUrl = jsonUrl;

        // Arrêter le jeu actuel si en cours
        StopAllCoroutines();

        // Nettoyer l'état actuel
        CleanupCurrentGame();

        // ✅ FORCER LA CRÉATION DES BANDES/LEDS
        StartCoroutine(ForceReloadWithLEDs());
    }
    
    /// <summary>
    /// Charge le jeu depuis les données de l'API (via GameDataManager)
    /// </summary>
    public void LoadGameFromApiData(APIGameData apiData)
    {
        if (apiData == null)
        {
            Debug.LogError("[GameManager] ❌ Données API nulles");
            return;
        }
        
        Debug.Log("[GameManager] ✅ Chargement depuis les données API");
        Debug.Log($"[GameManager] Background: {apiData.background?.type} - {apiData.background?.url}");
        Debug.Log($"[GameManager] Questions: {apiData.questions?.Length ?? 0}");
        Debug.Log($"[GameManager] Zones: {apiData.zones?.Length ?? 0}");
        
        // Arrêter le jeu actuel si en cours
        StopAllCoroutines();
        
        // Nettoyer l'état actuel
        CleanupCurrentGame();
        
        // 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...", LoadingContext.Game);
        }
        
        // Convertir les données API vers le format interne
        List<Question> questionsList = new List<Question>();
        if (apiData.questions != null)
        {
            foreach (var apiQ in apiData.questions)
            {
                Question q = new Question
                {
                    id = apiQ.id,
                    question = apiQ.question,
                    explanation = apiQ.explanation ?? "",
                    points = 1,
                    answers = new System.Collections.Generic.List<Answer>()
                };
                
                if (apiQ.answers != null)
                {
                    for (int i = 0; i < apiQ.answers.Length; i++)
                    {
                        var apiA = apiQ.answers[i];
                        q.answers.Add(new Answer
                        {
                            id = apiA.id,
                            text = apiA.text,
                            isCorrect = apiA.is_correct,
                            zoneId = apiA.zoneId ?? $"zone{i + 1}",
                            choiceIndex = i
                        });
                    }
                }
                
                questionsList.Add(q);
            }
        }
        questions = questionsList.ToArray();
        
        Debug.Log($"[GameManager] {questions.Length} questions chargées depuis l'API");
        
        // Configurer 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($"[GameManager] 🖼️ Backdrop URL: {backdropUrl}");
            
            // Appliquer le backdrop au layout (fromApi=true pour priorité)
            if (ShootingGameLayout.Instance != null)
            {
                ShootingGameLayout.Instance.SetBackdropUrl(backdropUrl, true);
            }
        }
        else
        {
            Debug.Log("[GameManager] ℹ️ Pas de backdrop dans les données API");
        }
        
        // Configurer le BACKGROUND (vidéo/image dans la zone de jeu)
        string bgUrl = "";
        if (apiData.background != null && !string.IsNullOrEmpty(apiData.background.url))
        {
            bgUrl = apiData.background.url;
            if (!bgUrl.StartsWith("http"))
            {
                if (apiData.background.type == "video")
                {
                    bgUrl = GeneralConfigManager.Instance.GetBackgroundVideoUrl(bgUrl);
                }
                else
                {
                    bgUrl = GeneralConfigManager.Instance.GetBackgroundImageUrl(bgUrl);
                }
            }
            
            Debug.Log($"[GameManager] 🎬 Background URL: {bgUrl}");
        }
        
        // Configurer les zones depuis l'API
        if (apiData.zones != null)
        {
            Debug.Log($"[GameManager] {apiData.zones.Length} zones dans les données API");
        }
        
        // Initialiser le jeu
        yield return StartCoroutine(InitializeGameFromApiData(apiData, bgUrl));
        
        // Masquer l'écran de chargement
        if (UnifiedLoadingManager.Instance != null)
        {
            UnifiedLoadingManager.HideLoadingAfterDelay(0.5f);
        }
    }
    
    private IEnumerator InitializeGameFromApiData(APIGameData apiData, string bgUrl)
    {
        // Charger le background vidéo si disponible
        if (!string.IsNullOrEmpty(bgUrl))
        {
            // Configurer le gameConfig temporairement pour LoadBackgroundVideo
            if (gameConfig == null)
            {
                gameConfig = new GameConfigData();
            }
            if (gameConfig.background == null)
            {
                gameConfig.background = new Background();
            }
            gameConfig.background.videoUrl = bgUrl;
            yield return StartCoroutine(LoadBackgroundVideo());
        }
        
        // S'assurer que les bandes UI existent avant de créer les LEDs
        if (topBand == null)
        {
            Debug.Log("[GameManager] topBand null, création des bandes UI...");
            CreateUIBands();
        }
        
        // Créer les LEDs (seulement si topBand existe maintenant)
        if (topBand != null)
        {
            CreateLEDsInTopBand();
        }
        else
        {
            Debug.LogWarning("[GameManager] topBand toujours null après CreateUIBands, LEDs non créées");
        }
        
        // Créer les zones de tir depuis l'API
        if (apiData.zones != null)
        {
            foreach (var zone in apiData.zones)
            {
                CreateTargetZoneFromApi(zone);
            }
        }
        
        // Configurer la première question
        if (questions != null && questions.Length > 0)
        {
            SetupCurrentQuestion();
        }
        
        gameInitialized = true;
    }
    
    private void CreateTargetZoneFromApi(APIGameZone apiZone)
    {
        GameObject zoneObj = new GameObject("Zone_API");
        zoneObj.transform.SetParent(transform);
        
        // Créer le collider pour la zone
        BoxCollider2D collider = zoneObj.AddComponent<BoxCollider2D>();
        collider.size = new Vector2(apiZone.width, apiZone.height);
        
        // Positionner la zone (convertir les coordonnées)
        float screenWidth = 1920f;
        float screenHeight = 1080f;
        float xPos = (apiZone.x / screenWidth - 0.5f) * screenWidth;
        float yPos = (0.5f - apiZone.y / screenHeight) * screenHeight;
        zoneObj.transform.position = new Vector3(xPos, yPos, 0);
        
        // Ajouter aux zones cibles
        targetZones.Add(zoneObj);
        
        Debug.Log($"[GameManager] Zone créée à ({xPos}, {yPos})");
    }

    IEnumerator ForceReloadWithLEDs()
    {
        yield return StartCoroutine(LoadGameConfiguration());

        // ✅ DOUBLE-CHECK : S'assurer que les LEDs sont créées
        if (leds == null || leds.Length == 0)
        {
            Debug.LogError("🔥 LEDs manquantes après chargement, création forcée !");
            if (topBand != null && questions != null)
            {
                CreateLEDsInTopBand();
            }
        }
    }

    void CleanupCurrentGame()
    {
        // Réinitialiser les variables
        currentQuestionIndex = 0;
        score = 0;
        answeredCorrectly.Clear();

        // Nettoyer l'UI
        if (feedbackPanel != null) feedbackPanel.SetActive(false);
        HideHover();

        // Nettoyer les zones
        foreach (GameObject zone in targetZones)
        {
            if (zone != null) Destroy(zone);
        }
        targetZones.Clear();

        // Nettoyer les LEDs
        if (leds != null)
        {
            foreach (GameObject led in leds)
            {
                if (led != null) DestroyImmediate(led);
            }
        }

        // CORRECTION : Nettoyer et cacher immédiatement la vidéo de fond
        if (backgroundVideo != null)
        {
            backgroundVideo.Stop();
            backgroundVideo.url = "";
            
            // Cacher immédiatement le GameObject de la vidéo pour éviter de voir l'ancien décor
            if (backgroundVideo.gameObject != null)
            {
                backgroundVideo.gameObject.SetActive(false);
            }
            
            // Aussi cacher via la caméra si c'est un rendu caméra
            if (backgroundVideo.targetCamera != null && backgroundVideo.renderMode == VideoRenderMode.CameraNearPlane)
            {
                backgroundVideo.targetCameraAlpha = 0f;
            }
            
        }
        
        // CORRECTION : Rendre la caméra noire immédiatement pour éviter de voir l'ancien décor
        if (mainCamera != null)
        {
            mainCamera.backgroundColor = Color.black;
            mainCamera.clearFlags = CameraClearFlags.SolidColor;
        }

        // CORRECTION : Cacher temporairement le crosshair et le gun pour éviter de voir les anciens sprites
        // On les réactivera après le chargement des nouveaux sprites
        if (crosshairManager != null && crosshairManager.gameObject != null)
        {
            // Cacher seulement l'image, pas le GameObject (pour que StartCoroutine fonctionne)
            var crosshairCanvasGroup = crosshairManager.GetComponent<CanvasGroup>();
            if (crosshairCanvasGroup != null)
            {
                crosshairCanvasGroup.alpha = 0f;
            }
        }
        
        if (gunSpriteManager != null && gunSpriteManager.gameObject != null)
        {
            // Cacher seulement l'image, pas le GameObject (pour que StartCoroutine fonctionne)
            var gunCanvasGroup = gunSpriteManager.GetComponent<CanvasGroup>();
            if (gunCanvasGroup != null)
            {
                gunCanvasGroup.alpha = 0f;
            }
        }

        // CORRECTION : Nettoyer les sprites chargés
        loadedSprites.Clear();
        loadedAudioClips.Clear();

        Debug.Log("Jeu précédent nettoyé");
    }


    public bool IsCrosshairInitialized()
    {
        return crosshairManager != null;
    }

    public Vector2 GetCrosshairPosition()
    {
        return crosshairManager?.transform.position ?? Vector2.zero;
    }


    // Crée/trouve un DialoguePlayer et le prépare (UI auto + racines globales)
    // Crée/trouve un DialoguePlayer et le prépare (UI auto + racines globales + ordre d'affichage)
    private bool EnsureDialoguePlayer()
    {
        if (dialoguePlayer != null) return true;

#if UNITY_2023_1_OR_NEWER
    dialoguePlayer = FindFirstObjectByType<DialoguePlayer>();
#else
        dialoguePlayer = FindObjectOfType<DialoguePlayer>();
#endif

        if (dialoguePlayer == null)
        {
            var go = new GameObject("DialoguePlayer (Auto)");
            dialoguePlayer = go.AddComponent<DialoguePlayer>();
            dialoguePlayer.autoCreateUI = true;
        }

        try { if (!string.IsNullOrEmpty(gImageRoot)) dialoguePlayer.SetMediaRoots(gImageRoot); } catch { }
        try { if (!string.IsNullOrEmpty(gVideoRoot)) dialoguePlayer.SetVideoRoot(gVideoRoot); } catch { }
        try { dialoguePlayer.BringToFront(50000); } catch { }

        return true;
    }



    private DialogueConfig GetDialogueConfigFromGameConfig()
    {
        try
        {
            if (gameConfig == null) return null;
            var t = gameConfig.GetType();

            var f = t.GetField("dialogueConfig");
            if (f != null) return f.GetValue(gameConfig) as DialogueConfig;

            var p = t.GetProperty("dialogueConfig");
            if (p != null) return p.GetValue(gameConfig) as DialogueConfig;
        }
        catch { /* ignore */ }

        return null;
    }



    private void KillStrayDialogueUI()
    {
        // Forcer le DialoguePlayer à nettoyer proprement
        if (dialoguePlayer != null)
        {
            dialoguePlayer.ForceHideAllUI();
        }

        // NOUVEAU : Forcer le SubtitleManager à nettoyer proprement
        #if UNITY_2023_1_OR_NEWER
            SubtitleManager subtitleManager = FindFirstObjectByType<SubtitleManager>();
        #else
            SubtitleManager subtitleManager = FindObjectOfType<SubtitleManager>();
        #endif
        
        if (subtitleManager != null)
        {
            Debug.Log("[KillStrayUI] SubtitleManager trouvé - nettoyage forcé");
            subtitleManager.ForceHideAllUI();
        }

        // FIX: Nettoyer spécifiquement le DialogueBottomBanner (fallback si SubtitleManager n'existe pas)
        GameObject bottomBanner = GameObject.Find("DialogueBottomBanner");
        if (bottomBanner != null)
        {
            Debug.Log("[KillStrayUI] ✅ DialogueBottomBanner trouvé et désactivé");
            bottomBanner.SetActive(false);
            // Ou le détruire complètement :
            // Destroy(bottomBanner);
        }

        // FIX MAC : Éliminer les overlays blancs qui créent le voile
        bool isMac = Application.platform == RuntimePlatform.OSXEditor || 
                     Application.platform == RuntimePlatform.OSXPlayer;
        
        // Nettoyer tous les RawImage/Image vides dans la hiérarchie
        foreach (var ri in FindObjectsByType<UnityEngine.UI.RawImage>(FindObjectsSortMode.None))
        {
            if (ri.texture == null || !ri.gameObject.activeInHierarchy)
            {
                ri.enabled = false;
                ri.gameObject.SetActive(false);
                var c = ri.color; c.a = 0f; ri.color = c;
            }
        }

        foreach (var img in FindObjectsByType<UnityEngine.UI.Image>(FindObjectsSortMode.None))
        {
            // FIX MAC : Si l'image est blanche et dans un canvas de dialogue, la rendre transparente
            if (isMac && img.sprite == null && img.color.r > 0.8f && img.color.g > 0.8f && img.color.b > 0.8f)
            {
                Canvas parentCanvas = img.GetComponentInParent<Canvas>();
                if (parentCanvas != null && parentCanvas.sortingOrder >= 40000)
                {
                    Color c = img.color;
                    c.a = 0f; // Transparent
                    img.color = c;
                    Debug.Log($"[KillStrayUI/MAC] ✅ Overlay blanc transparent: {img.name} (sortingOrder: {parentCanvas.sortingOrder})");
                }
            }
            
            // Nettoyage général des images blanches vides
            if (img.sprite == null && img.color == Color.white)
            {
                img.enabled = false;
                img.gameObject.SetActive(false);
                var c = img.color; c.a = 0f; img.color = c;
            }
        }

        // Rechercher spécifiquement les Canvas avec sortingOrder élevé
        foreach (var canvas in FindObjectsByType<Canvas>(FindObjectsSortMode.None))
        {
            if (canvas.sortingOrder >= 50000 && canvas.name.Contains("Dialogue"))
            {
                canvas.sortingOrder = 0;
                canvas.gameObject.SetActive(false);
            }
        }
    }



    private void BringGameplayToFront()
    {
        // Remettre le crosshair au premier plan
        if (crosshairManager != null)
        {
            var canvas = crosshairManager.GetComponent<Canvas>();
            if (canvas == null) canvas = crosshairManager.gameObject.AddComponent<Canvas>();
            canvas.overrideSorting = true;
            canvas.sortingOrder = 60000; // Plus élevé que les dialogues
        }

        // Remettre le gun au premier plan
        if (gunSpriteManager != null)
        {
            var canvas = gunSpriteManager.GetComponent<Canvas>();
            if (canvas == null) canvas = gunSpriteManager.gameObject.AddComponent<Canvas>();
            canvas.overrideSorting = true;
            canvas.sortingOrder = 60000;
        }
    }

    // FIX MAC : Nettoyer le voile blanc à chaque frame (ultra-agressif)
    private void CleanMacWhiteVeilInUpdate()
    {
        // Cache pour les objets déjà corrigés (éviter de les chercher chaque frame)
        if (!System.Object.ReferenceEquals(lastCleanFrame, null) && Time.frameCount - lastCleanFrame < 30)
        {
            return; // Ne nettoyer que toutes les 30 frames
        }
        lastCleanFrame = Time.frameCount;
        
        try
        {
            // Chercher tous les images blanches dans les dialogues
            var allImages = FindObjectsByType<UnityEngine.UI.Image>(FindObjectsSortMode.None);
            
            foreach (var img in allImages)
            {
                if (img == null || img.sprite != null) continue;
                
                // Critères : blanche, opaque, dans canvas dialogue
                bool isWhite = img.color.r > 0.85f && img.color.g > 0.85f && img.color.b > 0.85f;
                bool isOpaque = img.color.a > 0.1f;
                
                if (isWhite && isOpaque)
                {
                    Canvas canvas = img.GetComponentInParent<Canvas>();
                    if (canvas != null && canvas.sortingOrder >= 40000)
                    {
                        Color c = img.color;
                        c.a = 0f;
                        img.color = c;
                        
                        if (System.Object.ReferenceEquals(lastVeilCleanLog, img))
                        {
                            return; // Éviter spam de logs
                        }
                        
                        Debug.Log($"[MAC VOILE] ✅ Voile blanc nettoyé: {img.name} (canvas: {canvas.name})");
                        lastVeilCleanLog = img;
                    }
                }
            }
        }
        catch (System.Exception ex)
        {
            Debug.LogError($"[MAC VOILE] Erreur: {ex.Message}");
        }
    }
    
    /// <summary>
    /// Retour à la scène d'origine (Map ou Menu) lors de l'appui sur Escape
    /// </summary>
    void ReturnToOriginScene()
    {
        string returnToScene = PlayerPrefs.GetString("ReturnToScene", "menu");
        
        if (returnToScene.ToLower() == "map")
        {
            LevelManager levelManager = FindFirstObjectByType<LevelManager>();
            if (levelManager != null)
            {
                levelManager.ReturnToMap();
            }
            else
            {
                UnityEngine.SceneManagement.SceneManager.LoadScene("Map");
            }
        }
        else
        {
            UnityEngine.SceneManagement.SceneManager.LoadScene(returnToScene);
        }
    }
    
    private int lastCleanFrame = -100;
    private UnityEngine.UI.Image lastVeilCleanLog = null;

    #region Méthodes utilitaires pour les boutons stylés
    
    /// <summary>
    /// Convertit une couleur hexadécimale en Color Unity
    /// </summary>
    private Color HexToColor(string hex)
    {
        if (string.IsNullOrEmpty(hex)) return Color.white;
        
        hex = hex.TrimStart('#');
        
        float r = 1f, g = 1f, b = 1f, a = 1f;
        
        if (hex.Length >= 6)
        {
            r = int.Parse(hex.Substring(0, 2), System.Globalization.NumberStyles.HexNumber) / 255f;
            g = int.Parse(hex.Substring(2, 2), System.Globalization.NumberStyles.HexNumber) / 255f;
            b = int.Parse(hex.Substring(4, 2), System.Globalization.NumberStyles.HexNumber) / 255f;
        }
        if (hex.Length >= 8)
        {
            a = int.Parse(hex.Substring(6, 2), System.Globalization.NumberStyles.HexNumber) / 255f;
        }
        
        return new Color(r, g, b, a);
    }
    
    /// <summary>
    /// Crée un sprite avec dégradé et bordure pour les boutons stylés
    /// </summary>
    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;
        
        Color[] pixels = new Color[width * height];
        
        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                // Vérifier si on est dans les coins arrondis
                bool isInRoundedRect = IsPointInRoundedRect(x, y, width, height, radius);
                
                if (!isInRoundedRect)
                {
                    pixels[y * width + x] = Color.clear;
                    continue;
                }
                
                // Vérifier si on est dans la bordure
                bool isInBorder = borderWidth > 0 && !IsPointInRoundedRect(x, y, width, height, radius - borderWidth);
                
                if (isInBorder)
                {
                    pixels[y * width + x] = borderColor;
                }
                else
                {
                    // Dégradé vertical
                    float t = (float)y / height;
                    pixels[y * width + x] = Color.Lerp(endColor, startColor, t);
                }
            }
        }
        
        texture.SetPixels(pixels);
        texture.Apply();
        
        return Sprite.Create(texture, new Rect(0, 0, width, height), new Vector2(0.5f, 0.5f), 100f);
    }
    
    /// <summary>
    /// Vérifie si un point est dans un rectangle arrondi
    /// </summary>
    private bool IsPointInRoundedRect(int x, int y, int width, int height, float radius)
    {
        // Limiter le radius
        radius = Mathf.Min(radius, Mathf.Min(width, height) / 2f);
        
        // Coins
        if (x < radius && y < radius)
        {
            // Coin bas-gauche
            return Vector2.Distance(new Vector2(x, y), new Vector2(radius, radius)) <= radius;
        }
        if (x >= width - radius && y < radius)
        {
            // Coin bas-droit
            return Vector2.Distance(new Vector2(x, y), new Vector2(width - radius - 1, radius)) <= radius;
        }
        if (x < radius && y >= height - radius)
        {
            // Coin haut-gauche
            return Vector2.Distance(new Vector2(x, y), new Vector2(radius, height - radius - 1)) <= radius;
        }
        if (x >= width - radius && y >= height - radius)
        {
            // Coin haut-droit
            return Vector2.Distance(new Vector2(x, y), new Vector2(width - radius - 1, height - radius - 1)) <= radius;
        }
        
        return true;
    }
    
    #endregion
}