// Copyright (C) 2021-present sam (starshines.gay)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero 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; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

using System.ComponentModel;
using Catalogger.Backend.Cache.InMemoryCache;
using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Extensions;
using Remora.Commands.Attributes;
using Remora.Commands.Groups;
using Remora.Discord.API.Abstractions.Objects;
using Remora.Discord.API.Objects;
using Remora.Discord.Commands.Attributes;
using Remora.Discord.Commands.Feedback.Services;
using Remora.Discord.Commands.Services;
using Remora.Rest.Core;
using IResult = Remora.Results.IResult;

namespace Catalogger.Backend.Bot.Commands;

[Group("key-roles")]
[DiscordDefaultMemberPermissions(DiscordPermission.ManageGuild)]
public class KeyRoleCommands(
    GuildRepository guildRepository,
    ContextInjectionService contextInjection,
    IFeedbackService feedbackService,
    GuildCache guildCache,
    RoleCache roleCache
) : CommandGroup
{
    [Command("list")]
    [Description("List this server's key roles.")]
    public async Task<IResult> ListKeyRolesAsync()
    {
        var (_, guildId) = contextInjection.GetUserAndGuild();
        if (!guildCache.TryGet(guildId, out var guild))
            throw new CataloggerError("Guild not in cache");
        var guildRoles = roleCache.GuildRoles(guildId).ToList();
        var guildConfig = await guildRepository.GetAsync(guildId);

        if (guildConfig.KeyRoles.Count == 0)
            return await feedbackService.ReplyAsync(
                "There are no key roles to list. Add some with `/key-roles add`.",
                isEphemeral: true
            );

        var description = string.Join(
            "\n",
            guildConfig.KeyRoles.Select(id =>
            {
                var role = guildRoles.FirstOrDefault(r => r.ID.Value == id);
                return role != null ? $"- {role.Name} <@&{role.ID}>" : $"- unknown role {id}";
            })
        );

        return await feedbackService.SendContextualEmbedAsync(
            new Embed(
                Title: $"Key roles for {guild.Name}",
                Description: description,
                Colour: DiscordUtils.Purple
            )
        );
    }

    [Command("add")]
    [Description("Add a new key role.")]
    public async Task<IResult> AddKeyRoleAsync(
        [Option("role")]
        [Description("The role to add.")]
        [DiscordTypeHint(TypeHint.Role)]
            Snowflake roleId
    )
    {
        var (_, guildId) = contextInjection.GetUserAndGuild();
        var role = roleCache.GuildRoles(guildId).FirstOrDefault(r => r.ID == roleId);
        if (role == null)
            throw new CataloggerError("Role is not cached");

        var guildConfig = await guildRepository.GetAsync(guildId);
        if (guildConfig.KeyRoles.Any(id => role.ID.Value == id))
            return await feedbackService.ReplyAsync(
                $"{role.Name} is already a key role.",
                isEphemeral: true
            );

        guildConfig.KeyRoles.Add(role.ID.Value);
        await guildRepository.UpdateConfigAsync(guildId, guildConfig);
        return await feedbackService.ReplyAsync($"Added {role.Name} to this server's key roles!");
    }

    [Command("remove")]
    [Description("Remove a key role.")]
    public async Task<IResult> RemoveKeyRoleAsync(
        [Option("role")]
        [Description("The role to remove.")]
        [DiscordTypeHint(TypeHint.Role)]
            Snowflake roleId
    )
    {
        var (_, guildId) = contextInjection.GetUserAndGuild();
        var role = roleCache.GuildRoles(guildId).FirstOrDefault(r => r.ID == roleId);
        if (role == null)
            throw new CataloggerError("Role is not cached");

        var guildConfig = await guildRepository.GetAsync(guildId);
        if (guildConfig.KeyRoles.All(id => role.ID != id))
            return await feedbackService.ReplyAsync(
                $"{role.Name} is already not a key role.",
                isEphemeral: true
            );

        guildConfig.KeyRoles.Remove(role.ID.Value);
        await guildRepository.UpdateConfigAsync(guildId, guildConfig);
        return await feedbackService.ReplyAsync(
            $"Removed {role.Name} from this server's key roles!"
        );
    }
}
