using Dapper; using GmRelay.DiscordBot.Infrastructure.Discord; using GmRelay.Shared.Rendering; using Npgsql; namespace GmRelay.DiscordBot.Features.Sessions; public sealed record DiscordDeleteSessionResult( string ReplyText, SessionBatchViewModel? UpdatedView, string? EmptyMessage = null); public sealed class DiscordDeleteSessionHandler( NpgsqlDataSource dataSource, DiscordPermissionChecker permissionChecker, DiscordListSessionsHandler listSessionsHandler, ILogger logger) { public async Task HandleAsync( string guildId, string channelId, ulong userId, ulong resolvedPermissions, ulong guildOwnerId, Guid sessionId, CancellationToken cancellationToken) { await using var connection = await dataSource.OpenConnectionAsync(cancellationToken); var dbManagerUserIds = await connection.QueryAsync( @"SELECT CAST(p.external_user_id AS BIGINT) FROM group_managers gm JOIN players p ON p.id = gm.player_id JOIN game_groups g ON g.id = gm.group_id WHERE g.platform = 'Discord' AND g.external_group_id = @GuildId", new { GuildId = guildId }); if (!permissionChecker.CanManageSchedule(guildOwnerId, userId, dbManagerUserIds, resolvedPermissions)) { return new DiscordDeleteSessionResult( "Только owner, администратор или manager могут удалять сессии.", UpdatedView: null); } await using var transaction = await connection.BeginTransactionAsync(cancellationToken); var deletedRows = await connection.ExecuteAsync( """ DELETE FROM sessions s USING game_groups g WHERE s.group_id = g.id AND s.id = @SessionId AND g.platform = 'Discord' AND g.external_group_id = @GuildId """, new { SessionId = sessionId, GuildId = guildId }, transaction); await transaction.CommitAsync(cancellationToken); if (deletedRows == 0) { return new DiscordDeleteSessionResult( "Сессия не найдена или уже удалена.", UpdatedView: null); } logger.LogInformation("Deleted Discord session {SessionId} in guild {GuildId}", sessionId, guildId); var updatedView = await listSessionsHandler.BuildScheduleAsync( guildId, channelId, userId, resolvedPermissions, guildOwnerId, cancellationToken); return updatedView is null ? new DiscordDeleteSessionResult( "Сессия удалена.", UpdatedView: null, EmptyMessage: "В этом сервере нет предстоящих игр.") : new DiscordDeleteSessionResult("Сессия удалена.", updatedView); } }