// 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 Catalogger.Backend.Cache;
using Catalogger.Backend.Cache.InMemoryCache;
using Catalogger.Backend.Extensions;
using Remora.Discord.API.Abstractions.Objects;
using Remora.Discord.API.Objects;
using Remora.Rest.Core;

namespace Catalogger.Backend.Services;

public class PermissionResolverService(
    IMemberCache memberCache,
    RoleCache roleCache,
    ChannelCache channelCache
)
{
    public async Task<IDiscordPermissionSet> GetGuildPermissionsAsync(
        Snowflake guildId,
        Snowflake userId
    )
    {
        var member = await memberCache.TryGetAsync(guildId, userId);
        return member == null ? DiscordPermissionSet.Empty : GetGuildPermissions(guildId, member);
    }

    public async Task<IDiscordPermissionSet> GetChannelPermissionsAsync(
        Snowflake guildId,
        Snowflake userId,
        Snowflake channelId
    )
    {
        if (!channelCache.TryGet(channelId, out var channel))
            return DiscordPermissionSet.Empty;
        return await GetChannelPermissionsAsync(guildId, userId, channel);
    }

    public async Task<IDiscordPermissionSet> GetChannelPermissionsAsync(
        Snowflake guildId,
        Snowflake userId,
        IChannel channel
    )
    {
        var member = await memberCache.TryGetAsync(guildId, userId);
        return member == null
            ? DiscordPermissionSet.Empty
            : GetChannelPermissions(guildId, member, channel);
    }

    public IDiscordPermissionSet GetGuildPermissions(Snowflake guildId, IGuildMember member)
    {
        var roles = roleCache.GuildRoles(guildId).ToList();
        var everyoneRole = roles.First(r => r.ID == guildId);
        var memberRoles = roles.Where(r => member.Roles.Contains(r.ID)).ToList();

        return DiscordPermissionSet.ComputePermissions(
            member.User.GetOrThrow().ID,
            everyoneRole,
            memberRoles
        );
    }

    public IDiscordPermissionSet GetChannelPermissions(
        Snowflake guildId,
        IGuildMember member,
        IChannel channel
    )
    {
        var roles = roleCache.GuildRoles(guildId).ToList();
        var everyoneRole = roles.First(r => r.ID == guildId);
        var memberRoles = roles.Where(r => member.Roles.Contains(r.ID)).ToList();

        return DiscordPermissionSet.ComputePermissions(
            member.User.GetOrThrow().ID,
            everyoneRole,
            memberRoles,
            channel.PermissionOverwrites.OrDefault()
        );
    }
}
