using System.Runtime.CompilerServices;
using AyCode.Core.Loggers;

namespace AyCode.Core.Helpers
{
    public static class TaskHelper
    {
        /// <summary>
        /// Optional ambient logger for fire-and-forget faults. Set once at startup (after the DI
        /// container is built) in each host's entry point, e.g.
        /// <c>TaskHelper.Logger = new LoggerClient&lt;TaskHelper&gt;(writers);</c>.
        /// When null, faults are swallowed (legacy behaviour). A per-call <c>logger</c> argument
        /// passed to any <c>Forget</c> overload overrides this ambient instance.
        /// </summary>
        public static IAcLoggerBase? Logger { get; set; }

        /// <summary>
        /// Logs a fire-and-forget fault. Normal cancellation is filtered out by the callers,
        /// so anything reaching here is a real fault worth recording. Resolves the logger as
        /// "explicit per-call argument, else ambient <see cref="Logger"/>"; both null → silent.
        /// </summary>
        private static void LogForgetFault(Exception ex, IAcLoggerBase? logger, string caller, int line)
            => (logger ?? Logger)?.Error($"Fire-and-forget faulted @ {caller}:{line}", ex);

        public static bool WaitTo(Func<bool> predicate, int msTimeout = 10000, int msDelay = 5, int msFirstDelay = 0)
            => WaitToAsync(predicate, msTimeout, msDelay, msFirstDelay).GetAwaiter().GetResult();

        public static bool WaitTo(Func<bool> predicate, int msTimeout, int msDelay, int msFirstDelay, CancellationToken cancellationToken)
            => WaitToAsync(predicate, msTimeout, msDelay, msFirstDelay, cancellationToken).GetAwaiter().GetResult();

        public static Task<bool> WaitToAsync(Func<bool> predicate, int msTimeout = 10000, int msDelay = 5, int msFirstDelay = 0)
            => WaitToAsync(predicate, msTimeout, msDelay, msFirstDelay, CancellationToken.None);

        public static async Task<bool> WaitToAsync(Func<bool> predicate, int msTimeout, int msDelay, int msFirstDelay, CancellationToken cancellationToken)
        {
            // Use Environment.TickCount64 instead of DateTime.UtcNow.Ticks for better performance
            var endTick = Environment.TickCount64 + msTimeout;

            if (msFirstDelay > 0)
                await Task.Delay(msFirstDelay, cancellationToken).ConfigureAwait(false);

            // Check immediately first
            if (predicate())
                return true;

            // Use PeriodicTimer for efficient polling (.NET 6+)
            using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(msDelay));
            
            while (Environment.TickCount64 < endTick)
            {
                cancellationToken.ThrowIfCancellationRequested();
                
                if (predicate())
                    return true;

                try
                {
                    if (!await timer.WaitForNextTickAsync(cancellationToken).ConfigureAwait(false))
                        break;
                }
                catch (OperationCanceledException)
                {
                    break;
                }
            }

            // Final check
            return predicate();
        }

        public static void Forget(this Task task, IAcLoggerBase? logger = null, [CallerMemberName] string caller = "", [CallerLineNumber] int line = 0)
        {
            if (!task.IsCompleted || task.IsFaulted) _ = ForgetAwaited(task, logger, caller, line);
            return;

            static async Task ForgetAwaited(Task task, IAcLoggerBase? logger, string caller, int line)
            {
                try
                {
                    await task.ConfigureAwait(false);
                }
                catch (OperationCanceledException)
                {
                    // Normal cancellation — not a fault.
                }
                catch (Exception ex)
                {
                    LogForgetFault(ex, logger, caller, line);
                }
            }
        }

        public static void Forget(this ValueTask task, IAcLoggerBase? logger = null, [CallerMemberName] string caller = "", [CallerLineNumber] int line = 0)
        {
            if (!task.IsCompleted || task.IsFaulted) _ = ForgetAwaited(task, logger, caller, line);
            return;

            static async Task ForgetAwaited(ValueTask task, IAcLoggerBase? logger, string caller, int line)
            {
                try
                {
                    await task.ConfigureAwait(false);
                }
                catch (OperationCanceledException)
                {
                    // Normal cancellation — not a fault.
                }
                catch (Exception ex)
                {
                    LogForgetFault(ex, logger, caller, line);
                }
            }
        }

        public static void Forget<T>(this ValueTask<T> task, IAcLoggerBase? logger = null, [CallerMemberName] string caller = "", [CallerLineNumber] int line = 0)
        {
            if (!task.IsCompleted || task.IsFaulted) _ = ForgetAwaited(task, logger, caller, line);
            return;

            static async Task ForgetAwaited(ValueTask<T> task, IAcLoggerBase? logger, string caller, int line)
            {
                try
                {
                    await task.ConfigureAwait(false);
                }
                catch (OperationCanceledException)
                {
                    // Normal cancellation — not a fault.
                }
                catch (Exception ex)
                {
                    LogForgetFault(ex, logger, caller, line);
                }
            }
        }

        public static void RunOnThreadPool(this Action action) => ToThreadPoolTask(action).Forget();
        public static void RunOnThreadPool<T>(this Func<T> func) => ToThreadPoolTask(func).Forget();

        public static Task ToThreadPoolTask(this Action action) => Task.Run(action);
        public static Task<T> ToThreadPoolTask<T>(this Func<Task<T>> func) => Task.Run(func);
        public static Task<T> ToThreadPoolTask<T>(this Func<T> func) => Task.Run(func);
    }
}
