// 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.Net;
using System.Text;
using System.Text.Json;
using Catalogger.Backend.Api.Middleware;
using Catalogger.Backend.Bot;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Dapper;
using Microsoft.AspNetCore.Mvc;
using Remora.Discord.API.Abstractions.Rest;
using Remora.Discord.Extensions.Embeds;

namespace Catalogger.Backend.Api;

public partial class GuildsController
{
    [HttpPost("leave")]
    public async Task<IActionResult> LeaveGuildAsync(string id, [FromBody] LeaveGuildRequest req)
    {
        var (guildId, guild) = await ParseGuildAsync(id);
        if (guild.Name != req.Name)
        {
            throw new ApiError(
                HttpStatusCode.BadRequest,
                ErrorCode.BadRequest,
                "You must spell the server name correctly."
            );
        }

        var guildConfig = await guildRepository.GetAsync(guildId);
        var export = await ToExport(guildConfig);

        var logChannelId =
            webhookExecutor.GetLogChannel(guildConfig, LogChannelType.GuildUpdate)
            ?? webhookExecutor.GetLogChannel(guildConfig, LogChannelType.GuildMemberRemove);
        if (logChannelId != null)
        {
            var currentUser = await discordRequestService.GetMeAsync(CurrentToken);

            var embed = new EmbedBuilder()
                .WithTitle("Catalogger is leaving this server")
                .WithDescription(
                    $"""
                    A moderator in this server ({currentUser.Tag}, <@{currentUser.Id}>) requested that Catalogger leave.
                    All data related to this server will be deleted.

                    A backup of this server's configuration is attached to this message,
                    in case you want to use the bot again later.
                    """
                )
                .WithColour(DiscordUtils.Red)
                .WithCurrentTimestamp()
                .Build()
                .GetOrThrow();

            var exportData = JsonSerializer.Serialize(export, JsonUtils.ApiJsonOptions);
            var file = new FileData(
                "config-backup.json",
                new MemoryStream(Encoding.UTF8.GetBytes(exportData))
            );
            await webhookExecutor.SendLogAsync(logChannelId.Value, [embed], [file]);
        }
        else
        {
            _logger.Information(
                "Can't send guild {GuildId} a heads up when leaving as neither the guild update nor member remove events are logged",
                guildId
            );
        }

        var leaveRes = await userApi.LeaveGuildAsync(guildId);
        if (!leaveRes.IsSuccess)
        {
            _logger.Error("Couldn't leave guild {GuildId}: {Error}", guildId, leaveRes.Error);
            throw new ApiError(
                HttpStatusCode.InternalServerError,
                ErrorCode.InternalServerError,
                "There was an error making Catalogger leave the server."
            );
        }

        await using var tx = await dbConn.BeginTransactionAsync();

        var inviteCount = await dbConn.ExecuteAsync(
            "delete from invites where guild_id = @GuildId",
            new { GuildId = guildId.Value },
            tx
        );

        var watchlistCount = await dbConn.ExecuteAsync(
            "delete from watchlists where guild_id = @GuildId",
            new { GuildId = guildId.Value },
            tx
        );

        var messageCount = await dbConn.ExecuteAsync(
            "delete from messages where guild_id = @GuildId",
            new { GuildId = guildId.Value },
            tx
        );

        await dbConn.ExecuteAsync(
            "delete from guilds where id = @GuildId",
            new { GuildId = guildId.Value },
            tx
        );

        await tx.CommitAsync();

        _logger.Information(
            "Deleted {InviteCount} invites, {WatchlistCount} watchlist entries, and {MessageCount} messages for guild {GuildId}",
            inviteCount,
            watchlistCount,
            messageCount,
            guildId
        );

        _logger.Information("Left guild {GuildId} and removed all data for it", guildId);

        return Ok(export);
    }

    public record LeaveGuildRequest(string Name);
}
