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 = "https://unjoursansassurance.studioplc.tech/demo_assets/json/quete02_shooting_zombies.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;
    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 = true;

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

    [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);
        if (bottomBand) bottomBand.SetActive(visible);
        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
    }


    // 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
    private IEnumerator PlayDialogueUrl(string url, DialogueConfig overrideConfig)
    {
        if (dialoguePlayer == null || string.IsNullOrEmpty(url))
            yield break;

        bool done = false;
        DialogueConfig cfg = overrideConfig ?? dConfig ?? GetDialogueConfigFromGameConfig() ?? gDialogueDefault;

        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))
        {
            www.SetRequestHeader("Cache-Control", "no-cache, no-store, must-revalidate");
            www.SetRequestHeader("Pragma", "no-cache");
            www.SetRequestHeader("Expires", "0");

            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;

    // 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>();




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




    void Start()
    {
        Debug.Log("[GameManager] Start - Version simplifiée");

        if (targetZones == null)
        {
            targetZones = new List<GameObject>();
        }

        // Configurer le hover panel au démarrage
        if (hoverPanel != null)
        {
            hoverPanel.SetActive(false);
        }

        // Si pas de hover UI assigné, le créer automatiquement
        if (hoverPanel == null || hoverText == null)
        {
            SetupHoverUIAutomatically();
        }

        // Démarrer directement le chargement du jeu
        StartCoroutine(LoadGameDirectly());
    }



    IEnumerator LoadGameDirectly()
    {
        Debug.Log("Chargement direct du jeu...");

        // Afficher l'écran de chargement
        if (UnifiedLoadingManager.Instance != null)
        {
            UnifiedLoadingManager.ShowLoading("Chargement de la configuration du jeu...", LoadingContext.Game);
        }

        // NOUVEAU : Utiliser l'URL depuis PlayerPrefs au lieu de la variable locale
        string gameConfigUrl = PlayerPrefs.GetString("GameConfigUrl", configUrl);

        if (string.IsNullOrEmpty(gameConfigUrl))
        {
            Debug.LogError("GameConfigUrl introuvable dans PlayerPrefs ET configUrl est vide !");
            if (UnifiedLoadingManager.Instance != null)
            {
                UnifiedLoadingManager.HideLoading();
            }
            yield break;
        }

        Debug.Log($"[GameManager] Chargement depuis : {gameConfigUrl}");

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

            if (request.result == UnityWebRequest.Result.Success)
            {
                Debug.Log($"JSON reçu: {request.downloadHandler.text.Length} caractères");

                GameConfigWrapper wrapper = JsonUtility.FromJson<GameConfigWrapper>(request.downloadHandler.text);
                if (wrapper != null && wrapper.gameConfig != null)
                {
                    gameConfig = wrapper.gameConfig;
                    // NOUVEAU : Logs de debug
                    Debug.Log($"[LoadGameDirectly] gameConfig chargé : {gameConfig != null}");
                    Debug.Log($"[LoadGameDirectly] uiConfig : {gameConfig.uiConfig != null}");
                    Debug.Log($"[LoadGameDirectly] ledConfig : {gameConfig.uiConfig?.ledConfig != null}");
                }
                else
                {
                    Debug.LogError("Structure JSON invalide - wrapper ou gameConfig null");
                    yield break;
                }


                Debug.Log("Configuration parsée avec succès");

                yield return StartCoroutine(LoadQuestionsFromUrl());

                if (questions == null || questions.Length == 0)
                {
                    Debug.LogError("Aucune question chargée - arrêt");
                    yield break;
                }

                Debug.Log($"{questions.Length} questions chargées");

                yield return StartCoroutine(LoadAllAssets());

                //InitializeGame();

                Debug.Log("Jeu chargé et initialisé avec succès");
            }
            else
            {
                Debug.LogError($"Erreur chargement config: {request.error}");
            }
        }
    }







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

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

        using (UnityWebRequest www = UnityWebRequest.Get(gameConfigUrl))
        {
            www.SetRequestHeader("Cache-Control", "no-cache, no-store, must-revalidate");
            www.SetRequestHeader("Pragma", "no-cache");
            www.SetRequestHeader("Expires", "0");

            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");
            }
        }
    }



    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()
    {

        string questionsUrl = gameConfig.questionsUrl;

        // AJOUTER CES LOGS
        Debug.Log($"🔍 gameConfig null? {gameConfig == null}");
        Debug.Log($"🔍 gameConfig.questionsUrl (brut): '{questionsUrl}'");
        Debug.Log($"🔍 configUrl principal: '{configUrl}'");

        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))
            {
                questionsUrl = configUrl.Replace(".json", "_questions.json");
                Debug.Log($"🔄 URL questions générée: '{questionsUrl}'");
            }
            else
            {
                Debug.LogError("🔥 configUrl également vide !");
                yield break;
            }
        }
        else
        {
            // Construire l'URL complète via GeneralConfigManager si ce n'est pas déjà une URL complète
            if (!questionsUrl.StartsWith("http://") && !questionsUrl.StartsWith("https://") && !questionsUrl.StartsWith("file:///"))
            {
                questionsUrl = GeneralConfigManager.Instance.GetQuestionsUrl(questionsUrl);
                Debug.Log($"🔄 URL questions transformée via GetQuestionsUrl: '{questionsUrl}'");
            }
        }

        Debug.Log($"Chargement des questions depuis : {questionsUrl}");



        using (UnityWebRequest www = UnityWebRequest.Get(questionsUrl))
        {
            // Ajouter des headers pour éviter le cache
            www.SetRequestHeader("Cache-Control", "no-cache, no-store, must-revalidate");
            www.SetRequestHeader("Pragma", "no-cache");
            www.SetRequestHeader("Expires", "0");

            yield return www.SendWebRequest();

            if (www.result != UnityWebRequest.Result.Success)
            {
                Debug.LogError($"Erreur de chargement des questions : {www.error}");
                Debug.LogError($"URL utilisée : {questionsUrl}");
                Debug.LogError($"Code de réponse : {www.responseCode}");


            }

            try
            {
                string jsonData = www.downloadHandler.text;
                Debug.Log($"Données questions reçues ({jsonData.Length} caractères)");

                // Parser le JSON des questions
                QuestionsWrapper wrapper = JsonUtility.FromJson<QuestionsWrapper>(jsonData);

                if (wrapper?.questions != null && wrapper.questions.Length > 0)
                {
                    questions = wrapper.questions;
                    successConfig = wrapper.successConfig ?? new SuccessConfig(); // Fallback si pas de config
                    Debug.Log($"{questions.Length} questions chargées avec succès");
                    Debug.Log($"Configuration de réussite chargée:");
                    Debug.Log($"  - Description: {successConfig.description}");
                    Debug.Log($"  - usePercentage: {successConfig.usePercentage}");
                    Debug.Log($"  - minCorrectAnswers: {successConfig.minCorrectAnswers}");
                    Debug.Log($"  - minCorrectPercentage: {successConfig.minCorrectPercentage}");

                    // Validation des questions
                    ValidateQuestions();
                }
                else
                {
                    Debug.LogError("Aucune question trouvée dans le JSON ou structure invalide");
                    Debug.LogError($"Début du JSON : {jsonData.Substring(0, Mathf.Min(200, jsonData.Length))}...");
                }
            }
            catch (System.Exception e)
            {
                Debug.LogError($"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++)
            {
                yield return StartCoroutine(LoadSprite($"impact_frame_{i}", gameConfig.assets.impactEffect.frames[i]));
                Debug.Log($"Frame {i} chargée depuis: {gameConfig.assets.impactEffect.frames[i]}");
            }
        }

        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;
        }

        Debug.Log($"✅ {questions.Length} questions chargées avec succès");


        Debug.Log($"🔍 URL impact: '{gameConfig.assets.impact}'");
        Debug.Log($"🔍 URL ledOff: '{gameConfig.assets.ledOff}'");
        Debug.Log($"🔍 URL crosshair: '{gameConfig.assets.crosshair}'");
        Debug.Log($"🔍 URL gun: '{gameConfig.assets.gun}'");


        // 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));

        yield return StartCoroutine(LoadAudioClip("impact", gameConfig.sounds.impact));
        yield return StartCoroutine(LoadAudioClip("success", gameConfig.sounds.success));
        yield return StartCoroutine(LoadAudioClip("fail", gameConfig.sounds.fail));

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

        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);
                crosshairManager.LoadCrosshairFromURL(crosshairUrl);

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

                Debug.Log("Chargement du viseur depuis : " + crosshairUrl);
            }
            else
            {
                Debug.LogWarning("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);
                Debug.Log("Chargement du pistolet depuis : " + gunUrl);

                ApplyGunConfigFromJSON();
                Debug.Log("ApplyGunConfigFromJSON() appelée");
            }
            else
            {
                Debug.LogWarning("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 = gameConfig.feedbackMessages;

        // 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)
    {
        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;
                Debug.Log($"Sprite {key} chargé avec succès");
            }
            else
            {
                Debug.LogError($"Erreur de chargement du sprite {key} : {www.error}");
            }
        }
    }

    IEnumerator LoadAudioClip(string key, string url)
    {
        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;
                Debug.Log($"Audio {key} chargé avec succès");
            }
            else
            {
                Debug.LogError($"Erreur de chargement de l'audio {key} : {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)
        {
            string videoUrl;
            if (!string.IsNullOrEmpty(customVideoUrl))
            {
                videoUrl = customVideoUrl;
            }
            else
            {
                // Construire l'URL complète via GetBackgroundVideoUrl (les vidéos de fond sont dans /videos/)
                string videoFileName = gameConfig.background.videoUrl;
                videoUrl = GeneralConfigManager.Instance.GetBackgroundVideoUrl(videoFileName);
            }

            backgroundVideo.url = videoUrl;
            backgroundVideo.isLooping = true;
            backgroundVideo.Play();

            Debug.Log("Vidéo de fond configurée avec URL: " + videoUrl);
        }
        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()
    {
        // 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);
            
            Debug.Log($"[GameManager] ✅ Résolution de référence configurée: {refWidth}x{refHeight}");
            Debug.Log($"[GameManager] Résolution écran actuelle: {Screen.width}x{Screen.height}");
            
            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()
    {
        Debug.Log("🔥 === DÉBUT CreateLEDsInTopBand ===");
        Debug.Log($"🔥 gameConfig null? {gameConfig == null}");
        Debug.Log($"🔥 questions null? {questions == null}");
        Debug.Log($"🔥 topBand null? {topBand == null}");

        if (gameConfig == null || questions == null)
        {
            Debug.LogError("🔥 ERREUR : gameConfig ou questions null !");
            Debug.LogError($"🔥 gameConfig status: {(gameConfig != null ? "OK" : "NULL")}");
            Debug.LogError($"🔥 questions status: {(questions != null ? $"OK ({questions.Length} questions)" : "NULL")}");
            return;
        }

        if (topBand == null)
        {
            Debug.LogError("🔥 ERREUR : topBand null !");
            return;
        }

        // Nettoyer les LEDs existantes
        if (leds != null)
        {
            Debug.Log($"🔥 Nettoyage de {leds.Length} LEDs existantes");
            foreach (var led in leds)
            {
                if (led != null) DestroyImmediate(led);
            }
        }

        int totalQuestions = questions.Length;
        leds = new GameObject[totalQuestions];
        Debug.Log($"🔥 Création de {totalQuestions} LEDs");

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

        if (gameConfig?.uiConfig?.ledConfig != null)
        {
            var ledConfig = gameConfig.uiConfig.ledConfig;
            ledSize = ledConfig.ledSize;
            ledSpacing = ledConfig.ledSpacing;
            startX = ledConfig.marginLeft;
            Debug.Log($"🔥 Config LED JSON appliquée: size={ledSize}, spacing={ledSpacing}, startX={startX}");
        }
        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;
                Debug.Log($"🔥 Config LED depuis general-config defaults: size={ledSize}, spacing={ledSpacing}, startX={startX}");
            }
            else
            {
                Debug.LogWarning("🔥 Pas de config LED, utilisation valeurs hardcodées");
            }
        }

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

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

            // 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.sizeDelta = new Vector2(ledSize, ledSize);
            ledRect.anchoredPosition = new Vector2(startX + (i * ledSpacing), 0f);

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

            leds[i] = ledObj;
            Debug.Log($"🔥 LED {i} créée à position ({startX + (i * ledSpacing)}, 0)");
        }

        // Appliquer immédiatement un sprite OFF pour rendre les LEDs visibles
        if (loadedSprites.ContainsKey("ledOff") && loadedSprites["ledOff"] != null)
        {
            Debug.Log("🔥 Application sprites LED OFF depuis cache");
            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
        {
            Debug.LogWarning("🔥 Sprites LED OFF non trouvés, utilisation couleur par défaut");
            for (int i = 0; i < leds.Length; i++)
            {
                var img = leds[i].GetComponent<Image>();
                if (img != null)
                {
                    img.sprite = null;
                    img.color = Color.gray;
                }
            }
        }

        Debug.Log($"🔥 === FIN CreateLEDsInTopBand - {totalQuestions} LEDs créées ===");
        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...";
        topBandQuestionText.fontSize = 42f;
        topBandQuestionText.color = new Color(0.39f, 0.28f, 0.49f);
        topBandQuestionText.alignment = TextAlignmentOptions.Center;
        topBandQuestionText.fontStyle = FontStyles.Bold;

        ApplyQuestionStyling();

        Debug.Log("Question créée avec centrage absolu");
    }

    /// <summary>
    /// Applique le style de la question depuis la configuration JSON
    /// </summary>
    void ApplyQuestionStyling()
    {
        if (topBandQuestionText == null) return;

        if (gameConfig?.uiConfig?.questionDisplay != null)
        {
            var questionConfig = gameConfig.uiConfig.questionDisplay;

            topBandQuestionText.fontSize = questionConfig.fontSize;

            if (ColorUtility.TryParseHtmlString(questionConfig.fontColor, out Color textColor))
            {
                topBandQuestionText.color = textColor;
            }

            topBandQuestionText.fontStyle = questionConfig.fontBold ? FontStyles.Bold : FontStyles.Normal;

            Debug.Log("Style appliqué au texte manuel depuis JSON");
        }
        else
        {
            // Si pas de config dans le JSON, utiliser les defaults depuis general-config
            var defaultUIConfig = GeneralConfigManager.Instance?.GetDefaultUIConfig();
            if (defaultUIConfig != null && defaultUIConfig.questionDisplay != null)
            {
                topBandQuestionText.fontSize = defaultUIConfig.questionDisplay.fontSize;

                if (ColorUtility.TryParseHtmlString(defaultUIConfig.questionDisplay.fontColor, out Color textColor))
                {
                    topBandQuestionText.color = textColor;
                }

                topBandQuestionText.fontStyle = defaultUIConfig.questionDisplay.fontBold ? FontStyles.Bold : FontStyles.Normal;

                Debug.Log("Style appliqué au texte depuis general-config defaults");
            }
        }
    }

    /// <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;

        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 = Color.green;
                            }
                        }
                        else
                        {
                            if (loadedSprites.ContainsKey("ledRed") && loadedSprites["ledRed"] != null)
                            {
                                ledImage.sprite = loadedSprites["ledRed"];
                                ledImage.color = Color.white;
                            }
                            else
                            {
                                ledImage.sprite = null;
                                ledImage.color = Color.red;
                            }
                        }
                    }
                    // É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 = Color.gray;
                        }
                    }
                }
            }
        }
    }

    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;
        }

        // 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("=== CRÉATION DES ZONES ===");
        Debug.Log($"Question: {question.question}");
        Debug.Log($"Nombre de réponses: {question.answers.Count}");
        Debug.Log($"answerCount dans JSON: {question.answerCount}");

        // 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($"❌ TEMPLATE NULL !");
            return;
        }

        Debug.Log($"✅ Template trouvé avec {template.Count} zones");

        // 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

            Debug.Log($"Réponse {i}: '{answer.text}' -> choiceIndex={answer.choiceIndex} -> zoneIndex={zoneIndex}");

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

            TargetZoneData zoneData = template[zoneIndex];
            Debug.Log($"Zone {zoneIndex}: ({zoneData.x}, {zoneData.y}, {zoneData.width}, {zoneData.height})");

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

            AnswerWithZone enrichedAnswer = new AnswerWithZone
            {
                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 - ICI dans la boucle où les variables existent
            // Utiliser le flag global de debug zones
            var __cfg2 = GeneralConfigManager.Instance != null ? GeneralConfigManager.Instance.GetConfig() : null;
            bool globalShowDebug = __cfg2 != null && __cfg2.debug != null && __cfg2.debug.showDebugZones;

            if (showDebugZones && globalShowDebug)
            {
                Debug.Log($"[DEBUG] Création zone visuelle pour '{answer.text}' - showDebugZones={showDebugZones}");
                CreateVisualDebugZone(zoneObj, enrichedAnswer.zone, answer);
            }
            else
            {
                // Log réduit, uniquement si logs globaux activés
                var __cfg3 = GeneralConfigManager.Instance != null ? GeneralConfigManager.Instance.GetConfig() : null;
                if (__cfg3 == null || __cfg3.debug == null || __cfg3.debug.enableDebugLogs)
                {
                    Debug.Log("[DEBUG] showDebugZones désactivé - pas de zones visuelles");
                }
            }

            targetZones.Add(zoneObj);
        }

        Debug.Log($"=== FIN: {targetZones.Count} zones créées ===");
    }

    List<TargetZoneData> GetZoneTemplate(int answerCount)
    {
        Debug.Log($"🔍 GetZoneTemplate appelé avec answerCount={answerCount}");

        if (gameConfig?.zoneTemplates == null)
        {
            Debug.LogError("❌ gameConfig.zoneTemplates est NULL");
            return gameConfig?.targetZones;
        }

        switch (answerCount)
        {
            case 2:
                Debug.Log($"Template 2 choix: {gameConfig.zoneTemplates.twoChoices?.Count ?? 0} zones");
                return gameConfig.zoneTemplates.twoChoices;
            case 3:
                Debug.Log($"Template 3 choix: {gameConfig.zoneTemplates.threeChoices?.Count ?? 0} zones");
                return gameConfig.zoneTemplates.threeChoices;
            case 4:
                Debug.Log($"Template 4 choix: {gameConfig.zoneTemplates.fourChoices?.Count ?? 0} zones");
                return gameConfig.zoneTemplates.fourChoices;
            default:
                Debug.LogError($"❌ Pas de template pour {answerCount} réponses");
                return gameConfig?.targetZones; // Fallback
        }
    }

    void CreateVisualDebugZone(GameObject zoneObj, Zone zoneData, Answer answer)
    {
        float screenCenterX = zoneData.x + (zoneData.width / 2f);
        float screenCenterY = Screen.height - (zoneData.y + (zoneData.height / 2f));

        Vector3 worldPos = mainCamera.ScreenToWorldPoint(new Vector3(screenCenterX, screenCenterY, mainCamera.nearClipPlane + 1f));
        worldPos.z = 0;
        zoneObj.transform.position = worldPos;

        float cameraHeight = mainCamera.orthographicSize * 2f;
        float cameraWidth = cameraHeight * mainCamera.aspect;

        float worldWidth = (zoneData.width / (float)Screen.width) * cameraWidth;
        float worldHeight = (zoneData.height / (float)Screen.height) * cameraHeight;

        SpriteRenderer visual = zoneObj.AddComponent<SpriteRenderer>();

        // Éviter allocations répétées: utiliser une petite texture statique en cache
        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 * 100 + x] = borderColor;
                }
            }
        }

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

        visual.sprite = Sprite.Create(tex, new Rect(0, 0, 100, 100), new Vector2(0.5f, 0.5f));
        visual.color = Color.white;
        visual.sortingOrder = 100;

        zoneObj.transform.localScale = new Vector3(worldWidth, worldHeight, 1);

        Debug.Log($"Zone visuelle créée pour {answer.text}: pos={worldPos}, scale=({worldWidth:F2},{worldHeight:F2})");
    }


    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()
    {
        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;
        }

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



    void Update()
    {
        // Gérer la touche ESC pour retourner au menu (Nouveau Input System uniquement)
        if (Keyboard.current != null && Keyboard.current.escapeKey.wasPressedThisFrame)
        {
            Debug.Log("[GameManager] 🔙 Touche ESC détectée - Retour au menu");
            UnityEngine.SceneManagement.SceneManager.LoadScene("menu");
            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();
        HandleCrosshairResizeControls();

        // Dans GameManager.cs, dans Update()
        if (Keyboard.current != null && Keyboard.current.cKey.wasPressedThisFrame)
        {
            Debug.Log("=== NETTOYAGE FORCÉ ===");
            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 && Keyboard.current.f1Key.wasPressedThisFrame)
        {
            if (questions != null && questions.Length > 0) // CHANGÉ: .Count -> .Length
            {
                lastAnswerWasCorrect = true;
                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;
                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");
        }
    }

    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é");
        }

        isProcessingAnswer = false;

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

    GameObject ShowImpactEffect(Vector2 screenPosition)
    {
        GameObject impact = null;

        if (impactEffectPrefab != null)
        {
            Vector3 worldPos = mainCamera.ScreenToWorldPoint(new Vector3(screenPosition.x, screenPosition.y, 10f));
            worldPos.z = 0;

            impact = Instantiate(impactEffectPrefab, worldPos, Quaternion.identity);

            SpriteRenderer sr = impact.GetComponent<SpriteRenderer>();
            if (sr != null)
            {
                sr.sortingOrder = 1000;

                // Vérifier s'il y a une configuration d'animation
                if (gameConfig.assets.impactEffect != null && gameConfig.assets.impactEffect.type == "animated")
                {
                    // Démarrer l'animation frame par frame
                    StartCoroutine(PlayAnimatedImpact(impact, gameConfig.assets.impactEffect));
                    Debug.Log($"Effet d'impact animé créé à {worldPos} avec {gameConfig.assets.impactEffect.frames.Count} frames");
                }
                else
                {
                    // Fallback vers image statique
                    if (loadedSprites.ContainsKey("impact"))
                    {
                        sr.sprite = loadedSprites["impact"];
                    }

                    // Appliquer la taille pour l'image statique
                    if (gameConfig.assets.impactEffect != null)
                    {
                        var config = gameConfig.assets.impactEffect;
                        if (config.width > 0 && config.height > 0)
                        {
                            float pixelsPerUnit = sr.sprite?.pixelsPerUnit ?? 100f;
                            Vector3 fixedScale = new Vector3(config.width / pixelsPerUnit, config.height / pixelsPerUnit, 1f);
                            impact.transform.localScale = fixedScale;
                            Debug.Log($"Taille fixe appliquée: {config.width}x{config.height}px -> scale {fixedScale}");
                        }
                        else if (config.scale != 1f)
                        {
                            impact.transform.localScale = Vector3.one * config.scale;
                            Debug.Log($"Scale multiplier appliqué: {config.scale}");
                        }
                    }

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

        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;

        SpriteRenderer sr = impactObj.GetComponent<SpriteRenderer>();
        if (sr == null) yield break;

        float frameTime = 1f / config.frameRate;

        // Jouer chaque frame
        for (int i = 0; i < config.frames.Count; i++)
        {
            if (impactObj == null) yield break; // Sécurité si l'objet est détruit

            if (loadedSprites.ContainsKey($"impact_frame_{i}"))
            {
                sr.sprite = loadedSprites[$"impact_frame_{i}"];
            }

            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;
        Debug.Log($"Résultat stocké pour feedback: {lastAnswerWasCorrect}");

        // Compter les réponses
        totalAnswers++;
        if (answer.isCorrect)
        {
            correctAnswers++;
            Debug.Log("✅ BONNE RÉPONSE!");
            OnCorrectAnswer();
        }
        else
        {
            Debug.Log("❌ MAUVAISE RÉPONSE!");
            OnWrongAnswer();
        }

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

        ShowFeedback(currentQuestion);
    }

    /// <summary>
    /// Vérifie si le joueur a réussi selon la configuration de réussite
    /// </summary>
    bool CheckSuccess()
    {
        Debug.Log($"=== VÉRIFICATION RÉUSSITE ===");
        Debug.Log($"correctAnswers: {correctAnswers}");
        Debug.Log($"totalAnswers: {totalAnswers}");
        Debug.Log($"successConfig: {(successConfig != null ? "PRÉSENTE" : "NULL")}");

        if (successConfig == null)
        {
            Debug.LogWarning("Configuration de réussite manquante, utilisation du seuil par défaut");
            return correctAnswers >= 1; // Seuil par défaut
        }

        Debug.Log($"successConfig.usePercentage: {successConfig.usePercentage}");
        Debug.Log($"successConfig.minCorrectAnswers: {successConfig.minCorrectAnswers}");
        Debug.Log($"successConfig.minCorrectPercentage: {successConfig.minCorrectPercentage}");
        Debug.Log($"successConfig.description: {successConfig.description}");

        if (successConfig.usePercentage)
        {
            float percentage = (float)correctAnswers / totalAnswers * 100f;
            bool success = percentage >= successConfig.minCorrectPercentage;
            Debug.Log($"Vérification réussite (pourcentage): {correctAnswers}/{totalAnswers} = {percentage:F1}% >= {successConfig.minCorrectPercentage}% = {success}");
            return success;
        }
        else
        {
            bool success = correctAnswers >= successConfig.minCorrectAnswers;
            Debug.Log($"Vérification réussite (nombre): {correctAnswers}/{totalAnswers} >= {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;

        // 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>();

        // Position en bas d'écran, centré
        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, 15f);   // 15px du bas de l'écran
        panelRect.sizeDelta = new Vector2(0f, 80f);          // Hauteur 80px, largeur auto (suit les anchors)

        // Image de fond du panneau
        fixedHoverBackground = fixedHoverPanel.AddComponent<Image>();
        fixedHoverBackground.color = new Color(0.96f, 0.93f, 0.90f, 1f); // Couleur beige selon votre JSON
        fixedHoverBackground.raycastTarget = false; // Pas d'interaction

        // 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 = "";
        fixedHoverText.fontSize = 36f;
        fixedHoverText.color = new Color(0.392f, 0.278f, 0.498f, 1f); // Couleur violette de votre config
        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}");

        // Test temporaire : forcer une couleur visible pour debug
        fixedHoverBackground.color = new Color(1f, 0f, 0f, 0.8f); // Rouge semi-transparent
        fixedHoverText.text = "HOVER TEST - SI VOUS VOYEZ CECI, ÇA FONCTIONNE";

        //Debug.Log("[HOVER] Panel configuré avec test rouge");
    }

    /// <summary>
    /// Applique la configuration JSON au panneau de hover
    /// </summary>
    void ApplyHoverConfiguration()
    {
        if (gameConfig?.uiConfig == null) return; // CHANGÉ: supprimé .gameConfig

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

        if (fixedHoverText != null)
        {
            fixedHoverText.fontSize = uiConfig.hoverTextSize;

            if (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)
            {
                panelRect.sizeDelta = uiConfig.hoverPanelSize.ToVector2();

                if (uiConfig.useCustomPosition)
                {
                    panelRect.anchoredPosition = uiConfig.customPosition;
                }
            }
        }

        if (fixedHoverBackground != null)
        {
            if (ColorUtility.TryParseHtmlString(uiConfig.hoverBackgroundColor, out Color backgroundColor))
            {
                backgroundColor.a = uiConfig.hoverBackgroundAlpha;
                fixedHoverBackground.color = backgroundColor;
            }
        }
    }

    /// <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>();

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

        // 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>();

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

        // 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");
    }

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

        Debug.Log("=== DEBUG FEEDBACK PANEL ===");

        if (feedbackPanel != null)
        {
            Debug.Log($"FeedbackPanel enfants: {feedbackPanel.transform.childCount}");
            for (int i = 0; i < feedbackPanel.transform.childCount; i++)
            {
                Transform child = feedbackPanel.transform.GetChild(i);
                Debug.Log($"Enfant {i}: {child.name} - Actif: {child.gameObject.activeInHierarchy}");

                Image img = child.GetComponent<Image>();
                if (img != null)
                {
                    Debug.Log($"  -> Image couleur: {img.color}");
                }
            }

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

        Debug.Log($"=== AFFICHAGE FEEDBACK ===");
        Debug.Log($"Question: {question.question}");
        Debug.Log($"Résultat: {(lastAnswerWasCorrect ? "CORRECT" : "INCORRECT")}");
        Debug.Log($"Explication: {question.explanation}");

        // Appliquer la taille personnalisée du panneau
        ApplyFeedbackPanelSize();

        string feedbackContent = "";

        if (gameConfig?.feedbackMessages != null) // CHANGÉ: supprimé .gameConfig
        {
            // MODIFIÉ : Ne plus inclure la question, juste le message de résultat + explication
            // Si pas de feedbackMessages dans le JSON, utiliser les defaults
            var feedbackMessages = gameConfig.feedbackMessages;
            if (feedbackMessages == null)
            {
                var defaultFeedback = GeneralConfigManager.Instance?.GetDefaultFeedbackMessages();
                if (defaultFeedback != null)
                {
                    Debug.Log("Utilisation des messages feedback depuis general-config defaults");
                    // Note: Pour une utilisation complète, il faudrait adapter GameConfigUtils.BuildFeedbackMessage
                    // pour accepter aussi des DefaultFeedbackMessages ou convertir la structure
                }
            }

            feedbackContent = GameConfigUtils.BuildFeedbackMessage(gameConfig, lastAnswerWasCorrect, question.explanation);
            Debug.Log($"Message feedback généré avec GameConfigUtils: {feedbackContent.Length} caractères");
        }
        else
        {
            feedbackContent = question.explanation;
            Debug.LogWarning("Pas de configuration feedbackMessages trouvée, utilisation de l'explication simple");
        }

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

        // Appliquer le style du texte d'explication
        ApplyFeedbackTextStyling();

        Debug.Log($"Feedback affiché: '{feedbackContent.Substring(0, System.Math.Min(100, feedbackContent.Length))}...'");

        // NETTOYAGE ET APPLICATION DE L'IMAGE DE FOND
        ApplyFeedbackPanelStyling();

        // Appliquer l'image de fond si disponible
        ApplyFeedbackBackgroundImage();

        // SYSTÈME DE CLIC SIMPLIFIÉ
        SetupFeedbackClick();

        Debug.Log("=== FIN AFFICHAGE FEEDBACK ===");
    }


    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()
    {
        // Supprimer tous les anciens boutons/composants de clic
        Button[] buttons = feedbackPanel.GetComponentsInChildren<Button>();
        foreach (Button btn in buttons)
        {
            DestroyImmediate(btn);
        }

        // Ajouter un collider invisible pour détecter les clics
        if (feedbackPanel.GetComponent<Button>() == null)
        {
            Button clickHandler = feedbackPanel.AddComponent<Button>();
            clickHandler.transition = Selectable.Transition.None;

            // S'assurer que le bouton couvre tout le panel
            Image buttonImage = feedbackPanel.GetComponent<Image>();
            if (buttonImage == null)
            {
                buttonImage = feedbackPanel.AddComponent<Image>();
                buttonImage.color = Color.clear; // Invisible mais cliquable
            }

            clickHandler.targetGraphic = buttonImage;
            clickHandler.onClick.RemoveAllListeners();
            clickHandler.onClick.AddListener(() =>
            {
                Debug.Log("Clic détecté sur le panel feedback");
                OnFeedbackClick();
            });

            Debug.Log("Système de clic configuré sur tout le panel");
        }
    }


    void ApplyFeedbackPanelSize()
    {
        if (gameConfig?.feedbackMessages == null || !gameConfig.feedbackMessages.useCustomPanelSize) // CHANGÉ: supprimé .gameConfig
        {
            return;
        }

        var feedbackConfig = gameConfig.feedbackMessages; // CHANGÉ: supprimé .gameConfig
        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 (gameConfig?.feedbackMessages == null || feedbackText == null) // CHANGÉ: supprimé .gameConfig
        {
            return;
        }

        var feedbackConfig = gameConfig.feedbackMessages; // CHANGÉ: supprimé .gameConfig

        // 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;
        }

        // NOUVEAU : Centrer le texte si demandé
        if (feedbackConfig.centerTextInPanel)
        {
            feedbackText.alignment = TextAlignmentOptions.Center;

            // Centrer aussi le RectTransform du texte
            RectTransform textRect = feedbackText.GetComponent<RectTransform>();
            if (textRect != null)
            {
                textRect.anchorMin = Vector2.zero;
                textRect.anchorMax = Vector2.one;
                textRect.offsetMin = new Vector2(20f, 20f);
                textRect.offsetMax = new Vector2(-20f, -20f);
            }

            Debug.Log("Texte centré dans le panel");
        }

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

    void ApplyFeedbackBackgroundImage()
    {
        if (gameConfig?.feedbackMessages == null || // CHANGÉ: supprimé .gameConfig
            !gameConfig.feedbackMessages.useBackgroundImage) // CHANGÉ: supprimé .gameConfig
        {
            Debug.Log("Pas d'image de fond à appliquer");
            return;
        }

        var feedbackConfig = gameConfig.feedbackMessages; // CHANGÉ: supprimé .gameConfig

        // 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()
    {
        Debug.Log("=== CLIC FEEDBACK - QUESTION SUIVANTE ===");

        if (feedbackPanel != null)
        {
            feedbackPanel.SetActive(false);
            Debug.Log("Panneau de feedback masqué");
        }

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

        currentQuestionIndex++;
        Debug.Log($"Index question suivante: {currentQuestionIndex}");

        if (currentQuestionIndex >= questions.Length) // CHANGÉ: .Count -> .Length
        {
            Debug.Log("Toutes les questions terminées - Fin du jeu");
            EndGame();
        }
        else
        {
            Debug.Log($"Préparation question {currentQuestionIndex + 1}/{questions.Length}"); // CHANGÉ: .Count -> .Length
            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();

        // Récupérer l'URL appropriée depuis les PlayerPrefs
        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}");

        // ANCIEN SYSTÈME - À SUPPRIMER
        /*
        StartCoroutine(HandlePostGameDialogue());
        */

        // 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);
        if (gameConfig?.uiConfig != null) // CHANGÉ: supprimé .gameConfig
        {
            panelSize = gameConfig.uiConfig.hoverPanelSize.ToVector2(); // CHANGÉ: supprimé .gameConfig
            Debug.Log($"Taille panneau hover depuis JSON: {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);
        if (gameConfig?.uiConfig != null) // CHANGÉ: supprimé .gameConfig
        {
            var uiConfig = gameConfig.uiConfig; // CHANGÉ: supprimé .gameConfig
            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;

        if (gameConfig?.uiConfig != null) // CHANGÉ: supprimé .gameConfig
        {
            var uiConfig = gameConfig.uiConfig; // CHANGÉ: supprimé .gameConfig
            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()
    {
        Debug.Log("=== DÉBUT 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");
        }

        Debug.Log("=== FIN ApplyGunConfigFromJSON ===");






        // NOUVEAU : Debug de la config crosshair
        if (gameConfig?.crosshairConfig != null)
        {
            Debug.Log("=== DEBUG CROSSHAIR CONFIG DANS GAMEMANAGER ===");
            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");
            }

            Debug.Log("=== FIN DEBUG CROSSHAIR CONFIG ===");
        }
        else
        {
            Debug.LogError("gameConfig.crosshairConfig est NULL !");
        }



    }

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

    void ApplyFeedbackPanelStyling()
    {
        Debug.Log("=== NETTOYAGE COMPLET FEEDBACK PANEL ===");

        // 1. SUPPRIMER TOUTES les images du panel principal
        Image panelBackground = feedbackPanel.GetComponent<Image>();
        if (panelBackground != null)
        {
            panelBackground.color = Color.clear;
            panelBackground.sprite = null;
            Debug.Log("Image du panel principal supprimée");
        }

        // 2. SUPPRIMER tous les enfants sauf le texte et l'image PNG
        List<GameObject> toDestroy = new List<GameObject>();
        for (int i = 0; i < feedbackPanel.transform.childCount; i++)
        {
            Transform child = feedbackPanel.transform.GetChild(i);

            // Garder seulement le texte et l'image de fond
            if (child.name != "FeedbackBackgroundImage" &&
                child.GetComponent<TextMeshProUGUI>() == null &&
                child.GetComponent<TMPro.TextMeshProUGUI>() == null)
            {
                toDestroy.Add(child.gameObject);
                Debug.Log($"Marqué pour suppression: {child.name}");
            }
        }

        foreach (GameObject obj in toDestroy)
        {
            DestroyImmediate(obj);
        }

        Debug.Log("=== NETTOYAGE TERMINÉ ===");
    }

    /// <summary>
    /// Crée les bandes en haut et en bas de l'écran
    /// </summary>
    void CreateUIBands()
    {
        var bandsConfig = gameConfig?.uiConfig?.bands;
        
        // Si pas de config dans le JSON, utiliser les defaults depuis general-config
        if (bandsConfig == null || !bandsConfig.showBands)
        {
            var defaultUIConfig = GeneralConfigManager.Instance?.GetDefaultUIConfig();
            if (defaultUIConfig != null && defaultUIConfig.bands != null && defaultUIConfig.bands.showBands)
            {
                Debug.Log("Utilisation des bandes UI depuis general-config defaults");
                // On ne peut pas créer une GameUIBands depuis DefaultUIBands directement
                // mais on va utiliser les valeurs des defaults
                // Note: pour une intégration complète, il faudrait une méthode de conversion
                Debug.Log("Bandes UI configurées depuis defaults (nécessite conversion de structure)");
            }
            else
            {
                Debug.Log("Pas de bandes UI à créer");
                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;
        }

        CreateBand("TopBand", canvas, bandsConfig.bandHeight, bandColor, bandsConfig.sortingOrder, true);
        CreateBand("BottomBand", canvas, bandsConfig.bandHeight, bandColor, bandsConfig.sortingOrder, false);

        Debug.Log($"Bandes UI créées - Hauteur: {bandsConfig.bandHeight}px, Couleur: {bandsConfig.bandColor}");
    }

    /// <summary>
    /// Crée une bande individuelle
    /// </summary>
    void CreateBand(string name, Canvas canvas, float height, Color color, int sortingOrder, bool isTop)
    {
        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;

        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()
    {
        if (gameConfig?.uiConfig?.bands == null) // CHANGÉ: supprimé .gameConfig
        {
            return;
        }

        var bandsConfig = gameConfig.uiConfig.bands; // CHANGÉ: supprimé .gameConfig

        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;
            ApplyQuestionStyling();
            Debug.Log("Affichage de la question mis à jour");
        }
    }

    /// <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("=== ÉTAT BANDEAU SUPÉRIEUR ===");
        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}");
        }

        Debug.Log("==============================");
    }

    /// <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());
    }

    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);
            }
        }

        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();
        }

        // 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}");
        }
    }
    
    private int lastCleanFrame = -100;
    private UnityEngine.UI.Image lastVeilCleanLog = null;







}