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

namespace GmRelay.Bot.Features.Confirmation.SendConfirmation;

// ── DTOs for Dapper mapping ──────────────────────────────────────────

internal sealed record SessionInfo(
    Guid Id,
    string Title,
    DateTime ScheduledAt,
    Guid GroupId,
    long TelegramChatId,
    string NotificationMode);

internal sealed record ParticipantInfo(
    long TelegramId,
    string DisplayName,
    string? TelegramUsername);

// ── Handler ──────────────────────────────────────────────────────────

/// <summary>
/// Sends the interactive confirmation message (inline keyboard) to the group chat.
/// Called by SessionSchedulerService at T-24h.
/// </summary>
public sealed class SendConfirmationHandler(
    NpgsqlDataSource dataSource,
    ITelegramBotClient bot,
    DirectSessionNotificationSender directSender,
    ILogger<SendConfirmationHandler> logger) : ISendConfirmationHandler
{
    public async Task HandleAsync(Guid sessionId, CancellationToken ct)
    {
        await using var connection = await dataSource.OpenConnectionAsync(ct);

        // 1. Load session + group info
        var session = await connection.QuerySingleOrDefaultAsync<SessionInfo>(
            """
            SELECT s.id, s.title, s.scheduled_at AS ScheduledAt, s.group_id AS GroupId,
                   g.telegram_chat_id AS TelegramChatId,
                   s.notification_mode AS NotificationMode
            FROM sessions s
            JOIN game_groups g ON g.id = s.group_id
            WHERE s.id = @SessionId AND s.status = @Planned
            """,
            new { SessionId = sessionId, Planned = SessionStatus.Planned });

        if (session is null)
        {
            logger.LogWarning("Session {SessionId} not found or not in Planned status", sessionId);
            return;
        }

        // 2. Load non-GM participants
        var participants = (await connection.QueryAsync<ParticipantInfo>(
            """
            SELECT p.telegram_id AS TelegramId,
                   p.display_name AS DisplayName,
                   p.telegram_username AS TelegramUsername
            FROM session_participants sp
            JOIN players p ON p.id = sp.player_id
            WHERE sp.session_id = @SessionId
              AND sp.is_gm = false
              AND sp.registration_status = @Active
            """,
            new { SessionId = sessionId, Active = ParticipantRegistrationStatus.Active })).ToList();

        if (participants.Count == 0)
        {
            logger.LogWarning("Session {SessionId} has no non-GM participants", sessionId);
            return;
        }

        // 3. Build confirmation message
        var playerList = string.Join("\n", participants.Select(p =>
            $"   ⏳ {FormatPlayerName(p)}"));

        var text = $"""
            🎲 Подтвердите участие в «{session.Title}»
            📅 {session.ScheduledAt.FormatMoscow()} (МСК)

            {playerList}
            
            Статус: ожидаем подтверждения (0/{participants.Count})
            """;

        var keyboard = new InlineKeyboardMarkup([
            [
                InlineKeyboardButton.WithCallbackData("✅ Буду", $"rsvp:confirm:{sessionId}"),
                InlineKeyboardButton.WithCallbackData("❌ Не смогу", $"rsvp:decline:{sessionId}")
            ]
        ]);

        // 4. Send to group
        var message = await bot.SendMessage(
            chatId: session.TelegramChatId,
            text: text,
            replyMarkup: keyboard,
            cancellationToken: ct);

        // 5. Update session status, store message ID, and mark confirmation sent
        await connection.ExecuteAsync(
            """
            UPDATE sessions
            SET status = @Status,
                confirmation_message_id = @MessageId,
                confirmation_sent_at = now(),
                updated_at = now()
            WHERE id = @SessionId
              AND confirmation_sent_at IS NULL
            """,
            new
            {
                SessionId = sessionId,
                Status = SessionStatus.ConfirmationSent,
                MessageId = message.MessageId
            });

        var mode = SessionNotificationModeExtensions.FromDatabaseValue(session.NotificationMode);
        if (mode.ShouldSendDirectMessages())
        {
            var directText = $"""
                🎲 <b>Подтвердите участие в игре</b>

                📌 <b>{System.Net.WebUtility.HtmlEncode(session.Title)}</b>
                📅 {session.ScheduledAt.FormatMoscow()} (МСК)

                Ответьте кнопкой в групповом сообщении расписания.
                """;

            await directSender.SendAsync(
                participants.Select(p => new DirectNotificationRecipient(p.TelegramId, p.DisplayName)),
                directText,
                "confirmation",
                sessionId,
                ct);
        }

        logger.LogInformation(
            "Confirmation sent for session {SessionId} ({Title}), message_id={MessageId}",
            sessionId, session.Title, message.MessageId);
    }

    internal static string FormatPlayerName(ParticipantInfo p) =>
        p.TelegramUsername is not null ? $"@{p.TelegramUsername}" : p.DisplayName;
}
