#nullable enable

using System;
using UnityEngine;

namespace HyperstoneSdk
{
    public class Hyperstone
    {
        private static System.Threading.SynchronizationContext? _mainThreadContext;
        private static int _mainThreadId = -1;

#if UNITY_EDITOR
        [UnityEditor.InitializeOnLoadMethod]
#endif
        [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
        private static void InitializeMainThread()
        {
            _mainThreadContext = System.Threading.SynchronizationContext.Current;
            _mainThreadId = Environment.CurrentManagedThreadId;
        }

        private static bool IsMainThread
        {
            get
            {
                // If statics were lost (hot-reload), re-capture if we are on the main thread
                if (_mainThreadId == -1 && System.Threading.SynchronizationContext.Current != null)
                {
                    InitializeMainThread();
                }
                return Environment.CurrentManagedThreadId == _mainThreadId;
            }
        }

        private static void PostToMainThread(Action action)
        {
            if (_mainThreadContext != null)
            {
                _mainThreadContext.Post(_ =>
                {
                    try
                    {
                        action();
                    }
                    catch (Exception e)
                    {
                        Debug.LogException(e);
                    }
                }, null);
            }
            else
            {
                Debug.LogError("[Hyperstone] Cannot dispatch to main thread: SynchronizationContext is null. Make sure Hyperstone is initialized from the main thread.");
            }
        }

        private static HyperstoneClient? Instance;

        public static HyperstoneClient.LogLevel LogLevel
        {
            get => GetInstance().CurrentLogLevel;
            set => GetInstance().CurrentLogLevel = value;
        }

        public static void Initialize(string appId, string appSecret, HyperstoneClient.LogLevel logLevel = HyperstoneClient.LogLevel.Info)
        {
            if (IsMainThread)
            {
                var instance = GetInstance();
                instance.CurrentLogLevel = logLevel;
                instance.Initialize(appId, appSecret);
            }
            else
            {
                PostToMainThread(() => Initialize(appId, appSecret, logLevel));
            }
        }

        public static void Initialize(string appId, string appSecret, string apiUrl, HyperstoneClient.LogLevel logLevel = HyperstoneClient.LogLevel.Info)
        {
            if (IsMainThread)
            {
                var instance = GetInstance();
                instance.CurrentLogLevel = logLevel;
                instance.Initialize(appId, appSecret, apiUrl);
            }
            else
            {
                PostToMainThread(() => Initialize(appId, appSecret, apiUrl, logLevel));
            }
        }

        public static void WatchConfig(Action<HyperstoneConfig> configChanged)
        {
            if (IsMainThread)
            {
                GetInstance().WatchConfig(configChanged);
            }
            else
            {
                PostToMainThread(() => WatchConfig(configChanged));
            }
        }

        public static void UnwatchConfig(Action<HyperstoneConfig> configChanged)
        {
            if (IsMainThread)
            {
                GetInstance().UnwatchConfig(configChanged);
            }
            else
            {
                PostToMainThread(() => UnwatchConfig(configChanged));
            }
        }

        public static void IncrementCustomMetric(string metric, float value)
        {
            if (IsMainThread)
            {
                GetInstance().IncrementCustomMetric(metric, value);
            }
            else
            {
                PostToMainThread(() => IncrementCustomMetric(metric, value));
            }
        }

        public static void IncrementCustomMetric(string metric, float value, params Label[] labels)
        {
            if (IsMainThread)
            {
                GetInstance().IncrementCustomMetric(metric, value, labels);
            }
            else
            {
                PostToMainThread(() => IncrementCustomMetric(metric, value, labels));
            }
        }

        public static void LogCustomEvent(string eventName, params Label[] labels)
        {
            if (IsMainThread)
            {
                GetInstance().LogCustomEvent(eventName, labels);
            }
            else
            {
                PostToMainThread(() => LogCustomEvent(eventName, labels));
            }
        }

        public static void LogAdRequest(string adPlatform, string adFormat)
        {
            if (IsMainThread)
            {
                GetInstance().LogAdRequest(
                    adPlatform: adPlatform,
                    adFormat: adFormat
                );
            }
            else
            {
                PostToMainThread(() => LogAdRequest(adPlatform, adFormat));
            }
        }

        public static void LogAdImpression(string adPlatform, string adNetwork, string adFormat, double revenueUsd)
        {
            if (IsMainThread)
            {
                GetInstance().LogAdImpression(
                    adPlatform: adPlatform,
                    adNetwork: adNetwork,
                    adFormat: adFormat,
                    revenueUsd: revenueUsd
                );
            }
            else
            {
                PostToMainThread(() => LogAdImpression(adPlatform, adNetwork, adFormat, revenueUsd));
            }
        }

        public static void LogAdError(string adPlatform, string adNetwork, string adFormat, string failReason = "unknown")
        {
            if (IsMainThread)
            {
                GetInstance().LogAdError(
                    adPlatform: adPlatform,
                    adNetwork: adNetwork,
                    adFormat: adFormat,
                    failReason: failReason
                );
            }
            else
            {
                PostToMainThread(() => LogAdError(adPlatform, adNetwork, adFormat, failReason));
            }
        }

        public static void LogIapTransactionSuccess(string productId, string currency, double price)
        {
            if (IsMainThread)
            {
                GetInstance().LogIapTransactionSuccess(
                    productId: productId,
                    currency: currency,
                    price: price
                );
            }
            else
            {
                PostToMainThread(() => LogIapTransactionSuccess(productId, currency, price));
            }
        }

        public static void LogIapTransactionError(string productId, string failReason)
        {
            if (IsMainThread)
            {
                GetInstance().LogIapTransactionError(
                    productId: productId,
                    failReason: failReason
                );
            }
            else
            {
                PostToMainThread(() => LogIapTransactionError(productId, failReason));
            }
        }

        private static HyperstoneClient GetInstance()
        {
            if (Instance != null)
            {
                return Instance;
            }

            var existing = UnityEngine.Object.FindAnyObjectByType<HyperstoneClient>();
            if (existing != null)
            {
                Instance = existing;
                return existing;
            }

            var go = new GameObject
            {
                name = "[Hyperstone Client]"
            };
            var client = go.AddComponent<HyperstoneClient>();
            UnityEngine.Object.DontDestroyOnLoad(go);

            Instance = client;

            return client;
        }

        public static void ClearData()
        {
            if (IsMainThread)
            {
                GetInstance().ClearData();
            }
            else
            {
                PostToMainThread(() => ClearData());
            }
        }
    }
}
