#nullable enable
using System;
using System.Threading;
using System.Threading.Tasks;

namespace AdvChkSys.Threading
{
    /// <summary>
    /// Provides additional methods for the ChunkTaskScheduler.
    /// </summary>
    public static class ChunkTaskSchedulerExtensions
    {
        /// <summary>
        /// Maximum degree of parallelism for chunk operations.
        /// </summary>
        public static int MaxDegreeOfParallelism { get; set; } = Environment.ProcessorCount;

        /// <summary>
        /// Runs a batch of actions in parallel.
        /// </summary>
        /// <param name="actions">The actions to run</param>
        /// <param name="maxDegreeOfParallelism">Maximum degree of parallelism (null for default)</param>
        /// <param name="cancellationToken">Cancellation token</param>
        /// <returns>A task that completes when all actions are done</returns>
        public static Task RunBatchParallelAsync(
            Action[] actions,
            int? maxDegreeOfParallelism = null,
            CancellationToken cancellationToken = default)
        {
            if (actions == null || actions.Length == 0)
                return Task.CompletedTask;

            return Task.Run(() =>
            {
                var options = new ParallelOptions
                {
                    MaxDegreeOfParallelism = maxDegreeOfParallelism ?? MaxDegreeOfParallelism,
                    CancellationToken = cancellationToken
                };

                Parallel.ForEach(actions, options, action => action());
            }, cancellationToken);
        }

        /// <summary>
        /// Creates a cancellation token with a timeout.
        /// </summary>
        /// <param name="timeout">The timeout</param>
        /// <param name="cancellationToken">Optional token to combine with the timeout</param>
        /// <returns>A cancellation token that will be canceled after the timeout or when the input token is canceled</returns>
        public static CancellationToken CreateTimeoutToken(
            TimeSpan timeout,
            CancellationToken cancellationToken = default)
        {
            if (timeout == TimeSpan.MaxValue)
                return cancellationToken;

            var source = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
            source.CancelAfter(timeout);
            return source.Token;
        }
    }
}