using Dapper;
using GmRelay.Shared.Domain;
using Npgsql;
using Telegram.Bot;
namespace GmRelay.Bot.Features.Sessions.RescheduleSession;
// ── Command ──────────────────────────────────────────────────────────
public sealed record InitiateRescheduleCommand(
Guid SessionId,
long TelegramUserId,
string CallbackQueryId,
long ChatId,
int MessageId);
// ── DTOs ─────────────────────────────────────────────────────────────
internal sealed record RescheduleSessionInfoDto(string Title, bool CanManage);
// ── Handler ──────────────────────────────────────────────────────────
///
/// Handles the "⏰ Перенести" button press from the batch message.
/// Creates a reschedule proposal in AwaitingTime status and prompts
/// the GM to enter 2-3 new time options and a voting deadline.
///
public sealed class InitiateRescheduleHandler(
NpgsqlDataSource dataSource,
ITelegramBotClient bot,
ILogger logger)
{
public async Task HandleAsync(InitiateRescheduleCommand command, CancellationToken ct)
{
await using var connection = await dataSource.OpenConnectionAsync(ct);
// 1. Verify group management access.
var session = await connection.QuerySingleOrDefaultAsync(
"""
SELECT s.title AS Title,
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 AND s.status != @Cancelled
""",
new { command.SessionId, command.TelegramUserId, Cancelled = SessionStatus.Cancelled });
if (session is 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. Check no active proposal exists
var hasActive = await connection.ExecuteScalarAsync(
"""
SELECT EXISTS (
SELECT 1 FROM reschedule_proposals
WHERE session_id = @SessionId AND status IN ('AwaitingTime', 'Voting')
)
""",
new { command.SessionId });
if (hasActive)
{
await bot.AnswerCallbackQuery(command.CallbackQueryId,
"Уже есть активный запрос на перенос этой сессии.", showAlert: true, cancellationToken: ct);
return;
}
// 3. Create proposal in AwaitingTime status
await connection.ExecuteAsync(
"""
INSERT INTO reschedule_proposals (session_id, proposed_by, status)
VALUES (@SessionId, @GmId, 'AwaitingTime')
""",
new { command.SessionId, GmId = command.TelegramUserId });
logger.LogInformation("Reschedule initiated for session {SessionId} by GM {GmId}", command.SessionId, command.TelegramUserId);
// 4. Prompt GM in chat
await bot.AnswerCallbackQuery(command.CallbackQueryId,
"Введите 2-3 варианта времени и дедлайн голосования.", cancellationToken: ct);
await bot.SendMessage(
chatId: command.ChatId,
text: $"""
⏰ Укажите 2-3 варианта времени для сессии «{session.Title}» и дедлайн голосования.
Формат:
25.04.2026 19:30
26.04.2026 18:00
Дедлайн: 25.04.2026 12:00
Дедлайн должен быть в будущем и раньше первого предложенного времени.
""",
parseMode: Telegram.Bot.Types.Enums.ParseMode.Html,
cancellationToken: ct);
}
}