using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace MajacloudSdk.Internal
{
    /// <summary>
    /// Hidden, persistent coroutine host and main-thread dispatcher. Nothing
    /// in the SDK ever searches the scene; services hold a reference here.
    /// </summary>
    [DefaultExecutionOrder(-1000)]
    internal sealed class MajacloudRunner : MonoBehaviour
    {
        private static MajacloudRunner _instance;
        private static bool _quitting;
        private readonly Queue<Action> _queue = new Queue<Action>();
        private readonly object _lock = new object();

        public static bool IsQuitting => _quitting;

        public static MajacloudRunner Instance
        {
            get
            {
                if (_instance != null) return _instance;
                if (_quitting) return null;
                var go = new GameObject("[Majacloud]") { hideFlags = HideFlags.HideAndDontSave };
                _instance = go.AddComponent<MajacloudRunner>();
                if (Application.isPlaying) DontDestroyOnLoad(go);
                return _instance;
            }
        }

        public event Action<bool> Paused;
        public event Action<bool> Focused;
        public event Action Quitting;

        internal static void ResetStatics()
        {
            _instance = null;
            _quitting = false;
        }

        public Coroutine Run(IEnumerator routine) => StartCoroutine(routine);

        public void Post(Action action)
        {
            if (action == null) return;
            lock (_lock) _queue.Enqueue(action);
        }

        private void Update()
        {
            if (_queue.Count == 0) return;
            Action[] pending;
            lock (_lock)
            {
                pending = _queue.ToArray();
                _queue.Clear();
            }
            foreach (var action in pending)
            {
                try
                {
                    action();
                }
                catch (Exception e)
                {
                    Debug.LogException(e);
                }
            }
        }

        private void OnApplicationPause(bool paused) => Paused?.Invoke(paused);

        private void OnApplicationFocus(bool focused) => Focused?.Invoke(focused);

        private void OnApplicationQuit()
        {
            _quitting = true;
            Quitting?.Invoke();
        }

        private void OnDestroy()
        {
            if (_instance == this) _instance = null;
        }
    }
}
