using Catalogger.Backend.Database.Models;
using Dapper;
using NodaTime;
using Remora.Rest.Core;

namespace Catalogger.Backend.Database.Repositories;

public class TimeoutRepository(DatabaseConnection conn, IClock clock)
    : IDisposable,
        IAsyncDisposable
{
    public async Task<DiscordTimeout?> GetAsync(int id) =>
        await conn.QueryFirstOrDefaultAsync<DiscordTimeout>(
            "select * from timeouts where id = @id",
            new { id }
        );

    public async Task<DiscordTimeout?> GetAsync(Snowflake guildId, Snowflake userId) =>
        await conn.QueryFirstOrDefaultAsync<DiscordTimeout>(
            "select * from timeouts where guild_id = @GuildId and user_id = @UserId",
            new { GuildId = guildId.Value, UserId = userId.Value }
        );

    public async Task<List<DiscordTimeout>> GetAllAsync() =>
        (
            await conn.QueryAsync<DiscordTimeout>(
                "select * from timeouts where until > now() order by id"
            )
        ).ToList();

    public async Task<DiscordTimeout> SetAsync(
        Snowflake guildId,
        Snowflake userId,
        Instant until,
        Snowflake? moderatorId
    ) =>
        await conn.QueryFirstAsync<DiscordTimeout>(
            """
            insert into timeouts (user_id, guild_id, moderator_id, until)
            values (@UserId, @GuildId, @ModeratorId, @Until)
            on conflict (user_id, guild_id) do update
            set moderator_id = @ModeratorId,
                until = @Until
            returning *
            """,
            new
            {
                UserId = userId.Value,
                GuildId = guildId.Value,
                ModeratorId = moderatorId?.Value,
                Until = until,
            }
        );

    public async Task<DiscordTimeout?> RemoveAsync(int id) =>
        await conn.QueryFirstOrDefaultAsync<DiscordTimeout>(
            "delete from timeouts where id = @id returning *",
            new { id }
        );

    public async Task<DiscordTimeout?> RemoveAsync(Snowflake guildId, Snowflake userId) =>
        await conn.QueryFirstOrDefaultAsync<DiscordTimeout>(
            "delete from timeouts where guild_id = @GuildId and user_id = @UserId returning *",
            new { GuildId = guildId.Value, UserId = userId.Value }
        );

    public async Task<int> RemoveExpiredTimeoutsAsync() =>
        await conn.ExecuteAsync(
            "delete from timeouts where until < @Expiry",
            new { Expiry = clock.GetCurrentInstant() - Duration.FromMinutes(5) }
        );

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

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