using System.Runtime.InteropServices;
using UnityEngine;

/// <summary>
/// Pont de communication entre Unity WebGL et le site parent via window.postMessage.
/// Gère l'échange de tokens JWT pour l'authentification.
/// </summary>
public class PostMessageBridge : MonoBehaviour
{
    #region Singleton
    private static PostMessageBridge _instance;
    private static bool _applicationIsQuitting = false;
    
    public static PostMessageBridge Instance
    {
        get
        {
            // Ne pas créer d'instance si l'application est en train de se fermer
            if (_applicationIsQuitting)
            {
                return null;
            }
            
            if (_instance == null)
            {
                GameObject go = new GameObject("PostMessageBridge");
                _instance = go.AddComponent<PostMessageBridge>();
                DontDestroyOnLoad(go);
            }
            return _instance;
        }
    }
    
    void OnApplicationQuit()
    {
        _applicationIsQuitting = true;
    }
    
    void OnDestroy()
    {
        if (_instance == this)
        {
            _instance = null;
        }
    }
    #endregion
    
    // Logs verbeux désactivés en production
    // (static readonly pour éviter CS0162 "unreachable code detected" quand le compilateur voit une constante)
    private static readonly bool VERBOSE_DEBUG = false;
    [System.Diagnostics.Conditional("UNITY_EDITOR")]
    private void LogVerbose(string message) { if (VERBOSE_DEBUG) Debug.Log(message); }

    #region Imports des fonctions JavaScript

#if UNITY_WEBGL && !UNITY_EDITOR
    [DllImport("__Internal")]
    private static extern int PostMessage_Init();

    [DllImport("__Internal")]
    private static extern int PostMessage_IsInIframe();

    [DllImport("__Internal")]
    private static extern int PostMessage_GetToken(System.IntPtr bufferPtr, int bufferSize);

    [DllImport("__Internal")]
    private static extern int PostMessage_HasToken();

    [DllImport("__Internal")]
    private static extern int PostMessage_NotifyLogin(string token);

    [DllImport("__Internal")]
    private static extern int PostMessage_NotifyLogout();

    [DllImport("__Internal")]
    private static extern void PostMessage_ClearToken();

    [DllImport("__Internal")]
    private static extern int PostMessage_SetToken(string token);

    [DllImport("__Internal")]
    private static extern int PostMessage_SetParentOrigin(string origin);
    
    [DllImport("__Internal")]
    private static extern int PostMessage_NotifyReady();
#else
    // Stubs pour l'éditeur et les autres plateformes
    private static int PostMessage_Init() { return 0; }
    private static int PostMessage_IsInIframe() { return 0; }
    private static int PostMessage_GetToken(System.IntPtr bufferPtr, int bufferSize) { return 0; }
    private static int PostMessage_HasToken() { return 0; }
    private static int PostMessage_NotifyLogin(string token) { return 0; }
    private static int PostMessage_NotifyLogout() { return 0; }
    private static void PostMessage_ClearToken() { }
    private static int PostMessage_SetToken(string token) { return 0; }
    private static int PostMessage_SetParentOrigin(string origin) { return 0; }
    private static int PostMessage_NotifyReady() { return 0; }
#endif

    #endregion

    #region Propriétés publiques

    /// <summary>
    /// Indique si le jeu est chargé dans une iframe.
    /// </summary>
    public bool IsInIframe { get; private set; }

    /// <summary>
    /// Indique si un token JWT a été reçu du parent.
    /// </summary>
    public bool HasToken
    {
        get
        {
#if UNITY_WEBGL && !UNITY_EDITOR
            return PostMessage_HasToken() == 1;
#else
            return false;
#endif
        }
    }

    #endregion

    #region Événements

    /// <summary>
    /// Événement déclenché quand un token est reçu du parent.
    /// </summary>
    public event System.Action<string> OnTokenReceived;
    
    /// <summary>
    /// Événement déclenché quand un slug de projet est reçu du parent.
    /// </summary>
    public event System.Action<string> OnSlugReceived;

    #endregion
    
    #region Données reçues du parent
    
    /// <summary>
    /// Le slug du projet reçu du parent (si dans iframe).
    /// </summary>
    public string ReceivedSlug { get; private set; }
    
    /// <summary>
    /// Le dernier token reçu du parent (stocké pour les abonnés tardifs).
    /// </summary>
    public string PendingToken { get; private set; }
    
    /// <summary>
    /// Indique si un token a été reçu mais pas encore traité.
    /// </summary>
    public bool HasPendingToken => !string.IsNullOrEmpty(PendingToken);
    
    #endregion

    #region Cycle de vie Unity

    void Awake()
    {
        if (_instance != null && _instance != this)
        {
            Destroy(gameObject);
            return;
        }

        _instance = this;
        DontDestroyOnLoad(gameObject);

        // Initialiser la communication postMessage
        Initialize();
    }

    #endregion

    #region Méthodes publiques

    /// <summary>
    /// Initialise le système de communication postMessage.
    /// Doit être appelé au démarrage du jeu.
    /// </summary>
    public void Initialize()
    {
#if UNITY_WEBGL && !UNITY_EDITOR
        Debug.Log("═══════════════════════════════════════════════════════");
        LogVerbose("[PostMessageBridge] Initialisation de la communication iframe...");
        
        int result = PostMessage_Init();
        LogVerbose($"[PostMessageBridge] PostMessage_Init() retour: {result}");
        
        IsInIframe = PostMessage_IsInIframe() == 1;
        LogVerbose($"[PostMessageBridge] IsInIframe: {IsInIframe}");

        if (result == 1 && IsInIframe)
        {
            LogVerbose("[PostMessageBridge] ✓ Communication iframe activée");
            
            // Vérifier si un token a déjà été reçu
            LogVerbose("[PostMessageBridge] Vérification d'un token existant...");
            CheckForExistingToken();
            
            // Notifier le parent que Unity est prêt à recevoir le token
            NotifyParentUnityReady();
        }
        else
        {
            LogVerbose("[PostMessageBridge] Jeu non chargé dans une iframe - mode standalone");
        }
        Debug.Log("═══════════════════════════════════════════════════════");
#else
        IsInIframe = false;
        LogVerbose("[PostMessageBridge] Mode éditeur - communication iframe désactivée");
#endif
    }

    /// <summary>
    /// Récupère le token JWT stocké.
    /// </summary>
    /// <returns>Le token JWT ou null si aucun token n'est disponible</returns>
    public string GetToken()
    {
#if UNITY_WEBGL && !UNITY_EDITOR
        LogVerbose("[PostMessageBridge] GetToken() appelé");
        
        if (!HasToken)
        {
            LogVerbose("[PostMessageBridge] HasToken = false, retour null");
            return null;
        }

        LogVerbose("[PostMessageBridge] HasToken = true, allocation du buffer...");
        // Allouer un buffer pour recevoir le token
        System.IntPtr buffer = Marshal.AllocHGlobal(2048);
        try
        {
            LogVerbose("[PostMessageBridge] Appel de PostMessage_GetToken()...");
            int result = PostMessage_GetToken(buffer, 2048);
            LogVerbose($"[PostMessageBridge] PostMessage_GetToken() retour: {result}");
            
            if (result == 1)
            {
                string token = Marshal.PtrToStringAnsi(buffer);
                LogVerbose($"[PostMessageBridge] ✓ Token récupéré (longueur: {token?.Length ?? 0})");
                if (!string.IsNullOrEmpty(token))
                {
                    LogVerbose($"[PostMessageBridge] Token (début): {token.Substring(0, System.Math.Min(50, token.Length))}...");
                }
                return token;
            }
            else
            {
                Debug.LogWarning("[PostMessageBridge] PostMessage_GetToken() a retourné 0");
            }
        }
        catch (System.Exception e)
        {
            Debug.LogError($"[PostMessageBridge] Erreur lors de la récupération du token: {e.Message}");
        }
        finally
        {
            Marshal.FreeHGlobal(buffer);
        }
#endif
        return null;
    }

    /// <summary>
    /// Envoie un message UJSA_LOGIN au parent après une connexion réussie.
    /// </summary>
    /// <param name="token">Le token JWT à envoyer au parent</param>
    /// <returns>True si le message a été envoyé avec succès</returns>
    public bool NotifyLogin(string token)
    {
        if (string.IsNullOrEmpty(token))
        {
            Debug.LogWarning("[PostMessageBridge] Impossible d'envoyer un token vide");
            return false;
        }

#if UNITY_WEBGL && !UNITY_EDITOR
        if (!IsInIframe)
        {
            LogVerbose("[PostMessageBridge] Pas dans une iframe - notification annulée");
            return false;
        }

        LogVerbose("[PostMessageBridge] Envoi de la notification de login au parent...");
        int result = PostMessage_NotifyLogin(token);
        
        if (result == 1)
        {
            LogVerbose("[PostMessageBridge] ✓ Notification de login envoyée avec succès");
            return true;
        }
        else
        {
            Debug.LogError("[PostMessageBridge] ⛔ Échec de l'envoi de la notification de login");
            return false;
        }
#else
        LogVerbose("[PostMessageBridge] Mode éditeur - notification simulée");
        return true;
#endif
    }

    /// <summary>
    /// Notifie le parent qu'une déconnexion a été déclenchée côté Unity.
    /// Utile en iframe: sinon le parent peut renvoyer le token immédiatement après le reload,
    /// ce qui empêche l'affichage de la popup d'identification.
    /// </summary>
    public bool NotifyLogout()
    {
#if UNITY_WEBGL && !UNITY_EDITOR
        if (!IsInIframe) return false;
        int result = PostMessage_NotifyLogout();
        return result == 1;
#else
        return false;
#endif
    }

    /// <summary>
    /// Nettoie le token stocké (déconnexion).
    /// </summary>
    public void ClearToken()
    {
#if UNITY_WEBGL && !UNITY_EDITOR
        LogVerbose("[PostMessageBridge] Suppression du token JWT");
        PostMessage_ClearToken();
#endif
    }

    /// <summary>
    /// Définit manuellement un token (pour debug ou cas particuliers).
    /// </summary>
    public bool SetToken(string token)
    {
        if (string.IsNullOrEmpty(token))
        {
            return false;
        }

#if UNITY_WEBGL && !UNITY_EDITOR
        LogVerbose("[PostMessageBridge] Définition manuelle du token");
        return PostMessage_SetToken(token) == 1;
#else
        return false;
#endif
    }

    /// <summary>
    /// Change l'origin autorisé du parent (pour environnement de dev).
    /// Par défaut: https://www.newsassurancespro.com
    /// </summary>
    public bool SetParentOrigin(string origin)
    {
        if (string.IsNullOrEmpty(origin))
        {
            return false;
        }

#if UNITY_WEBGL && !UNITY_EDITOR
        LogVerbose($"[PostMessageBridge] Changement de l'origin du parent: {origin}");
        return PostMessage_SetParentOrigin(origin) == 1;
#else
        return false;
#endif
    }
    
    /// <summary>
    /// Notifie le parent que Unity est prêt à recevoir le token.
    /// Le parent peut alors envoyer le token via SET_TOKEN.
    /// </summary>
    public void NotifyParentUnityReady()
    {
#if UNITY_WEBGL && !UNITY_EDITOR
        Debug.Log("[PostMessageBridge] 📤 Envoi du signal UNITY_READY au parent...");
        int result = PostMessage_NotifyReady();
        if (result == 1)
        {
            Debug.Log("[PostMessageBridge] ✓ Signal UNITY_READY envoyé avec succès");
        }
        else
        {
            Debug.LogWarning("[PostMessageBridge] ✗ Échec de l'envoi du signal UNITY_READY");
        }
#else
        LogVerbose("[PostMessageBridge] Mode éditeur - signal UNITY_READY ignoré");
#endif
    }

    #endregion

    #region Méthodes privées

    /// <summary>
    /// Vérifie si un token a déjà été reçu lors de l'initialisation.
    /// </summary>
    private void CheckForExistingToken()
    {
        LogVerbose("[PostMessageBridge] CheckForExistingToken() appelé");
        LogVerbose($"[PostMessageBridge] HasToken: {HasToken}");
        
        if (HasToken)
        {
            LogVerbose("[PostMessageBridge] Token détecté, récupération...");
            string token = GetToken();
            LogVerbose($"[PostMessageBridge] Token récupéré: {(string.IsNullOrEmpty(token) ? "VIDE" : $"{token.Length} caractères")}");
            
            if (!string.IsNullOrEmpty(token))
            {
                LogVerbose("[PostMessageBridge] ✓ Token déjà présent lors de l'initialisation");
                LogVerbose($"[PostMessageBridge] Nombre d'abonnés à OnTokenReceived: {OnTokenReceived?.GetInvocationList().Length ?? 0}");
                OnTokenReceived?.Invoke(token);
                LogVerbose("[PostMessageBridge] Événement OnTokenReceived déclenché");
            }
        }
        else
        {
            LogVerbose("[PostMessageBridge] Aucun token présent pour le moment");
        }
    }

    /// <summary>
    /// Callback appelé par JavaScript quand un token est reçu du parent.
    /// Cette méthode est appelée via SendMessage depuis le .jslib.
    /// Le GameObject doit s'appeler "PostMessageBridge" pour recevoir ce callback.
    /// </summary>
    /// <param name="token">Le token JWT reçu</param>
    public void OnTokenReceivedFromJS(string token)
    {
        Debug.Log($"[PostMessageBridge] ✓ Token reçu depuis le parent (longueur: {token?.Length ?? 0})");
        
        if (!string.IsNullOrEmpty(token))
        {
            // Stocker le token pour les abonnés tardifs
            PendingToken = token;
            Debug.Log($"[PostMessageBridge] Token stocké en attente (abonnés: {OnTokenReceived?.GetInvocationList()?.Length ?? 0})");
            
            // S'assurer que IframeAuthManager existe pour traiter le token
            if (OnTokenReceived == null || OnTokenReceived.GetInvocationList().Length == 0)
            {
                Debug.Log("[PostMessageBridge] 🔧 Forçage de la création de IframeAuthManager...");
                // Forcer la création de IframeAuthManager s'il n'existe pas
                var iframeAuth = IframeAuthManager.Instance;
                Debug.Log($"[PostMessageBridge] IframeAuthManager créé: {iframeAuth != null}");
            }
            
            // Notifier les abonnés actuels
            if (OnTokenReceived != null)
            {
                Debug.Log($"[PostMessageBridge] 📤 Notification de {OnTokenReceived.GetInvocationList().Length} abonné(s)...");
                OnTokenReceived.Invoke(token);
            }
            else
            {
                Debug.LogWarning("[PostMessageBridge] ⚠ Toujours aucun abonné après création de IframeAuthManager");
            }
        }
    }
    
    /// <summary>
    /// Consomme le token en attente (appelé par IframeAuthManager après traitement).
    /// </summary>
    public void ClearPendingToken()
    {
        PendingToken = null;
    }
    
    /// <summary>
    /// Callback appelé par JavaScript quand un slug est reçu du parent.
    /// Cette méthode est appelée via SendMessage depuis le .jslib.
    /// Le GameObject doit s'appeler "PostMessageBridge" pour recevoir ce callback.
    /// </summary>
    /// <param name="slug">Le slug du projet reçu</param>
    public void OnSlugReceivedFromJS(string slug)
    {
        Debug.Log($"[PostMessageBridge] ✓ Slug reçu depuis le parent: {slug}");
        
        if (!string.IsNullOrEmpty(slug))
        {
            ReceivedSlug = slug;
            OnSlugReceived?.Invoke(slug);
        }
    }

    #endregion
}

