using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;

/// <summary>
/// Manager singleton pour stocker les données du jeu récupérées depuis l'API
/// Endpoint: /api/ujsa/games/{gameId}?difficulty={difficulty}
/// Inclut un système de préchargement et cache d'assets pour une transition fluide vers le jeu
/// </summary>
public class GameDataManager : MonoBehaviour
{
    private static GameDataManager _instance;
    public static GameDataManager Instance
    {
        get
        {
            if (_instance == null)
            {
                GameObject go = new GameObject("GameDataManager");
                _instance = go.AddComponent<GameDataManager>();
                DontDestroyOnLoad(go);
            }
            return _instance;
        }
    }

    // Données du jeu en cours
    public APIGameData CurrentGameData { get; private set; }
    public int CurrentGameId { get; private set; } // ID du jeu pour l'API answers
    public bool IsLoading { get; private set; }
    public bool HasData => CurrentGameData != null;
    
    // ========================================
    // SYSTÈME DE CACHE D'ASSETS PRÉCHARGÉS
    // ========================================
    
    // Cache pour les textures (crosshair, gun, impact, etc.)
    private Dictionary<string, Texture2D> cachedTextures = new Dictionary<string, Texture2D>();
    
    // Cache pour les sprites (créés à partir des textures)
    private Dictionary<string, Sprite> cachedSprites = new Dictionary<string, Sprite>();
    
    // Cache pour les clips audio (sons du jeu)
    private Dictionary<string, AudioClip> cachedAudioClips = new Dictionary<string, AudioClip>();
    
    // État du préchargement
    public bool IsPreloading { get; private set; }
    public bool AssetsPreloaded { get; private set; }
    public float PreloadProgress { get; private set; }
    
    // Nombre d'assets à précharger et chargés
    private int totalAssetsToPreload = 0;
    private int assetsPreloaded = 0;

    void Awake()
    {
        if (_instance == null)
        {
            _instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else if (_instance != this)
        {
            Destroy(gameObject);
        }
    }

    /// <summary>
    /// Charge les données d'un jeu depuis l'API
    /// </summary>
    /// <param name="gameId">ID du jeu</param>
    /// <param name="difficulty">Niveau de difficulté (défaut: "Débutant")</param>
    /// <param name="onSuccess">Callback appelé en cas de succès</param>
    /// <param name="onError">Callback appelé en cas d'erreur</param>
    public void LoadGameData(int gameId, string difficulty = "Débutant", System.Action<APIGameData> onSuccess = null, System.Action<string> onError = null)
    {
        StartCoroutine(LoadGameDataCoroutine(gameId, difficulty, onSuccess, onError));
    }

    private IEnumerator LoadGameDataCoroutine(int gameId, string difficulty, System.Action<APIGameData> onSuccess, System.Action<string> onError)
    {
        IsLoading = true;
        CurrentGameData = null;
        CurrentGameId = gameId; // Stocker l'ID du jeu pour l'API answers
        
        // 🧹 IMPORTANT : Vider le cache des assets de l'ancien jeu
        // Sinon les textures/sprites de l'ancien jeu seront réutilisées !
        ClearAssetCache();
        Debug.Log($"[GameDataManager] 🧹 Cache des assets vidé pour le nouveau jeu {gameId}");

        // Construire l'URL de l'API
        string apiUrl = GeneralConfigManager.Instance?.GetGameConfigApiUrl(gameId, difficulty);
        
        if (string.IsNullOrEmpty(apiUrl))
        {
            Debug.LogError("[GameDataManager] ❌ Impossible de construire l'URL de l'API games");
            IsLoading = false;
            onError?.Invoke("URL API non disponible");
            yield break;
        }

        Debug.Log($"[GameDataManager] 🎮 Chargement des données du jeu {gameId} (difficulté: {difficulty})...");
        Debug.Log($"[GameDataManager] 🌐 URL: {apiUrl}");

        // Récupérer le token d'authentification
        string token = UserDataManager.Instance?.token;

        using (UnityWebRequest www = UnityWebRequest.Get(apiUrl))
        {
            // Ajouter l'authentification Bearer Token
            if (!string.IsNullOrEmpty(token))
            {
                www.SetRequestHeader("Authorization", $"Bearer {token}");
                Debug.Log($"[GameDataManager] ✅ Token ajouté à la requête");
            }
            else
            {
                Debug.LogWarning("[GameDataManager] ⚠️ Pas de token - requête sans authentification");
            }

            yield return www.SendWebRequest();

            if (www.result != UnityWebRequest.Result.Success)
            {
                Debug.LogError($"[GameDataManager] ❌ Erreur de chargement: {www.error}");
                Debug.LogError($"[GameDataManager] Code HTTP: {www.responseCode}");
                if (!string.IsNullOrEmpty(www.downloadHandler?.text))
                {
                    Debug.LogError($"[GameDataManager] Réponse: {www.downloadHandler.text}");
                }
                IsLoading = false;
                onError?.Invoke(www.error);
                yield break;
            }

            string jsonData = www.downloadHandler.text;
            Debug.Log($"[GameDataManager] ✅ Données reçues ({jsonData.Length} caractères)");
            // Afficher les 1000 premiers caractères pour éviter la troncature
            string jsonPreview = jsonData.Length > 1000 ? jsonData.Substring(0, 1000) + "..." : jsonData;
            Debug.Log($"[GameDataManager] 📋 JSON (aperçu): {jsonPreview}");

            try
            {
                APIGameResponse response = JsonUtility.FromJson<APIGameResponse>(jsonData);

                if (response != null && response.status == "success" && response.data != null)
                {
                    CurrentGameData = response.data;
                    
                    Debug.Log($"[GameDataManager] ✅ Données du jeu parsées avec succès");
                    Debug.Log($"[GameDataManager] 📊 Background: {CurrentGameData.background?.type} - {CurrentGameData.background?.url}");
                    Debug.Log($"[GameDataManager] 📊 Questions: {CurrentGameData.questions?.Length ?? 0}");
                    Debug.Log($"[GameDataManager] 📊 Zones: {CurrentGameData.zones?.Length ?? 0}");
                    Debug.Log($"[GameDataManager] 📊 Dialogues:");
                    Debug.Log($"[GameDataManager]    - Intro: {(CurrentGameData.dialogues?.intro != null ? "✅ " + (CurrentGameData.dialogues.intro.lines?.Length ?? 0) + " lignes" : "❌")}");
                    Debug.Log($"[GameDataManager]    - Success: {(CurrentGameData.dialogues?.success != null ? "✅ " + (CurrentGameData.dialogues.success.lines?.Length ?? 0) + " lignes" : "❌")}");
                    Debug.Log($"[GameDataManager]    - Fail: {(CurrentGameData.dialogues?.fail != null ? "✅ " + (CurrentGameData.dialogues.fail.lines?.Length ?? 0) + " lignes" : "❌")}");

                    IsLoading = false;
                    onSuccess?.Invoke(CurrentGameData);
                }
                else
                {
                    Debug.LogError($"[GameDataManager] ❌ Réponse API invalide");
                    Debug.LogError($"[GameDataManager] Status: {response?.status}");
                    Debug.LogError($"[GameDataManager] Message: {response?.message}");
                    IsLoading = false;
                    onError?.Invoke(response?.message ?? "Réponse API invalide");
                }
            }
            catch (System.Exception e)
            {
                Debug.LogError($"[GameDataManager] ❌ Erreur de parsing: {e.Message}");
                Debug.LogError($"[GameDataManager] Stack: {e.StackTrace}");
                IsLoading = false;
                onError?.Invoke($"Erreur de parsing: {e.Message}");
            }
        }
    }

    /// <summary>
    /// Efface les données du jeu en cours
    /// </summary>
    public void ClearGameData()
    {
        CurrentGameData = null;
        CurrentGameId = 0;
        
        // Vider aussi le cache des assets pour éviter les mélanges entre jeux
        ClearAssetCache();
        
        Debug.Log("[GameDataManager] 🗑️ Données du jeu et cache des assets effacés");
    }

    /// <summary>
    /// Récupère les questions du jeu en cours au format compatible avec le système existant
    /// </summary>
    public Question[] GetQuestionsForGame()
    {
        if (CurrentGameData?.questions == null) return null;

        Question[] questions = new Question[CurrentGameData.questions.Length];
        
        for (int i = 0; i < CurrentGameData.questions.Length; i++)
        {
            var apiQ = CurrentGameData.questions[i];
            questions[i] = new Question
            {
                id = apiQ.id,
                question = apiQ.question,
                explanation = apiQ.explanation ?? "",
                points = 1, // Par défaut 1 point par question
                answerCount = apiQ.answers?.Length ?? 0,
                answers = new System.Collections.Generic.List<Answer>()
            };

            if (apiQ.answers != null)
            {
                for (int j = 0; j < apiQ.answers.Length; j++)
                {
                    var apiA = apiQ.answers[j];
                    questions[i].answers.Add(new Answer
                    {
                        id = apiA.id, // ID de l'option pour l'API answers
                        text = apiA.text,
                        isCorrect = apiA.is_correct,
                        zoneId = apiA.zoneId ?? $"zone{j + 1}", // Utiliser zoneId de l'API ou générer
                        choiceIndex = j
                    });
                }
            }
        }

        return questions;
    }
    
    // ========================================
    // MÉTHODES DE PRÉCHARGEMENT D'ASSETS
    // ========================================
    
    /// <summary>
    /// Précharge tous les assets du jeu en parallèle pendant la phase de dialogue.
    /// Appelé automatiquement quand le dialogue commence.
    /// </summary>
    public void PreloadGameAssets()
    {
        if (CurrentGameData == null)
        {
            Debug.LogWarning("[GameDataManager] ⚠️ Pas de données de jeu à précharger");
            return;
        }
        
        if (IsPreloading)
        {
            Debug.Log("[GameDataManager] ⏳ Préchargement déjà en cours...");
            return;
        }
        
        StartCoroutine(PreloadGameAssetsCoroutine());
    }
    
    private IEnumerator PreloadGameAssetsCoroutine()
    {
        IsPreloading = true;
        AssetsPreloaded = false;
        PreloadProgress = 0f;
        assetsPreloaded = 0;
        totalAssetsToPreload = 0;
        
        Debug.Log("[GameDataManager] 🚀 Début du préchargement des assets du jeu...");
        
        // Collecter toutes les URLs d'assets à précharger
        List<(string url, string type, string key)> assetsToLoad = new List<(string, string, string)>();
        
        // Assets visuels (gun, crosshair, impact)
        if (CurrentGameData.assets != null)
        {
            if (!string.IsNullOrEmpty(CurrentGameData.assets.gun))
            {
                string gunUrl = GeneralConfigManager.Instance?.GetGameAssetsUrl(CurrentGameData.assets.gun);
                if (!string.IsNullOrEmpty(gunUrl))
                    assetsToLoad.Add((gunUrl, "texture", "gun"));
            }
            
            if (!string.IsNullOrEmpty(CurrentGameData.assets.crosshair))
            {
                string crosshairUrl = GeneralConfigManager.Instance?.GetCrosshairUrl(CurrentGameData.assets.crosshair);
                if (!string.IsNullOrEmpty(crosshairUrl))
                    assetsToLoad.Add((crosshairUrl, "texture", "crosshair"));
            }
            
            if (!string.IsNullOrEmpty(CurrentGameData.assets.impact))
            {
                string impactUrl = GeneralConfigManager.Instance?.GetGameAssetsUrl(CurrentGameData.assets.impact);
                if (!string.IsNullOrEmpty(impactUrl))
                    assetsToLoad.Add((impactUrl, "texture", "impact"));
            }
            
            // Assets spécifiques au jeu calculator
            if (!string.IsNullOrEmpty(CurrentGameData.assets.calculatorImage))
            {
                string calcUrl = GeneralConfigManager.Instance?.GetGameAssetsUrl(CurrentGameData.assets.calculatorImage);
                if (!string.IsNullOrEmpty(calcUrl))
                    assetsToLoad.Add((calcUrl, "texture", "calculatorImage"));
            }
            
            if (!string.IsNullOrEmpty(CurrentGameData.assets.ledOff))
            {
                string ledOffUrl = GeneralConfigManager.Instance?.GetGameAssetsUrl(CurrentGameData.assets.ledOff);
                if (!string.IsNullOrEmpty(ledOffUrl))
                    assetsToLoad.Add((ledOffUrl, "texture", "ledOff"));
            }
            
            if (!string.IsNullOrEmpty(CurrentGameData.assets.ledGreen))
            {
                string ledGreenUrl = GeneralConfigManager.Instance?.GetGameAssetsUrl(CurrentGameData.assets.ledGreen);
                if (!string.IsNullOrEmpty(ledGreenUrl))
                    assetsToLoad.Add((ledGreenUrl, "texture", "ledGreen"));
            }
            
            if (!string.IsNullOrEmpty(CurrentGameData.assets.ledRed))
            {
                string ledRedUrl = GeneralConfigManager.Instance?.GetGameAssetsUrl(CurrentGameData.assets.ledRed);
                if (!string.IsNullOrEmpty(ledRedUrl))
                    assetsToLoad.Add((ledRedUrl, "texture", "ledRed"));
            }
        }
        
        // Sons du jeu - utiliser GetGameAssetsUrl car les sons sont généralement dans le même dossier
        // Note: Le préchargement des sons est optionnel et moins critique que les textures visuelles
        if (CurrentGameData.sounds != null)
        {
            if (!string.IsNullOrEmpty(CurrentGameData.sounds.shoot))
            {
                // Les sons peuvent être des URLs complètes ou des noms de fichiers
                string shootUrl = CurrentGameData.sounds.shoot;
                if (!shootUrl.StartsWith("http"))
                {
                    shootUrl = GeneralConfigManager.Instance?.GetGameAssetsUrl(CurrentGameData.sounds.shoot);
                }
                if (!string.IsNullOrEmpty(shootUrl))
                    assetsToLoad.Add((shootUrl, "audio", "shoot"));
            }
            
            if (!string.IsNullOrEmpty(CurrentGameData.sounds.impact))
            {
                string impactSoundUrl = CurrentGameData.sounds.impact;
                if (!impactSoundUrl.StartsWith("http"))
                {
                    impactSoundUrl = GeneralConfigManager.Instance?.GetGameAssetsUrl(CurrentGameData.sounds.impact);
                }
                if (!string.IsNullOrEmpty(impactSoundUrl))
                    assetsToLoad.Add((impactSoundUrl, "audio", "impactSound"));
            }
            
            if (!string.IsNullOrEmpty(CurrentGameData.sounds.success))
            {
                string successUrl = CurrentGameData.sounds.success;
                if (!successUrl.StartsWith("http"))
                {
                    successUrl = GeneralConfigManager.Instance?.GetGameAssetsUrl(CurrentGameData.sounds.success);
                }
                if (!string.IsNullOrEmpty(successUrl))
                    assetsToLoad.Add((successUrl, "audio", "success"));
            }
            
            if (!string.IsNullOrEmpty(CurrentGameData.sounds.fail))
            {
                string failUrl = CurrentGameData.sounds.fail;
                if (!failUrl.StartsWith("http"))
                {
                    failUrl = GeneralConfigManager.Instance?.GetGameAssetsUrl(CurrentGameData.sounds.fail);
                }
                if (!string.IsNullOrEmpty(failUrl))
                    assetsToLoad.Add((failUrl, "audio", "fail"));
            }
        }
        
        // 🆕 Images de feedback depuis defaultFeedbackMessages (utilisées par tous les types de jeux)
        var defaultFeedback = GeneralConfigManager.Instance?.GetDefaultFeedbackMessages();
        if (defaultFeedback != null && defaultFeedback.useBackgroundImage)
        {
            if (!string.IsNullOrEmpty(defaultFeedback.successBackgroundImageUrl))
            {
                string successBgUrl = GeneralConfigManager.Instance?.GetUIUrl(defaultFeedback.successBackgroundImageUrl);
                if (!string.IsNullOrEmpty(successBgUrl))
                {
                    assetsToLoad.Add((successBgUrl, "texture", "success_bg"));
                    assetsToLoad.Add((successBgUrl, "texture", "feedbackSuccess")); // Alias pour calculator
                }
            }
            
            if (!string.IsNullOrEmpty(defaultFeedback.failureBackgroundImageUrl))
            {
                string failureBgUrl = GeneralConfigManager.Instance?.GetUIUrl(defaultFeedback.failureBackgroundImageUrl);
                if (!string.IsNullOrEmpty(failureBgUrl))
                {
                    assetsToLoad.Add((failureBgUrl, "texture", "failure_bg"));
                    assetsToLoad.Add((failureBgUrl, "texture", "feedbackFailure")); // Alias pour calculator
                }
            }
            
            Debug.Log($"[GameDataManager] 📦 Images de feedback ajoutées au préchargement");
        }
        
        // 🆕 Backdrop (habillage extérieur) - utilisé par tous les types de jeux
        if (CurrentGameData.backdrop != null && !string.IsNullOrEmpty(CurrentGameData.backdrop.url))
        {
            string backdropUrl = CurrentGameData.backdrop.url;
            if (!backdropUrl.StartsWith("http"))
            {
                backdropUrl = GeneralConfigManager.Instance?.GetBackgroundImageUrl(backdropUrl);
            }
            if (!string.IsNullOrEmpty(backdropUrl))
            {
                assetsToLoad.Add((backdropUrl, "texture", "backdrop"));
                Debug.Log($"[GameDataManager] 📦 Backdrop ajouté au préchargement: {backdropUrl}");
            }
        }
        
        totalAssetsToPreload = assetsToLoad.Count;
        
        if (totalAssetsToPreload == 0)
        {
            Debug.Log("[GameDataManager] ✅ Aucun asset à précharger");
            AssetsPreloaded = true;
            IsPreloading = false;
            PreloadProgress = 1f;
            yield break;
        }
        
        Debug.Log($"[GameDataManager] 📦 {totalAssetsToPreload} assets à précharger");
        
        // Lancer tous les téléchargements en parallèle
        List<Coroutine> downloadCoroutines = new List<Coroutine>();
        
        foreach (var asset in assetsToLoad)
        {
            if (asset.type == "texture")
            {
                downloadCoroutines.Add(StartCoroutine(PreloadTextureAsync(asset.url, asset.key)));
            }
            else if (asset.type == "audio")
            {
                downloadCoroutines.Add(StartCoroutine(PreloadAudioAsync(asset.url, asset.key)));
            }
        }
        
        // Attendre que tous les téléchargements soient terminés
        foreach (var coroutine in downloadCoroutines)
        {
            yield return coroutine;
        }
        
        Debug.Log($"[GameDataManager] ✅ Préchargement terminé: {assetsPreloaded}/{totalAssetsToPreload} assets chargés");
        
        AssetsPreloaded = true;
        IsPreloading = false;
        PreloadProgress = 1f;
    }
    
    /// <summary>
    /// Précharge une texture de manière asynchrone
    /// </summary>
    private IEnumerator PreloadTextureAsync(string url, string key)
    {
        if (cachedTextures.ContainsKey(key))
        {
            Debug.Log($"[GameDataManager] ♻️ Texture '{key}' déjà en cache");
            assetsPreloaded++;
            UpdatePreloadProgress();
            yield break;
        }
        
        Debug.Log($"[GameDataManager] 📥 Préchargement texture '{key}' depuis {url}");
        
        // Utiliser MacImageLoader pour la compatibilité multi-plateforme
        bool success = false;
        Texture2D texture = null;
        
        yield return MacImageLoader.LoadTexture(url,
            (loadedTexture) => {
                success = true;
                texture = loadedTexture;
            },
            (error) => {
                success = false;
                Debug.LogWarning($"[GameDataManager] ⚠️ Échec préchargement texture '{key}': {error}");
            }
        );
        
        if (success && texture != null)
        {
            cachedTextures[key] = texture;
            
            // Créer aussi le sprite correspondant
            Sprite sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
            cachedSprites[key] = sprite;
            
            Debug.Log($"[GameDataManager] ✅ Texture '{key}' préchargée ({texture.width}x{texture.height})");
        }
        
        assetsPreloaded++;
        UpdatePreloadProgress();
    }
    
    /// <summary>
    /// Précharge un clip audio de manière asynchrone
    /// </summary>
    private IEnumerator PreloadAudioAsync(string url, string key)
    {
        if (cachedAudioClips.ContainsKey(key))
        {
            Debug.Log($"[GameDataManager] ♻️ Audio '{key}' déjà en cache");
            assetsPreloaded++;
            UpdatePreloadProgress();
            yield break;
        }
        
        Debug.Log($"[GameDataManager] 📥 Préchargement audio '{key}' depuis {url}");
        
        // Déterminer le type audio
        AudioType audioType = AudioType.UNKNOWN;
        string lowerUrl = url.ToLower();
        if (lowerUrl.EndsWith(".mp3")) audioType = AudioType.MPEG;
        else if (lowerUrl.EndsWith(".wav")) audioType = AudioType.WAV;
        else if (lowerUrl.EndsWith(".ogg")) audioType = AudioType.OGGVORBIS;
        
        using (UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip(url, audioType))
        {
            yield return www.SendWebRequest();
            
            if (www.result == UnityWebRequest.Result.Success)
            {
                AudioClip clip = DownloadHandlerAudioClip.GetContent(www);
                if (clip != null)
                {
                    cachedAudioClips[key] = clip;
                    Debug.Log($"[GameDataManager] ✅ Audio '{key}' préchargé ({clip.length:F1}s)");
                }
            }
            else
            {
                Debug.LogWarning($"[GameDataManager] ⚠️ Échec préchargement audio '{key}': {www.error}");
            }
        }
        
        assetsPreloaded++;
        UpdatePreloadProgress();
    }
    
    private void UpdatePreloadProgress()
    {
        if (totalAssetsToPreload > 0)
        {
            PreloadProgress = (float)assetsPreloaded / totalAssetsToPreload;
        }
    }
    
    // ========================================
    // MÉTHODES D'ACCÈS AU CACHE
    // ========================================
    
    /// <summary>
    /// Récupère une texture préchargée du cache
    /// </summary>
    public Texture2D GetCachedTexture(string key)
    {
        if (cachedTextures.TryGetValue(key, out Texture2D texture))
        {
            Debug.Log($"[GameDataManager] ♻️ Texture '{key}' récupérée du cache");
            return texture;
        }
        return null;
    }
    
    /// <summary>
    /// Récupère un sprite préchargé du cache
    /// </summary>
    public Sprite GetCachedSprite(string key)
    {
        if (cachedSprites.TryGetValue(key, out Sprite sprite))
        {
            Debug.Log($"[GameDataManager] ♻️ Sprite '{key}' récupéré du cache");
            return sprite;
        }
        return null;
    }
    
    /// <summary>
    /// Récupère un clip audio préchargé du cache
    /// </summary>
    public AudioClip GetCachedAudioClip(string key)
    {
        if (cachedAudioClips.TryGetValue(key, out AudioClip clip))
        {
            Debug.Log($"[GameDataManager] ♻️ Audio '{key}' récupéré du cache");
            return clip;
        }
        return null;
    }
    
    /// <summary>
    /// Vérifie si un asset est en cache
    /// </summary>
    public bool HasCachedAsset(string key)
    {
        return cachedTextures.ContainsKey(key) || cachedSprites.ContainsKey(key) || cachedAudioClips.ContainsKey(key);
    }
    
    /// <summary>
    /// Efface le cache des assets
    /// </summary>
    public void ClearAssetCache()
    {
        // Marquer que le préchargement n'est plus valide
        // (les coroutines en cours vérifieront ce flag si nécessaire)
        IsPreloading = false;
        
        // Détruire les textures pour libérer la mémoire
        foreach (var texture in cachedTextures.Values)
        {
            if (texture != null)
                Destroy(texture);
        }
        
        // Détruire les sprites
        foreach (var sprite in cachedSprites.Values)
        {
            if (sprite != null)
                Destroy(sprite);
        }
        
        cachedTextures.Clear();
        cachedSprites.Clear();
        cachedAudioClips.Clear();
        
        AssetsPreloaded = false;
        PreloadProgress = 0f;
        totalAssetsToPreload = 0;
        assetsPreloaded = 0;
        
        Debug.Log("[GameDataManager] 🗑️ Cache des assets effacé");
    }
    
    /// <summary>
    /// Efface les données du jeu ET le cache des assets
    /// </summary>
    public void ClearAll()
    {
        ClearGameData();
        ClearAssetCache();
    }
}

