using System.Collections;
using UnityEngine;
#if DAVANCI_GA
using GameAnalyticsSDK;
#endif
#if DAVANCI_APPSFLYER
using AppsFlyerSDK;
#endif
#if DAVANCI_FIREBASE
using Firebase;
using Firebase.Analytics;
using Firebase.Extensions;
#endif
#if (UNITY_IOS || UNITY_TVOS)
#if DAVANCI_GA
using GameAnalyticsSDK.iOS;
#endif
#if DAVANCI_NOTIFICATIONS
using Unity.Notifications.iOS;
#endif
#elif UNITY_ANDROID
#if DAVANCI_NOTIFICATIONS
using Unity.Notifications.Android;
#endif
#endif

namespace Davanci.Nexus
{
    // Handles ATT, push-notification permissions and SDK startup (AppsFlyer +
    // GameAnalytics + Firebase). All SDK calls are gated behind scripting defines so
    // the class compiles with no SDKs installed. Values are loaded from NexusConfig.
    public class AuthorizationManager : SingletonMB<AuthorizationManager>
#if DAVANCI_GA
        , IGameAnalyticsATTListener
#endif
#if DAVANCI_APPSFLYER
        , IAppsFlyerConversionData
#endif
    {
        bool requestATT = true;
        bool requestNotifications = true;
        string appsFlyerDevKey = "";
        string appsFlyerAppID = "";

#pragma warning disable 0414 // assigned-but-unused when some SDK defines are off
        bool _attCompleted;
        bool _notificationsCompleted;
        bool _consentStarted;
        bool _consentDone;
#pragma warning restore 0414
        bool _permissionsStarted;

        public bool IsATTAuthorized { get; private set; }
        public bool AreNotificationsAuthorized { get; private set; }

        protected override void Awake()
        {
            base.Awake();

            var config = NexusConfig.Load();
            if (config != null)
            {
                requestATT = config.requestATT;
                requestNotifications = config.requestNotifications;
                if (!string.IsNullOrEmpty(config.appsFlyerDevKey)) appsFlyerDevKey = config.appsFlyerDevKey;
                if (!string.IsNullOrEmpty(config.appsFlyerAppID)) appsFlyerAppID = config.appsFlyerAppID;
            }

            // CMP starts in the permissions coroutine (not Awake) so the P/Invoke callback and
            // coroutine wait are registered before InMobi calls back.

#if DAVANCI_APPSFLYER
            AppsFlyer.initSDK(appsFlyerDevKey, appsFlyerAppID, this);
#if UNITY_IOS && !UNITY_EDITOR
            ((AppsFlyeriOS)AppsFlyer.instance).waitForATTUserAuthorizationWithTimeoutInterval(120);
#endif
#endif
        }

        // The splash scene starts the flow once the brand has been shown (BeginPermissions).
        // A game launched without it (e.g. Play pressed on a game scene in the editor) starts it here.
        IEnumerator Start()
        {
            yield return null; // let the first scene's objects wake up
            if (!NexusSplashController.OwnsPermissions)
                BeginPermissions();
        }

        // Starts CMP → ATT → SDK start → notifications. Runs once; later calls are ignored.
        public void BeginPermissions()
        {
            if (_permissionsStarted) return;
            StartCoroutine(RequestAllPermissions());
        }

        public IEnumerator RequestAllPermissions()
        {
            if (_permissionsStarted) yield break;
            _permissionsStarted = true;

            _attCompleted = false;
            _notificationsCompleted = false;
            IsATTAuthorized = false;
            AreNotificationsAuthorized = false;

            yield return WaitUntilActive();

            var consentCfg = NexusConfig.Load();
            if (consentCfg != null && consentCfg.enableCmp)
            {
                _consentStarted = true;
                _consentDone = false;
                NexusInMobiCmp.Request(consentCfg, () => _consentDone = true);
                Debug.LogWarning("[Nexus] CMP consent (InMobi Choice) started.");
                yield return null; // one frame so native callback registration is settled
            }

            // Wait until the user dismisses the CMP (no timeout).
            if (_consentStarted)
            {
                while (!_consentDone)
                    yield return null;

                Debug.LogWarning("[Nexus] CMP consent resolved — proceeding to ATT.");

                // Let the consent WebView fully dismiss before ATT.
                yield return new WaitForSeconds(1f);
            }

            bool? attForAds = null;

#if (UNITY_IOS || UNITY_TVOS) && DAVANCI_GA
            if (requestATT && (Application.platform == RuntimePlatform.IPhonePlayer || Application.platform == RuntimePlatform.tvOS))
            {
                yield return WaitUntilActive();
                Debug.LogWarning("[Nexus] Requesting ATT permission...");
                GameAnalyticsATTClient.Instance.RequestTrackingAuthorization(this);

                while (!_attCompleted)
                    yield return null;

                Debug.LogWarning("[Nexus] ATT resolved — starting analytics and ads.");

                yield return new WaitForSeconds(1f);
                for (int i = 0; i < 60 && !Application.isFocused; i++)
                    yield return null;

                attForAds = IsATTAuthorized;
            }
            else
            {
                _attCompleted = true;
            }
#else
            _attCompleted = true;
#endif

            // CMP → ATT → notifications (sequential). Ads/analytics start right after ATT — not blocked by the notification dialog.
            InitializeAnalyticsAndAds(attForAds);

            if (requestNotifications)
            {
                Debug.Log("[Nexus] ATT resolved — requesting notification permission.");
                yield return RequestNotificationPermission();
#if DAVANCI_NOTIFICATIONS
                NexusNotifications.OnPermissionsComplete(AreNotificationsAuthorized);
#endif
            }

            NexusStartup.MarkStartupComplete();
            Debug.Log("[Nexus] Startup complete (CMP → ATT → notifications).");
        }

        // iOS ignores a permission request made while the app is not active: ATT then reports
        // NotDetermined and never shows its dialog. Every prompt therefore waits for focus plus a
        // short settle, with no timeout. The editor has no system prompts to wait for.
        static IEnumerator WaitUntilActive()
        {
            if (Application.isEditor) yield break;
            while (!Application.isFocused)
                yield return null;
            yield return new WaitForSecondsRealtime(0.5f);
        }

        void OnApplicationPause(bool paused)
        {
            if (paused) return;

            if (DavanciNexus.IsGameAnalyticsReady && !DavanciNexus.IsFirebaseReady)
                InitializeFirebase();
        }

        // GameAnalytics (sync), Firebase (async), AppsFlyer, then mediation — all after ATT on iOS.
        void InitializeAnalyticsAndAds(bool? attForAds)
        {
            InitializeGameAnalytics();
#if !UNITY_EDITOR
            InitializeFirebase();
#endif
            NexusAttribution.ReapplyToAnalytics();

#if DAVANCI_APPSFLYER
            AppsFlyer.startSDK();
#endif

            NexusBootstrap.InitializeAds(attForAds);
        }

        void InitializeGameAnalytics()
        {
#if DAVANCI_GA
            if (!GameAnalytics.Initialized)
            {
                GameAnalytics.Initialize();
                Debug.Log("[Nexus] GameAnalytics initialized.");
            }

            DavanciNexus.MarkGameAnalyticsReady();
#endif
        }

        void InitializeFirebase()
        {
#if DAVANCI_FIREBASE
            // ContinueWithOnMainThread keeps the callback on Unity's main thread.
            FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task =>
            {
                if (task.Result == DependencyStatus.Available)
                {
                    FirebaseAnalytics.SetAnalyticsCollectionEnabled(true);
                    Debug.Log("[Nexus] Firebase initialized.");
                    DavanciNexus.MarkFirebaseReady();
                }
                else
                {
                    Debug.LogError($"[Nexus] Firebase dependencies not available: {task.Result}");
                }
            });
#endif
        }

#if DAVANCI_APPSFLYER
        const string AfStatusKey = "af_status";
        const string AfMediaSourceKey = "media_source";

        // AppsFlyer attribution callbacks. Tags the user paid/organic + media source for analytics.
        public void onConversionDataSuccess(string conversionData)
        {
            var dict = AppsFlyer.CallbackStringToDictionary(conversionData);
            string status = dict != null && dict.TryGetValue(AfStatusKey, out var s) ? s?.ToString() : null;
            string media = dict != null && dict.TryGetValue(AfMediaSourceKey, out var m) ? m?.ToString() : null;
            NexusAttribution.Capture(status, media);
        }

        public void onConversionDataFail(string error) =>
            Debug.LogWarning("[Nexus] AppsFlyer conversion data failed: " + error);

        public void onAppOpenAttribution(string attributionData) { }
        public void onAppOpenAttributionFailure(string error) { }
#endif

        IEnumerator RequestNotificationPermission()
        {
#if UNITY_EDITOR
            Debug.Log("[Nexus] Notifications skipped in Editor.");
            AreNotificationsAuthorized = false;
            _notificationsCompleted = true;
#elif (UNITY_IOS || UNITY_TVOS) && DAVANCI_NOTIFICATIONS
            Debug.Log("[Nexus] Requesting iOS notification permission...");
            var request = new AuthorizationRequest(
                AuthorizationOption.Alert | AuthorizationOption.Badge | AuthorizationOption.Sound,
                true);

            while (!request.IsFinished)
                yield return null;

            AreNotificationsAuthorized = request.Granted;
            Debug.Log($"[Nexus] iOS notification authorization granted = {request.Granted}");
            request.Dispose();
            _notificationsCompleted = true;

#elif UNITY_ANDROID && DAVANCI_NOTIFICATIONS
            if (Application.platform == RuntimePlatform.Android)
            {
                int apiLevel = 0;
                try
                {
                    using (var version = new AndroidJavaClass("android.os.Build$VERSION"))
                        apiLevel = version.GetStatic<int>("SDK_INT");
                }
                catch (System.Exception e)
                {
                    Debug.LogWarning($"[Nexus] Could not get Android API level: {e.Message}");
                }

                if (apiLevel >= 33)
                {
                    var permission = "android.permission.POST_NOTIFICATIONS";
                    bool hasPermission = UnityEngine.Android.Permission.HasUserAuthorizedPermission(permission);

                    if (!hasPermission)
                    {
                        UnityEngine.Android.Permission.RequestUserPermission(permission);
                        yield return new WaitForSeconds(0.5f);

                        float timeout = 15f, elapsed = 0f, checkInterval = 0.2f;
                        while (elapsed < timeout)
                        {
                            yield return new WaitForSeconds(checkInterval);
                            elapsed += checkInterval;
                            hasPermission = UnityEngine.Android.Permission.HasUserAuthorizedPermission(permission);
                            if (hasPermission) break;
                        }
                    }

                    AreNotificationsAuthorized = hasPermission;
                }
                else
                {
                    AreNotificationsAuthorized = true;
                }
            }
            else
            {
                AreNotificationsAuthorized = true;
            }

            _notificationsCompleted = true;

#else
            Debug.Log("[Nexus] Notifications not supported / package not installed on this platform.");
            AreNotificationsAuthorized = false;
            _notificationsCompleted = true;
#endif
            yield return null;
        }

#if DAVANCI_GA
        public void GameAnalyticsATTListenerNotDetermined()
        {
            Debug.Log("[Nexus] ATT status: Not Determined");
            IsATTAuthorized = false;
            _attCompleted = true;
        }

        public void GameAnalyticsATTListenerRestricted()
        {
            Debug.Log("[Nexus] ATT status: Restricted");
            IsATTAuthorized = false;
            _attCompleted = true;
        }

        public void GameAnalyticsATTListenerDenied()
        {
            Debug.Log("[Nexus] ATT status: Denied");
            IsATTAuthorized = false;
            _attCompleted = true;
        }

        public void GameAnalyticsATTListenerAuthorized()
        {
            Debug.Log("[Nexus] ATT status: Authorized");
            IsATTAuthorized = true;
            _attCompleted = true;
        }
#endif
    }
}
