using System;
using UnityEngine;

namespace Davanci.Nexus
{
    // Wrapper around InMobi Choice CMP. When the native SDK is present (DAVANCI_INMOBI_CMP),
    // this shows the certified CMP UI and writes IAB TCF/GPP strings that mediation (LevelPlay /
    // ironSource) reads automatically. When the SDK isn't present (e.g. Editor, or platform not
    // built), it just proceeds so startup isn't blocked — ads then run in the default/limited state.
    public static class NexusInMobiCmp
    {
        public static void Request(NexusConfig config, Action onComplete)
        {
            if (config == null || string.IsNullOrEmpty(config.inmobiCmpPcode))
            {
                Debug.LogWarning("[Nexus] InMobi CMP pcode is empty — skipping consent UI.");
                onComplete?.Invoke();
                return;
            }

#if !DAVANCI_INMOBI_CMP
            // SDK not installed (Nexus Setup → Privacy). Proceed so startup isn't blocked; ads run limited.
            onComplete?.Invoke();
#elif UNITY_EDITOR
            Debug.Log("[Nexus] InMobi CMP: editor no-op (native UI only runs on device).");
            onComplete?.Invoke();
#elif UNITY_IOS
            NexusInMobiCmp_iOS.Request(config.inmobiCmpPcode, onComplete);
#elif UNITY_ANDROID
            NexusInMobiCmp_Android.Request(config.inmobiCmpPcode, onComplete);
#else
            onComplete?.Invoke();
#endif
        }
    }

    internal static class NexusInMobiCmp_Android
    {
        public static void Request(string pcode, Action onComplete)
        {
            try
            {
                var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
                var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
                var app = activity.Call<AndroidJavaObject>("getApplication");

                var callback = new ChoiceCallbackProxy(onComplete);
                // ChoiceCmp.startChoice(Application app, String pCode, String packageId, ChoiceCmpCallback cb, ChoiceStyle style)
                var choiceCmp = new AndroidJavaClass("com.inmobi.cmp.ChoiceCmp");
                choiceCmp.CallStatic(
                    "startChoice",
                    app,
                    pcode,
                    Application.identifier,
                    callback,
                    null);
            }
            catch (Exception e)
            {
                Debug.LogWarning("[Nexus] InMobi CMP Android start failed, proceeding without consent UI: " + e.Message);
                onComplete?.Invoke();
            }
        }

        sealed class ChoiceCallbackProxy : AndroidJavaProxy
        {
            readonly Action _onComplete;
            bool _done;
            bool _uiShown;
            bool _userInteracted;

            public ChoiceCallbackProxy(Action onComplete)
                : base("com.inmobi.cmp.ChoiceCmpCallback")
            {
                _onComplete = onComplete;
            }

            void Finish()
            {
                if (_done) return;
                _done = true;
                _onComplete?.Invoke();
            }

            void onCmpLoaded(AndroidJavaObject pingReturn)
            {
                if (pingReturn == null) return;
                try
                {
                    using (var status = pingReturn.Call<AndroidJavaObject>("getDisplayStatus"))
                    {
                        if (status == null) return;
                        var name = status.Call<string>("name");
                        if (name == "VISIBLE") { _uiShown = true; return; }
                        if (!_uiShown && (name == "DISMISSED" || name == "DISABLED"))
                            Finish();
                    }
                }
                catch { /* wait for UI status callbacks */ }
            }

            void onCMPUIStatusChanged(AndroidJavaObject displayInfo)
            {
                if (IsStatus(displayInfo, "VISIBLE")) { _uiShown = true; return; }
                if (IsStatus(displayInfo, "DISMISSED")) { Finish(); return; }
                if ((_uiShown || _userInteracted) && IsStatus(displayInfo, "HIDDEN"))
                    Finish();
            }

            void onActionButtonClicked(AndroidJavaObject _) => _userInteracted = true;

            static bool IsStatus(AndroidJavaObject displayInfo, string statusName)
            {
                if (displayInfo == null) return false;
                try
                {
                    using (var status = displayInfo.Call<AndroidJavaObject>("getDisplayStatus"))
                        return status != null && status.Call<string>("name") == statusName;
                }
                catch { return false; }
            }

            void onCmpError(AndroidJavaObject _choiceError) => Finish();

            void onIABVendorConsentGiven(AndroidJavaObject gdprData)
            {
                if (_done) return;

                if (!_uiShown && !_userInteracted)
                {
                    Finish();
                    return;
                }

                _userInteracted = true;
            }
            void onReceiveUSRegulationsConsent(AndroidJavaObject _usReg) { }
            void onNonIABVendorConsentGiven(AndroidJavaObject _nonIabData) { }
            void onGoogleVendorConsentGiven(AndroidJavaObject _acData) { }
            void onCCPAConsentGiven(string _s) { }
            void onGoogleBasicConsentChange(AndroidJavaObject _gbc) { }
            void onUserMovedToOtherState() { }
        }
    }

    internal static class NexusInMobiCmp_iOS
    {
        delegate void CmpDoneDelegate();

        static Action _onComplete;
        static bool _done;

        internal static void SignalDone(string source)
        {
            if (_done) return;
            _done = true;
            Debug.LogWarning("[Nexus] InMobi CMP native callback (" + source + ") — consent step complete.");
            _onComplete?.Invoke();
        }

        [AOT.MonoPInvokeCallback(typeof(CmpDoneDelegate))]
        static void NativeOnCmpDone() => SignalDone("P/Invoke");

#if UNITY_IOS && !UNITY_EDITOR && DAVANCI_INMOBI_CMP
        [System.Runtime.InteropServices.DllImport("__Internal")]
        static extern void NexusInMobiCmp_SetDoneCallback(CmpDoneDelegate fn);

        [System.Runtime.InteropServices.DllImport("__Internal")]
        static extern void NexusInMobiCmp_StartChoice(string pcode, string gameObjectName);
#endif

        public static void Request(string pcode, Action onComplete)
        {
#if UNITY_IOS && !UNITY_EDITOR && DAVANCI_INMOBI_CMP
            try
            {
                _onComplete = onComplete;
                _done = false;

                var receiver = NexusInMobiCmpReceiver.GetOrCreate();
                receiver.SetCallback(onComplete);

                NexusInMobiCmp_SetDoneCallback(NativeOnCmpDone);
                NexusInMobiCmp_StartChoice(pcode, receiver.gameObject.name);
            }
            catch (Exception e)
            {
                Debug.LogWarning("[Nexus] InMobi CMP iOS start failed, proceeding without consent UI: " + e.Message);
                onComplete?.Invoke();
            }
#else
            onComplete?.Invoke();
#endif
        }
    }

    // Receives UnitySendMessage fallback from iOS native (P/Invoke is primary).
    [UnityEngine.Scripting.Preserve]
    internal sealed class NexusInMobiCmpReceiver : MonoBehaviour
    {
        static NexusInMobiCmpReceiver _instance;
        Action _onComplete;

        public static NexusInMobiCmpReceiver GetOrCreate()
        {
            if (_instance) return _instance;
            var go = new GameObject("NexusInMobiCmpReceiver");
            DontDestroyOnLoad(go);
            _instance = go.AddComponent<NexusInMobiCmpReceiver>();
            return _instance;
        }

        public void SetCallback(Action onComplete)
        {
            _onComplete = onComplete;
        }

        [UnityEngine.Scripting.Preserve]
        public void OnCmpDone(string _) => NexusInMobiCmp_iOS.SignalDone("UnitySendMessage");
    }
}
