// ... UpdateRouter will have CancelSessionHandler and cancel_session route instead of close_recruitment
using GmRelay.Shared.Domain;
using GmRelay.Shared.Rendering;
using GmRelay.Bot.Features.Confirmation.HandleRsvp;
using GmRelay.Bot.Features.Sessions.CreateSession;
using GmRelay.Bot.Features.Sessions.ListSessions;
using GmRelay.Bot.Features.Sessions.ExportCalendar;
using GmRelay.Bot.Features.Sessions.RescheduleSession;
using Telegram.Bot;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types.ReplyMarkups;

namespace GmRelay.Bot.Infrastructure.Telegram;

/// <summary>
/// Routes incoming Telegram updates to the appropriate feature handler.
/// No reflection — all routing is explicit (AOT-safe).
/// </summary>
public sealed class UpdateRouter(
    HandleRsvpHandler rsvpHandler,
    CreateSessionHandler createSessionHandler,
    JoinSessionHandler joinSessionHandler,
    LeaveSessionHandler leaveSessionHandler,
    PromoteWaitlistedPlayerHandler promoteWaitlistedPlayerHandler,
    CancelSessionHandler cancelSessionHandler,
    DeleteSessionHandler deleteSessionHandler,
    ListSessionsHandler listSessionsHandler,
    ExportCalendarHandler exportCalendarHandler,
    InitiateRescheduleHandler initiateRescheduleHandler,
    HandleRescheduleTimeInputHandler rescheduleTimeInputHandler,
    HandleRescheduleVoteHandler rescheduleVoteHandler,
    ITelegramBotClient bot,
    IConfiguration configuration,
    ILogger<UpdateRouter> logger) : ITelegramUpdateHandler
{
    public async Task RouteAsync(Update update, CancellationToken ct)
    {
        switch (update)
        {
            case { CallbackQuery: { } query }:
                await HandleCallbackQueryAsync(query, ct);
                break;

            case { Message: { } message }:
                var commandText = GetCommandText(message);
                if (commandText.StartsWith("/", StringComparison.Ordinal))
                {
                    await HandleCommandAsync(message, commandText, ct);
                    break;
                }

                if (message.Text is not null)
                {
                    await rescheduleTimeInputHandler.TryHandleAsync(message, ct);
                }

                break;
        }
    }

    internal static string GetCommandText(Message message)
        => (message.Text ?? message.Caption ?? string.Empty).TrimStart();

    private async Task HandleCallbackQueryAsync(CallbackQuery query, CancellationToken ct)
    {
        if (query.Data is not { } data || query.Message is not { } message)
            return;

        var parts = data.Split(':', 3);
        var action = parts[0];

        if (action == "join_session" && parts.Length >= 2 && Guid.TryParse(parts[1], out var joinSessionId))
        {
            var command = new JoinSessionCommand(
                SessionId: joinSessionId,
                TelegramUserId: query.From.Id,
                DisplayName: query.From.FirstName + (string.IsNullOrEmpty(query.From.LastName) ? "" : $" {query.From.LastName}"),
                TelegramUsername: query.From.Username,
                CallbackQueryId: query.Id,
                ChatId: message.Chat.Id,
                MessageId: message.MessageId);
            
            await joinSessionHandler.HandleAsync(command, ct);
            return;
        }

        if (action == "leave_session" && parts.Length >= 2 && Guid.TryParse(parts[1], out var leaveSessionId))
        {
            var command = new LeaveSessionCommand(
                SessionId: leaveSessionId,
                TelegramUserId: query.From.Id,
                CallbackQueryId: query.Id,
                ChatId: message.Chat.Id,
                MessageId: message.MessageId);

            await leaveSessionHandler.HandleAsync(command, ct);
            return;
        }

        if (action == "cancel_session" && parts.Length >= 2 && Guid.TryParse(parts[1], out var cancelSessionId))
        {
            var command = new CancelSessionCommand(
                SessionId: cancelSessionId,
                TelegramUserId: query.From.Id,
                CallbackQueryId: query.Id,
                ChatId: message.Chat.Id,
                MessageId: message.MessageId);

            await cancelSessionHandler.HandleAsync(command, ct);
            return;
        }

        if (action == "promote_waitlist" && parts.Length >= 2 && Guid.TryParse(parts[1], out var promoteSessionId))
        {
            var command = new PromoteWaitlistedPlayerCommand(
                SessionId: promoteSessionId,
                TelegramUserId: query.From.Id,
                CallbackQueryId: query.Id,
                ChatId: message.Chat.Id,
                MessageId: message.MessageId);

            await promoteWaitlistedPlayerHandler.HandleAsync(command, ct);
            return;
        }

        if (action == "delete_session" && parts.Length >= 2 && Guid.TryParse(parts[1], out var deleteSessionId))
        {
            var command = new DeleteSessionCommand(
                SessionId: deleteSessionId,
                TelegramUserId: query.From.Id,
                CallbackQueryId: query.Id,
                ChatId: message.Chat.Id,
                MessageId: message.MessageId);

            await deleteSessionHandler.HandleAsync(command, ct);
            return;
        }

        if (action == "reschedule_session" && parts.Length >= 2 && Guid.TryParse(parts[1], out var rescheduleSessionId))
        {
            var command = new InitiateRescheduleCommand(
                SessionId: rescheduleSessionId,
                TelegramUserId: query.From.Id,
                CallbackQueryId: query.Id,
                ChatId: message.Chat.Id,
                MessageId: message.MessageId);

            await initiateRescheduleHandler.HandleAsync(command, ct);
            return;
        }

        if (action == "reschedule_vote" && parts.Length >= 2 && Guid.TryParse(parts[1], out var optionId))
        {
            var command = new HandleRescheduleVoteCommand(
                OptionId: optionId,
                TelegramUserId: query.From.Id,
                CallbackQueryId: query.Id,
                ChatId: message.Chat.Id,
                MessageId: message.MessageId);

            await rescheduleVoteHandler.HandleAsync(command, ct);
            return;
        }

        if (action == "rsvp")
        {
            if (parts.Length < 3 || !Guid.TryParse(parts[2], out var sessionId))
                return;

            var status = parts[1] switch
            {
                "confirm" => RsvpStatus.Confirmed,
                "decline" => RsvpStatus.Declined,
                _ => (string?)null
            };

            if (status is null)
                return;

            var command = new HandleRsvpCommand(
                SessionId: sessionId,
                TelegramUserId: query.From.Id,
                Status: status,
                CallbackQueryId: query.Id,
                ChatId: message.Chat.Id,
                MessageId: message.MessageId);

            await rsvpHandler.HandleAsync(command, ct);
        }
    }

    private async Task HandleCommandAsync(Message message, string text, CancellationToken ct)
    {
        // Извлекаем команду: берём первую строку, первое слово, убираем @BotUsername
        var firstLine = text.Split('\n')[0].Trim();
        var command = firstLine.Split(' ')[0].Split('@')[0].ToLowerInvariant();

        switch (command)
        {
            case "/start":
                await SendStartMessageAsync(message, ct);
                break;

            case "/newsession":
                await createSessionHandler.HandleAsync(message, ct);
                break;

            case "/listsessions":
                await listSessionsHandler.HandleAsync(message, ct);
                break;

            case "/exportcalendar":
                await exportCalendarHandler.HandleAsync(message, ct);
                break;

            case "/help":
                await bot.SendMessage(
                    chatId: message.Chat.Id,
                    text: """
                        GM-Relay — бот для управления игровыми сессиями.

                        /newsession
                        Название: My Game
                        Время: 15.05.2026 19:30
                        Мест: 4
                        Ссылка: https://link
                        Картинка: https://cover

                        Для регулярного расписания можно указать одну дату:
                        Игр: 4
                        Интервал: 7

                        /listsessions — список предстоящих сессий
                        Для owner/co-GM /listsessions показывает кнопки отмены, переноса, удаления и повышения из листа ожидания.
                        Игроки могут записаться кнопкой «На дату» и сняться кнопкой «Выйти».
                        /help — эта справка
                        """,
                    cancellationToken: ct);
                break;

            // TODO: /listsessions — will be implemented as features
            default:
                logger.LogDebug("Unknown command: {Command}", command);
                break;
        }
    }

    private async Task SendStartMessageAsync(Message message, CancellationToken ct)
    {
        var miniAppUrl = configuration["Telegram:MiniAppUrl"];
        if (string.IsNullOrWhiteSpace(miniAppUrl))
        {
            await bot.SendMessage(
                chatId: message.Chat.Id,
                text: "GM-Relay Bot ready. Use /help for commands.",
                cancellationToken: ct);
            return;
        }

        await bot.SendMessage(
            chatId: message.Chat.Id,
            text: "GM-Relay Bot ready. Откройте dashboard внутри Telegram или используйте /help для команд.",
            replyMarkup: new InlineKeyboardMarkup(
                InlineKeyboardButton.WithWebApp("Открыть dashboard", new WebAppInfo(miniAppUrl))),
            cancellationToken: ct);
    }
}
