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

public class MapManager : MonoBehaviour
{
    [Header("Configuration")]
    public string mapsRegistryUrl = ""; // Si vide, utilise la valeur de general-config.json

    [Header("Debug")]
    public bool showDebugZones = true;
    public bool decorativeVideosVisible = true;
    
    // Référence statique au shader pour éviter les problèmes de Shader.Find en WebGL
    private static Shader videoAlphaMaskShader = null;
    private static bool shaderChecked = false;

    [Header("Transparence Vidéos Décoratives")]
    [Tooltip("Multiplier pour l'étendue du dégradé (1.2 = défaut). Plus petit = transparence plus proche des bords")]
    [Range(0.5f, 2.0f)]
    public float vignetteExtent = 1.2f;
    
    [Tooltip("Point où la transparence commence (0.5 = 50% du rayon). Plus petit = centre transparent plus grand")]
    [Range(0.0f, 1.0f)]
    public float vignetteStartRadius = 0.5f;
    
    [Tooltip("Courbe du dégradé (0.8 = défaut). Plus petit = dégradé plus doux, plus grand = dégradé plus dur")]
    [Range(0.1f, 3.0f)]
    public float vignetteCurve = 0.8f;
    
    [Tooltip("Désactiver complètement le masque de transparence")]
    public bool disableVignetteMask = true;

    [Header("UI References")]
    public Camera mainCamera;

    private string mapConfigUrl;
    private string currentMapId;
    private MapConfigData mapConfig;
    private string currentStepId;
    private MapStep currentStep;
    private List<MapZoneClickable> zones = new List<MapZoneClickable>();
    // NOTE: headerPanel supprimé - maintenant géré par HeaderManager
    private GameObject popupContainer;
    private MapZone currentZoneData; // Zone actuellement cliquée (pour récupérer gameType)
    private GameObject backgroundObject;
    public GameObject scrollViewObject; // Public pour que SettingsManager puisse désactiver le ScrollRect
    private GameObject viewportObject;
    private GameObject contentObject;
    private bool isDragging = false;
    private Vector2 dragStartPos;
    private Vector2 contentStartPos;
    private List<GameObject> decorators = new List<GameObject>();
    private List<GameObject> decorativeVideos = new List<GameObject>();
    private List<GameObject> stepDecorativeImages = new List<GameObject>(); // Images décoratives du step actuel
    private GameObject activePopup; // Popup actuellement ouverte
    private Vector2 popupRelativePosition; // Position relative de la popup par rapport au scroll
    private RectTransform activePopupZone; // Zone associée au popup actif (pour suivre le scroll)
    private bool isFullscreenVideoPlaying = false; // Indique si une vidéo fullscreen est en cours

    /// <summary>
    /// Vérifie si une vidéo plein écran est actuellement en cours de lecture
    /// </summary>
    public bool IsFullscreenVideoPlaying()
    {
        return isFullscreenVideoPlaying;
    }

    // Fonction helper pour trouver le Canvas principal (pas PopupContainer)
    private Canvas GetMainCanvas()
    {
        Canvas canvas = null;
        Canvas[] allCanvases = FindObjectsByType<Canvas>(FindObjectsSortMode.None);
        
        // Chercher le Canvas nommé "Canvas"
        foreach (Canvas c in allCanvases)
        {
            if (c.gameObject.name == "Canvas")
            {
                canvas = c;
                break;
            }
        }
        
        // Fallback : prendre le premier qui n'est pas PopupContainer
        if (canvas == null)
        {
            foreach (Canvas c in allCanvases)
            {
                if (c.gameObject.name != "PopupContainer")
                {
                    canvas = c;
                    break;
                }
            }
        }
        
        return canvas;
    }
    
    void Awake()
    {
        Debug.Log("[MapManager] 🔵 Awake() appelé - MapManager initialisé");
    }
    
    void Start()
    {
        Debug.Log("[MapManager] 🚀 Start() appelé - Début de l'initialisation");
        StartCoroutine(InitializeMapManager());
    }
    
    IEnumerator InitializeMapManager()
    {
        Debug.Log("[MapManager] 🔄 InitializeMapManager() démarré");
        
        // **NETTOYER LES CANVAS DES JEUX PRÉCÉDENTS** (Fix WebGL)
        Debug.Log("[MapManager] 🧹 Nettoyage des canvas de jeux précédents...");
        Canvas[] allCanvases = FindObjectsByType<Canvas>(FindObjectsSortMode.None);
        int cleanedCount = 0;
        foreach (Canvas existingCanvas in allCanvases)
        {
            if (existingCanvas != null && existingCanvas.gameObject != null)
            {
                // Ne pas détruire :
                // - Les canvas persistants (DontDestroyOnLoad)
                // - Les canvas de loading
                // - Le canvas de la Map (on va le réutiliser)
                bool isPersistent = existingCanvas.gameObject.scene.name == "DontDestroyOnLoad";
                bool isLoading = existingCanvas.name.Contains("Loading") || 
                                existingCanvas.GetComponentInParent<LoadingScreenManager>() != null || 
                                existingCanvas.GetComponentInParent<UnifiedLoadingManager>() != null;
                bool isMapCanvas = existingCanvas.name.Contains("MapCanvas") || existingCanvas.GetComponent<MapManager>() != null;
                
                // Détecter les canvas de jeux (ShootingGameLayout, CalculatorGameLayout, TrousGameLayout, etc.)
                bool isGameCanvas = existingCanvas.GetComponentInChildren<ShootingGameLayout>() != null ||
                                   existingCanvas.GetComponentInChildren<CalculatorGameLayout>() != null ||
                                   existingCanvas.GetComponentInChildren<TrousGameLayout>() != null ||
                                   existingCanvas.name.Contains("GameCanvas") ||
                                   existingCanvas.name.Contains("ShootingGame") ||
                                   existingCanvas.name.Contains("Calculator") ||
                                   existingCanvas.name.Contains("Trous") ||
                                   existingCanvas.sortingOrder >= 5; // Les jeux ont souvent un sortingOrder élevé
                
                if (!isPersistent && !isLoading && (isGameCanvas || (!isMapCanvas && existingCanvas.sortingOrder > 0)))
                {
                    Debug.Log($"[MapManager] 🗑️ Destruction canvas de jeu: {existingCanvas.name} (sortingOrder: {existingCanvas.sortingOrder})");
                    DestroyImmediate(existingCanvas.gameObject);
                    cleanedCount++;
                }
            }
        }
        Debug.Log($"[MapManager] ✅ Nettoyage terminé - {cleanedCount} canvas détruits");
        
        // Masquer l'overlay de transition de l'intro dès le début
        // (évite l'écran noir prolongé si on vient directement de l'intro)
        if (IntroTransitionOverlay.Instance != null)
        {
            Debug.Log("[MapManager] ✅ Masquage de l'overlay de transition de l'intro");
            IntroTransitionOverlay.HideOverlay();
        }
        
        // Créer GeneralConfigManager s'il n'existe pas
        if (GeneralConfigManager.Instance == null)
        {
            GameObject configManagerObj = new GameObject("GeneralConfigManager");
            configManagerObj.AddComponent<GeneralConfigManager>();
            Debug.Log("[MapManager] GeneralConfigManager créé automatiquement");
        }
        
        // Attendre que GeneralConfigManager soit initialisé
        while (GeneralConfigManager.Instance == null)
        {
            yield return null;
        }
        
        // Attendre que la configuration soit chargée
        while (!GeneralConfigManager.Instance.IsConfigLoaded())
        {
            yield return null;
        }
        
        Debug.Log("[MapManager] GeneralConfigManager chargé et prêt");
        
        // Initialiser le volume audio depuis les paramètres
        bool soundEnabled = PlayerPrefs.GetInt("SoundEnabled", 1) == 1;
        AudioListener.volume = soundEnabled ? 1f : 0f;
        Debug.Log($"[MapManager] Volume audio initialisé: {AudioListener.volume} (SoundEnabled: {soundEnabled})");
        
        // Utiliser la configuration générale pour la map par défaut
        string defaultMapId = GeneralConfigManager.Instance.GetDefaultMapId();
        Debug.Log($"[MapManager] Map par défaut depuis GeneralConfigManager: {defaultMapId}");
        
        currentMapId = PlayerPrefs.GetString("CurrentMapId", defaultMapId);
        
        // Validation préliminaire du mapId - format attendu: "map-Q0" à "map-Q9"
        const int MAX_VALID_MAP_INDEX = 9;
        bool isValidMapId = false;
        if (!string.IsNullOrEmpty(currentMapId) && currentMapId.StartsWith("map-Q"))
        {
            string indexStr = currentMapId.Replace("map-Q", "");
            if (int.TryParse(indexStr, out int mapIndex) && mapIndex >= 0 && mapIndex <= MAX_VALID_MAP_INDEX)
            {
                isValidMapId = true;
            }
        }
        
        if (!isValidMapId)
        {
            Debug.LogWarning($"[MapManager] ⚠️ MapId invalide détecté: '{currentMapId}', nettoyage et fallback vers '{defaultMapId}'");
            PlayerPrefs.SetString("CurrentMapId", defaultMapId);
            PlayerPrefs.DeleteKey("CurrentStepId_" + currentMapId);
            
            // Extraire et mettre à jour le QuestId depuis le mapId par défaut
            // Format: map-Q0 → Q0 → index 0 → questId = 0 + 1 = 1
            // Format: map-Q1 → Q1 → index 1 → questId = 1 + 1 = 2
            if (defaultMapId.StartsWith("map-Q"))
            {
                string indexStr = defaultMapId.Replace("map-Q", "");
                if (int.TryParse(indexStr, out int index))
                {
                    int defaultQuestId = index + 1; // Q0 = questId 1, Q1 = questId 2, etc.
                    PlayerPrefs.SetInt("CurrentQuestId", defaultQuestId);
                    Debug.Log($"[MapManager] ✅ CurrentQuestId mis à jour: {defaultQuestId} (index: {index})");
                }
            }
            
            PlayerPrefs.Save();
            currentMapId = defaultMapId;
        }
        
        currentStepId = PlayerPrefs.GetString("CurrentStepId_" + currentMapId, "");

        Debug.Log($"[MapManager] Configuration chargée - Map: {currentMapId}, Step: {currentStepId}");
        Debug.Log($"[MapManager] Map par défaut depuis config: {defaultMapId}");
        Debug.Log($"[MapManager] DEBUG - PlayerPrefs CurrentStepId: '{PlayerPrefs.GetString("CurrentStepId_" + currentMapId, "")}'");
        Debug.Log($"[MapManager] DEBUG - PlayerPrefs NextStepId: '{PlayerPrefs.GetString("NextStepId_" + currentMapId, "")}'");

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

        // TOUJOURS récupérer le questId depuis l'API projects pour être sûr d'avoir le bon ID
        // (le questId dans PlayerPrefs peut être incorrect car calculé depuis le mapId)
        int questId = 0;
        bool hasToken = UserDataManager.Instance != null && !string.IsNullOrEmpty(UserDataManager.Instance.token);
        Debug.Log($"[MapManager] 🔍 Token disponible: {hasToken}");
        Debug.Log($"[MapManager] 🔍 GeneralConfigManager.Instance != null: {GeneralConfigManager.Instance != null}");
        Debug.Log($"[MapManager] 🔍 UserDataManager.Instance != null: {UserDataManager.Instance != null}");
        
        // Récupérer le questId depuis l'API projects pour avoir le vrai ID
        if (hasToken && GeneralConfigManager.Instance != null && !string.IsNullOrEmpty(currentMapId) && currentMapId.StartsWith("map-Q"))
        {
            Debug.Log($"[MapManager] 🔍 Récupération du questId depuis l'API projects pour mapId: {currentMapId}");
            yield return StartCoroutine(LoadQuestIdFromProjectsApi(currentMapId));
            questId = PlayerPrefs.GetInt("CurrentQuestId", 0);
            Debug.Log($"[MapManager] 🔍 QuestId récupéré depuis l'API: {questId}");
        }
        else
        {
            // Fallback : utiliser le questId depuis PlayerPrefs si l'API n'est pas disponible
            questId = PlayerPrefs.GetInt("CurrentQuestId", 0);
            Debug.Log($"[MapManager] 🔍 QuestId depuis PlayerPrefs (fallback): {questId}");
        }
        
        // TOUJOURS charger depuis l'API uniquement - plus de fichiers locaux
        if (questId > 0 && GeneralConfigManager.Instance != null && UserDataManager.Instance != null && hasToken)
        {
            Debug.Log($"[MapManager] 🌐 QuestId trouvé ({questId}), chargement depuis l'API");
            yield return StartCoroutine(LoadMapConfiguration());
        }
        else
        {
            if (questId <= 0)
            {
                Debug.LogError($"[MapManager] ❌ ERREUR : QuestId invalide ou manquant ({questId}). Impossible de charger la map depuis l'API.");
            }
            else if (GeneralConfigManager.Instance == null)
            {
                Debug.LogError("[MapManager] ❌ ERREUR : GeneralConfigManager non disponible. Impossible de charger la map depuis l'API.");
            }
            else if (UserDataManager.Instance == null || !hasToken)
            {
                Debug.LogError("[MapManager] ❌ ERREUR : Utilisateur non connecté ou token manquant. Impossible de charger la map depuis l'API.");
            }
            // Plus de fallback vers LoadMapFromRegistry - les fichiers locaux n'existent plus
        }
    }


    IEnumerator LoadMapFromRegistry()
    {
        // Utiliser l'URL du registre depuis la configuration générale
        string registryUrl = "";
        if (GeneralConfigManager.Instance != null)
        {
            registryUrl = GeneralConfigManager.Instance.GetMapsRegistryUrl();
        }
        else if (!string.IsNullOrEmpty(mapsRegistryUrl))
        {
            registryUrl = mapsRegistryUrl;
        }
        
        if (string.IsNullOrEmpty(registryUrl))
        {
            Debug.LogError("[MapManager] mapsRegistryUrl introuvable et GeneralConfigManager non disponible");
            yield break;
        }
            
        Debug.Log($"[MapManager] Chargement registre: {registryUrl}");

        using (UnityWebRequest www = UnityWebRequest.Get(registryUrl))
        {
            www.SetRequestHeader("Cache-Control", "no-cache");
            yield return www.SendWebRequest();

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

            string json = www.downloadHandler.text;
            MapRegistryWrapper registry = JsonUtility.FromJson<MapRegistryWrapper>(json);
            
            if (registry == null || registry.maps == null)
            {
                Debug.LogError("[MapManager] ❌ ERREUR : Le registre des maps est vide ou invalide !");
                Debug.LogError($"[MapManager] JSON reçu: {json}");
                yield break;
            }
            
            Debug.Log($"[MapManager] Registre chargé - {registry.maps.Count} maps disponibles");
            foreach (var map in registry.maps)
            {
                Debug.Log($"[MapManager]   - {map.id}: {map.name} (configUrl: {map.configUrl})");
            }
            
            MapInfo mapInfo = registry.maps.Find(m => m.id == currentMapId);

            if (mapInfo == null)
            {
                Debug.LogWarning($"[MapManager] ⚠️ Map introuvable dans le registre: '{currentMapId}'");
                Debug.LogWarning($"[MapManager] Maps disponibles dans le registre:");
                foreach (var map in registry.maps)
                {
                    Debug.LogWarning($"[MapManager]   - {map.id}");
                }
                
                // Fallback vers la map par défaut
                string defaultMapId = GeneralConfigManager.Instance?.GetDefaultMapId() ?? "map-Q0";
                Debug.Log($"[MapManager] 🔄 Fallback vers la map par défaut: {defaultMapId}");
                
                // Nettoyer l'ancien mapId invalide des PlayerPrefs
                PlayerPrefs.SetString("CurrentMapId", defaultMapId);
                PlayerPrefs.Save();
                currentMapId = defaultMapId;
                
                mapInfo = registry.maps.Find(m => m.id == currentMapId);
                if (mapInfo == null)
                {
                    // Si même la map par défaut n'existe pas, prendre la première map disponible
                    if (registry.maps.Count > 0)
                    {
                        mapInfo = registry.maps[0];
                        currentMapId = mapInfo.id;
                        PlayerPrefs.SetString("CurrentMapId", currentMapId);
                        PlayerPrefs.Save();
                        Debug.Log($"[MapManager] 🔄 Utilisation de la première map disponible: {currentMapId}");
                    }
                    else
                    {
                        Debug.LogError("[MapManager] ❌ ERREUR : Aucune map disponible dans le registre !");
                        yield break;
                    }
                }
            }
            
            if (string.IsNullOrEmpty(mapInfo.configUrl))
            {
                Debug.LogError($"[MapManager] ❌ ERREUR : configUrl est vide pour la map '{currentMapId}' !");
                Debug.LogError($"[MapManager] Vérifiez le fichier maps-registry.json");
                yield break;
            }

            // Utiliser GetMapConfigUrl pour construire l'URL complète avec le bon préfixe
            mapConfigUrl = GeneralConfigManager.Instance.GetMapConfigUrl(mapInfo.configUrl);
            Debug.Log($"[MapManager] Map trouvée: {mapInfo.name}, URL: {mapConfigUrl}");

            yield return StartCoroutine(LoadMapConfiguration());
        }
    }

    /// <summary>
    /// Charge les quêtes depuis l'API projects pour trouver le questId réel correspondant au mapId
    /// </summary>
    IEnumerator LoadQuestIdFromProjectsApi(string mapId)
    {
        if (GeneralConfigManager.Instance == null || UserDataManager.Instance == null)
        {
            Debug.LogWarning("[MapManager] ⚠️ Impossible de charger les quêtes : GeneralConfigManager ou UserDataManager non disponible");
            yield break;
        }
        
        string apiUrl = GeneralConfigManager.Instance.GetMainSceneConfigApiUrl();
        if (string.IsNullOrEmpty(apiUrl))
        {
            Debug.LogWarning("[MapManager] ⚠️ Impossible de construire l'URL de l'API projects");
            yield break;
        }
        
        Debug.Log($"[MapManager] 🔍 Chargement des quêtes depuis: {apiUrl}");
        
        string token = UserDataManager.Instance.token;
        
        using (UnityWebRequest www = UnityWebRequest.Get(apiUrl))
        {
            www.SetRequestHeader("Authorization", $"Bearer {token}");
            yield return www.SendWebRequest();
            
            if (www.result != UnityWebRequest.Result.Success)
            {
                Debug.LogWarning($"[MapManager] ⚠️ Erreur lors du chargement des quêtes: {www.error}");
                yield break;
            }
            
            string jsonData = www.downloadHandler.text;
            ApiMainSceneResponse apiResponse = JsonUtility.FromJson<ApiMainSceneResponse>(jsonData);
            
            if (apiResponse != null && apiResponse.status == "success" && apiResponse.data != null && apiResponse.data.quests != null)
            {
                Debug.Log($"[MapManager] 🔍 Recherche du questId pour mapId: {mapId}");
                Debug.Log($"[MapManager] 🔍 Nombre de quêtes dans l'API: {apiResponse.data.quests.Count}");
                
                // Trouver la quête correspondant au mapId
                // Le mapId est généré dans MainSceneManager comme: map-Q{index}
                // où index est la position dans la liste (0, 1, 2, ...), PAS quest.id - 1
                if (mapId.StartsWith("map-Q"))
                {
                    string indexStr = mapId.Replace("map-Q", "");
                    if (int.TryParse(indexStr, out int mapIndex))
                    {
                        Debug.Log($"[MapManager] 🔍 mapId index extrait: {mapIndex}");
                        
                        // Le mapIndex correspond à l'index dans la liste des quêtes
                        if (mapIndex >= 0 && mapIndex < apiResponse.data.quests.Count)
                        {
                            var quest = apiResponse.data.quests[mapIndex];
                            int realQuestId = quest.id;
                            PlayerPrefs.SetInt("CurrentQuestId", realQuestId);
                            PlayerPrefs.Save();
                            Debug.Log($"[MapManager] ✅ QuestId trouvé depuis l'API: {realQuestId} pour mapId: {mapId} (index: {mapIndex})");
                            yield break;
                        }
                        else
                        {
                            Debug.LogWarning($"[MapManager] ⚠️ Index {mapIndex} hors limites (0-{apiResponse.data.quests.Count - 1})");
                        }
                    }
                }
                
                Debug.LogWarning($"[MapManager] ⚠️ Aucune quête trouvée pour mapId: {mapId}");
                Debug.LogWarning($"[MapManager] ⚠️ MapIds disponibles dans l'API:");
                for (int i = 0; i < apiResponse.data.quests.Count; i++)
                {
                    var quest = apiResponse.data.quests[i];
                    string generatedMapId = $"map-Q{i}";
                    Debug.LogWarning($"[MapManager]   - Index {i}: Quest id={quest.id} → {generatedMapId}");
                }
            }
            else
            {
                Debug.LogWarning("[MapManager] ⚠️ Réponse API invalide ou sans quêtes");
            }
        }
    }

    /// <summary>
    /// Charge la configuration de la map depuis l'API
    /// </summary>
    IEnumerator LoadMapConfigurationFromApi(int questId)
    {
        string apiUrl = GeneralConfigManager.Instance.GetQuestConfigApiUrl(questId);
        
        if (string.IsNullOrEmpty(apiUrl))
        {
            Debug.LogError("[MapManager] ❌ Impossible de construire l'URL de l'API quest");
            yield break;
        }

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

        string token = UserDataManager.Instance.token;

        using (UnityWebRequest www = UnityWebRequest.Get(apiUrl))
        {
            // Ajouter l'authentification Bearer Token
            www.SetRequestHeader("Authorization", $"Bearer {token}");

            yield return www.SendWebRequest();

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

            string jsonData = www.downloadHandler.text;
            Debug.Log($"[MapManager] ✅ Configuration reçue depuis l'API ({jsonData.Length} caractères)");
            Debug.Log($"[MapManager] 🔍 Début de la réponse: {jsonData.Substring(0, Mathf.Min(500, jsonData.Length))}...");

            // Parser la réponse de l'API
            // Format API possible 1: {"status": "success", "message": "...", "data": {MapConfigData}}
            // Format API possible 2: {"status": "success", "message": "...", "data": {"mapConfig": {MapConfigData}}}
            // Format fichier local: {"mapConfig": {...}}
            ApiQuestResponse apiResponse = null;
            ApiQuestResponseWithWrapper apiResponseWrapper = null;
            MapConfigWrapper fileWrapper = null;
            
            try
            {
                // Essayer d'abord le format API direct (data = MapConfigData)
                apiResponse = JsonUtility.FromJson<ApiQuestResponse>(jsonData);
                Debug.Log($"[MapManager] 🔍 Parsing API direct - apiResponse != null: {apiResponse != null}");
                if (apiResponse != null)
                {
                    Debug.Log($"[MapManager] 🔍 status: '{apiResponse.status}', data != null: {apiResponse.data != null}");
                    if (apiResponse.data != null)
                    {
                        Debug.Log($"[MapManager] 🔍 data.steps != null: {apiResponse.data.steps != null}, steps count: {apiResponse.data.steps?.Count ?? 0}");
                    }
                }
                
                if (apiResponse != null && apiResponse.data != null && apiResponse.status == "success")
                {
                    mapConfig = apiResponse.data;
                    Debug.Log("[MapManager] ✅ Configuration parsée depuis l'API (format API direct)");
                    Debug.Log($"[MapManager] 🔍 Steps count: {mapConfig.steps?.Count ?? 0}");
                    if (mapConfig.steps != null && mapConfig.steps.Count > 0)
                    {
                        Debug.Log($"[MapManager] 🔍 Premier step: id={mapConfig.steps[0].id}, type={mapConfig.steps[0].type}");
                    }
                }
                else
                {
                    // Essayer le format API avec wrapper (data = MapConfigWrapper)
                    Debug.LogWarning("[MapManager] ⚠️ Format API direct non valide, essai format API avec wrapper...");
                    apiResponseWrapper = JsonUtility.FromJson<ApiQuestResponseWithWrapper>(jsonData);
                    Debug.Log($"[MapManager] 🔍 Parsing API wrapper - apiResponseWrapper != null: {apiResponseWrapper != null}");
                    if (apiResponseWrapper != null)
                    {
                        Debug.Log($"[MapManager] 🔍 status: '{apiResponseWrapper.status}', data != null: {apiResponseWrapper.data != null}");
                        if (apiResponseWrapper.data != null)
                        {
                            Debug.Log($"[MapManager] 🔍 data.mapConfig != null: {apiResponseWrapper.data.mapConfig != null}");
                        }
                    }
                    
                    if (apiResponseWrapper != null && apiResponseWrapper.data != null && apiResponseWrapper.data.mapConfig != null && apiResponseWrapper.status == "success")
                    {
                        mapConfig = apiResponseWrapper.data.mapConfig;
                        Debug.Log("[MapManager] ✅ Configuration parsée depuis l'API (format API avec wrapper)");
                        Debug.Log($"[MapManager] 🔍 Steps count: {mapConfig.steps?.Count ?? 0}");
                    }
                    else
                    {
                        // Essayer le format fichier local (fallback)
                        Debug.LogWarning("[MapManager] ⚠️ Format API wrapper non valide, essai format fichier local...");
                        fileWrapper = JsonUtility.FromJson<MapConfigWrapper>(jsonData);
                        Debug.Log($"[MapManager] 🔍 Parsing fichier - fileWrapper != null: {fileWrapper != null}");
                        if (fileWrapper != null)
                        {
                            Debug.Log($"[MapManager] 🔍 mapConfig != null: {fileWrapper.mapConfig != null}");
                        }
                        
                        if (fileWrapper != null && fileWrapper.mapConfig != null)
                        {
                            mapConfig = fileWrapper.mapConfig;
                            Debug.Log("[MapManager] ✅ Configuration parsée avec wrapper (format fichier local)");
                            Debug.Log($"[MapManager] 🔍 Steps count: {mapConfig.steps?.Count ?? 0}");
                        }
                        else
                        {
                            Debug.LogError("[MapManager] ❌ Structure de réponse invalide depuis l'API");
                            Debug.LogError($"[MapManager] Réponse complète (premiers 1000 caractères): {jsonData.Substring(0, Mathf.Min(1000, jsonData.Length))}...");
                        }
                    }
                }
            }
            catch (System.Exception e)
            {
                Debug.LogError($"[MapManager] ❌ Erreur lors du parsing de la réponse API : {e.Message}");
                Debug.LogError($"[MapManager] Stack trace: {e.StackTrace}");
                Debug.LogError($"[MapManager] JSON reçu (premiers 500 caractères): {jsonData.Substring(0, Mathf.Min(500, jsonData.Length))}...");
            }
            
            // Vérification finale
            if (mapConfig == null)
            {
                Debug.LogError("[MapManager] ❌ mapConfig est null après parsing - Le chargement a échoué");
                Debug.LogError("[MapManager] Vérifiez le format de la réponse API dans les logs ci-dessus");
            }
            else
            {
                Debug.Log($"[MapManager] ✅ mapConfig chargé avec succès - {mapConfig.steps?.Count ?? 0} steps");
            }
        }
    }

    IEnumerator LoadMapConfiguration()
    {
        Debug.Log("[MapManager] 🔍 Début de LoadMapConfiguration()");
        
        // TOUJOURS charger depuis l'API uniquement - plus de fichiers locaux
        int questId = PlayerPrefs.GetInt("CurrentQuestId", 0);
        Debug.Log($"[MapManager] 🔍 CurrentQuestId depuis PlayerPrefs: {questId}");
        Debug.Log($"[MapManager] 🔍 GeneralConfigManager.Instance != null: {GeneralConfigManager.Instance != null}");
        Debug.Log($"[MapManager] 🔍 UserDataManager.Instance != null: {UserDataManager.Instance != null}");
        
        bool hasToken = UserDataManager.Instance != null && !string.IsNullOrEmpty(UserDataManager.Instance.token);
        Debug.Log($"[MapManager] 🔍 Token disponible: {hasToken}");
        
        // Si le questId n'est pas valide, essayer de le récupérer depuis l'API projects
        if (questId <= 0 && hasToken && GeneralConfigManager.Instance != null && !string.IsNullOrEmpty(currentMapId) && currentMapId.StartsWith("map-Q"))
        {
            Debug.Log($"[MapManager] 🔍 QuestId invalide ou manquant, tentative de récupération depuis l'API projects pour mapId: {currentMapId}");
            yield return StartCoroutine(LoadQuestIdFromProjectsApi(currentMapId));
            questId = PlayerPrefs.GetInt("CurrentQuestId", 0);
            Debug.Log($"[MapManager] 🔍 QuestId récupéré depuis l'API: {questId}");
        }
        
        // Vérifier que tous les prérequis sont disponibles pour charger depuis l'API
        if (questId <= 0)
        {
            Debug.LogError($"[MapManager] ❌ ERREUR : QuestId non disponible ({questId}). Impossible de charger la configuration depuis l'API.");
            yield break;
        }
        
        if (GeneralConfigManager.Instance == null)
        {
            Debug.LogError("[MapManager] ❌ ERREUR : GeneralConfigManager non disponible. Impossible de charger la configuration depuis l'API.");
            yield break;
        }
        
        if (UserDataManager.Instance == null || !hasToken)
        {
            Debug.LogError("[MapManager] ❌ ERREUR : Utilisateur non connecté ou token manquant. Impossible de charger la configuration depuis l'API.");
            yield break;
        }
        
        // Charger depuis l'API
        Debug.Log($"[MapManager] 🌐 Tentative de chargement depuis l'API avec questId: {questId}");
        yield return StartCoroutine(LoadMapConfigurationFromApi(questId));
        
        // Vérifier que la configuration a été chargée
        if (mapConfig == null)
        {
            Debug.LogError("[MapManager] ❌ ERREUR : Impossible de charger la configuration depuis l'API");
            yield break;
        }
        
        Debug.Log($"[MapManager] ✅ Configuration chargée depuis l'API avec succès - {mapConfig.steps?.Count ?? 0} steps");
        
        // DIAGNOSTIC : Afficher tous les steps
        for (int i = 0; i < mapConfig.steps.Count; i++)
        {
            MapStep step = mapConfig.steps[i];
            Debug.Log($"[MapManager] Step {i}: {step.id} - Type: {step.type} - NextStep: {step.nextStepId}");
        }

        // LOGIQUE SIMPLIFIÉE ET CORRIGÉE
        Debug.Log($"[MapManager] DEBUG - currentStepId initial='{currentStepId}', steps.Count={mapConfig.steps.Count}");

        if (mapConfig.steps.Count == 0)
        {
            Debug.LogError("[MapManager] Aucun step dans la config !");
            yield break;
        }

        MapStep firstStep = mapConfig.steps[0];
        string nextStepId = PlayerPrefs.GetString("NextStepId_" + currentMapId, "");
        string savedCurrentStepId = PlayerPrefs.GetString("CurrentStepId_" + currentMapId, "");
        bool introWatched = PlayerPrefs.GetInt("IntroWatched_" + currentMapId, 0) == 1;
        int lastCompletedGame = PlayerPrefs.GetInt("LastCompletedGame_" + currentMapId, 0);
        bool launchedFromPreview = PlayerPrefs.GetInt("LaunchedFromPreview_" + currentMapId, 0) == 1;
        
        Debug.Log($"[MapManager] Premier step: id='{firstStep.id}', type='{firstStep.type}'");
        Debug.Log($"[MapManager] NextStepId sauvegardé: '{nextStepId}'");
        Debug.Log($"[MapManager] CurrentStepId sauvegardé: '{savedCurrentStepId}'");
        Debug.Log($"[MapManager] IntroWatched: {introWatched}");
        Debug.Log($"[MapManager] LastCompletedGame: {lastCompletedGame}");
        Debug.Log($"[MapManager] LaunchedFromPreview: {launchedFromPreview}");

        // CAS 1: Lancement depuis le panel de prévisualisation → Utiliser EXCLUSIVEMENT last_completed_game
        // C'est la source de vérité de l'API pour savoir où reprendre
        if (launchedFromPreview)
        {
            // Nettoyer le flag immédiatement
            PlayerPrefs.DeleteKey("LaunchedFromPreview_" + currentMapId);
            PlayerPrefs.DeleteKey("LastCompletedGame_" + currentMapId);
            
            if (lastCompletedGame > 0)
            {
                // On a un dernier jeu terminé → Se positionner sur le step SUIVANT
                string resumeStepId = FindStepIdAfterGame(lastCompletedGame);
                if (!string.IsNullOrEmpty(resumeStepId))
                {
                    currentStepId = resumeStepId;
                    Debug.Log($"[MapManager] ✅ CAS 1a: Reprise depuis panel - Après jeu {lastCompletedGame} → Step: {currentStepId}");
                }
                else
                {
                    // Le gameId n'a pas été trouvé, commencer au début (après intro si vue)
                    currentStepId = GetFirstPlayableStepId(firstStep, introWatched);
                    Debug.LogWarning($"[MapManager] ⚠️ CAS 1a fallback: gameId {lastCompletedGame} non trouvé → Step: {currentStepId}");
                }
            }
            else
            {
                // Pas de progression (last_completed_game == 0) → Commencer au début
                currentStepId = GetFirstPlayableStepId(firstStep, introWatched);
                Debug.Log($"[MapManager] ✅ CAS 1b: Lancement depuis panel sans progression → Step: {currentStepId}");
            }
            
            PlayerPrefs.SetString("CurrentStepId_" + currentMapId, currentStepId);
            PlayerPrefs.Save();
        }
        // CAS 2: On a un NextStepId → On revient d'un niveau via MapManager.LaunchLevel
        else if (!string.IsNullOrEmpty(nextStepId))
        {
            currentStepId = nextStepId;
            PlayerPrefs.SetString("CurrentStepId_" + currentMapId, currentStepId);
            PlayerPrefs.DeleteKey("NextStepId_" + currentMapId);
            PlayerPrefs.Save();
            Debug.Log($"[MapManager] ✅ CAS 2: Retour de niveau (via NextStepId) - Passage au step: {currentStepId}");
        }
        // CAS 3: On a un CurrentStepId sauvegardé (navigation interne sur la map)
        else if (!string.IsNullOrEmpty(savedCurrentStepId))
        {
            currentStepId = savedCurrentStepId;
            Debug.Log($"[MapManager] ✅ CAS 3: Reprise depuis CurrentStepId sauvegardé: {currentStepId}");
        }
        // CAS 4: Premier chargement de la map (intro jamais vue)
        else if (firstStep.type == "fullscreen_video" && !introWatched)
        {
            currentStepId = firstStep.id;
            PlayerPrefs.SetString("CurrentStepId_" + currentMapId, currentStepId);
            PlayerPrefs.Save();
            Debug.Log($"[MapManager] ✅ CAS 4: PREMIÈRE VISITE - Démarrage avec l'intro: {currentStepId}");
        }
        // CAS 5: Pas de step sauvegardé → Démarrer au début
        else
        {
            currentStepId = GetFirstPlayableStepId(firstStep, introWatched);
            PlayerPrefs.SetString("CurrentStepId_" + currentMapId, currentStepId);
            PlayerPrefs.Save();
            Debug.Log($"[MapManager] ✅ CAS 5: Démarrage au premier step jouable: {currentStepId}");
        }

        currentStep = mapConfig.steps.Find(s => s.id == currentStepId);
        Debug.Log($"[MapManager] currentStep trouvé: {currentStep?.id} - Type: {currentStep?.type}");

        // OPTIMISATION : Si le step actuel est une vidéo fullscreen, la lancer IMMÉDIATEMENT
        // pendant que les autres éléments se chargent en arrière-plan
        bool isVideoStep = currentStep != null && currentStep.type == "fullscreen_video";
        
        if (isVideoStep)
        {
            Debug.Log("[MapManager] 🎬 OPTIMISATION: Lancement de la vidéo EN PREMIER");
            
            // Masquer l'overlay de transition immédiatement
            if (IntroTransitionOverlay.Instance != null)
            {
                IntroTransitionOverlay.HideOverlay();
            }
            if (UnifiedLoadingManager.Instance != null)
            {
                UnifiedLoadingManager.HideLoading();
            }
            
            // Lancer la vidéo IMMÉDIATEMENT
            ShowFullscreenVideo();
            
            // Charger les autres éléments EN PARALLÈLE pendant que la vidéo joue
            StartCoroutine(LoadMapElementsInBackground());
        }
        else
        {
            // FONCTIONNEMENT CLASSIQUE : Créer la carte directement
            Debug.Log("[MapManager] Création de la carte directement");
            yield return StartCoroutine(LoadMapElementsSequential());
            LoadCurrentStep();
        }
        
        // Restaurer la position du scroll si elle existe
        ScrollRect scrollRect = scrollViewObject != null ? scrollViewObject.GetComponent<ScrollRect>() : null;
        if (scrollRect != null && scrollRect.content != null)
        {
            float savedScrollX = PlayerPrefs.GetFloat("ScrollPosX_" + currentMapId, 0);
            float savedScrollY = PlayerPrefs.GetFloat("ScrollPosY_" + currentMapId, 0);
            
            if (savedScrollX != 0 || savedScrollY != 0)
            {
                // Attendre une frame pour que le layout soit calculé
                yield return null;
                
                scrollRect.content.anchoredPosition = new Vector2(savedScrollX, savedScrollY);
                Debug.Log($"[MapManager] ✅ Position scroll restaurée: ({savedScrollX}, {savedScrollY})");
            }
            else
            {
                Debug.Log($"[MapManager] Aucune position scroll sauvegardée, démarrage à (0, 0)");
            }
        }

        // NE PAS masquer le loading ici - laisser le SceneReadyManager le gérer
        // L'écran de chargement sera masqué automatiquement quand tout sera prêt
        Debug.Log("[MapManager] ✅ Initialisation terminée - en attente de SceneReadyManager");
        
        // ANCIEN CODE (désactivé) :
        // if (UnifiedLoadingManager.Instance != null)
        // {
        //     UnifiedLoadingManager.HideLoadingAfterDelay(0.5f);
        // }
    }

    /// <summary>
    /// Charge les éléments de la map EN ARRIÈRE-PLAN pendant que la vidéo joue
    /// Quand la vidéo se termine, tout est déjà prêt !
    /// </summary>
    IEnumerator LoadMapElementsInBackground()
    {
        Debug.Log("[MapManager] 🔄 Chargement des éléments en arrière-plan (pendant la vidéo)...");
        
        // Charger le background
        yield return StartCoroutine(SetupBackground());
        Debug.Log("[MapManager] ✅ Background chargé (en arrière-plan)");

        // Utiliser HeaderManager avec la configuration depuis general-config.json
        DefaultHeaderConfig headerConfig = GeneralConfigManager.Instance.GetDefaultHeaderConfig();
        if (headerConfig != null && headerConfig.show)
        {
            Canvas canvas = GetMainCanvas();
            if (canvas != null)
            {
                yield return StartCoroutine(HeaderManager.Instance.CreateHeader(canvas, OnHeaderElementClick));
                Debug.Log("[MapManager] ✅ Header chargé (en arrière-plan)");
            }
        }

        // Créer les vidéos décoratives et le popup container
        CreateDecorativeVideos();
        CreatePopupContainer();
        
        Debug.Log("[MapManager] ✅ Tous les éléments chargés en arrière-plan - Prêt pour après la vidéo !");
        
        // Marquer comme prêt pour le SceneReadyManager
        backgroundElementsLoaded = true;
    }
    
    /// <summary>
    /// Chargement séquentiel classique (pour les steps non-vidéo)
    /// </summary>
    IEnumerator LoadMapElementsSequential()
    {
        yield return StartCoroutine(SetupBackground());

        // Utiliser HeaderManager avec la configuration depuis general-config.json
        DefaultHeaderConfig headerConfig = GeneralConfigManager.Instance.GetDefaultHeaderConfig();
        if (headerConfig != null && headerConfig.show)
        {
            Canvas canvas = GetMainCanvas();
            if (canvas != null)
            {
                yield return StartCoroutine(HeaderManager.Instance.CreateHeader(canvas, OnHeaderElementClick));
            }
        }

        CreateDecorativeVideos();
        CreatePopupContainer();
        
        // Masquer l'overlay de transition
        if (IntroTransitionOverlay.Instance != null)
        {
            IntroTransitionOverlay.HideOverlay();
        }
    }
    
    // Flag pour savoir si les éléments de fond sont chargés
    private bool backgroundElementsLoaded = false;
    
    void LoadCurrentStep()
    {
        Debug.Log($"[MapManager] ═══ LoadCurrentStep ═══");
        Debug.Log($"[MapManager] currentStepId au début: '{currentStepId}'");
        Debug.Log($"[MapManager] currentMapId: '{currentMapId}'");
        
        if (mapConfig == null || mapConfig.steps == null || mapConfig.steps.Count == 0)
        {
            Debug.LogError("[MapManager] Aucun step défini!");
            return;
        }

        if (string.IsNullOrEmpty(currentStepId))
        {
            Debug.LogWarning("[MapManager] currentStepId VIDE - utilisation du premier step");
            currentStepId = mapConfig.steps[0].id;
            SaveCurrentStep();
        }

        currentStep = mapConfig.steps.Find(s => s.id == currentStepId);

        if (currentStep == null)
        {
            Debug.LogError($"[MapManager] ❌ Step introuvable: '{currentStepId}'");
            Debug.LogError($"[MapManager] Steps disponibles:");
            foreach (var step in mapConfig.steps)
            {
                Debug.LogError($"  - {step.id} ({step.type})");
            }
            currentStep = mapConfig.steps[0];
            currentStepId = currentStep.id;
            SaveCurrentStep();
        }

        Debug.Log($"[MapManager] ✅ Step actuel: {currentStep.id} ({currentStep.type})");
        Debug.Log($"[MapManager] Zones dans ce step: {currentStep.zones?.Count ?? 0}");
        Debug.Log($"[MapManager] Images décoratives: {currentStep.decorativeImages?.Count ?? 0}");

        // Mettre à jour la jauge de zénitude du header selon l'avancement
        if (HeaderManager.Instance != null)
        {
            HeaderManager.Instance.RefreshMapJauge();
        }

        if (currentStep.type == "fullscreen_video")
        {
            ShowFullscreenVideo();
        }
        else if (currentStep.type == "zones")
        {
            ShowZones();
        }
        
        Debug.Log($"[MapManager] ═══ Fin LoadCurrentStep ═══");
    }

    /// <summary>
    /// Calcule la progression dans la quête (0.0 à 1.0) et retourne la phase de jauge appropriée (1, 2 ou 3)
    /// Retourne 0 si la progression ne peut pas être calculée
    /// </summary>
    public int GetJaugePhase()
    {
        if (mapConfig == null || mapConfig.steps == null || mapConfig.steps.Count == 0)
        {
            Debug.LogWarning("[MapManager] Impossible de calculer la phase : mapConfig ou steps invalides");
            return 0;
        }

        if (string.IsNullOrEmpty(currentStepId))
        {
            Debug.LogWarning("[MapManager] Impossible de calculer la phase : currentStepId vide");
            return 0;
        }

        // IMPORTANT: la jauge doit ignorer les steps non jouables (ex: fullscreen_video)
        // et ne compter que les steps de type "zones".
        List<MapStep> zoneSteps = mapConfig.steps.FindAll(s => s != null && s.type == "zones");
        if (zoneSteps == null || zoneSteps.Count == 0)
        {
            Debug.LogWarning("[MapManager] Impossible de calculer la phase : aucun step de type 'zones'");
            return 0;
        }

        // Trouver l'index du step courant dans la liste des steps "zones"
        int currentZoneIndex = zoneSteps.FindIndex(s => s.id == currentStepId);
        if (currentZoneIndex < 0)
        {
            // Si on est sur un step non-zones, prendre le dernier step zones atteint (selon l'ordre global)
            int globalIndex = mapConfig.steps.FindIndex(s => s != null && s.id == currentStepId);
            if (globalIndex < 0)
            {
                Debug.LogWarning($"[MapManager] Impossible de trouver le step actuel: {currentStepId}");
                return 0;
            }

            currentZoneIndex = 0;
            for (int i = 0; i < zoneSteps.Count; i++)
            {
                int zi = mapConfig.steps.FindIndex(s => s != null && s.id == zoneSteps[i].id);
                if (zi <= globalIndex) currentZoneIndex = i;
                else break;
            }
        }

        // Calculer la progression (0.0 à 1.0) sur les steps "zones" uniquement
        float progression = (float)(currentZoneIndex + 1) / zoneSteps.Count;

        // Déterminer la phase selon la progression
        // 30% premiers steps → phase 1
        // 30% steps suivants → phase 2
        // 40% derniers steps → phase 3
        if (progression <= 0.30f)
        {
            return 1;
        }
        else if (progression <= 0.60f) // 30% + 30% = 60%
        {
            return 2;
        }
        else
        {
            return 3;
        }
    }

    void ShowFullscreenVideo()
    {
        Debug.Log($"[MapManager] ========================================");
        Debug.Log($"[MapManager] DEBUT ShowFullscreenVideo()");
        Debug.Log($"[MapManager] ========================================");
        Debug.Log($"[MapManager] Step: {currentStep.id}");
        Debug.Log($"[MapManager] Video URL originale: {currentStep.videoUrl}");
        
        Canvas canvas = GetMainCanvas();
        if (canvas == null)
        {
            Debug.LogError("[MapManager] Aucun Canvas trouvé pour afficher la vidéo !");
            return;
        }
        Debug.Log($"[MapManager] Canvas trouvé: {canvas.name}");

        // Masquer le header pendant la vidéo plein écran
        if (HeaderManager.Instance != null)
        {
            HeaderManager.Instance.HideHeader();
            Debug.Log("[MapManager] Header masqué pour la vidéo plein écran");
        }

        GameObject videoObj = new GameObject("FullscreenVideo");
        videoObj.transform.SetParent(canvas.transform, false);
        videoObj.transform.SetAsLastSibling();

        RectTransform videoRect = videoObj.AddComponent<RectTransform>();
        videoRect.anchorMin = Vector2.zero;
        videoRect.anchorMax = Vector2.one;
        videoRect.offsetMin = Vector2.zero;
        videoRect.offsetMax = Vector2.zero;

        // Créer une copie du step avec l'URL complète
        string fullVideoUrl = GeneralConfigManager.Instance.GetVideoUrl(currentStep.videoUrl);
        Debug.Log($"[MapManager] Video URL complète: {fullVideoUrl}");
        
        MapStep stepWithFullUrl = new MapStep
        {
            id = currentStep.id,
            type = currentStep.type,
            nextStepId = currentStep.nextStepId,
            videoUrl = fullVideoUrl,
            skipable = currentStep.skipable,
            autoAdvance = currentStep.autoAdvance,
            zones = currentStep.zones,
            decorativeImages = currentStep.decorativeImages
        };

        Debug.Log($"[MapManager] Ajout du composant MapFullscreenVideo...");
        MapFullscreenVideo fullscreenVideo = videoObj.AddComponent<MapFullscreenVideo>();
        Debug.Log($"[MapManager] Appel de Initialize()...");
        
        isFullscreenVideoPlaying = true; // Bloquer les clics globaux pendant la vidéo
        
        // Masquer le header pendant la vidéo plein écran
        if (HeaderManager.Instance != null)
        {
            HeaderManager.Instance.HideHeader();
        }
        
        fullscreenVideo.Initialize(stepWithFullUrl, OnFullscreenVideoComplete);
        Debug.Log($"[MapManager] ShowFullscreenVideo() terminé");
        Debug.Log($"[MapManager] ========================================");
    }

    void OnFullscreenVideoComplete()
    {
        Debug.Log($"[MapManager] OnFullscreenVideoComplete - Step actuel: {currentStep.id}, NextStepId: {currentStep.nextStepId}");

        isFullscreenVideoPlaying = false; // Réactiver les clics globaux
        
        // Réafficher le header après la vidéo plein écran
        if (HeaderManager.Instance != null)
        {
            HeaderManager.Instance.ShowHeader();
        }
        
        // Si c'est l'intro, la marquer comme vue
        if (currentStep.id == "intro" || currentStep.id.Contains("intro"))
        {
            PlayerPrefs.SetInt("IntroWatched_" + currentMapId, 1);
            PlayerPrefs.Save();
            Debug.Log($"[MapManager] ✅ Intro marquée comme vue pour la map {currentMapId}");
        }

        // Attendre que les éléments de fond soient chargés avant de continuer
        StartCoroutine(WaitForBackgroundAndContinue());
    }
    
    /// <summary>
    /// Attend que les éléments de fond soient chargés puis passe au step suivant
    /// </summary>
    IEnumerator WaitForBackgroundAndContinue()
    {
        // Si les éléments ne sont pas encore chargés, attendre
        if (!backgroundElementsLoaded)
        {
            Debug.Log("[MapManager] ⏳ Vidéo terminée - Attente des éléments en arrière-plan...");
            
            float timeout = 10f;
            float elapsed = 0f;
            
            while (!backgroundElementsLoaded && elapsed < timeout)
            {
                elapsed += Time.deltaTime;
                yield return null;
            }
            
            if (!backgroundElementsLoaded)
            {
                Debug.LogWarning("[MapManager] ⚠️ Timeout - Les éléments n'ont pas fini de charger, on continue quand même");
            }
        }
        
        Debug.Log("[MapManager] ✅ Éléments chargés - Passage au step suivant INSTANTANÉ !");

        if (!string.IsNullOrEmpty(currentStep.nextStepId))
        {
            Debug.Log("[MapManager] Passage au step suivant");
            GoToNextStep(currentStep.nextStepId);
        }
        else
        {
            Debug.Log("[MapManager] Dernier step atteint (vidéo outro terminée) - retour scène Main");
            
            // Utiliser UnifiedLoadingManager si disponible pour une meilleure transition
            if (UnifiedLoadingManager.Instance != null)
            {
                UnifiedLoadingManager.LoadMenuScene("main");
            }
            else
            {
                SceneManager.LoadScene("main");
            }
        }
    }


    void ShowZones()
    {
        Debug.Log($"[MapManager] ═══ ShowZones ═══");
        Debug.Log($"[MapManager] currentStep: {currentStep?.id}");
        Debug.Log($"[MapManager] Zones à afficher: {currentStep?.zones?.Count ?? 0}");
        Debug.Log($"[MapManager] backgroundObject null? {backgroundObject == null}, actif? {backgroundObject?.activeInHierarchy}");
        
        ClearZones();
        CreateClickableZones();
        CreateStepDecorativeImages(); // Charger les images décoratives du step
        
        Debug.Log($"[MapManager] ✅ ShowZones terminé - {zones.Count} zones créées, {decorators.Count} decorators dans la liste");
        Debug.Log($"[MapManager] ═══ Fin ShowZones ═══");
    }

    void ClearZones()
    {
        foreach (MapZoneClickable zone in zones)
        {
            if (zone != null)
                Destroy(zone.gameObject);
        }
        zones.Clear();

        foreach (GameObject decorator in decorators)
        {
            if (decorator != null)
                Destroy(decorator);
        }
        decorators.Clear();

        // Nettoyer les images décoratives du step
        foreach (GameObject decorImage in stepDecorativeImages)
        {
            if (decorImage != null)
                Destroy(decorImage);
        }
        stepDecorativeImages.Clear();
    }

    public void GoToNextStep(string nextStepId)
    {
        if (string.IsNullOrEmpty(nextStepId))
        {
            Debug.LogError("[MapManager] nextStepId vide!");
            return;
        }

        currentStepId = nextStepId;
        SaveCurrentStep();
        LoadCurrentStep();
    }

    void SaveCurrentStep()
    {
        PlayerPrefs.SetString("CurrentStepId_" + currentMapId, currentStepId);
        PlayerPrefs.Save();
        Debug.Log($"[MapManager] Step sauvegardé: {currentStepId}");
    }
    
    /// <summary>
    /// Retourne l'ID du premier step jouable (après l'intro si elle a déjà été vue)
    /// </summary>
    string GetFirstPlayableStepId(MapStep firstStep, bool introWatched)
    {
        // Si l'intro a déjà été vue et que le premier step est une vidéo, passer au suivant
        if (introWatched && firstStep.type == "fullscreen_video" && !string.IsNullOrEmpty(firstStep.nextStepId))
        {
            return firstStep.nextStepId;
        }
        return firstStep.id;
    }
    
    /// <summary>
    /// Trouve le step suivant celui qui contient le jeu avec le gameId spécifié
    /// Utilisé pour reprendre une quête après le dernier jeu terminé
    /// </summary>
    /// <param name="gameId">ID du dernier jeu terminé</param>
    /// <returns>ID du step suivant, ou null si non trouvé</returns>
    string FindStepIdAfterGame(int gameId)
    {
        if (mapConfig == null || mapConfig.steps == null || gameId <= 0)
        {
            return null;
        }
        
        foreach (var step in mapConfig.steps)
        {
            // Vérifier seulement les steps de type "zones"
            if (step.type != "zones" || step.zones == null)
            {
                continue;
            }
            
            // Chercher si une des zones contient ce gameId
            foreach (var zone in step.zones)
            {
                if (zone.gameId == gameId)
                {
                    Debug.Log($"[MapManager] 🎯 Jeu {gameId} trouvé dans step '{step.id}' (zone: {zone.id})");
                    
                    // Si ce step a un nextStepId, le retourner
                    if (!string.IsNullOrEmpty(step.nextStepId))
                    {
                        Debug.Log($"[MapManager] ➡️ Step suivant après gameId {gameId}: '{step.nextStepId}'");
                        return step.nextStepId;
                    }
                    else
                    {
                        Debug.Log($"[MapManager] ⚠️ Step '{step.id}' n'a pas de nextStepId - c'est le dernier step");
                        // C'est le dernier step, on reste dessus
                        return step.id;
                    }
                }
            }
        }
        
        Debug.LogWarning($"[MapManager] ⚠️ gameId {gameId} non trouvé dans aucun step");
        return null;
    }

    IEnumerator SetupBackground()
    {
        Canvas canvas = GetMainCanvas();
        if (canvas == null)
        {
            Debug.LogError("[MapManager] Canvas principal introuvable!");
            yield break;
        }

        Debug.Log($"[MapManager] SetupBackground - Canvas trouvé: {canvas.gameObject.name}");

        scrollViewObject = new GameObject("ScrollView");
        scrollViewObject.transform.SetParent(canvas.transform, false);
        scrollViewObject.transform.SetAsFirstSibling();

        // Calculer headerHeight pour le log (depuis general-config.json maintenant)
        DefaultHeaderConfig headerConfig = GeneralConfigManager.Instance.GetDefaultHeaderConfig();
        float headerHeight = (headerConfig != null && headerConfig.show) ? headerConfig.height : 0f;

        RectTransform scrollRect = scrollViewObject.AddComponent<RectTransform>();
        scrollRect.anchorMin = Vector2.zero;
        scrollRect.anchorMax = Vector2.one;
        scrollRect.offsetMin = Vector2.zero;

        // PAS d'offset pour le header - le ScrollView fait toute la hauteur de l'écran
        // Le header sera par-dessus en overlay
        scrollRect.offsetMax = Vector2.zero;  // Pas de -headerHeight
        scrollRect.anchoredPosition = Vector2.zero;
        scrollRect.sizeDelta = Vector2.zero;

        Debug.Log($"[MapManager] SetupBackground - ScrollView créé (plein écran), headerHeight (overlay): {headerHeight}");

        viewportObject = new GameObject("Viewport");
        viewportObject.transform.SetParent(scrollViewObject.transform, false);

        RectTransform viewportRect = viewportObject.AddComponent<RectTransform>();
        viewportRect.anchorMin = Vector2.zero;
        viewportRect.anchorMax = Vector2.one;
        viewportRect.pivot = new Vector2(0.5f, 0.5f);
        viewportRect.anchoredPosition = Vector2.zero;
        viewportRect.sizeDelta = Vector2.zero;
        viewportRect.offsetMin = Vector2.zero;
        viewportRect.offsetMax = Vector2.zero;

        Image viewportImage = viewportObject.AddComponent<Image>();
        viewportImage.color = Color.white;
        // En WebGL, activer raycastTarget pour permettre le scroll avec la molette
        #if UNITY_WEBGL && !UNITY_EDITOR
        viewportImage.raycastTarget = true; // Permet le scroll en WebGL
        #else
        viewportImage.raycastTarget = false;
        #endif

        // NOTE: PAS de Mask pour permettre aux decorators (sprites des personnages) 
        // d'être toujours visibles. Si on a besoin de masquer le contenu hors viewport,
        // il faudrait utiliser une autre approche qui ne masque pas les decorators.

        contentObject = new GameObject("Content");
        contentObject.transform.SetParent(viewportObject.transform, false);

        RectTransform contentRect = contentObject.AddComponent<RectTransform>();
        // Content doit être ancré en haut à gauche avec une taille fixe
        contentRect.anchorMin = new Vector2(0, 1);  // Haut gauche
        contentRect.anchorMax = new Vector2(0, 1);  // Haut gauche
        contentRect.pivot = new Vector2(0, 1);      // Pivot en haut gauche
        contentRect.anchoredPosition = Vector2.zero;
        // La taille sera définie dans LoadBackgroundImageScrollable

        Debug.Log("[MapManager] SetupBackground - Structure créée, chargement image...");

        if (mapConfig.background.type == "image")
        {
            yield return StartCoroutine(LoadBackgroundImageScrollable(contentRect));
        }

        Debug.Log("[MapManager] SetupBackground - Image chargée, ajout ScrollRect...");

        ScrollRect scrollRectComponent = scrollViewObject.AddComponent<ScrollRect>();
        scrollRectComponent.content = contentRect;
        scrollRectComponent.viewport = viewportRect;
        scrollRectComponent.horizontal = true;  // Activer le scroll horizontal
        scrollRectComponent.vertical = false;
        scrollRectComponent.movementType = ScrollRect.MovementType.Clamped;
        scrollRectComponent.scrollSensitivity = 20f; // Sensibilité du scroll (molette de souris)
        scrollRectComponent.enabled = true;
        
        // En WebGL, s'assurer que le ScrollRect peut recevoir les événements de scroll et drag
        #if UNITY_WEBGL && !UNITY_EDITOR
        // Le ScrollRect natif de Unity devrait gérer automatiquement la molette de souris et le drag en WebGL
        // Mais on s'assure que tout est activé et que le viewport peut recevoir les raycasts
        scrollRectComponent.enabled = true;
        
        // **FIX WEBGL : Désactiver l'inertia pour éviter le scroll automatique après avoir lâché**
        scrollRectComponent.inertia = false; // Désactive l'effet de momentum
        Debug.Log("[MapManager] ⚠️ Inertia désactivée pour WebGL - scroll s'arrête immédiatement");
        
        // S'assurer que le viewport peut recevoir les raycasts pour le drag
        if (viewportImage != null)
        {
            viewportImage.raycastTarget = true; // Permet le drag en WebGL
        }
        Debug.Log("[MapManager] ScrollRect configuré pour WebGL - horizontal: true, scrollSensitivity: 20, drag activé, inertia désactivée");
        #endif

        Debug.Log("[MapManager] SetupBackground - TERMINÉ");
    }

    IEnumerator LoadBackgroundImageScrollable(RectTransform contentRect)
    {
        // Construire l'URL complète avec GeneralConfigManager
        string fullUrl = mapConfig.background.type == "image" 
            ? GeneralConfigManager.Instance.GetBackgroundImageUrl(mapConfig.background.url)
            : GeneralConfigManager.Instance.GetBackgroundVideoUrl(mapConfig.background.url);
            
        Debug.Log($"[MapManager] LoadBackgroundImageScrollable - URL: {fullUrl}");

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

            if (www.result == UnityWebRequest.Result.Success)
            {
                // Déclarer les variables avant le try-catch pour pouvoir les utiliser après
                DefaultHeaderConfig headerConfig = GeneralConfigManager.Instance.GetDefaultHeaderConfig();
                float headerHeight = (headerConfig != null && headerConfig.show) ? headerConfig.height : 0f;
                
                // CORRECTION WebGL : Utiliser la résolution de référence du CanvasScaler au lieu de Screen.height
                // En WebGL, Screen.height peut être différent de la résolution de référence à cause du scaling
                float fullScreenHeight = 1080f;  // Résolution de référence du CanvasScaler
                
                // Si on peut récupérer le CanvasScaler, utiliser sa résolution de référence
                Canvas canvas = GetMainCanvas();
                if (canvas != null)
                {
                    CanvasScaler scaler = canvas.GetComponent<CanvasScaler>();
                    if (scaler != null && scaler.uiScaleMode == CanvasScaler.ScaleMode.ScaleWithScreenSize)
                    {
                        fullScreenHeight = scaler.referenceResolution.y;
                        Debug.Log($"[MapManager] Utilisation de la résolution de référence du CanvasScaler: {fullScreenHeight}px");
                    }
                }
                
                float imageAspect = 1f;
                float calculatedWidth = 0f;
                Texture2D loadedTexture = null;
                
                try
                {
                    loadedTexture = ((DownloadHandlerTexture)www.downloadHandler).texture;
                    Debug.Log($"[MapManager] Image chargée: {loadedTexture.width}x{loadedTexture.height}");

                    if (contentObject == null)
                    {
                        Debug.LogError("[MapManager] contentObject est NULL !");
                        yield break;
                    }

                    backgroundObject = new GameObject("MapBackground");
                    backgroundObject.transform.SetParent(contentObject.transform, false);
                    Debug.Log("[MapManager] MapBackground créé");

                    RectTransform bgRect = backgroundObject.AddComponent<RectTransform>();
                    // Ancrer en haut à gauche avec une taille FIXE
                    bgRect.anchorMin = new Vector2(0, 1);  // Haut gauche
                    bgRect.anchorMax = new Vector2(0, 1);  // Haut gauche
                    bgRect.pivot = new Vector2(0, 1);      // Pivot en haut gauche
                    bgRect.anchoredPosition = Vector2.zero;
                    Debug.Log("[MapManager] RectTransform configuré avec ancrage fixe en haut à gauche");

                    // Calculer les dimensions
                    imageAspect = (float)loadedTexture.width / loadedTexture.height;
                    calculatedWidth = fullScreenHeight * imageAspect;  // 1080 × 2.666667 = 2880

                    Debug.Log($"[MapManager] Calculs - fullScreenHeight: {fullScreenHeight}, imageAspect: {imageAspect}, calculatedWidth: {calculatedWidth}");
                    Debug.Log($"[MapManager] Dimensions image originale: {loadedTexture.width}x{loadedTexture.height}");
                    Debug.Log($"[MapManager] Dimensions calculées pour l'affichage: {calculatedWidth}x{fullScreenHeight}");

                    // Définir une taille FIXE pour l'image
                    bgRect.sizeDelta = new Vector2(calculatedWidth, fullScreenHeight);
                    Debug.Log($"[MapManager] bgRect.sizeDelta défini: {calculatedWidth}x{fullScreenHeight}");

                    RawImage rawImage = backgroundObject.AddComponent<RawImage>();
                    rawImage.texture = loadedTexture;
                    rawImage.raycastTarget = false;
                    rawImage.color = Color.white;
                    rawImage.uvRect = new Rect(0, 0, 1, 1);
                    
                    Debug.Log($"[MapManager] RawImage configurée sans AspectRatioFitter (taille fixe)");
                    
                    // Content doit avoir la largeur calculée pour permettre le scroll horizontal
                    // et la hauteur de l'écran complet
                    contentRect.sizeDelta = new Vector2(calculatedWidth, fullScreenHeight);
                    Debug.Log($"[MapManager] Tailles appliquées - bgRect: {calculatedWidth}x{fullScreenHeight}, contentRect: {calculatedWidth}x{fullScreenHeight}");
                }
                catch (System.Exception e)
                {
                    Debug.LogError($"[MapManager] ERREUR dans LoadBackgroundImageScrollable: {e.Message}");
                    Debug.LogError($"[MapManager] Stack trace: {e.StackTrace}");
                    yield break;
                }
                
                // Attendre une frame pour que le layout soit calculé
                yield return null;
                
                // Vérifier les dimensions réelles après le layout
                if (backgroundObject != null)
                {
                    RectTransform bgRect = backgroundObject.GetComponent<RectTransform>();
                    if (bgRect != null)
                    {
                        float actualWidth = bgRect.rect.width;
                        float actualHeight = bgRect.rect.height;
                        float actualAspect = actualWidth / actualHeight;
                        
                        Debug.Log($"[MapManager] ⚠️ DIMENSIONS RÉELLES après layout - width: {actualWidth}, height: {actualHeight}, actualAspect: {actualAspect:F6} (attendu: {imageAspect:F6})");
                        Debug.Log($"[MapManager] Attendu: {calculatedWidth}x{fullScreenHeight} = {imageAspect:F6}");
                        
                        if (Mathf.Abs(actualAspect - imageAspect) > 0.01f)
                        {
                            Debug.LogWarning($"[MapManager] ⚠️ PROBLÈME DE RATIO - l'image sera déformée ! Ratio actuel: {actualAspect:F6}, ratio attendu: {imageAspect:F6}");
                        }
                    }
                }
                
                // Utiliser les variables déjà calculées pour CreateScrollDragArea
                // Si l'image n'a pas été chargée, utiliser des valeurs par défaut
                if (calculatedWidth == 0f && backgroundObject != null)
                {
                    RawImage rawImg = backgroundObject.GetComponent<RawImage>();
                    if (rawImg != null && rawImg.texture != null)
                    {
                        Texture2D tex = rawImg.texture as Texture2D;
                        if (tex != null)
                        {
                            imageAspect = (float)tex.width / tex.height;
                            calculatedWidth = fullScreenHeight * imageAspect;
                        }
                    }
                }
                
                CreateScrollDragArea(calculatedWidth, fullScreenHeight);

                Debug.Log("[MapManager] LoadBackgroundImageScrollable - TERMINÉ");
            }
            else
            {
                Debug.LogError($"[MapManager] Erreur chargement image: {www.error}");
            }
        }
    }

    void CreateScrollDragArea(float width, float height)
    {
        if (contentObject == null) return;

        GameObject dragArea = new GameObject("ScrollDragArea");
        dragArea.transform.SetParent(contentObject.transform, false);

        RectTransform dragRect = dragArea.AddComponent<RectTransform>();
        dragRect.anchorMin = Vector2.zero;
        dragRect.anchorMax = Vector2.zero;
        dragRect.pivot = new Vector2(0, 0.5f);
        dragRect.anchoredPosition = Vector2.zero;
        dragRect.sizeDelta = new Vector2(width, height);

        Image dragImage = dragArea.AddComponent<Image>();
        dragImage.color = new Color(0, 0, 0, 0);
        dragImage.raycastTarget = true;
    }

    // NOTE: CreateHeader() et CreateHeaderElement() supprimés - maintenant géré par HeaderManager

    void CreateClickableZones()
    {
        if (backgroundObject == null || currentStep == null || currentStep.zones == null)
        {
            Debug.LogError("[MapManager] Impossible de créer les zones");
            return;
        }

        Debug.Log($"[MapManager] CreateClickableZones - {currentStep.zones.Count} zones à créer");

        GameObject zonesContainerObj = new GameObject("ZonesContainer");
        zonesContainerObj.transform.SetParent(backgroundObject.transform, false);

        RectTransform zonesRect = zonesContainerObj.AddComponent<RectTransform>();
        zonesRect.anchorMin = Vector2.zero;
        zonesRect.anchorMax = Vector2.one;
        zonesRect.pivot = new Vector2(0, 0);
        zonesRect.anchoredPosition = Vector2.zero;
        zonesRect.offsetMin = Vector2.zero;
        zonesRect.offsetMax = Vector2.zero;

        RawImage rawImage = backgroundObject.GetComponent<RawImage>();
        Texture2D texture = rawImage.texture as Texture2D;
        RectTransform bgRect = backgroundObject.GetComponent<RectTransform>();

        float scaleX = bgRect.sizeDelta.x / texture.width;
        float scaleY = bgRect.sizeDelta.y / texture.height;  // Utiliser la hauteur réelle de l'image

        Debug.Log($"[MapManager] CreateClickableZones - bgRect: {bgRect.sizeDelta.x}x{bgRect.sizeDelta.y}, texture: {texture.width}x{texture.height}, scaleX: {scaleX}, scaleY: {scaleY}");

        Texture2D whiteTex = new Texture2D(1, 1);
        whiteTex.SetPixel(0, 0, Color.white);
        whiteTex.Apply();
        Sprite whiteSprite = Sprite.Create(whiteTex, new Rect(0, 0, 1, 1), new Vector2(0.5f, 0.5f));

        foreach (MapZone zoneData in currentStep.zones)
        {
            GameObject zoneObj = new GameObject($"Zone_{zoneData.id}");
            zoneObj.transform.SetParent(zonesContainerObj.transform, false);

            RectTransform rt = zoneObj.AddComponent<RectTransform>();
            rt.anchorMin = Vector2.zero;
            rt.anchorMax = Vector2.zero;
            rt.pivot = new Vector2(0.5f, 0.5f);

            Vector2 scaledPos = new Vector2(zoneData.position.x * scaleX, zoneData.position.y * scaleY);
            Vector2 scaledSize = new Vector2(zoneData.size.x * scaleX, zoneData.size.y * scaleY);

            rt.anchoredPosition = scaledPos;
            rt.sizeDelta = scaledSize;

            Image img = zoneObj.AddComponent<Image>();
            img.sprite = whiteSprite;
            img.raycastTarget = true;
            img.enabled = showDebugZones;

            if (ColorUtility.TryParseHtmlString(zoneData.debugColor, out Color debugColor))
            {
                img.color = debugColor;
            }

            MapZoneClickable clickable = zoneObj.AddComponent<MapZoneClickable>();
            clickable.Initialize(zoneData, this);

            zones.Add(clickable);

            if (!string.IsNullOrEmpty(zoneData.decoratorImageUrl))
            {
                StartCoroutine(CreateZoneDecoratorWithImage(zoneData, zonesContainerObj.transform, scaleX, scaleY));
            }
        }

        Debug.Log($"[MapManager] {zones.Count} zones créées pour step {currentStep.id}");
        Debug.Log($"[MapManager] ZonesContainer actif: {zonesContainerObj.activeInHierarchy}, parent: {zonesContainerObj.transform.parent?.name}");
    }

    string GetFullPath(Transform transform)
    {
        string path = transform.name;
        while (transform.parent != null)
        {
            transform = transform.parent;
            path = transform.name + "/" + path;
        }
        return path;
    }

    IEnumerator CreateZoneDecoratorWithImage(MapZone zone, Transform parent, float scaleX, float scaleY)
    {
        // Construire l'URL complète avec GeneralConfigManager
        string fullImageUrl = GeneralConfigManager.Instance.GetDecoratorImageUrl(zone.decoratorImageUrl);
        Debug.Log($"[MapManager] 🖼️ Chargement decorator pour zone '{zone.id}': {fullImageUrl}");
        
        using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(fullImageUrl))
        {
            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));

                GameObject decorObj = new GameObject($"Decorator_{zone.id}");
                decorObj.transform.SetParent(parent, false);

                RectTransform rect = decorObj.AddComponent<RectTransform>();
                rect.anchorMin = Vector2.zero;
                rect.anchorMax = Vector2.zero;
                rect.pivot = new Vector2(0.5f, 0.5f);

                Vector2 scaledPos = new Vector2(zone.position.x * scaleX, zone.position.y * scaleY);

                float decoratorWidth = zone.decoratorSize?.x ?? mapConfig.zoneDecorator?.size?.x ?? 50f;
                float decoratorHeight = zone.decoratorSize?.y ?? mapConfig.zoneDecorator?.size?.y ?? 50f;
                Vector2 scaledSize = new Vector2(decoratorWidth * scaleX, decoratorHeight * scaleY);

                rect.anchoredPosition = scaledPos;
                rect.sizeDelta = scaledSize;

                Image image = decorObj.AddComponent<Image>();
                image.sprite = sprite;
                image.raycastTarget = true;  // Activer les clics sur le decorator
                image.color = Color.white; // Couleur blanche = opaque
                image.enabled = true;
                
                // Ajouter MapZoneClickable sur le decorator
                // Il est maintenant sûr car MapZoneClickable ne touche plus la couleur si sprite != null
                MapZoneClickable clickable = decorObj.AddComponent<MapZoneClickable>();
                clickable.Initialize(zone, this);

                // IMPORTANT : S'assurer que le decorator est bien actif et visible
                decorObj.SetActive(true);
                image.enabled = true;
                
                decorators.Add(decorObj);
                
                // CRUCIAL : Ajouter le clickable du decorator à la liste zones
                // Sinon HandleClick() ne le détectera jamais !
                zones.Add(clickable);
                
                Debug.Log($"[MapManager] ✅ Decorator créé pour '{zone.id}' à la position {scaledPos}, taille {scaledSize}, actif: {decorObj.activeInHierarchy}");
                Debug.Log($"[MapManager] Decorator parent: {decorObj.transform.parent?.name}, hiérarchie complète: {GetFullPath(decorObj.transform)}");
                Debug.Log($"[MapManager] Image enabled: {image.enabled}, color: {image.color}, sprite null?: {image.sprite == null}, raycastTarget: {image.raycastTarget}");
                Debug.Log($"[MapManager] ✅ Decorator ajouté à la liste zones (total zones: {zones.Count})");
            }
            else
            {
                Debug.LogError($"[MapManager] ❌ Erreur chargement decorator pour '{zone.id}': {www.error}");
            }
        }
    }

    void CreatePopupContainer()
    {
        popupContainer = new GameObject("PopupContainer");

        Canvas popupCanvas = popupContainer.AddComponent<Canvas>();
        popupCanvas.renderMode = RenderMode.ScreenSpaceOverlay;
        popupCanvas.sortingOrder = 1000;

        CanvasScaler scaler = popupContainer.AddComponent<CanvasScaler>();
        scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
        scaler.referenceResolution = new Vector2(1920, 1080);

        popupContainer.AddComponent<GraphicRaycaster>();

        RectTransform rect = popupContainer.GetComponent<RectTransform>();
        rect.anchorMin = Vector2.zero;
        rect.anchorMax = Vector2.one;
        rect.sizeDelta = Vector2.zero;

        DontDestroyOnLoad(popupContainer);
        popupContainer.SetActive(true);
    }

    void CreateDecorativeVideos()
    {
        if (mapConfig.decorativeVideos == null || mapConfig.decorativeVideos.Count == 0 || backgroundObject == null)
        {
            Debug.Log("[MapManager] CreateDecorativeVideos - Aucune vidéo ou background null");
            return;
        }

        Debug.Log($"[MapManager] CreateDecorativeVideos - {mapConfig.decorativeVideos.Count} vidéos à créer");

        RawImage rawImage = backgroundObject.GetComponent<RawImage>();
        Texture2D texture = rawImage.texture as Texture2D;
        RectTransform bgRect = backgroundObject.GetComponent<RectTransform>();

        float scaleX = bgRect.sizeDelta.x / texture.width;
        float scaleY = bgRect.sizeDelta.y / texture.height;  // Utiliser la hauteur réelle de l'image

        Debug.Log($"[MapManager] CreateDecorativeVideos - bgRect: {bgRect.sizeDelta.x}x{bgRect.sizeDelta.y}, texture: {texture.width}x{texture.height}, scaleX: {scaleX}, scaleY: {scaleY}");

        foreach (MapDecorativeVideo videoData in mapConfig.decorativeVideos)
        {
            StartCoroutine(CreateDecorativeVideo(videoData, scaleX, scaleY));
        }
    }

    // Helper pour résoudre les paramètres de vignette (vidéo > global > défaut)
    (float extent, float startRadius, float curve, bool disableMask) ResolveVignetteParams(MapDecorativeVideo videoData)
    {
        // Si la vidéo a sa propre config, l'utiliser directement
        if (videoData.vignetteConfig != null)
        {
            float extent = videoData.vignetteConfig.extent > 0f ? videoData.vignetteConfig.extent : vignetteExtent;
            float startRadius = (videoData.vignetteConfig.startRadius >= 0f && videoData.vignetteConfig.startRadius <= 1f) 
                ? videoData.vignetteConfig.startRadius : vignetteStartRadius;
            float curve = videoData.vignetteConfig.curve > 0f ? videoData.vignetteConfig.curve : vignetteCurve;
            bool disableMask = videoData.vignetteConfig.disableMask;
            
            Debug.Log($"[MapManager] Vignette résolue pour {videoData.id} (config vidéo) - extent: {extent}, startRadius: {startRadius}, curve: {curve}, disableMask: {disableMask}");
            return (extent, startRadius, curve, disableMask);
        }

        // Sinon, utiliser la config globale si elle existe
        if (mapConfig.vignetteConfig != null)
        {
            float extent = mapConfig.vignetteConfig.extent > 0f ? mapConfig.vignetteConfig.extent : vignetteExtent;
            float startRadius = (mapConfig.vignetteConfig.startRadius >= 0f && mapConfig.vignetteConfig.startRadius <= 1f) 
                ? mapConfig.vignetteConfig.startRadius : vignetteStartRadius;
            float curve = mapConfig.vignetteConfig.curve > 0f ? mapConfig.vignetteConfig.curve : vignetteCurve;
            bool disableMask = mapConfig.vignetteConfig.disableMask;
            
            Debug.Log($"[MapManager] Vignette résolue pour {videoData.id} (config globale) - extent: {extent}, startRadius: {startRadius}, curve: {curve}, disableMask: {disableMask}");
            return (extent, startRadius, curve, disableMask);
        }

        // Sinon, utiliser les valeurs par défaut
        Debug.Log($"[MapManager] Vignette résolue pour {videoData.id} (valeurs par défaut) - extent: {vignetteExtent}, startRadius: {vignetteStartRadius}, curve: {vignetteCurve}, disableMask: {disableVignetteMask}");
        return (vignetteExtent, vignetteStartRadius, vignetteCurve, disableVignetteMask);
    }

    IEnumerator CreateDecorativeVideo(MapDecorativeVideo videoData, float scaleX, float scaleY)
    {
        GameObject videoContainer = new GameObject($"DecorativeVideo_{videoData.id}");
        videoContainer.transform.SetParent(backgroundObject.transform, false);

        RectTransform videoRect = videoContainer.AddComponent<RectTransform>();
        videoRect.anchorMin = Vector2.zero;
        videoRect.anchorMax = Vector2.zero;
        videoRect.pivot = new Vector2(0.5f, 0.5f);

        Vector2 scaledPos = new Vector2(videoData.position.x * scaleX, videoData.position.y * scaleY);
        Vector2 scaledSize = new Vector2(videoData.size.x * scaleX, videoData.size.y * scaleY);

        videoRect.anchoredPosition = scaledPos;
        videoRect.sizeDelta = scaledSize;

        RenderTexture renderTexture = new RenderTexture(1024, 1024, 0);
        renderTexture.Create();

        GameObject videoPlayerObj = new GameObject("VideoPlayer");
        videoPlayerObj.transform.SetParent(videoContainer.transform, false);

        VideoPlayer videoPlayer = videoPlayerObj.AddComponent<VideoPlayer>();
        videoPlayer.playOnAwake = false;
        videoPlayer.renderMode = VideoRenderMode.RenderTexture;
        videoPlayer.targetTexture = renderTexture;
        videoPlayer.isLooping = videoData.loop;
        videoPlayer.aspectRatio = VideoAspectRatio.FitInside;
        videoPlayer.audioOutputMode = VideoAudioOutputMode.None;
        
        // Construire l'URL complète avec GeneralConfigManager
        string fullVideoUrl = GeneralConfigManager.Instance.GetVideoUrl(videoData.videoUrl);
        videoPlayer.url = fullVideoUrl;

        GameObject rawImageObj = new GameObject("VideoDisplay");
        rawImageObj.transform.SetParent(videoContainer.transform, false);

        RectTransform rawImageRect = rawImageObj.AddComponent<RectTransform>();
        rawImageRect.anchorMin = Vector2.zero;
        rawImageRect.anchorMax = Vector2.one;
        rawImageRect.offsetMin = Vector2.zero;
        rawImageRect.offsetMax = Vector2.zero;

        RawImage rawImage = rawImageObj.AddComponent<RawImage>();
        rawImage.texture = renderTexture;
        rawImage.raycastTarget = false;

        // Résoudre les paramètres de vignette (vidéo > global > défaut)
        Debug.Log($"[MapManager] CreateDecorativeVideo - {videoData.id} - vignetteConfig null? {videoData.vignetteConfig == null}");
        if (videoData.vignetteConfig != null)
        {
            Debug.Log($"[MapManager] CreateDecorativeVideo - {videoData.id} - vignetteConfig: extent={videoData.vignetteConfig.extent}, startRadius={videoData.vignetteConfig.startRadius}, curve={videoData.vignetteConfig.curve}, disableMask={videoData.vignetteConfig.disableMask}");
        }
        var vignetteParams = ResolveVignetteParams(videoData);
        
        // Créer la texture de masque de vignette (toujours créée, même si le shader n'est pas disponible)
        Texture2D maskTex = CreateVignetteTexture(512, vignetteParams.extent, vignetteParams.startRadius, vignetteParams.curve, vignetteParams.disableMask);
        
        // CORRECTION WebGL : Charger le shader une seule fois et le réutiliser
        if (!shaderChecked)
        {
            // Essayer Shader.Find d'abord
            videoAlphaMaskShader = Shader.Find("UI/VideoAlphaMask");
            
            // Si ça échoue (WebGL), charger depuis Resources
            if (videoAlphaMaskShader == null)
            {
                videoAlphaMaskShader = Resources.Load<Shader>("Shaders/UIVideoAlphaMask");
                if (videoAlphaMaskShader != null)
                {
                    Debug.Log("[MapManager] ✅ Shader 'UI/VideoAlphaMask' chargé depuis Resources");
                }
            }
            else
            {
                Debug.Log($"[MapManager] ✅ Shader 'UI/VideoAlphaMask' trouvé via Shader.Find");
            }
            
            if (videoAlphaMaskShader == null)
            {
                Debug.LogWarning("[MapManager] ⚠️ Shader 'UI/VideoAlphaMask' introuvable!");
            }
            
            shaderChecked = true;
        }
        
        Shader videoShader = videoAlphaMaskShader;
        
        // Vérifier si le shader est supporté sur cette plateforme
        if (videoShader != null && !videoShader.isSupported)
        {
            Debug.LogWarning($"[MapManager] ⚠️ Shader 'UI/VideoAlphaMask' non supporté sur cette plateforme");
            videoShader = null;
        }
        
        // Si le shader n'est toujours pas trouvé, utiliser UI/Default
        if (videoShader == null)
        {
            Debug.LogWarning($"[MapManager] ⚠️ Shader 'UI/VideoAlphaMask' introuvable ou non supporté, utilisation de 'UI/Default' pour {videoData.id}");
            videoShader = Shader.Find("UI/Default");
        }
        
        if (videoShader != null)
        {
            Material videoMaskMat = new Material(videoShader);
            
            // Seulement définir la texture de masque si le shader le supporte
            if (videoShader.name.Contains("VideoAlphaMask"))
            {
                videoMaskMat.SetTexture("_MaskTex", maskTex);
                Debug.Log($"[MapManager] ✅ Texture de masque appliquée au shader VideoAlphaMask pour {videoData.id}");
            }
            else
            {
                // Si on utilise UI/Default, le shader ne supporte pas le masque
                // En WebGL, on peut utiliser un CanvasGroup avec alpha pour simuler la transparence
                // mais cela ne donnera pas l'effet de vignette exact
                Debug.LogWarning($"[MapManager] ⚠️ Shader VideoAlphaMask non disponible, effet de vignette désactivé pour {videoData.id}");
            }
            
            rawImage.material = videoMaskMat;
            Debug.Log($"[MapManager] ✅ Matériau vidéo créé avec shader: {videoShader.name}");
        }
        else
        {
            Debug.LogError($"[MapManager] ❌ Aucun shader disponible pour les vidéos décoratives!");
        }
        
        rawImage.color = Color.white;

        videoPlayer.Prepare();
        videoPlayer.prepareCompleted += (source) =>
        {
            if (videoData.autoPlay)
            {
                videoPlayer.Play();
            }
        };

        decorativeVideos.Add(videoContainer);
        yield return null;
    }

    Texture2D CreateVignetteTexture(int size, float extent, float startRadius, float curve, bool disableMask)
    {
        Texture2D texture = new Texture2D(size, size, TextureFormat.RGBA32, false);
        Color[] pixels = new Color[size * size];

        float center = size / 2f;
        float maxDist = center * extent;

        for (int y = 0; y < size; y++)
        {
            for (int x = 0; x < size; x++)
            {
                float dx = x - center;
                float dy = y - center;
                float dist = Mathf.Sqrt(dx * dx + dy * dy);
                
                // Si le masque est désactivé, alpha = 1 partout
                float alpha;
                if (disableMask)
                {
                    alpha = 1f;
                }
                else
                {
                    alpha = 1f - Mathf.Clamp01((dist - center * startRadius) / (maxDist - center * startRadius));
                    alpha = Mathf.Pow(alpha, curve);
                }
                
                pixels[y * size + x] = new Color(1f, 1f, 1f, alpha);
            }
        }

        texture.SetPixels(pixels);
        texture.Apply();
        texture.filterMode = FilterMode.Bilinear;
        return texture;
    }

    void CreateStepDecorativeImages()
    {
        if (currentStep == null || currentStep.decorativeImages == null || currentStep.decorativeImages.Count == 0 || backgroundObject == null)
        {
            Debug.Log("[MapManager] CreateStepDecorativeImages - Aucune image décorative ou background null");
            return;
        }

        Debug.Log($"[MapManager] CreateStepDecorativeImages - {currentStep.decorativeImages.Count} images à créer");

        RawImage rawImage = backgroundObject.GetComponent<RawImage>();
        Texture2D texture = rawImage.texture as Texture2D;
        RectTransform bgRect = backgroundObject.GetComponent<RectTransform>();

        float scaleX = bgRect.sizeDelta.x / texture.width;
        float scaleY = bgRect.sizeDelta.y / texture.height;  // Utiliser la hauteur réelle de l'image

        Debug.Log($"[MapManager] CreateStepDecorativeImages - bgRect: {bgRect.sizeDelta.x}x{bgRect.sizeDelta.y}, texture: {texture.width}x{texture.height}, scaleX: {scaleX}, scaleY: {scaleY}");

        foreach (MapDecorativeImage imageData in currentStep.decorativeImages)
        {
            StartCoroutine(CreateStepDecorativeImage(imageData, scaleX, scaleY));
        }
    }

    IEnumerator CreateStepDecorativeImage(MapDecorativeImage imageData, float scaleX, float scaleY)
    {
        // Construire l'URL complète avec GeneralConfigManager
        string fullImageUrl = GeneralConfigManager.Instance.GetDecoratorImageUrl(imageData.imageUrl);
        
        using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(fullImageUrl))
        {
            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));

                GameObject imageObj = new GameObject($"StepDecorativeImage_{imageData.id}");
                imageObj.transform.SetParent(backgroundObject.transform, false);

                RectTransform rect = imageObj.AddComponent<RectTransform>();
                rect.anchorMin = Vector2.zero;
                rect.anchorMax = Vector2.zero;
                rect.pivot = new Vector2(0.5f, 0.5f);

                Vector2 scaledPos = new Vector2(imageData.position.x * scaleX, imageData.position.y * scaleY);
                Vector2 scaledSize = new Vector2(imageData.size.x * scaleX, imageData.size.y * scaleY);

                rect.anchoredPosition = scaledPos;
                rect.sizeDelta = scaledSize;

                Image image = imageObj.AddComponent<Image>();
                image.sprite = sprite;
                image.raycastTarget = false;

                Canvas canvas = imageObj.AddComponent<Canvas>();
                canvas.overrideSorting = true;
                canvas.sortingOrder = imageData.sortingOrder;

                stepDecorativeImages.Add(imageObj);
                
                // Ajouter un effet de clignotement pour attirer l'attention
                StartCoroutine(BlinkDecorativeImage(image));

                Debug.Log($"[MapManager] Image décorative créée avec effet de clignotement: {imageData.id} à la position {scaledPos}");
            }
            else
            {
                Debug.LogError($"[MapManager] Erreur chargement image décorative {imageData.id}: {www.error}");
            }
        }
    }

    /// <summary>
    /// Fait clignoter une image décorative pour attirer l'attention du joueur
    /// </summary>
    IEnumerator BlinkDecorativeImage(Image image)
    {
        if (image == null) yield break;
        
        float blinkDuration = 0.8f; // Durée d'un cycle complet (fade out + fade in)
        float minAlpha = 0.3f; // Alpha minimum (30% visible)
        float maxAlpha = 1f; // Alpha maximum (100% visible)
        
        while (image != null && image.gameObject != null && image.gameObject.activeInHierarchy)
        {
            // Fade out
            float elapsed = 0f;
            while (elapsed < blinkDuration / 2)
            {
                if (image == null || image.gameObject == null) yield break;
                
                elapsed += Time.deltaTime;
                float t = elapsed / (blinkDuration / 2);
                Color color = image.color;
                color.a = Mathf.Lerp(maxAlpha, minAlpha, t);
                image.color = color;
                yield return null;
            }
            
            // Fade in
            elapsed = 0f;
            while (elapsed < blinkDuration / 2)
            {
                if (image == null || image.gameObject == null) yield break;
                
                elapsed += Time.deltaTime;
                float t = elapsed / (blinkDuration / 2);
                Color color = image.color;
                color.a = Mathf.Lerp(minAlpha, maxAlpha, t);
                image.color = color;
                yield return null;
            }
        }
    }

    void Update()
    {
        // Gérer la touche ESC pour retourner à la scène main
        if (Keyboard.current != null && Keyboard.current.escapeKey.wasPressedThisFrame)
        {
            Debug.Log("[MapManager] 🔙 Touche ESC détectée - Retour à la scène main");
            
            // IMPORTANT : Sauvegarder l'état actuel avant de quitter
            if (!string.IsNullOrEmpty(currentStepId))
            {
                PlayerPrefs.SetString("CurrentStepId_" + currentMapId, currentStepId);
                // CRUCIAL : Supprimer NextStepId car on ne revient PAS d'un niveau
                // Sinon NextStepId aura la priorité au prochain chargement !
                PlayerPrefs.DeleteKey("NextStepId_" + currentMapId);
                
                // IMPORTANT : Sauvegarder la position du scroll
                ScrollRect scrollRect = scrollViewObject != null ? scrollViewObject.GetComponent<ScrollRect>() : null;
                if (scrollRect != null && scrollRect.content != null)
                {
                    Vector2 scrollPos = scrollRect.content.anchoredPosition;
                    PlayerPrefs.SetFloat("ScrollPosX_" + currentMapId, scrollPos.x);
                    PlayerPrefs.SetFloat("ScrollPosY_" + currentMapId, scrollPos.y);
                    Debug.Log($"[MapManager] ✅ Position scroll sauvegardée: {scrollPos}");
                }
                
                PlayerPrefs.Save();
                Debug.Log($"[MapManager] ✅ État sauvegardé - CurrentStepId: {currentStepId}");
                Debug.Log($"[MapManager] ✅ NextStepId nettoyé pour éviter les conflits");
            }
            
            if (UnifiedLoadingManager.Instance != null)
            {
                UnifiedLoadingManager.LoadMenuScene("main");
            }
            else
            {
                SceneManager.LoadScene("main");
            }
            return;
        }

        // Ne pas gérer les clics si le panneau des paramètres ou la popup de login est ouvert
        if (SettingsManager.Instance != null && SettingsManager.Instance.settingsPanel != null && SettingsManager.Instance.settingsPanel.activeSelf)
        {
            // Panneau des paramètres ouvert, ne pas gérer les clics mais continuer le scroll
            HandleDragScroll();
            HandleScroll();
            HandleDebugToggle();
            UpdatePopupPosition();
            CheckDecorativeVideosPlaying();
            return;
        }
        
        // Vérifier si la popup de login est ouverte
        LoginPopup loginPopup = FindFirstObjectByType<LoginPopup>();
        if (loginPopup != null)
        {
            // Popup de login ouverte, ne pas gérer les clics mais continuer le scroll
            HandleDragScroll();
            HandleScroll();
            HandleDebugToggle();
            UpdatePopupPosition();
            CheckDecorativeVideosPlaying();
            return;
        }
        
        HandleDragScroll();
        HandleScroll();
        HandleClick();
        HandleDebugToggle();
        UpdatePopupPosition(); // NOUVEAU : Mettre à jour la position de la popup
        CheckDecorativeVideosPlaying(); // Vérifier que les vidéos décoratives jouent
    }

    void HandleDebugToggle()
    {
        if (Keyboard.current != null && Keyboard.current.dKey.wasPressedThisFrame)
        {
            showDebugZones = !showDebugZones;
            ToggleZonesVisibility();
            
            // Toggle des vidéos décoratives
            decorativeVideosVisible = !decorativeVideosVisible;
            ToggleDecorativeVideos();
            
            Debug.Log($"[MapManager] Debug toggle - Zones: {showDebugZones}, Vidéos: {decorativeVideosVisible}");
        }
    }

    void UpdatePopupPosition()
    {
        // Mettre à jour la position du popup pour qu'il suive la zone pendant le scroll
        if (activePopup != null && activePopupZone != null)
        {
            // Obtenir les coins de la zone et du canvas popup en coordonnées monde
            Vector3[] zoneCorners = new Vector3[4];
            activePopupZone.GetWorldCorners(zoneCorners);
            Vector3 zoneCenter = (zoneCorners[0] + zoneCorners[2]) * 0.5f;
            
            // Pour ScreenSpaceOverlay, GetWorldCorners retourne directement des positions en pixels écran
            // Obtenir le coin inférieur gauche du canvas popup
            RectTransform popupCanvasRect = popupContainer.GetComponent<RectTransform>();
            Vector3[] popupCanvasCorners = new Vector3[4];
            popupCanvasRect.GetWorldCorners(popupCanvasCorners);
            Vector3 popupCanvasBottomLeft = popupCanvasCorners[0];
            
            // Calculer l'offset du centre de la zone depuis le coin inférieur gauche du canvas popup
            Vector2 offset = new Vector2(
                zoneCenter.x - popupCanvasBottomLeft.x,
                zoneCenter.y - popupCanvasBottomLeft.y
            );
            
            RectTransform popupRect = activePopup.GetComponent<RectTransform>();
            popupRect.anchoredPosition = offset;
        }
    }

    void CheckDecorativeVideosPlaying()
    {
        // Vérifier toutes les 60 frames (environ 1 seconde à 60fps) pour ne pas surcharger
        if (Time.frameCount % 60 != 0) return;
        
        if (mapConfig == null || mapConfig.decorativeVideos == null) return;
        
        foreach (GameObject videoContainer in decorativeVideos)
        {
            if (videoContainer == null) continue;
            
            VideoPlayer videoPlayer = videoContainer.GetComponentInChildren<VideoPlayer>();
            if (videoPlayer == null) continue;
            
            // Trouver les données de cette vidéo pour vérifier autoPlay
            string videoId = videoContainer.name.Replace("DecorativeVideo_", "");
            MapDecorativeVideo videoData = mapConfig.decorativeVideos.Find(v => v.id == videoId);
            
            if (videoData == null || !videoData.autoPlay) continue;
            
            // Si la vidéo est préparée mais ne joue pas, la relancer
            if (videoPlayer.isPrepared && !videoPlayer.isPlaying)
            {
                videoPlayer.Play();
            }
        }
    }

    Vector2 ConvertScreenToMapPosition(Vector2 screenPos)
    {
        // Convertir la position de clic de l'écran vers l'espace de la carte
        // Résolution de votre map : 5760x2160
        if (scrollViewObject != null && backgroundObject != null)
        {
            ScrollRect scrollRect = scrollViewObject.GetComponent<ScrollRect>();
            RectTransform bgRect = backgroundObject.GetComponent<RectTransform>();
            
            if (scrollRect != null && bgRect != null)
            {
                // Obtenir la position actuelle du scroll
                Vector2 scrollOffset = scrollRect.content.anchoredPosition;
                
                // Calculer les facteurs de scale pour votre résolution spécifique
                float mapWidth = 5760f;  // Votre résolution de map
                float mapHeight = 2160f;
                
                // Calculer le scale basé sur la taille réelle de l'image affichée
                RawImage rawImage = backgroundObject.GetComponent<RawImage>();
                if (rawImage != null && rawImage.texture != null)
                {
                    Texture2D texture = rawImage.texture as Texture2D;
                    
                    // CORRECTION : Vérifier que les dimensions de texture sont valides
                    if (texture == null || texture.width <= 0 || texture.height <= 0)
                    {
                        Debug.LogWarning($"[MapManager] Texture invalide pour conversion de position. Utilisation de la taille du RectTransform.");
                        // Utiliser directement screenPos comme position relative
                        return screenPos;
                    }
                    
                    float scaleX = bgRect.sizeDelta.x / texture.width;
                    float scaleY = bgRect.sizeDelta.y / texture.height;
                    
                    // CORRECTION : Vérifier que les scales ne sont pas zéro ou invalides
                    if (scaleX <= 0 || scaleY <= 0 || float.IsInfinity(scaleX) || float.IsInfinity(scaleY))
                    {
                        Debug.LogWarning($"[MapManager] Scale invalide (scaleX={scaleX}, scaleY={scaleY}). Utilisation de screenPos.");
                        return screenPos;
                    }
                    
                    // Convertir la position écran en position map
                    Vector2 mapPos = screenPos;
                    
                    // Compenser le scroll
                    mapPos.x -= scrollOffset.x;
                    mapPos.y -= scrollOffset.y;
                    
                    // Convertir en coordonnées de la texture originale
                    mapPos.x = mapPos.x / scaleX;
                    mapPos.y = mapPos.y / scaleY;
                    
                    // Ajuster pour votre résolution spécifique
                    mapPos.x = (mapPos.x / texture.width) * mapWidth;
                    mapPos.y = (mapPos.y / texture.height) * mapHeight;
                    
                    // CORRECTION : Vérifier que le résultat final est valide
                    if (float.IsInfinity(mapPos.x) || float.IsInfinity(mapPos.y) || float.IsNaN(mapPos.x) || float.IsNaN(mapPos.y))
                    {
                        Debug.LogWarning($"[MapManager] Position calculée invalide ({mapPos}). Utilisation de screenPos.");
                        return screenPos;
                    }
                    
                    return mapPos;
                }
            }
        }

        return screenPos; // Fallback
    }

    void ToggleZonesVisibility()
    {
        foreach (MapZoneClickable zone in zones)
        {
            if (zone != null)
            {
                // NE PAS désactiver l'Image si c'est un decorator (sprite de personnage)
                // Les decorators ont un nom qui commence par "Decorator_"
                bool isDecorator = zone.gameObject.name.StartsWith("Decorator_");
                
                if (!isDecorator)
                {
                    // C'est une zone de debug (rectangle coloré), toggle son Image
                    Image img = zone.GetComponent<Image>();
                    if (img != null)
                    {
                        img.enabled = showDebugZones;
                    }
                }
                // Si c'est un decorator, on ne touche PAS à son Image (il reste toujours visible)
            }
        }
        
        // IMPORTANT : S'assurer que tous les decorators (sprites des personnages) restent visibles
        // Même quand showDebugZones = false
        foreach (GameObject decorator in decorators)
        {
            if (decorator != null)
            {
                // Les decorators sont toujours visibles (on ne les masque jamais)
                decorator.SetActive(true);
                
                // S'assurer que l'Image du decorator est aussi enabled
                Image decoratorImg = decorator.GetComponent<Image>();
                if (decoratorImg != null)
                {
                    decoratorImg.enabled = true;
                }
            }
        }
    }
    
    void ToggleDecorativeVideos()
    {
        if (decorativeVideosVisible)
        {
            // Recréer les vidéos décoratives
            CreateDecorativeVideos();
        }
        else
        {
            // Effacer les vidéos décoratives
            ClearDecorativeVideos();
        }
    }
    
    void ClearDecorativeVideos()
    {
        foreach (GameObject videoContainer in decorativeVideos)
        {
            if (videoContainer != null)
            {
                DestroyImmediate(videoContainer);
            }
        }
        decorativeVideos.Clear();
        Debug.Log("[MapManager] Toutes les vidéos décoratives ont été effacées");
    }

    // Fonction pour recréer les vidéos avec les nouveaux paramètres de transparence
    [ContextMenu("Recréer les vidéos décoratives")]
    public void RecreateDecorativeVideos()
    {
        Debug.Log("[MapManager] Recréation des vidéos décoratives avec les nouveaux paramètres de transparence...");
        ClearDecorativeVideos();
        CreateDecorativeVideos();
        Debug.Log($"[MapManager] Vidéos recréées - Extent: {vignetteExtent}, Start: {vignetteStartRadius}, Curve: {vignetteCurve}, Masque désactivé: {disableVignetteMask}");
    }

    void HandleDragScroll()
    {
        if (scrollViewObject == null || contentObject == null) return;

        ScrollRect scrollRect = scrollViewObject.GetComponent<ScrollRect>();
        if (scrollRect == null) return;

        bool isPressed = false;
        Vector2 currentPos = Vector2.zero;

        #if UNITY_WEBGL && !UNITY_EDITOR
        // En WebGL, utiliser WebGLClickReceiver pour détecter le drag
        if (WebGLClickReceiver.IsDragging())
        {
            Vector2 dragStart = WebGLClickReceiver.GetDragStartPosition();
            Vector2 dragCurrent = WebGLClickReceiver.GetDragCurrentPosition();
            
            if (!isDragging)
            {
                // Démarrer le drag
                isDragging = true;
                dragStartPos = dragStart;
                contentStartPos = scrollRect.content.anchoredPosition;
            }
            
            // Continuer le drag
            float dragDistance = dragCurrent.x - dragStart.x;
            Vector2 newPos = contentStartPos;
            newPos.x += dragDistance;

            float maxScroll = Mathf.Max(0, scrollRect.content.sizeDelta.x - scrollRect.viewport.rect.width);
            newPos.x = Mathf.Clamp(newPos.x, -maxScroll, 0);

            scrollRect.content.anchoredPosition = newPos;
        }
        else if (isDragging)
        {
            // Arrêter le drag
            isDragging = false;
        }
        #else
        // Détecter le drag avec le nouveau Input System pour les autres plateformes
        if (Mouse.current != null)
        {
            isPressed = Mouse.current.leftButton.isPressed;
            currentPos = Mouse.current.position.ReadValue();
        }

        // Code de drag manuel pour les autres plateformes
        if (isPressed && !isDragging)
        {
            isDragging = true;
            dragStartPos = currentPos;
            contentStartPos = scrollRect.content.anchoredPosition;
        }

        if (isDragging && isPressed)
        {
            float dragDistance = currentPos.x - dragStartPos.x;
            Vector2 newPos = contentStartPos;
            newPos.x += dragDistance;

            float maxScroll = Mathf.Max(0, scrollRect.content.sizeDelta.x - scrollRect.viewport.rect.width);
            newPos.x = Mathf.Clamp(newPos.x, -maxScroll, 0);

            scrollRect.content.anchoredPosition = newPos;
        }

        if (!isPressed && isDragging)
        {
            isDragging = false;
        }
        #endif
    }

    void HandleClick()
    {
        // Ne pas gérer les clics si une vidéo fullscreen est en cours
        if (isFullscreenVideoPlaying)
        {
            return;
        }
        
        bool released = false;
        Vector2 clickPos = Vector2.zero;

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

        if (Mouse.current != null && Mouse.current.leftButton.wasReleasedThisFrame)
        {
            released = true;
            clickPos = Mouse.current.position.ReadValue();
        }
        
        // WebGL fallback: vérifier si un clic a été reçu depuis JavaScript
        #if UNITY_WEBGL && !UNITY_EDITOR
        if (WebGLClickReceiver.WasClickedThisFrame())
        {
            released = true;
            clickPos = WebGLClickReceiver.GetLastClickPosition();
            dragStartPos = clickPos; // Pas de drag en WebGL via JavaScript
            Debug.Log($"[MapManager] Clic WebGL détecté: ({clickPos.x:F0}, {clickPos.y:F0})");
            Application.ExternalCall("console.log", $"[Unity] MapManager: Clic WebGL détecté: ({clickPos.x:F0}, {clickPos.y:F0})");
        }
        #endif

        if (released)
        {
            float distance = Vector2.Distance(clickPos, dragStartPos);
            Debug.Log($"[MapManager] HandleClick - Clic détecté à ({clickPos.x:F0}, {clickPos.y:F0}), distance: {distance:F1}px, zones disponibles: {zones.Count}");

            if (distance < 15f)
            {
                bool clickedOnZone = false;
                foreach (MapZoneClickable zone in zones)
                {
                    RectTransform zoneRect = zone.GetComponent<RectTransform>();

                    if (RectTransformUtility.RectangleContainsScreenPoint(zoneRect, clickPos, null))
                    {
                        clickedOnZone = true;
                        MapZone zoneData = zone.GetZoneData();
                        
                        Debug.Log($"[MapManager] ✅ Clic sur zone: {zoneData.id}, directClick: {zoneData.directClick}, hasPopup: {zoneData.hasPopup}");
                        #if UNITY_WEBGL && !UNITY_EDITOR
                        Application.ExternalCall("console.log", $"[Unity] MapManager: Clic sur zone {zoneData.id}");
                        #endif

                        // Si directClick est true, aller directement au jeu sans afficher la popup
                        if (zoneData.directClick)
                        {
                            // IMPORTANT : Définir currentZoneData avant LaunchLevel pour récupérer gameType
                            currentZoneData = zoneData;
                            // Clic direct vers le jeu sans afficher la popup
                            LaunchLevel(zoneData.targetLevelId);
                        }
                        else if (zoneData.hasPopup)
                        {
                            // Afficher la popup (avec ou sans bouton selon popupClickable)
                            // CORRECTION : Passer le RectTransform de la zone au popup
                            // pour qu'il puisse suivre la zone pendant le scroll
                            ShowPopup(zoneData, zoneRect);
                        }
                        else
                        {
                            // IMPORTANT : Définir currentZoneData avant OnClick pour récupérer gameType
                            currentZoneData = zoneData;
                            zone.OnClick();
                        }
                        break;
                    }
                }

                if (!clickedOnZone)
                {
                    Debug.Log($"[MapManager] ❌ Aucune zone cliquée à cette position");
                    HideAllPopups();
                }
            }
            else
            {
                Debug.Log($"[MapManager] ⚠️ Clic ignoré car distance trop grande (drag détecté): {distance:F1}px");
            }
        }
    }

    void HandleScroll()
    {
        if (scrollViewObject == null) return;

        ScrollRect scrollRect = scrollViewObject.GetComponent<ScrollRect>();
        if (scrollRect == null) return;

        float scrollSpeed = 500f;
        float movement = 0f;

        // Support clavier (flèches gauche/droite) - sens inversé
        if (Keyboard.current != null)
        {
            if (Keyboard.current.leftArrowKey.isPressed)
            {
                movement = scrollSpeed * Time.deltaTime; // Flèche gauche → scroll vers la droite
            }
            else if (Keyboard.current.rightArrowKey.isPressed)
            {
                movement = -scrollSpeed * Time.deltaTime; // Flèche droite → scroll vers la gauche
            }
        }
        
        // Support molette de souris en WebGL
        #if UNITY_WEBGL && !UNITY_EDITOR
        if (Mouse.current != null)
        {
            Vector2 scrollDelta = Mouse.current.scroll.ReadValue();
            if (scrollDelta.y != 0f)
            {
                // La molette de la souris en WebGL
                movement = scrollDelta.y * scrollSpeed * Time.deltaTime * 2f; // Multiplier pour plus de sensibilité
            }
        }
        #else
        // Support molette de souris pour les autres plateformes
        if (Mouse.current != null)
        {
            Vector2 scrollDelta = Mouse.current.scroll.ReadValue();
            if (scrollDelta.y != 0f)
            {
                movement = scrollDelta.y * scrollSpeed * Time.deltaTime;
            }
        }
        #endif

        if (movement != 0f)
        {
            Vector2 pos = scrollRect.content.anchoredPosition;
            pos.x += movement;

            float maxScroll = Mathf.Max(0, scrollRect.content.sizeDelta.x - scrollRect.viewport.rect.width);
            pos.x = Mathf.Clamp(pos.x, -maxScroll, 0);

            scrollRect.content.anchoredPosition = pos;
        }
    }

    void ShowPopup(MapZone zoneData, RectTransform zoneRect)
    {
        HideAllPopups();

        if (popupContainer != null)
        {
            popupContainer.SetActive(true);
        }

        GameObject popupObj = new GameObject($"Popup_{zoneData.id}");
        popupObj.transform.SetParent(popupContainer.transform, false);

        RectTransform popupRect = popupObj.AddComponent<RectTransform>();
        popupRect.anchorMin = Vector2.zero;
        popupRect.anchorMax = Vector2.zero;
        popupRect.pivot = new Vector2(0.5f, 0.5f);
        popupRect.sizeDelta = new Vector2(mapConfig.popupConfig.width, mapConfig.popupConfig.height);
        
        // Obtenir les coins de la zone et du canvas popup en coordonnées monde
        Vector3[] zoneCorners = new Vector3[4];
        zoneRect.GetWorldCorners(zoneCorners);
        Vector3 zoneCenter = (zoneCorners[0] + zoneCorners[2]) * 0.5f;
        
        // Pour ScreenSpaceOverlay, GetWorldCorners retourne directement des positions en pixels écran
        // Obtenir le coin inférieur gauche du canvas popup
        RectTransform popupCanvasRect = popupContainer.GetComponent<RectTransform>();
        Vector3[] popupCanvasCorners = new Vector3[4];
        popupCanvasRect.GetWorldCorners(popupCanvasCorners);
        Vector3 popupCanvasBottomLeft = popupCanvasCorners[0];
        
        // Calculer l'offset du centre de la zone depuis le coin inférieur gauche du canvas popup
        Vector2 offset = new Vector2(
            zoneCenter.x - popupCanvasBottomLeft.x,
            zoneCenter.y - popupCanvasBottomLeft.y
        );
        
        popupRect.anchoredPosition = offset;
        
        Debug.Log($"[MapManager] Popup positionné - Zone: {zoneData.id}, Offset: {offset}, ZoneCenter: {zoneCenter}");

        // Stocker la zone pour suivre le scroll
        activePopup = popupObj;
        activePopupZone = zoneRect;

        Debug.Log($"[MapManager] Popup lié à la zone: {zoneRect.name}");

        CanvasGroup cg = popupObj.AddComponent<CanvasGroup>();
        cg.alpha = 0f;

        MapPopup popup = popupObj.AddComponent<MapPopup>();
        popup.Initialize(mapConfig.popupConfig, this);
        
        // Stocker la zone actuelle pour récupérer gameType
        currentZoneData = zoneData;
        
        // Construire l'URL complète avec GeneralConfigManager
        string fullPopupVideoUrl = GeneralConfigManager.Instance.GetPopupVideoUrl(zoneData.popupVideo);
        
        popup.SetupPopupContent(fullPopupVideoUrl, zoneData.popupText, zoneData.targetLevelId, zoneData.popupClickable, zoneData.buttonText);
        popup.Show();
    }

    void HideAllPopups()
    {
        if (popupContainer != null)
        {
            foreach (Transform child in popupContainer.transform)
            {
                Destroy(child.gameObject);
            }
        }

        // NOUVEAU : Nettoyer les références
        activePopup = null;
        activePopupZone = null;
    }

    /// <summary>
    /// Appelé par WebGLClickReceiver quand un clic est reçu depuis JavaScript
    /// </summary>
    public void OnWebGLClick(Vector2 clickPosition)
    {
        Debug.Log($"[MapManager] OnWebGLClick: ({clickPosition.x:F0}, {clickPosition.y:F0})");
        #if UNITY_WEBGL && !UNITY_EDITOR
        Application.ExternalCall("console.log", $"[Unity] MapManager.OnWebGLClick: ({clickPosition.x:F0}, {clickPosition.y:F0})");
        #endif
        
        // Vérifier si on a cliqué sur une zone
        foreach (MapZoneClickable zone in zones)
        {
            RectTransform zoneRect = zone.GetComponent<RectTransform>();
            if (zoneRect != null && RectTransformUtility.RectangleContainsScreenPoint(zoneRect, clickPosition, null))
            {
                MapZone zoneData = zone.GetZoneData();
                Debug.Log($"[MapManager] ✅ OnWebGLClick sur zone: {zoneData.id}");
                
                #if UNITY_WEBGL && !UNITY_EDITOR
                Application.ExternalCall("console.log", $"[Unity] MapManager: Clic sur zone {zoneData.id}");
                #endif
                
                if (zoneData.directClick)
                {
                    currentZoneData = zoneData;
                    LaunchLevel(zoneData.targetLevelId);
                }
                else if (zoneData.hasPopup)
                {
                    ShowPopup(zoneData, zoneRect);
                }
                return;
            }
        }
        
        Debug.Log("[MapManager] ❌ OnWebGLClick: Aucune zone cliquée");
    }
    
    public void LaunchLevel(string levelId)
    {
        Debug.Log($"[MapManager] Lancement niveau: {levelId}");

        if (popupContainer != null)
        {
            Destroy(popupContainer);
        }

        if (currentStep != null && !string.IsNullOrEmpty(currentStep.nextStepId))
        {
            PlayerPrefs.SetString("NextStepId_" + currentMapId, currentStep.nextStepId);
            PlayerPrefs.Save();
            Debug.Log($"[MapManager] NextStepId sauvegardé: {currentStep.nextStepId} (depuis step: {currentStep.id})");
        }
        else
        {
            Debug.LogWarning($"[MapManager] Pas de NextStepId pour le step: {currentStep?.id}");
        }

        // IMPORTANT : Définir que nous venons de la Map pour retourner ici après le jeu
        PlayerPrefs.SetString("ReturnToScene", "Map");
        PlayerPrefs.SetString("CurrentMapId", currentMapId);
        Debug.Log($"[MapManager] ✅ ReturnToScene défini à 'Map'");
        Debug.Log($"[MapManager] ✅ CurrentMapId sauvegardé: {currentMapId}");

        // NOUVEAU : Déterminer le type de jeu (depuis la zone OU auto-détection)
        string gameType = "shooting"; // Par défaut
        
        if (currentZoneData != null && !string.IsNullOrEmpty(currentZoneData.gameType))
        {
            gameType = currentZoneData.gameType;
            Debug.Log($"[MapManager] Type de jeu depuis la zone (avant normalisation): {gameType}");
            // IMPORTANT : Normaliser le type (text_hole -> trous)
            gameType = NormalizeGameType(gameType);
            Debug.Log($"[MapManager] Type de jeu normalisé: {gameType}");
        }
        else
        {
            Debug.LogWarning($"[MapManager] Pas de gameType spécifié, utilisation auto-détection pour: {levelId}");
            gameType = DetectGameType(levelId);
            Debug.Log($"[MapManager] Type détecté automatiquement: {gameType}");
        }
        
        // IMPORTANT : Définir le type de jeu dans PlayerPrefs AVANT de sauvegarder
        Debug.Log($"[MapManager] ═══════════════════════════════════════");
        Debug.Log($"[MapManager] DÉFINITION DU TYPE DE JEU DANS PLAYERPREFS");
        Debug.Log($"[MapManager] LevelId: {levelId}");
        Debug.Log($"[MapManager] GameType: {gameType}");
        Debug.Log($"[MapManager] ═══════════════════════════════════════");
        PlayerPrefs.SetString("CurrentLevelType", gameType);
        PlayerPrefs.SetString("CurrentLevelId", levelId);
        
        // Construire l'URL de la config (les niveaux sont dans json/, même dossier que les maps)
        string configUrl = GeneralConfigManager.Instance != null 
            ? GeneralConfigManager.Instance.GetMapConfigUrl(levelId + ".json")
            : $"STREAMING_ASSETS/json/{levelId}.json";
        
        PlayerPrefs.SetString("GameConfigUrl", configUrl);
        PlayerPrefs.SetString("CurrentLevelId", levelId);
        PlayerPrefs.SetString("GamePhase", "Before");
        PlayerPrefs.Save();
        
        // NOUVEAU : Si un gameId est disponible, charger les données du jeu depuis l'API
        int gameId = currentZoneData?.gameId ?? 0;
        
        Debug.Log($"[MapManager] ═══════════════════════════════════════");
        Debug.Log($"[MapManager] 🔍 DIAGNOSTIC GAME ID");
        Debug.Log($"[MapManager] currentZoneData null: {currentZoneData == null}");
        Debug.Log($"[MapManager] gameId récupéré: {gameId}");
        if (currentZoneData != null)
        {
            Debug.Log($"[MapManager] Zone ID: {currentZoneData.id}");
            Debug.Log($"[MapManager] Zone targetLevelId: {currentZoneData.targetLevelId}");
            Debug.Log($"[MapManager] Zone gameId: {currentZoneData.gameId}");
            Debug.Log($"[MapManager] Zone gameType: {currentZoneData.gameType}");
        }
        Debug.Log($"[MapManager] ═══════════════════════════════════════");
        
        if (gameId > 0)
        {
            Debug.Log($"[MapManager] 🎮 GameId disponible: {gameId} - Chargement des données depuis l'API...");
            StartCoroutine(LoadGameDataAndLaunch(gameId, gameType, configUrl));
        }
        else
        {
            Debug.Log($"[MapManager] ⚠️ Pas de gameId (gameId={gameId}) - Lancement direct avec la config locale");
            Debug.Log($"[MapManager] Transition vers scène Player pour dialogue...");
            Debug.Log($"[MapManager] Type: {gameType}, Config: {configUrl}");
            UnityEngine.SceneManagement.SceneManager.LoadScene("Player");
        }
    }
    
    /// <summary>
    /// Charge les données du jeu depuis l'API puis lance la scène
    /// </summary>
    private IEnumerator LoadGameDataAndLaunch(int gameId, string gameType, string configUrl)
    {
        // Afficher un écran de chargement
        if (UnifiedLoadingManager.Instance != null)
        {
            UnifiedLoadingManager.ShowLoading("Chargement du jeu...", LoadingContext.Game);
        }
        
        bool loadingComplete = false;
        bool loadingSuccess = false;
        
        // Récupérer la difficulté depuis PlayerPrefs (sauvegardée par MainSceneManager)
        string difficulty = PlayerPrefs.GetString("CurrentQuestDifficulty", "Débutant");
        Debug.Log($"[MapManager] 📊 Difficulté récupérée: {difficulty}");
        
        // Charger les données du jeu via GameDataManager
        GameDataManager.Instance.LoadGameData(
            gameId,
            difficulty,
            onSuccess: (gameData) => {
                Debug.Log($"[MapManager] ✅ Données du jeu {gameId} chargées avec succès (difficulté: {difficulty})");
                loadingComplete = true;
                loadingSuccess = true;
            },
            onError: (error) => {
                Debug.LogError($"[MapManager] ❌ Erreur lors du chargement du jeu {gameId}: {error}");
                loadingComplete = true;
                loadingSuccess = false;
            }
        );
        
        // Attendre la fin du chargement
        while (!loadingComplete)
        {
            yield return null;
        }
        
        // Lancer la scène même si le chargement API a échoué (fallback sur config locale)
        if (!loadingSuccess)
        {
            Debug.LogWarning($"[MapManager] ⚠️ Fallback sur la configuration locale: {configUrl}");
        }
        
        Debug.Log($"[MapManager] Transition vers scène Player pour dialogue...");
        Debug.Log($"[MapManager] Type: {gameType}, Config: {configUrl}");
        Debug.Log($"[MapManager] Données API disponibles: {GameDataManager.Instance.HasData}");
        
        UnityEngine.SceneManagement.SceneManager.LoadScene("Player");
    }
    
    /// <summary>
    /// Normalise le type de jeu pour correspondre aux noms de scène Unity
    /// Même logique que GameEntry.GetNormalizedType()
    /// </summary>
    private string NormalizeGameType(string rawType)
    {
        if (string.IsNullOrEmpty(rawType))
        {
            return "shooting";
        }
        
        switch (rawType.ToLower())
        {
            case "shooting":
                return "shooting";
            case "text_hole":
            case "trous":
                return "trous";
            case "calculator":
                return "calculator";
            default:
                Debug.LogWarning($"[MapManager] Type de jeu inconnu '{rawType}', utilisation de 'shooting' par défaut");
                return "shooting";
        }
    }
    
    private string DetectGameType(string levelId)
    {
        Debug.Log($"[MapManager] DetectGameType appelé pour: {levelId}");
        
        // 1. Essayer de trouver le type depuis LevelManager (levels-config.json)
        if (LevelManager.Instance != null)
        {
            Debug.Log($"[MapManager] LevelManager.Instance trouvé");
            if (LevelManager.Instance.IsConfigurationLoaded())
            {
                Debug.Log($"[MapManager] Configuration LevelManager chargée");
                LevelData level = LevelManager.Instance.GetLevel(levelId);
                if (level != null)
                {
                    Debug.Log($"[MapManager] LevelData trouvé: id={level.id}, type={level.type}");
                    if (!string.IsNullOrEmpty(level.type))
                    {
                        Debug.Log($"[MapManager] ✅ Type '{level.type}' trouvé dans levels-config.json pour {levelId}");
                        return level.type;
                    }
                    else
                    {
                        Debug.LogWarning($"[MapManager] ⚠️ LevelData.type est vide pour {levelId}");
                    }
                }
                else
                {
                    Debug.LogWarning($"[MapManager] ⚠️ LevelData introuvable pour {levelId} dans levels-config.json");
                }
            }
            else
            {
                Debug.LogWarning($"[MapManager] ⚠️ Configuration LevelManager non chargée");
            }
        }
        else
        {
            Debug.LogWarning($"[MapManager] ⚠️ LevelManager.Instance est null");
        }
        
        // 2. Détection depuis le nom du fichier (fallback)
        string levelIdLower = levelId.ToLowerInvariant();
        if (levelIdLower.Contains("trous") || levelIdLower.Contains("trou"))
        {
            Debug.Log($"[MapManager] Type 'trous' détecté depuis le nom: {levelId}");
            return "trous";
        }
        if (levelIdLower.Contains("calculator") || levelIdLower.Contains("calc"))
        {
            Debug.Log($"[MapManager] Type 'calculator' détecté depuis le nom: {levelId}");
            return "calculator";
        }
        
        // 3. Par défaut, retourner "shooting"
        Debug.LogWarning($"[MapManager] ⚠️ Auto-détection: utilisation du type par défaut 'shooting' pour {levelId}");
        return "shooting";
    }

    public void OnHeaderElementClick(string action)
    {
        Debug.Log($"[MapManager] OnHeaderElementClick appelé avec action: {action}");

        switch (action)
        {
            case "settings":
                Debug.Log("[MapManager] Tentative d'ouverture des paramètres...");
                
                // Obtenir ou créer l'instance du SettingsManager
                SettingsManager settingsManager = SettingsManager.GetOrCreateInstance();
                
                if (settingsManager != null)
                {
                    // Si l'instance vient d'être créée, attendre une frame pour l'initialisation
                    if (settingsManager.settingsPanel == null)
                    {
                        Debug.Log("[MapManager] ⚙️ SettingsManager vient d'être créé, attente de l'initialisation...");
                        StartCoroutine(WaitForSettingsManagerAndOpen());
                    }
                    else
                    {
                        Debug.Log("[MapManager] ✅ SettingsManager.Instance trouvé, appel de OpenSettings()");
                        settingsManager.OpenSettings();
                    }
                }
                else
                {
                    Debug.LogError("[MapManager] ❌ Impossible de créer le SettingsManager !");
                }
                break;

            case "back":
                SceneManager.LoadScene("menu");
                break;

            default:
                Debug.LogWarning($"[MapManager] Action inconnue: {action}");
                break;
        }
    }
    
    /// <summary>
    /// Attend que SettingsManager soit initialisé puis ouvre le panneau
    /// </summary>
    private System.Collections.IEnumerator WaitForSettingsManagerAndOpen()
    {
        yield return null; // Attendre une frame pour que Awake() soit appelé
        
        if (SettingsManager.Instance != null)
        {
            Debug.Log("[MapManager] ✅ SettingsManager.Instance créé, ouverture du panneau...");
            SettingsManager.Instance.OpenSettings();
        }
        else
        {
            Debug.LogError("[MapManager] ❌ SettingsManager.Instance est toujours NULL après création !");
        }
    }





}