// 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.Database.Models;
using Dapper;
using Remora.Rest.Core;

namespace Catalogger.Backend.Database.Repositories;

public class InviteRepository(ILogger logger, DatabaseConnection conn)
    : IDisposable,
        IAsyncDisposable
{
    private readonly ILogger _logger = logger.ForContext<InviteRepository>();

    public async Task<List<Invite>> GetGuildInvitesAsync(Snowflake guildId) =>
        (
            await conn.QueryAsync<Invite>(
                "select * from invites where guild_id = @GuildId",
                new { GuildId = guildId.Value }
            )
        ).ToList();

    public async Task SetInviteNameAsync(Snowflake guildId, string code, string name) =>
        await conn.ExecuteAsync(
            """
            insert into invites (code, guild_id, name) values
            (@Code, @GuildId, @Name) on conflict (code) do update set name = @Name
            """,
            new
            {
                Code = code,
                GuildId = guildId.Value,
                Name = name,
            }
        );

    public async Task<Invite?> GetInviteAsync(Snowflake guildId, string code) =>
        await conn.QueryFirstOrDefaultAsync<Invite>(
            "select * from invites where guild_id = @GuildId and code = @Code",
            new { GuildId = guildId.Value, Code = code }
        );

    public async Task<string> GetInviteNameAsync(Snowflake guildId, string code) =>
        await conn.ExecuteScalarAsync<string>(
            "select name from invites where guild_id = @GuildId and code = @Code",
            new { GuildId = guildId.Value, Code = code }
        ) ?? "(unnamed)";

    public async Task<int> DeleteInviteAsync(Snowflake guildId, string code) =>
        await conn.ExecuteAsync(
            "delete from invites where guild_id = @GuildId and code = @Code",
            new { GuildId = guildId.Value, Code = code }
        );

    /// <summary>
    /// Bulk imports an array of invite codes and names.
    /// The GuildId property in the Invite object is ignored.
    /// </summary>
    public async Task ImportInvitesAsync(Snowflake guildId, IEnumerable<Invite> invites)
    {
        await using var tx = await conn.BeginTransactionAsync();
        foreach (var invite in invites)
        {
            await conn.ExecuteAsync(
                """
                insert into invites (code, guild_id, name)
                values (@Code, @GuildId, @Name) on conflict (code)
                    do update set name = @Name
                """,
                new
                {
                    GuildId = guildId.Value,
                    invite.Code,
                    invite.Name,
                },
                transaction: tx
            );
        }

        await tx.CommitAsync();
    }

    public void Dispose()
    {
        conn.Dispose();
        GC.SuppressFinalize(this);
    }

    public async ValueTask DisposeAsync()
    {
        await conn.DisposeAsync();
        GC.SuppressFinalize(this);
    }
}
