using System;
using System.Collections;
using System.Threading.Tasks;

namespace MajacloudSdk
{
    /// <summary>
    /// Bridges async methods to coroutine callers. Wrappers are eager: the
    /// request starts immediately and the callback fires exactly once on the
    /// main thread; yielding the enumerator just waits for completion.
    /// </summary>
    public static class MajacloudCoroutine
    {
        public static IEnumerator Run<T>(Task<MajacloudResult<T>> task, Action<MajacloudResult<T>> callback)
        {
            Attach(task, callback);
            while (!task.IsCompleted) yield return null;
        }

        public static IEnumerator Run(Task<MajacloudResult> task, Action<MajacloudResult> callback)
        {
            Attach(task, callback);
            while (!task.IsCompleted) yield return null;
        }

        /// <summary>Wait for any task inside a coroutine (`yield return MajacloudCoroutine.Wait(task)`).</summary>
        public static IEnumerator Wait(Task task)
        {
            while (task != null && !task.IsCompleted) yield return null;
        }

        private static async void Attach<T>(Task<MajacloudResult<T>> task, Action<MajacloudResult<T>> callback)
        {
            MajacloudResult<T> result;
            try
            {
                result = await task;
            }
            catch (Exception e)
            {
                result = MajacloudResult<T>.Fail(MajacloudErrorCode.Unknown, e.Message);
            }
            Invoke(callback, result);
        }

        private static async void Attach(Task<MajacloudResult> task, Action<MajacloudResult> callback)
        {
            MajacloudResult result;
            try
            {
                result = await task;
            }
            catch (Exception e)
            {
                result = MajacloudResult.Fail(MajacloudErrorCode.Unknown, e.Message);
            }
            Invoke(callback, result);
        }

        private static void Invoke<TResult>(Action<TResult> callback, TResult result)
        {
            if (callback == null) return;
            try
            {
                callback(result);
            }
            catch (Exception e)
            {
                UnityEngine.Debug.LogException(e);
            }
        }
    }
}
