using System;
using System.Collections;
using System.Threading;
using System.Threading.Tasks;
using MajacloudSdk.Internal;
using UnityEngine;

namespace MajacloudSdk
{
    /// <summary>
    /// Task helpers that never touch threads (safe on WebGL): delays and
    /// frame waits are coroutines completing a TaskCompletionSource.
    /// </summary>
    public static class MajacloudTask
    {
        public static Task Delay(float seconds, CancellationToken cancellationToken = default)
        {
            var tcs = new TaskCompletionSource<bool>();
            var runner = MajacloudRunner.Instance;
            if (runner == null || seconds <= 0f)
            {
                tcs.TrySetResult(true);
                return tcs.Task;
            }
            var coroutine = runner.Run(DelayRoutine(seconds, tcs));
            if (cancellationToken.CanBeCanceled)
            {
                cancellationToken.Register(() =>
                {
                    if (runner != null && coroutine != null) runner.StopCoroutine(coroutine);
                    tcs.TrySetCanceled();
                });
            }
            return tcs.Task;
        }

        public static Task NextFrame()
        {
            var tcs = new TaskCompletionSource<bool>();
            var runner = MajacloudRunner.Instance;
            if (runner == null)
            {
                tcs.TrySetResult(true);
                return tcs.Task;
            }
            runner.Run(NextFrameRoutine(tcs));
            return tcs.Task;
        }

        /// <summary>Fire-and-forget: exceptions are logged instead of lost.</summary>
        public static void Forget(this Task task)
        {
            if (task == null) return;
            task.ContinueWith(t =>
            {
                if (t.IsFaulted && t.Exception != null) Majacloud.Log.Error("Unobserved task failure: " + t.Exception.GetBaseException().Message);
            }, TaskScheduler.FromCurrentSynchronizationContext());
        }

        private static IEnumerator DelayRoutine(float seconds, TaskCompletionSource<bool> tcs)
        {
            yield return new WaitForSecondsRealtime(seconds);
            tcs.TrySetResult(true);
        }

        private static IEnumerator NextFrameRoutine(TaskCompletionSource<bool> tcs)
        {
            yield return null;
            tcs.TrySetResult(true);
        }
    }
}
