using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace SharedLibraryCore.Events;

public static class EventExtensions
{
    public static Task InvokeAsync<TEventType>(this Func<TEventType, CancellationToken, Task> function,
        TEventType eventArgType, CancellationToken token)
    {
        if (function is null)
        {
            return Task.CompletedTask;
        }

        return Task.WhenAll(function.GetInvocationList().Cast<Func<TEventType, CancellationToken, Task>>()
            .Select(x => RunHandler(x, eventArgType, token)));
    }

    private static async Task RunHandler<TEventType>(Func<TEventType, CancellationToken, Task> handler,
        TEventType eventArgType, CancellationToken token)
    {
        if (token == CancellationToken.None)
        {
            // special case to allow tasks like request after delay to run longer
            await handler(eventArgType, token);
        }

        using var timeoutToken = new CancellationTokenSource(Utilities.DefaultCommandTimeout);
        using var tokenSource =
            CancellationTokenSource.CreateLinkedTokenSource(token, timeoutToken.Token);

        try
        {
            await handler(eventArgType, tokenSource.Token);
        }
        catch (Exception)
        {
            // ignored
        }
    }
}