using Dapper; using GmRelay.Bot.Features.Notifications; using GmRelay.Shared.Domain; using GmRelay.Shared.Rendering; using Npgsql; using Telegram.Bot; using Telegram.Bot.Types; namespace GmRelay.Bot.Features.Sessions.CreateSession; public sealed record CancelSessionCommand( Guid SessionId, long TelegramUserId, string CallbackQueryId, long ChatId, int MessageId); // DTOs for AOT compilation internal sealed record CancelSessionInfoDto(string Title, Guid BatchId, bool CanManage, string NotificationMode); public sealed class CancelSessionHandler( NpgsqlDataSource dataSource, ITelegramBotClient bot, DirectSessionNotificationSender directSender, ILogger logger) { public async Task HandleAsync(CancelSessionCommand command, CancellationToken ct) { await using var connection = await dataSource.OpenConnectionAsync(ct); await using var transaction = await connection.BeginTransactionAsync(ct); // 1. Проверяем, что запрос делает управляющий данной группы. var session = await connection.QuerySingleOrDefaultAsync( """ SELECT s.title AS Title, s.batch_id AS BatchId, s.notification_mode AS NotificationMode, EXISTS ( SELECT 1 FROM group_managers gm JOIN players p ON p.id = gm.player_id WHERE gm.group_id = s.group_id AND p.telegram_id = @TelegramUserId ) AS CanManage FROM sessions s WHERE s.id = @SessionId """, new { command.SessionId, command.TelegramUserId }, transaction); if (session == null) { await bot.AnswerCallbackQuery(command.CallbackQueryId, "Сессия не найдена.", cancellationToken: ct); return; } if (!session.CanManage) { await bot.AnswerCallbackQuery(command.CallbackQueryId, "Только owner или co-GM может отменять сессию.", showAlert: true, cancellationToken: ct); return; } // 2. Отменяем сессию await connection.ExecuteAsync( "UPDATE sessions SET status = @Status WHERE id = @Id", new { Id = command.SessionId, Status = SessionStatus.Cancelled }, transaction); // 3. Загружаем весь батч для перерисовки var batchSessions = await connection.QueryAsync( @"SELECT id as SessionId, scheduled_at as ScheduledAt, status as Status, max_players as MaxPlayers FROM sessions WHERE batch_id = @BatchId ORDER BY scheduled_at", new { BatchId = session.BatchId }, transaction); var batchParticipants = await connection.QueryAsync( @"SELECT sp.session_id as SessionId, p.display_name as DisplayName, p.telegram_username as TelegramUsername, sp.registration_status as RegistrationStatus FROM session_participants sp JOIN players p ON sp.player_id = p.id JOIN sessions s ON sp.session_id = s.id WHERE s.batch_id = @BatchId AND sp.is_gm = false ORDER BY sp.registration_status ASC, sp.created_at ASC, sp.responded_at ASC, p.created_at ASC", new { BatchId = session.BatchId }, transaction); var directRecipients = (await connection.QueryAsync( """ SELECT p.telegram_id AS TelegramId, p.display_name AS DisplayName FROM session_participants sp JOIN players p ON sp.player_id = p.id WHERE sp.session_id = @SessionId AND sp.is_gm = false AND sp.registration_status = @Active """, new { command.SessionId, Active = ParticipantRegistrationStatus.Active }, transaction)).ToList(); await transaction.CommitAsync(ct); // 4. Перерисовываем сообщение var renderResult = SessionBatchRenderer.Render(session.Title, batchSessions.ToList(), batchParticipants.ToList()); try { await bot.EditMessageText( chatId: command.ChatId, messageId: command.MessageId, text: renderResult.Text, parseMode: Telegram.Bot.Types.Enums.ParseMode.Html, replyMarkup: renderResult.Markup, cancellationToken: ct); await bot.AnswerCallbackQuery(command.CallbackQueryId, "Сессия отменена!", cancellationToken: ct); // Опционально: написать отдельное сообщение в чат await bot.SendMessage(command.ChatId, $"❌ Внимание! Сессия \"{System.Net.WebUtility.HtmlEncode(session.Title)}\" отменена.", parseMode: Telegram.Bot.Types.Enums.ParseMode.Html, cancellationToken: ct); var mode = SessionNotificationModeExtensions.FromDatabaseValue(session.NotificationMode); if (mode.ShouldSendDirectMessages()) { await directSender.SendAsync( directRecipients, $"❌ Сессия отменена\n\n📌 {System.Net.WebUtility.HtmlEncode(session.Title)}", "session-cancelled", command.SessionId, ct); } } catch (Exception ex) { logger.LogError(ex, "Failed to update batch message after cancelling session {SessionId}", command.SessionId); await bot.AnswerCallbackQuery(command.CallbackQueryId, "Ошибка при обновлении сообщения.", cancellationToken: ct); } } }