using System;
using System.Threading.Tasks;
using UnityEngine;

namespace CoolGameX
{
    /// <summary>
    /// Runs the SDK: signs the player in, keeps the session fresh, reconnects,
    /// syncs offline changes and tracks playtime.
    ///
    /// You usually don't add this yourself — with <b>Auto-initialize</b> on
    /// (Tools › Cool GameX › Setup) it's created automatically. Adding it to a
    /// scene still works, and its fields override the project settings.
    ///
    /// Unlock from anywhere: <code>CoolGameXManager.Unlock("speed_runner");</code>
    /// </summary>
    [AddComponentMenu("Cool GameX/Cool GameX Manager")]
    [DisallowMultipleComponent]
    [DefaultExecutionOrder(-1000)]
    public class CoolGameXManager : MonoBehaviour
    {
        [Header("Overrides (leave empty to use Tools › Cool GameX › Setup)")]
        [Tooltip("Game slug. Empty = use the project settings.")]
        public string gameSlug = "";

        [Tooltip("Backend URL. Empty = use the project settings.")]
        public string serverUrl = "";

        [Header("Playtime")]
        [Tooltip("Untick to never track playtime from the SDK.")]
        public bool trackPlaytime = true;

        [Tooltip("Report playtime every N minutes. 0 = use the project settings.")]
        public int heartbeatMinutes = 0;

        [Header("Editor testing (optional)")]
        [Tooltip("Paste a token here only if you can't use the Editor sign-in in Tools › Cool GameX › Setup. Ignored in builds.")]
        [TextArea] public string editorTestToken = "";

        /// <summary>Raised when an achievement unlocks for the first time (slug).</summary>
        public static event Action<string> AchievementUnlocked;

        public static CoolGameXManager Instance { get; private set; }

        /// <summary>True once a player is signed in.</summary>
        public static bool Ready { get; private set; }

        private CoolGameXSettings settings;
        private bool tracking;
        private float heartbeatTimer;
        private float maintenanceTimer;
        private bool maintaining;

        private async void Awake()
        {
            if (Instance != null && Instance != this) { Destroy(gameObject); return; }
            Instance = this;
            DontDestroyOnLoad(gameObject);

            settings = BuildSettings();
            if (!CoolAPI.Configure(settings)) return;

            CoolAPI.Achievements.OnAchievementUnlocked += HandleUnlocked;
            AddDeveloperTools();

            string manualToken = Application.isEditor ? editorTestToken?.Trim() : null;
            Ready = await CoolAPI.ConnectAsync(manualToken);

            tracking = ShouldTrackPlaytime();
            if (tracking) CoolAPI.Playtime.StartSession();
        }

        private CoolGameXSettings BuildSettings()
        {
            var asset = CoolGameXSettings.Load();
            var s = asset != null ? Instantiate(asset) : ScriptableObject.CreateInstance<CoolGameXSettings>();
            // "your-game-slug" was the 1.x placeholder.
            if (!string.IsNullOrWhiteSpace(gameSlug) && gameSlug != "your-game-slug") s.gameSlug = gameSlug.Trim();
            if (!string.IsNullOrWhiteSpace(serverUrl)) s.serverUrl = serverUrl.Trim();
            if (!trackPlaytime) s.playtime = PlaytimeTracking.Never;
            if (heartbeatMinutes > 0) s.heartbeatMinutes = heartbeatMinutes;
            return s;
        }

        private bool ShouldTrackPlaytime()
        {
            if (!CoolAPI.IsAuthenticated) return false;
            switch (settings.playtime)
            {
                case PlaytimeTracking.Always: return true;
                case PlaytimeTracking.Never: return false;
                default: return !CoolAPI.LaunchedFromLauncher; // the launcher times games it starts
            }
        }

        private void AddDeveloperTools()
        {
            bool overlay;
            switch (settings.debugOverlay)
            {
                case DebugOverlayMode.Always: overlay = true; break;
                case DebugOverlayMode.EditorOnly: overlay = Application.isEditor; break;
                case DebugOverlayMode.EditorAndDevelopmentBuilds: overlay = Debug.isDebugBuild; break;
                default: overlay = false; break;
            }
            if (overlay && GetComponent<CoolGameXDebugOverlay>() == null)
                gameObject.AddComponent<CoolGameXDebugOverlay>().toggleKey = settings.debugOverlayKey;

            bool toasts = settings.inGameToasts == ToastMode.Always ||
                          (settings.inGameToasts == ToastMode.WhenNotLaunchedFromLauncher && !CoolAPI.LaunchedFromLauncher);
            if (toasts && GetComponent<CoolGameXToasts>() == null)
                gameObject.AddComponent<CoolGameXToasts>();
        }

        private void Update()
        {
            if (!CoolAPI.IsInitialized || settings == null) return;
            float dt = Time.unscaledDeltaTime;

            if (tracking && CoolAPI.Playtime.IsActive)
            {
                heartbeatTimer += dt;
                if (heartbeatTimer >= Mathf.Max(1, settings.heartbeatMinutes) * 60f)
                {
                    heartbeatTimer = 0f;
                    int minutes = CoolAPI.Playtime.TakeMinutes(round: false);
                    if (minutes > 0) _ = CoolAPI.Playtime.ReportMinutes(minutes);
                }
            }

            maintenanceTimer += dt;
            if (maintenanceTimer >= 30f && !maintaining)
            {
                maintenanceTimer = 0f;
                _ = Maintain();
            }
        }

        /// <summary>Every 30 s: reconnect if offline, refresh an expiring session, sync the queue.</summary>
        private async Task Maintain()
        {
            maintaining = true;
            try
            {
                if (!CoolAPI.IsConnected) await CoolAPI.CheckConnection();
                if (!CoolAPI.IsConnected) return;
                if (CoolAPI.IsAuthenticated) await CoolAPI.Auth.RefreshIfNeeded(TimeSpan.FromMinutes(45));
                if (CoolAPI.Queue.PendingCount > 0) await CoolAPI.Queue.Flush();
            }
            catch (Exception e) { Debug.LogException(e); }
            finally { maintaining = false; }
        }

        private void OnApplicationQuit()
        {
            // No time for a request while quitting — the remainder is sent next launch.
            if (tracking) CoolAPI.Playtime?.QueueRemainder();
        }

        private void OnDestroy()
        {
            if (Instance != this) return;
            if (CoolAPI.Achievements != null) CoolAPI.Achievements.OnAchievementUnlocked -= HandleUnlocked;
            Instance = null;
            Ready = false;
        }

        private static void HandleUnlocked(string slug) => AchievementUnlocked?.Invoke(slug);

        // ---- Static helpers ------------------------------------------------

        /// <summary>Unlock an achievement by slug (fire-and-forget, optional callback).</summary>
        public static void Unlock(string achievementSlug, Action<UnlockResult> onDone = null)
        {
            if (!CoolAPI.IsInitialized)
            {
                Debug.LogWarning("[CoolAPI] Unlock ignored — the SDK isn't running. Set your game slug in Tools > Cool GameX > Setup.");
                return;
            }
            Run(CoolAPI.Achievements.UnlockDetailed(achievementSlug), onDone);
        }

        /// <summary>Unlock an achievement and await the detailed result.</summary>
        public static Task<UnlockResult> UnlockAsync(string achievementSlug)
            => CoolAPI.Achievements.UnlockDetailed(achievementSlug);

        /// <summary>Set a numeric stat. Fire-and-forget.</summary>
        public static void SetStat(string key, int value)
        {
            if (!Check(nameof(SetStat))) return;
            _ = CoolAPI.Stats.Set(key, value);
        }

        /// <summary>Increment a numeric stat. Fire-and-forget.</summary>
        public static void AddStat(string key, int amount = 1)
        {
            if (!Check(nameof(AddStat))) return;
            _ = CoolAPI.Stats.Add(key, amount);
        }

        /// <summary>Submit a leaderboard score (fire-and-forget, optional callback).</summary>
        public static void SubmitScore(string board, long score, Action<SubmitResult> onDone = null)
        {
            if (!Check(nameof(SubmitScore))) return;
            Run(CoolAPI.Leaderboards.Submit(board, score), onDone);
        }

        private static bool Check(string what)
        {
            if (CoolAPI.IsInitialized) return true;
            Debug.LogWarning($"[CoolAPI] {what} ignored — the SDK isn't running. Set your game slug in Tools > Cool GameX > Setup.");
            return false;
        }

        private static async void Run<T>(Task<T> task, Action<T> onDone)
        {
            try
            {
                T result = await task;
                onDone?.Invoke(result);
            }
            catch (Exception e) { Debug.LogException(e); }
        }

        // Support "Enter Play Mode" without a domain reload.
        [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
        private static void ResetStatics()
        {
            Instance = null;
            Ready = false;
            AchievementUnlocked = null;
        }
    }

    /// <summary>Starts the SDK automatically when a settings asset with a game slug exists.</summary>
    internal static class CoolGameXBootstrap
    {
        [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
        private static void AutoStart()
        {
            if (CoolGameXManager.Instance != null) return;
            var s = CoolGameXSettings.Load();
            if (s == null || !s.autoInitialize || string.IsNullOrWhiteSpace(s.gameSlug)) return;
#if UNITY_2023_1_OR_NEWER
            if (UnityEngine.Object.FindFirstObjectByType<CoolGameXManager>() != null) return;
#else
            if (UnityEngine.Object.FindObjectOfType<CoolGameXManager>() != null) return;
#endif
            new GameObject("Cool GameX").AddComponent<CoolGameXManager>();
        }
    }
}
