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

namespace CoolGameX
{
    /// <summary>
    /// Main entry point for the Cool GameX platform SDK.
    ///
    /// Zero-code: set your game slug in <b>Tools › Cool GameX › Setup</b> and the SDK
    /// starts itself. Then from anywhere:
    /// <code>
    /// CoolGameXManager.Unlock("speed_runner");
    /// await CoolAPI.Leaderboards.Submit("high_score", 9001);
    /// await CoolAPI.CloudSave.Save("slot1", mySave);
    /// </code>
    /// Manual control is still available via <see cref="InitializeAsync"/>.
    /// </summary>
    public static class CoolAPI
    {
        /// <summary>SDK version.</summary>
        public const string Version = "2.0.0";

        /// <summary>The game's slug, as registered on Cool GameX.</summary>
        public static string GameSlug { get; private set; }

        /// <summary>Base URL of the Cool GameX backend.</summary>
        public static string ServerUrl { get; private set; }

        /// <summary>The current session token (game-scoped when launched from Cool GameX).</summary>
        public static string Token { get; internal set; }

        /// <summary>The settings the SDK was started with, if any.</summary>
        public static CoolGameXSettings Settings { get; private set; }

        public static bool IsInitialized { get; private set; }

        /// <summary>True once a player session is present.</summary>
        public static bool IsAuthenticated => !string.IsNullOrEmpty(Token);

        /// <summary>True if the last request reached the server.</summary>
        public static bool IsConnected { get; private set; }

        /// <summary>True when this process was started by the Cool GameX launcher.</summary>
        public static bool LaunchedFromLauncher => !string.IsNullOrEmpty(ReadLaunchToken());

        /// <summary>Details of the most recent failed request.</summary>
        public static string LastError { get; internal set; }

        /// <summary>HTTP status code of the most recent request.</summary>
        public static long LastStatus { get; internal set; }

        /// <summary>Console verbosity. Defaults to warnings and errors.</summary>
        public static CGXLogLevel LogLevel { get; set; } = CGXLogLevel.Warnings;

        /// <summary>Raised when the connection to the server is gained or lost.</summary>
        public static event Action<bool> ConnectionChanged;

        /// <summary>Raised after a player session is verified.</summary>
        public static event Action<PlayerProfile> SignedIn;

        public static Auth Auth { get; private set; }
        public static Player Player { get; private set; }
        public static Achievements Achievements { get; private set; }
        public static Stats Stats { get; private set; }
        public static Leaderboards Leaderboards { get; private set; }
        public static CloudSave CloudSave { get; private set; }
        public static DLC DLC { get; private set; }
        public static Ownership Ownership { get; private set; }
        public static Playtime Playtime { get; private set; }
        /// <summary>Changes made while offline, waiting to sync.</summary>
        public static OfflineQueue Queue { get; private set; }

        private static bool connectionKnown;

        /// <summary>
        /// One call to get going: configure from <paramref name="settings"/> (or the project
        /// settings asset), check the connection, sign the player in and sync anything
        /// queued offline. Returns true when a player is signed in.
        /// </summary>
        public static async Task<bool> InitializeAsync(CoolGameXSettings settings = null)
        {
            if (!Configure(settings)) return false;
            return await ConnectAsync();
        }

        /// <summary>
        /// Configure the SDK without any network calls. Returns false if no game slug is set.
        /// </summary>
        public static bool Configure(CoolGameXSettings settings = null)
        {
            if (settings == null) settings = CoolGameXSettings.Load();
            if (settings == null || string.IsNullOrWhiteSpace(settings.gameSlug))
            {
                Log.Error("No game slug configured. Open Tools > Cool GameX > Setup and enter your game's slug.");
                return false;
            }
            Settings = settings;
            LogLevel = settings.logLevel;
            ConfigureCore(settings.gameSlug, settings.serverUrl);
            if (Queue != null) Queue.Enabled = settings.offlineQueue;
            return true;
        }

        /// <summary>
        /// Check the connection and sign in using, in order: the launcher's token, an
        /// explicit <paramref name="tokenOverride"/>, or your Editor sign-in.
        /// </summary>
        public static async Task<bool> ConnectAsync(string tokenOverride = null)
        {
            EnsureReady();
            await CheckConnection();

            string token = ReadLaunchToken();
            if (string.IsNullOrEmpty(token)) token = string.IsNullOrEmpty(tokenOverride) ? EditorSession.TokenFor(GameSlug) : tokenOverride;
            if (string.IsNullOrEmpty(token))
            {
                Log.Info(Application.isEditor
                    ? "Not signed in. Sign in from Tools > Cool GameX > Setup to test in the Editor."
                    : "Not signed in (start the game from the Cool GameX launcher).");
                return false;
            }

            bool ok = await Auth.LoginWithToken(token);
            if (ok)
            {
                _ = Achievements.GetAll(refresh: true);   // warm the cache (IsUnlocked, toasts)
                if (Queue.PendingCount > 0) _ = Queue.Flush();
            }
            return ok;
        }

        /// <summary>
        /// 1.x entry point: configure with a slug and server URL (reads the launcher
        /// token, doesn't make network calls). Prefer <see cref="InitializeAsync"/>.
        /// </summary>
        public static Task Initialize(string gameSlug, string serverUrl)
        {
            if (string.IsNullOrEmpty(gameSlug)) throw new ArgumentException("gameSlug is required", nameof(gameSlug));
            if (string.IsNullOrEmpty(serverUrl)) throw new ArgumentException("serverUrl is required", nameof(serverUrl));
            ConfigureCore(gameSlug, serverUrl);
            return Task.CompletedTask;
        }

        private static void ConfigureCore(string gameSlug, string serverUrl)
        {
            // When started by the launcher, the token belongs to the launcher's backend.
            string launcherApi = ReadLaunchArg("--cgx-api=");
            if (!string.IsNullOrEmpty(launcherApi)) serverUrl = launcherApi;
            if (string.IsNullOrWhiteSpace(serverUrl)) serverUrl = CoolGameXSettings.DefaultServerUrl;

            string slug = gameSlug.Trim();
            bool fresh = Auth == null || !string.Equals(GameSlug, slug, StringComparison.Ordinal);
            GameSlug = slug;
            ServerUrl = serverUrl.Trim().TrimEnd('/');

            if (fresh)
            {
                Auth = new Auth();
                Player = new Player();
                Achievements = new Achievements();
                Stats = new Stats();
                Leaderboards = new Leaderboards();
                CloudSave = new CloudSave();
                DLC = new DLC();
                Ownership = new Ownership();
                Playtime = new Playtime();
                Queue = new OfflineQueue();
            }

            string launcherToken = ReadLaunchToken();
            if (!string.IsNullOrEmpty(launcherToken))
            {
                Token = launcherToken;
                Auth.Inspect(launcherToken);
            }

            IsInitialized = true;
            Log.Info($"v{Version} ready for '{GameSlug}' → {ServerUrl}{(LaunchedFromLauncher ? " (launched from Cool GameX)" : "")}");
        }

        /// <summary>Reads the <c>--cgx-token=</c> launch argument, or null outside the launcher.</summary>
        public static string ReadLaunchToken() => ReadLaunchArg("--cgx-token=");

        /// <summary>Reads a <c>--name=value</c> command-line argument.</summary>
        public static string ReadLaunchArg(string prefix)
        {
            foreach (var arg in Environment.GetCommandLineArgs())
                if (arg.StartsWith(prefix, StringComparison.Ordinal))
                    return arg.Substring(prefix.Length);
            return null;
        }

        /// <summary>Pings the server and updates <see cref="IsConnected"/>.</summary>
        public static async Task<bool> CheckConnection()
        {
            var res = await Net.Get("/api/health", auth: false, retry: false);
            return res.Success;
        }

        /// <summary>One-line status string for debugging.</summary>
        public static string StatusLine() =>
            $"initialized={IsInitialized} connected={IsConnected} authenticated={IsAuthenticated} queued={Queue?.PendingCount ?? 0}";

        internal static void SetConnected(bool connected)
        {
            if (connectionKnown && IsConnected == connected) return;
            bool changed = connectionKnown;
            connectionKnown = true;
            IsConnected = connected;
            if (changed) Log.Info(connected ? "Back online." : "Connection lost — changes will be queued.");
            try { ConnectionChanged?.Invoke(connected); } catch (Exception e) { Debug.LogException(e); }
        }

        internal static void RaiseSignedIn(PlayerProfile profile)
        {
            try { SignedIn?.Invoke(profile); } catch (Exception e) { Debug.LogException(e); }
        }

        internal static void EnsureReady()
        {
            if (!IsInitialized)
                throw new InvalidOperationException(
                    "CoolAPI isn't initialized yet. Set your game slug in Tools > Cool GameX > Setup (auto-start), " +
                    "add a Cool GameX Manager to your first scene, or call CoolAPI.InitializeAsync().");
        }

        // Support "Enter Play Mode" without a domain reload.
        [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
        private static void ResetStatics()
        {
            GameSlug = ServerUrl = Token = LastError = null;
            LastStatus = 0;
            IsInitialized = IsConnected = connectionKnown = false;
            Settings = null;
            Auth = null; Player = null; Achievements = null; Stats = null; Leaderboards = null;
            CloudSave = null; DLC = null; Ownership = null; Playtime = null; Queue = null;
            ConnectionChanged = null;
            SignedIn = null;
            LogLevel = CGXLogLevel.Warnings;
            Log.Clear();
        }
    }
}
