using System;
using MajacloudSdk.Internal;
using UnityEngine;

namespace MajacloudSdk
{
    /// <summary>
    /// Entry point. Initialises itself from Assets/Resources/MajacloudSettings
    /// before the first scene loads; call <see cref="Initialize"/> only to
    /// supply settings yourself.
    ///
    /// <code>
    /// using MajacloudSdk;
    /// await Majacloud.Auth.SignInAnonymouslyAsync();
    /// var top = await Majacloud.Leaderboards.GetTopAsync("coins", 10);
    /// </code>
    /// </summary>
    public static class Majacloud
    {
        public const string Version = "2.1.0";

        private static MajacloudSettings _settings;
        private static IMajacloudStorage _storage;
        private static MajacloudHttp _http;
        private static string _deviceId;
        private static bool _warnedMissing;

        public static MajacloudLog Log { get; private set; } = new MajacloudLog();
        public static IMajacloudJsonSerializer JsonSerializer { get; set; } = new JsonUtilitySerializer();

        public static bool IsInitialized => _http != null;
        public static MajacloudSettings Settings => _settings;
        public static string DeviceId => _deviceId;
        public static MajacloudAuth Auth { get; private set; }
        public static MajacloudLeaderboards Leaderboards { get; private set; }
        public static MajacloudRemoteConfig RemoteConfig { get; private set; }
        public static MajacloudCloudSave CloudSave { get; private set; }
        public static MajacloudRewards Rewards { get; private set; }
        public static MajacloudProject Project { get; private set; }

        public static event Action Initialized;

        [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
        private static void ResetStatics()
        {
            _settings = null;
            _storage = null;
            _http = null;
            _deviceId = null;
            _warnedMissing = false;
            Auth = null;
            Leaderboards = null;
            RemoteConfig = null;
            CloudSave = null;
            Rewards = null;
            Project = null;
            Initialized = null;
            Log = new MajacloudLog();
            JsonSerializer = new JsonUtilitySerializer();
            MajacloudRunner.ResetStatics();
        }

        [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
        private static void AutoInitialize()
        {
            if (IsInitialized) return;
            var settings = Resources.Load<MajacloudSettings>(MajacloudSettings.ResourceName);
            if (settings == null)
            {
                if (!_warnedMissing) Log.Warning("No MajacloudSettings asset found in Resources. Open Tools ▸ Majacloud ▸ Setup.");
                _warnedMissing = true;
                return;
            }
            Initialize(settings);
        }

        /// <summary>Initialise with explicit settings (tests, multiple environments). Returns false when not configured.</summary>
        public static bool Initialize(MajacloudSettings settings = null)
        {
            if (IsInitialized)
            {
                if (settings != null && settings != _settings) Log.Warning("Majacloud is already initialised; call Shutdown() before re-initialising with other settings.");
                return true;
            }
            settings = settings != null ? settings : Resources.Load<MajacloudSettings>(MajacloudSettings.ResourceName);
            if (settings == null)
            {
                Log.Error("No MajacloudSettings asset. Open Tools ▸ Majacloud ▸ Setup.");
                return false;
            }
            if (!settings.TryValidate(out var error))
            {
                Log.Error(error);
                return false;
            }
            return InitializeInternal(settings, new PlayerPrefsStorage());
        }

        internal static bool InitializeInternal(MajacloudSettings settings, IMajacloudStorage storage)
        {
            _settings = settings;
            _storage = storage;
            Log.Level = settings.LogLevel;
            Log.LogRequests = settings.LogRequests;
            _deviceId = MajacloudDevice.GetOrCreate(storage);
            _http = new MajacloudHttp(settings, _deviceId, Log);
            Auth = new MajacloudAuth(_http, new SessionStore(storage, settings.ProjectId, _deviceId), Log);
            _http.Auth = Auth;
            Leaderboards = new MajacloudLeaderboards(_http);
            RemoteConfig = new MajacloudRemoteConfig(_http, settings, new ConfigCache(storage, settings.ProjectId, settings.ConfigCacheMaxBytes), Log);
            CloudSave = new MajacloudCloudSave(_http);
            Rewards = new MajacloudRewards(_http);
            Project = new MajacloudProject(_http);
            RemoteConfig.LoadCacheIntoMemory();
            if (Application.isPlaying)
            {
                var runner = MajacloudRunner.Instance;
                if (runner != null) runner.Quitting += () => storage.Flush();
                if (settings.FetchConfigOnInitialize) RemoteConfig.FetchAsync().Forget();
            }
            Log.Info("Initialised for project " + settings.ProjectId + " at " + settings.BaseUrl);
            Initialized?.Invoke();
            return true;
        }

        /// <summary>Tear down (keeps the stored session on disk). Mainly for tests and editor tools.</summary>
        public static void Shutdown()
        {
            _storage?.Flush();
            _settings = null;
            _storage = null;
            _http = null;
            Auth = null;
            Leaderboards = null;
            RemoteConfig = null;
            CloudSave = null;
            Rewards = null;
            Project = null;
        }
    }
}
