// Project Makoto
// Copyright (C) 2024  Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY

namespace ProjectMakoto.Commands.ReactionRolesCommand;

internal sealed class ConfigCommand : BaseCommand
{
    public override Task<bool> BeforeExecution(SharedCommandContext ctx) => this.CheckAdmin();

    public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
    {
        return Task.Run(async () =>
        {
            var CommandKey = this.t.Commands.Config.ReactionRoles;

            if (await ctx.DbUser.Cooldown.WaitForLight(ctx))
                return;

            _ = await this.RespondOrEdit(new DiscordEmbedBuilder
            {
                Description = this.GetString(CommandKey.LoadingReactionRoles, true)
            }.AsLoading(ctx, this.GetString(CommandKey.Title)));

            _ = await ReactionRolesCommandAbstractions.CheckForInvalid(ctx);

            var AddButton = new DiscordButtonComponent(ButtonStyle.Primary, Guid.NewGuid().ToString(), this.GetString(CommandKey.AddNewReactionRole), (ctx.DbGuild.ReactionRoles.Length > 100), new DiscordComponentEmoji(DiscordEmoji.FromUnicode("➕")));
            var RemoveButton = new DiscordButtonComponent(ButtonStyle.Danger, Guid.NewGuid().ToString(), this.GetString(CommandKey.RemoveReactionRole), (ctx.DbGuild.ReactionRoles.Length == 0), new DiscordComponentEmoji(DiscordEmoji.FromUnicode("✖")));

            var embed = new DiscordEmbedBuilder
            {
                Description = this.GetString(CommandKey.ReactionRoleCount, true, new TVar("Count", ctx.DbGuild.ReactionRoles.Length))
            }.AsAwaitingInput(ctx, this.GetString(CommandKey.Title));

            _ = await this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(embed)
            .AddComponents(new List<DiscordComponent>
            {
                AddButton, RemoveButton
            })
            .AddComponents(MessageComponents.GetCancelButton(ctx.DbUser, ctx.Bot)));

            var e = await ctx.WaitForButtonAsync(TimeSpan.FromMinutes(2));

            if (e.TimedOut)
            {
                this.ModifyToTimedOut(true);
                return;
            }

            _ = e.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);

            if (e.GetCustomId() == AddButton.CustomId)
            {
                DiscordMessage selectedMessage = null;
                DiscordEmoji selectedEmoji = null;
                DiscordRole selectedRole = null;

                while (true)
                {
                    var SelectMessage = new DiscordButtonComponent((selectedMessage is null ? ButtonStyle.Primary : ButtonStyle.Secondary), Guid.NewGuid().ToString(), this.GetString(CommandKey.SelectMessage), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("💬")));
                    var SelectEmoji = new DiscordButtonComponent((selectedEmoji is null ? ButtonStyle.Primary : ButtonStyle.Secondary), Guid.NewGuid().ToString(), this.GetString(CommandKey.SelectEmoji), (selectedMessage is null), new DiscordComponentEmoji(DiscordEmoji.FromUnicode("😀")));
                    var SelectRole = new DiscordButtonComponent((selectedRole is null ? ButtonStyle.Primary : ButtonStyle.Secondary), Guid.NewGuid().ToString(), this.GetString(CommandKey.SelectRole), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("👤")));
                    var Finish = new DiscordButtonComponent(ButtonStyle.Success, Guid.NewGuid().ToString(), this.GetString(this.t.Common.Submit), (selectedMessage is null || selectedRole is null || selectedEmoji is null), new DiscordComponentEmoji(DiscordEmoji.FromUnicode("✅")));

                    var pad = TranslationUtil.CalculatePadding(ctx.DbUser, CommandKey.Message, CommandKey.Emoji, CommandKey.Role);

                    var action_embed = new DiscordEmbedBuilder
                    {
                        Description = $"`{this.GetString(CommandKey.Message).PadRight(pad)}`: {(selectedMessage is null ? this.GetString(this.t.Common.NotSelected, true) : $"[`{this.GetString(this.t.Common.JumpToMessage)}`]({selectedMessage.JumpLink})")}\n" +
                                      $"`{this.GetString(CommandKey.Emoji).PadRight(pad)}`: {(selectedEmoji is null ? this.GetString(this.t.Common.NotSelected, true) : selectedEmoji.ToString())}\n" +
                                      $"`{this.GetString(CommandKey.Role).PadRight(pad)}`: {(selectedRole is null ? this.GetString(this.t.Common.NotSelected, true) : selectedRole.Mention)}"
                    }.AsAwaitingInput(ctx, this.GetString(CommandKey.Title));


                    if (ctx.DbGuild.ReactionRoles.Length > 100)
                    {
                        action_embed.Description = this.GetString(CommandKey.ReactionRoleLimitReached, true);
                        _ = await this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(action_embed.AsError(ctx, this.GetString(CommandKey.Title))));
                        await Task.Delay(5000);
                        await this.ExecuteCommand(ctx, arguments);
                        return;
                    }

                    _ = await this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(action_embed)
                        .AddComponents(new List<DiscordComponent> { SelectMessage, SelectEmoji, SelectRole, Finish })
                        .AddComponents(MessageComponents.GetCancelButton(ctx.DbUser, ctx.Bot)));

                    var Menu = await ctx.WaitForButtonAsync();

                    if (Menu.TimedOut)
                    {
                        this.ModifyToTimedOut();
                        return;
                    }

                    if (Menu.GetCustomId() == SelectMessage.CustomId)
                    {
                        var modal = new DiscordInteractionModalBuilder(this.GetString(CommandKey.Title), Guid.NewGuid().ToString())
                        .AddTextComponent(new DiscordTextComponent(TextComponentStyle.Small, "url", this.GetString(CommandKey.MessageUrl), "https://discord.com/channels/012345678901234567/012345678901234567/012345678912345678", null, null, true));

                        var ModalResult = await this.PromptModalWithRetry(Menu.Result.Interaction, modal, new DiscordEmbedBuilder
                        {
                            Description = this.GetString(CommandKey.MessageUrlInstructions, true),
                            ImageUrl = "https://cdn.discordapp.com/attachments/906976602557145110/967753175241203712/unknown.png"
                        }.AsAwaitingInput(ctx, this.GetString(CommandKey.Title)), false);

                        if (ModalResult.TimedOut)
                        {
                            this.ModifyToTimedOut(true);
                            return;
                        }
                        else if (ModalResult.Cancelled)
                        {
                            continue;
                        }
                        else if (ModalResult.Errored)
                        {
                            throw ModalResult.Exception;
                        }

                        var url = ModalResult.Result.Interaction.GetModalValueByCustomId("url");

                        if (!RegexTemplates.DiscordChannelUrl.IsMatch(url) || !url.TryParseMessageLink(out var GuildId, out var ChannelId, out var MessageId))
                        {
                            action_embed.Description = $"{this.GetString(CommandKey.InvalidMessageUrl, true)}\n" +
                                                       $"`https://discord.com/channels/012345678901234567/012345678901234567/012345678912345678`\n" +
                                                       $"`https://ptb.discord.com/channels/012345678901234567/012345678901234567/012345678912345678`\n" +
                                                       $"`https://canary.discord.com/channels/012345678901234567/012345678901234567/012345678912345678`";
                            action_embed.ImageUrl = "";
                            _ = await this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(action_embed.AsError(ctx, this.GetString(CommandKey.Title))));
                            await Task.Delay(3000);
                            continue;
                        }

                        if (GuildId != ctx.Guild.Id)
                        {
                            action_embed.Description = this.GetString(CommandKey.MessageUrlWrongGuild, true);
                            action_embed.ImageUrl = "";
                            _ = await this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(action_embed.AsError(ctx, this.GetString(CommandKey.Title))));
                            await Task.Delay(3000);
                            continue;
                        }

                        if (!ctx.Guild.Channels.ContainsKey(ChannelId))
                        {
                            action_embed.Description = this.GetString(CommandKey.MessageUrlNoChannel, true);
                            action_embed.ImageUrl = "";
                            _ = await this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(action_embed.AsError(ctx, this.GetString(CommandKey.Title))));
                            await Task.Delay(3000);
                            continue;
                        }

                        var channel = ctx.Guild.GetChannel(ChannelId);

                        if (!channel.TryGetMessage(MessageId, out var reactionMessage))
                        {
                            action_embed.Description = this.GetString(CommandKey.MessageUrlNoMessage, true);
                            action_embed.ImageUrl = "";
                            _ = await this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(action_embed.AsError(ctx, this.GetString(CommandKey.Title))));
                            await Task.Delay(3000);
                            continue;
                        }

                        selectedMessage = reactionMessage;
                        continue;
                    }
                    else if (Menu.GetCustomId() == SelectEmoji.CustomId)
                    {
                        _ = Menu.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);

                        action_embed.Description = this.GetString(CommandKey.ReactWithEmoji, true);
                        action_embed.ImageUrl = "";
                        _ = await this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(action_embed.AsAwaitingInput(ctx, this.GetString(CommandKey.Title))));

                        var emoji_wait = await ctx.Client.GetInteractivity().WaitForReactionAsync(x => x.Channel.Id == ctx.Channel.Id && x.User.Id == ctx.User.Id && x.Message.Id == selectedMessage.Id, TimeSpan.FromMinutes(2));

                        if (emoji_wait.TimedOut)
                        {
                            this.ModifyToTimedOut(true);
                            return;
                        }

                        try
                        { _ = emoji_wait.Result.Message.DeleteReactionAsync(emoji_wait.Result.Emoji, ctx.User); }
                        catch { }

                        var emoji = emoji_wait.Result.Emoji;

                        if (emoji.Id != 0 && !ctx.Guild.Emojis.ContainsKey(emoji.Id))
                        {
                            action_embed.Description = this.GetString(CommandKey.NoAccessToEmoji, true);
                            _ = await this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(action_embed.AsError(ctx, this.GetString(CommandKey.Title))));
                            await Task.Delay(3000);
                            continue;
                        }

                        if (ctx.DbGuild.ReactionRoles.Any(x => (x.MessageId == selectedMessage.Id && x.EmojiName == emoji.GetUniqueDiscordName())))
                        {
                            action_embed.Description = this.GetString(CommandKey.EmojiAlreadyUsed, true);
                            _ = await this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(action_embed.AsError(ctx, this.GetString(CommandKey.Title))));
                            await Task.Delay(3000);
                            continue;
                        }

                        selectedEmoji = emoji;
                        continue;
                    }
                    else if (Menu.GetCustomId() == SelectRole.CustomId)
                    {
                        _ = Menu.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);

                        _ = await this.RespondOrEdit(action_embed.WithDescription(this.GetString(CommandKey.SelectRolePrompt, true)).AsAwaitingInput(ctx, this.GetString(CommandKey.Title)));

                        var RoleResult = await this.PromptRoleSelection();

                        if (RoleResult.TimedOut)
                        {
                            this.ModifyToTimedOut(true);
                            return;
                        }
                        else if (RoleResult.Cancelled)
                        {
                            continue;
                        }
                        else if (RoleResult.Failed)
                        {
                            if (RoleResult.Exception.GetType() == typeof(NullReferenceException))
                            {
                                _ = await this.RespondOrEdit(new DiscordEmbedBuilder().AsError(ctx).WithDescription(this.GetString(CommandKey.NoRoles, true)));
                                await Task.Delay(3000);
                                continue;
                            }

                            throw RoleResult.Exception;
                        }

                        if (ctx.DbGuild.ReactionRoles.Any(x => x.RoleId == RoleResult.Result.Id))
                        {
                            action_embed.Description = this.GetString(CommandKey.RoleAlreadyUsed, true);
                            _ = await this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(action_embed.AsError(ctx, this.GetString(CommandKey.Title))));
                            await Task.Delay(3000);
                            continue;
                        }

                        selectedRole = RoleResult.Result;
                        continue;
                    }
                    else if (Menu.GetCustomId() == Finish.CustomId)
                    {
                        _ = Menu.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);

                        if (ctx.DbGuild.ReactionRoles.Length > 100)
                        {
                            action_embed.Description = this.GetString(CommandKey.ReactionRoleLimitReached, true);
                            _ = await this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(action_embed.AsError(ctx, this.GetString(CommandKey.Title))));
                            await Task.Delay(5000);
                            await this.ExecuteCommand(ctx, arguments);
                            return;
                        }

                        if (ctx.DbGuild.ReactionRoles.Any(x => x.RoleId == selectedRole.Id))
                        {
                            action_embed.Description = this.GetString(CommandKey.RoleAlreadyUsed, true);
                            _ = await this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(action_embed.AsError(ctx, this.GetString(CommandKey.Title))));
                            await Task.Delay(5000);
                            await this.ExecuteCommand(ctx, arguments);
                            return;
                        }

                        if (selectedEmoji.Id != 0 && !ctx.Guild.Emojis.ContainsKey(selectedEmoji.Id))
                        {
                            action_embed.Description = this.GetString(CommandKey.NoAccessToEmoji, true);
                            _ = await this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(action_embed.AsError(ctx, this.GetString(CommandKey.Title))));
                            await Task.Delay(5000);
                            await this.ExecuteCommand(ctx, arguments);
                            return;
                        }

                        if (ctx.DbGuild.ReactionRoles.Any(x => (x.MessageId == selectedMessage.Id && x.EmojiName == selectedEmoji.GetUniqueDiscordName())))
                        {
                            action_embed.Description = this.GetString(CommandKey.EmojiAlreadyUsed, true);
                            _ = await this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(action_embed.AsError(ctx, this.GetString(CommandKey.Title))));
                            await Task.Delay(5000);
                            await this.ExecuteCommand(ctx, arguments);
                            return;
                        }

                        ctx.DbGuild.ReactionRoles = ctx.DbGuild.ReactionRoles.Add(new()
                        {
                            ChannelId = selectedMessage.Channel.Id,
                            RoleId = selectedRole.Id,
                            EmojiId = selectedEmoji.Id,
                            EmojiName = selectedEmoji.GetUniqueDiscordName(),
                            MessageId = selectedMessage.Id
                        });

                        await selectedMessage.CreateReactionAsync(selectedEmoji);

                        embed.Description = this.GetString(CommandKey.AddedReactionRole, true,
                            new TVar("Role", selectedRole.Mention),
                            new TVar("User", selectedMessage.Author.Mention),
                            new TVar("Channel", selectedMessage.Channel.Mention),
                            new TVar("Emoji", selectedEmoji));
                        _ = await this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(action_embed.AsSuccess(ctx, this.GetString(CommandKey.Title))));
                        await Task.Delay(5000);
                        await this.ExecuteCommand(ctx, arguments);
                        return;
                    }
                    else if (Menu.GetCustomId() == MessageComponents.CancelButtonId)
                    {
                        _ = Menu.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);

                        await this.ExecuteCommand(ctx, arguments);
                        return;
                    }

                    return;
                }
            }
            else if (e.GetCustomId() == RemoveButton.CustomId)
            {
                var RoleResult = await this.PromptCustomSelection(ctx.DbGuild.ReactionRoles
                    .Select(x => new DiscordStringSelectComponentOption($"@{ctx.Guild.GetRole(x.RoleId).Name}", x.UUID, $"in Channel #{ctx.Guild.GetChannel(x.ChannelId).Name}", emoji: new DiscordComponentEmoji(x.GetEmoji(ctx.Client)))).ToList());

                if (RoleResult.TimedOut)
                {
                    this.ModifyToTimedOut(true);
                    return;
                }
                else if (RoleResult.Cancelled)
                {
                    await this.ExecuteCommand(ctx, arguments);
                    return;
                }
                else if (RoleResult.Errored)
                {
                    throw RoleResult.Exception;
                }

                var obj = ctx.DbGuild.ReactionRoles.First(x => x.UUID == RoleResult.Result);

                if (ctx.Guild.GetChannel(obj.ChannelId).TryGetMessage(obj.MessageId, out var reactionMessage))
                    _ = reactionMessage.DeleteReactionsEmojiAsync(obj.GetEmoji(ctx.Client));

                var role = ctx.Guild.GetRole(obj.RoleId);

                ctx.DbGuild.ReactionRoles = ctx.DbGuild.ReactionRoles.Remove(x => x.MessageId.ToString(), obj);


                embed.Description = this.GetString(CommandKey.RemovedReactionRole, true,
                    new TVar("Role", role.Mention),
                    new TVar("User", reactionMessage?.Author.Mention ?? "`/`"),
                    new TVar("Channel", reactionMessage?.Channel.Mention ?? "`/`"),
                    new TVar("Emoji", obj.GetEmoji(ctx.Client)));
                _ = await this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(embed.AsSuccess(ctx, this.GetString(CommandKey.Title))));
                await Task.Delay(5000);
                await this.ExecuteCommand(ctx, arguments);
                return;
            }
            else if (e.GetCustomId() == MessageComponents.CancelButtonId)
            {
                this.DeleteOrInvalidate();
                return;
            }
        });
    }
}