using CompatBot.EventHandlers;
using DSharpPlus.Interactivity;
using Microsoft.Extensions.Caching.Memory;

namespace CompatBot.Utils;

public static class InteractivityExtensions
{
    public static Task<(DiscordMessage? message, DiscordMessage? text, MessageReactionAddedEventArgs? reaction)> WaitForMessageOrReactionAsync(
        this InteractivityExtension interactivity,
        DiscordMessage message,
        DiscordUser user,
        params DiscordEmoji?[] reactions)
        => WaitForMessageOrReactionAsync(interactivity, message, user, null, reactions);

    public static async Task<(DiscordMessage? message, DiscordMessage? text, MessageReactionAddedEventArgs? reaction)> WaitForMessageOrReactionAsync(
        this InteractivityExtension interactivity,
        DiscordMessage message,
        DiscordUser user,
        TimeSpan? timeout,
        params DiscordEmoji?[] reactions)
    {
        if (reactions.Length == 0)
            throw new ArgumentException("At least one reaction must be specified", nameof(reactions));

        DiscordMessage? result = message;
        try
        {
            reactions = reactions.Where(r => r != null).ToArray();
            foreach (var emoji in reactions)
                await message.ReactWithAsync(emoji!).ConfigureAwait(false);
            var expectedChannel = message.Channel;
            var waitTextResponseTask = interactivity.WaitForMessageAsync(m => m.Author == user && m.Channel == expectedChannel && !string.IsNullOrEmpty(m.Content), timeout);
            var waitReactionResponse = interactivity.WaitForReactionAsync(arg => reactions.Contains(arg.Emoji), message, user, timeout);
            await Task.WhenAny(
                waitTextResponseTask,
                waitReactionResponse
            ).ConfigureAwait(false);
            try
            {
                await message.DeleteAllReactionsAsync().ConfigureAwait(false);
            }
            catch
            {
                await message.DeleteAsync().ConfigureAwait(false);
                result = null;
            }
            DiscordMessage? text = null;
            MessageReactionAddedEventArgs? reaction = null;
            if (waitTextResponseTask.IsCompletedSuccessfully)
                text = (await waitTextResponseTask).Result;
            if (waitReactionResponse.IsCompletedSuccessfully)
                reaction = (await waitReactionResponse).Result;
            if (text != null)
                try
                {
                    DeletedMessagesMonitor.RemovedByBotCache.Set(text.Id, true, DeletedMessagesMonitor.CacheRetainTime);
                    await text.DeleteAsync().ConfigureAwait(false);
                }
                catch {}
            return (result, text, reaction);
        }
        catch (Exception e)
        {
            Config.Log.Warn(e, "Failed to get interactive reaction");
            return (result, null, null);
        }
    }

    public static async Task<(DiscordMessage? text, ComponentInteractionCreatedEventArgs? reaction)> WaitForMessageOrButtonAsync(
        this InteractivityExtension interactivity,
        DiscordMessage message,
        DiscordUser user,
        TimeSpan? timeout
    )
    {
        try
        {
            if (message.Channel is null)
                throw new InvalidOperationException("Provided message.Channel was null");
                
            var expectedChannel = message.Channel;
            var waitButtonTask = interactivity.WaitForButtonAsync(message, user, timeout);
            var waitTextResponseTask = interactivity.WaitForMessageAsync(m => m.Author == user && m.Channel == expectedChannel && !string.IsNullOrEmpty(m.Content), timeout);
            await Task.WhenAny(
                waitTextResponseTask,
                waitButtonTask
            ).ConfigureAwait(false);
            DiscordMessage? text = null;
            ComponentInteractionCreatedEventArgs? reaction = null;
            if (waitTextResponseTask.IsCompletedSuccessfully)
                text = (await waitTextResponseTask).Result;
            if (waitButtonTask.IsCompletedSuccessfully)
            {
                reaction = (await waitButtonTask).Result;
                if (reaction is not null)
                    await reaction.Interaction.CreateResponseAsync(DiscordInteractionResponseType.DeferredMessageUpdate).ConfigureAwait(false);
            }
            if (text is not null && !message.Channel.IsPrivate)
                try
                {
                    DeletedMessagesMonitor.RemovedByBotCache.Set(text.Id, true, DeletedMessagesMonitor.CacheRetainTime);
                    await text.DeleteAsync().ConfigureAwait(false);
                }
                catch {}
            return (text, reaction);                
        }
        catch (Exception e)
        {
            Config.Log.Warn(e, "Failed to get interactive reaction");
            return (null, null);
        }
    }
}