using System.Text.Json.Nodes;
using JetBrains.Annotations;
using Remora.Discord.API.Abstractions.Gateway.Events;
using Remora.Discord.API.Abstractions.Rest;
using Remora.Discord.Extensions.Embeds;
using Remora.Discord.Gateway.Responders;
using Remora.Rest.Core;
using Remora.Results;
using TeamOctolings.Octobot.Data;
using TeamOctolings.Octobot.Extensions;
using TeamOctolings.Octobot.Services;

namespace TeamOctolings.Octobot.Responders;

/// <summary>
///     Handles sending a guild's <see cref="GuildSettings.WelcomeMessage" /> if one is set.
///     If <see cref="GuildSettings.ReturnRolesOnRejoin" /> is enabled, roles will be returned.
/// </summary>
/// <seealso cref="GuildSettings.WelcomeMessage" />
[UsedImplicitly]
public sealed class GuildMemberJoinedResponder : IResponder<IGuildMemberAdd>
{
    private readonly IDiscordRestChannelAPI _channelApi;
    private readonly IDiscordRestGuildAPI _guildApi;
    private readonly GuildDataService _guildData;

    public GuildMemberJoinedResponder(
        IDiscordRestChannelAPI channelApi, GuildDataService guildData, IDiscordRestGuildAPI guildApi)
    {
        _channelApi = channelApi;
        _guildData = guildData;
        _guildApi = guildApi;
    }

    public async Task<Result> RespondAsync(IGuildMemberAdd gatewayEvent, CancellationToken ct = default)
    {
        if (!gatewayEvent.User.IsDefined(out var user))
        {
            return new ArgumentNullError(nameof(gatewayEvent.User));
        }

        var data = await _guildData.GetData(gatewayEvent.GuildID, ct);
        var cfg = data.Settings;
        var memberData = data.GetOrCreateMemberData(user.ID);

        memberData.Kicked = false;

        var returnRolesResult = await TryReturnRolesAsync(cfg, memberData, gatewayEvent.GuildID, user.ID, ct);
        if (!returnRolesResult.IsSuccess)
        {
            return ResultExtensions.FromError(returnRolesResult);
        }

        if (GuildSettings.WelcomeMessagesChannel.Get(cfg).Empty()
            || GuildSettings.WelcomeMessage.Get(cfg) is "off" or "disable" or "disabled")
        {
            return Result.Success;
        }

        Messages.Culture = GuildSettings.Language.Get(cfg);
        var welcomeMessage = GuildSettings.WelcomeMessage.Get(cfg) is "default" or "reset"
            ? Messages.DefaultWelcomeMessage
            : GuildSettings.WelcomeMessage.Get(cfg);

        var guildResult = await _guildApi.GetGuildAsync(gatewayEvent.GuildID, ct: ct);
        if (!guildResult.IsDefined(out var guild))
        {
            return ResultExtensions.FromError(guildResult);
        }

        var embed = new EmbedBuilder()
            .WithSmallTitle(string.Format(welcomeMessage, user.GetTag(), guild.Name), user)
            .WithGuildFooter(guild)
            .WithTimestamp(gatewayEvent.JoinedAt)
            .WithColour(ColorsList.Green)
            .Build();

        return await _channelApi.CreateMessageWithEmbedResultAsync(
            GuildSettings.WelcomeMessagesChannel.Get(cfg), embedResult: embed,
            allowedMentions: Utility.NoMentions, ct: ct);
    }

    private async Task<Result> TryReturnRolesAsync(
        JsonNode cfg, MemberData memberData, Snowflake guildId, Snowflake userId, CancellationToken ct = default)
    {
        if (!GuildSettings.ReturnRolesOnRejoin.Get(cfg))
        {
            return Result.Success;
        }

        var assignRoles = new List<Snowflake>();

        if (memberData.MutedUntil is null || !GuildSettings.RemoveRolesOnMute.Get(cfg))
        {
            assignRoles.AddRange(memberData.Roles.ConvertAll(r => r.ToSnowflake()));
        }

        if (memberData.MutedUntil is not null)
        {
            assignRoles.Add(GuildSettings.MuteRole.Get(cfg));
        }

        return await _guildApi.ModifyGuildMemberAsync(
            guildId, userId, roles: assignRoles, ct: ct);
    }
}