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

/// <summary>
/// Service pour envoyer les réponses des joueurs au serveur central.
/// Utilisé par les différents types de jeux (shooting, calculator, trous).
/// </summary>
public class GameAnswerService : MonoBehaviour
{
    public static GameAnswerService Instance { get; private set; }

    [Header("Debug")]
    public bool showDebugLogs = true;

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

    // ==========================================
    // CLASSES DE DONNÉES POUR L'API
    // ==========================================

    /// <summary>
    /// Réponse QCM (shooting) - question avec option sélectionnée
    /// </summary>
    [Serializable]
    public class QCMAnswerData
    {
        public int question_id;
        public int answer_option_id;
    }

    /// <summary>
    /// Réponse numérique (calculator) - question avec valeur numérique
    /// </summary>
    [Serializable]
    public class NumericAnswerData
    {
        public int question_id;
        public float numeric_answer;
    }

    /// <summary>
    /// Réponse texte à trous - question avec option sélectionnée par trou
    /// </summary>
    [Serializable]
    public class TrousAnswerData
    {
        public int question_id;
        public int answer_option_id;
    }

    /// <summary>
    /// Corps de la requête pour envoyer les réponses
    /// </summary>
    [Serializable]
    private class AnswersRequestBody
    {
        public string difficulty; // Sera rempli dynamiquement
        public List<AnswerItem> answers = new List<AnswerItem>();
    }

    /// <summary>
    /// Item de réponse générique pour le JSON
    /// </summary>
    [Serializable]
    private class AnswerItem
    {
        public int question_id;
        public int? answer_option_id;
        public float? numeric_answer;

        // Constructeur pour QCM/Trous
        public AnswerItem(int questionId, int answerOptionId)
        {
            question_id = questionId;
            answer_option_id = answerOptionId;
            numeric_answer = null;
        }

        // Constructeur pour réponse numérique
        public AnswerItem(int questionId, float numericAnswer)
        {
            question_id = questionId;
            answer_option_id = null;
            numeric_answer = numericAnswer;
        }
    }

    // ==========================================
    // MÉTHODES PUBLIQUES
    // ==========================================

    /// <summary>
    /// Reset les réponses d'un jeu avant de commencer
    /// Doit être appelé au démarrage de chaque jeu
    /// </summary>
    /// <param name="gameId">ID du jeu</param>
    /// <param name="onSuccess">Callback en cas de succès (status: success)</param>
    /// <param name="onError">Callback en cas d'erreur</param>
    public void ResetGameAnswers(int gameId, Action onSuccess = null, Action<string> onError = null)
    {
        if (showDebugLogs)
        {
            Debug.Log($"[GameAnswerService] 🔄 Reset des réponses - gameId: {gameId}");
        }

        StartCoroutine(ResetGameAnswersCoroutine(gameId, onSuccess, onError));
    }

    /// <summary>
    /// Envoie une réponse QCM (shooting) au serveur
    /// </summary>
    /// <param name="gameId">ID du jeu</param>
    /// <param name="questionId">ID de la question</param>
    /// <param name="answerOptionId">ID de l'option de réponse sélectionnée</param>
    /// <param name="onSuccess">Callback en cas de succès</param>
    /// <param name="onError">Callback en cas d'erreur</param>
    public void SendQCMAnswer(int gameId, int questionId, int answerOptionId, Action onSuccess = null, Action<string> onError = null)
    {
        if (showDebugLogs)
        {
            Debug.Log($"[GameAnswerService] 📤 Envoi réponse QCM - gameId: {gameId}, questionId: {questionId}, answerOptionId: {answerOptionId}");
        }

        StartCoroutine(SendAnswerCoroutine(gameId, new AnswerItem(questionId, answerOptionId), onSuccess, onError));
    }

    /// <summary>
    /// Envoie une réponse numérique (calculator) au serveur
    /// </summary>
    /// <param name="gameId">ID du jeu</param>
    /// <param name="questionId">ID de la question</param>
    /// <param name="numericAnswer">Valeur numérique de la réponse</param>
    /// <param name="onSuccess">Callback en cas de succès</param>
    /// <param name="onError">Callback en cas d'erreur</param>
    public void SendNumericAnswer(int gameId, int questionId, float numericAnswer, Action onSuccess = null, Action<string> onError = null)
    {
        if (showDebugLogs)
        {
            Debug.Log($"[GameAnswerService] 📤 Envoi réponse numérique - gameId: {gameId}, questionId: {questionId}, numericAnswer: {numericAnswer}");
        }

        StartCoroutine(SendAnswerCoroutine(gameId, new AnswerItem(questionId, numericAnswer), onSuccess, onError));
    }

    /// <summary>
    /// Envoie une réponse texte à trous au serveur
    /// </summary>
    /// <param name="gameId">ID du jeu</param>
    /// <param name="questionId">ID de la question</param>
    /// <param name="answerOptionId">ID de l'option de réponse sélectionnée (hole id)</param>
    /// <param name="onSuccess">Callback en cas de succès</param>
    /// <param name="onError">Callback en cas d'erreur</param>
    public void SendTrousAnswer(int gameId, int questionId, int answerOptionId, Action onSuccess = null, Action<string> onError = null)
    {
        if (showDebugLogs)
        {
            Debug.Log($"[GameAnswerService] 📤 Envoi réponse trous - gameId: {gameId}, questionId: {questionId}, answerOptionId: {answerOptionId}");
        }

        StartCoroutine(SendAnswerCoroutine(gameId, new AnswerItem(questionId, answerOptionId), onSuccess, onError));
    }

    /// <summary>
    /// Envoie plusieurs réponses trous en une seule requête (une par trou)
    /// </summary>
    /// <param name="gameId">ID du jeu</param>
    /// <param name="answers">Liste de tuples (questionId, answerOptionId)</param>
    /// <param name="onSuccess">Callback en cas de succès</param>
    /// <param name="onError">Callback en cas d'erreur</param>
    public void SendMultipleTrousAnswers(int gameId, List<(int questionId, int answerOptionId)> answers, Action onSuccess = null, Action<string> onError = null)
    {
        if (showDebugLogs)
        {
            Debug.Log($"[GameAnswerService] 📤 Envoi {answers.Count} réponses trous - gameId: {gameId}");
        }

        List<AnswerItem> answerItems = new List<AnswerItem>();
        foreach (var answer in answers)
        {
            answerItems.Add(new AnswerItem(answer.questionId, answer.answerOptionId));
        }

        StartCoroutine(SendMultipleAnswersCoroutine(gameId, answerItems, onSuccess, onError));
    }

    // ==========================================
    // COROUTINES PRIVÉES
    // ==========================================

    private IEnumerator ResetGameAnswersCoroutine(int gameId, Action onSuccess, Action<string> onError)
    {
        // Vérifier que GeneralConfigManager est disponible
        if (GeneralConfigManager.Instance == null || !GeneralConfigManager.Instance.IsConfigLoaded())
        {
            string error = "GeneralConfigManager non disponible ou config non chargée";
            Debug.LogError($"[GameAnswerService] ❌ {error}");
            onError?.Invoke(error);
            yield break;
        }

        // Obtenir l'URL de l'API reset
        string apiUrl = GeneralConfigManager.Instance.GetGameAnswersResetApiUrl(gameId);
        if (string.IsNullOrEmpty(apiUrl))
        {
            string error = "Impossible de construire l'URL de l'API answers/reset";
            Debug.LogError($"[GameAnswerService] ❌ {error}");
            onError?.Invoke(error);
            yield break;
        }

        // Construire le JSON avec la difficulté
        string difficulty = PlayerPrefs.GetString("CurrentQuestDifficulty", "Débutant");
        string jsonBody = $"{{\"difficulty\":\"{difficulty}\"}}";

        if (showDebugLogs)
        {
            Debug.Log($"[GameAnswerService] 🌐 POST {apiUrl}");
            Debug.Log($"[GameAnswerService] 📦 Body: {jsonBody} (difficulté: {difficulty})");
        }

        // Créer la requête POST
        using (UnityWebRequest www = new UnityWebRequest(apiUrl, "POST"))
        {
            byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(jsonBody);
            www.uploadHandler = new UploadHandlerRaw(bodyRaw);
            www.downloadHandler = new DownloadHandlerBuffer();
            www.SetRequestHeader("Content-Type", "application/json");

            // Ajouter le token d'authentification si disponible
            string token = UserDataManager.Instance?.token;
            if (!string.IsNullOrEmpty(token))
            {
                www.SetRequestHeader("Authorization", $"Bearer {token}");
                if (showDebugLogs)
                {
                    Debug.Log($"[GameAnswerService] 🔑 Token ajouté à la requête reset");
                }
            }

            yield return www.SendWebRequest();

            // Toujours logger le statut de retour pour le reset
            Debug.Log($"[GameAnswerService] ═══════════════════════════════════════════");
            Debug.Log($"[GameAnswerService] 🔄 RÉPONSE RESET API - POST {apiUrl}");
            Debug.Log($"[GameAnswerService] 📤 Envoyé: {jsonBody}");
            Debug.Log($"[GameAnswerService] 📊 HTTP Status: {www.responseCode}");

            if (www.result == UnityWebRequest.Result.Success)
            {
                string responseText = www.downloadHandler.text;
                Debug.Log($"[GameAnswerService] 📥 Réponse serveur: {responseText}");

                // Vérifier que la réponse contient "status": "success"
                if (responseText.Contains("\"status\"") && responseText.Contains("\"success\""))
                {
                    Debug.Log($"[GameAnswerService] ✅ RESET SUCCÈS pour gameId: {gameId}");
                    Debug.Log($"[GameAnswerService] ═══════════════════════════════════════════");
                    onSuccess?.Invoke();
                }
                else
                {
                    string error = $"Reset échoué - réponse inattendue: {responseText}";
                    Debug.LogError($"[GameAnswerService] ❌ RESET ÉCHEC - {error}");
                    Debug.Log($"[GameAnswerService] ═══════════════════════════════════════════");
                    onError?.Invoke(error);
                }
            }
            else
            {
                string errorMsg = $"Erreur {www.responseCode}: {www.error}";
                if (!string.IsNullOrEmpty(www.downloadHandler?.text))
                {
                    errorMsg += $" - {www.downloadHandler.text}";
                }
                Debug.LogError($"[GameAnswerService] ❌ RESET ÉCHEC - {errorMsg}");
                Debug.Log($"[GameAnswerService] ═══════════════════════════════════════════");
                onError?.Invoke(errorMsg);
            }
        }
    }

    private IEnumerator SendAnswerCoroutine(int gameId, AnswerItem answer, Action onSuccess, Action<string> onError)
    {
        // Construire le corps de la requête
        var requestBody = new AnswersRequestBody();
        requestBody.difficulty = PlayerPrefs.GetString("CurrentQuestDifficulty", "Débutant");
        requestBody.answers.Add(answer);

        yield return StartCoroutine(SendRequestCoroutine(gameId, requestBody, onSuccess, onError));
    }

    private IEnumerator SendMultipleAnswersCoroutine(int gameId, List<AnswerItem> answers, Action onSuccess, Action<string> onError)
    {
        // Construire le corps de la requête
        var requestBody = new AnswersRequestBody();
        requestBody.difficulty = PlayerPrefs.GetString("CurrentQuestDifficulty", "Débutant");
        requestBody.answers.AddRange(answers);

        yield return StartCoroutine(SendRequestCoroutine(gameId, requestBody, onSuccess, onError));
    }

    private IEnumerator SendRequestCoroutine(int gameId, AnswersRequestBody requestBody, Action onSuccess, Action<string> onError)
    {
        // Vérifier que GeneralConfigManager est disponible
        if (GeneralConfigManager.Instance == null || !GeneralConfigManager.Instance.IsConfigLoaded())
        {
            string error = "GeneralConfigManager non disponible ou config non chargée";
            Debug.LogError($"[GameAnswerService] ❌ {error}");
            onError?.Invoke(error);
            yield break;
        }

        // Obtenir l'URL de l'API
        string apiUrl = GeneralConfigManager.Instance.GetGameAnswersApiUrl(gameId);
        if (string.IsNullOrEmpty(apiUrl))
        {
            string error = "Impossible de construire l'URL de l'API answers";
            Debug.LogError($"[GameAnswerService] ❌ {error}");
            onError?.Invoke(error);
            yield break;
        }

        // Vérifier que l'utilisateur est connecté
        if (UserDataManager.Instance == null || string.IsNullOrEmpty(UserDataManager.Instance.token))
        {
            string error = "Utilisateur non connecté - impossible d'envoyer la réponse";
            Debug.LogWarning($"[GameAnswerService] ⚠️ {error}");
            // On continue quand même mais on log l'avertissement
            // L'API retournera probablement une erreur 401
        }

        // Convertir en JSON
        // Note: JsonUtility ne gère pas bien les nullable, on crée donc un JSON manuel
        string jsonBody = BuildJsonBody(requestBody);

        if (showDebugLogs)
        {
            Debug.Log($"[GameAnswerService] 🌐 POST {apiUrl}");
            Debug.Log($"[GameAnswerService] 📦 Body: {jsonBody}");
        }

        // Créer la requête POST
        using (UnityWebRequest www = new UnityWebRequest(apiUrl, "POST"))
        {
            byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(jsonBody);
            www.uploadHandler = new UploadHandlerRaw(bodyRaw);
            www.downloadHandler = new DownloadHandlerBuffer();
            www.SetRequestHeader("Content-Type", "application/json");

            // Ajouter le token d'authentification si disponible
            string token = UserDataManager.Instance?.token;
            if (!string.IsNullOrEmpty(token))
            {
                www.SetRequestHeader("Authorization", $"Bearer {token}");
                if (showDebugLogs)
                {
                    Debug.Log($"[GameAnswerService] 🔑 Token ajouté à la requête");
                }
            }

            yield return www.SendWebRequest();

            // Toujours logger le statut de retour (pas seulement en mode debug)
            Debug.Log($"[GameAnswerService] ═══════════════════════════════════════════");
            Debug.Log($"[GameAnswerService] 📡 RÉPONSE API - POST {apiUrl}");
            Debug.Log($"[GameAnswerService] 📤 Envoyé: {jsonBody}");
            Debug.Log($"[GameAnswerService] 📊 HTTP Status: {www.responseCode}");
            
            if (www.result == UnityWebRequest.Result.Success)
            {
                Debug.Log($"[GameAnswerService] ✅ SUCCÈS - Réponse envoyée");
                Debug.Log($"[GameAnswerService] 📥 Réponse serveur: {www.downloadHandler.text}");
                Debug.Log($"[GameAnswerService] ═══════════════════════════════════════════");
                onSuccess?.Invoke();
            }
            else
            {
                string errorMsg = $"Erreur {www.responseCode}: {www.error}";
                if (!string.IsNullOrEmpty(www.downloadHandler?.text))
                {
                    errorMsg += $" - {www.downloadHandler.text}";
                }
                Debug.LogError($"[GameAnswerService] ❌ ÉCHEC - {errorMsg}");
                Debug.Log($"[GameAnswerService] ═══════════════════════════════════════════");
                onError?.Invoke(errorMsg);
            }
        }
    }

    /// <summary>
    /// Construit le JSON manuellement pour gérer les champs nullable
    /// </summary>
    private string BuildJsonBody(AnswersRequestBody requestBody)
    {
        var answersJsonParts = new List<string>();

        foreach (var answer in requestBody.answers)
        {
            string answerJson;
            if (answer.answer_option_id.HasValue)
            {
                answerJson = $"{{\"question_id\":{answer.question_id},\"answer_option_id\":{answer.answer_option_id.Value}}}";
            }
            else if (answer.numeric_answer.HasValue)
            {
                // Utiliser InvariantCulture pour le format décimal avec point
                answerJson = $"{{\"question_id\":{answer.question_id},\"numeric_answer\":{answer.numeric_answer.Value.ToString(System.Globalization.CultureInfo.InvariantCulture)}}}";
            }
            else
            {
                // Cas par défaut (ne devrait pas arriver)
                answerJson = $"{{\"question_id\":{answer.question_id}}}";
            }
            answersJsonParts.Add(answerJson);
        }

        // Inclure le champ difficulty dans le JSON
        return $"{{\"difficulty\":\"{requestBody.difficulty}\",\"answers\":[{string.Join(",", answersJsonParts)}]}}";
    }

    // ==========================================
    // MÉTHODES UTILITAIRES
    // ==========================================

    /// <summary>
    /// Crée une instance du service si elle n'existe pas
    /// </summary>
    public static GameAnswerService EnsureInstance()
    {
        if (Instance == null)
        {
            GameObject serviceObj = new GameObject("GameAnswerService");
            Instance = serviceObj.AddComponent<GameAnswerService>();
            Debug.Log("[GameAnswerService] Instance créée automatiquement");
        }
        return Instance;
    }
}

