using Dapper;
using GmRelay.Bot.Features.Notifications;
using GmRelay.Shared.Domain;
using GmRelay.Shared.Rendering;
using Npgsql;
using Telegram.Bot;
using Telegram.Bot.Types;
using GmRelay.Bot.Infrastructure.Telegram;

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, int? BatchMessageId, bool CanManage, string NotificationMode);

public sealed class CancelSessionHandler(
    NpgsqlDataSource dataSource,
    ITelegramBotClient bot,
    DirectSessionNotificationSender directSender,
    ILogger<CancelSessionHandler> 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<CancelSessionInfoDto>(
            """
            SELECT s.title AS Title,
                   s.batch_id AS BatchId,
                   s.batch_message_id AS BatchMessageId,
                   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<SessionBatchDto>(
            @"SELECT id as SessionId, scheduled_at as ScheduledAt, status as Status, max_players as MaxPlayers, join_link as JoinLink
              FROM sessions
              WHERE batch_id = @BatchId
              ORDER BY scheduled_at",
            new { BatchId = session.BatchId }, transaction);

        var batchParticipants = await connection.QueryAsync<ParticipantBatchDto>(
            @"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<DirectNotificationRecipient>(
            """
            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 view = SessionBatchViewBuilder.Build(session.Title, batchSessions.ToList(), batchParticipants.ToList());
        var renderResult = TelegramSessionBatchRenderer.Render(view);

        try
        {
            await BatchMessageEditor.EditBatchMessageAsync(
                bot,
                chatId: command.ChatId,
                messageId: session.BatchMessageId ?? command.MessageId,
                text: renderResult.Text,
                replyMarkup: renderResult.Markup,
                ct);

            await bot.AnswerCallbackQuery(command.CallbackQueryId, "Сессия отменена!", cancellationToken: ct);
            
            // Опционально: написать отдельное сообщение в чат
            await bot.SendMessage(command.ChatId, $"❌ <b>Внимание!</b> Сессия \"{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,
                    $"❌ <b>Сессия отменена</b>\n\n📌 <b>{System.Net.WebUtility.HtmlEncode(session.Title)}</b>",
                    "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);
        }
    }
}
