import { Injectable } from '@nestjs/common';
import { PrismaService } from '../../infra/prisma.service';
import { BotCallbackRegistryService } from './bot-callback-registry.service';
import { BotInboundQueueMessage } from './bot-inbound-publisher.service';
import { BotUpdateQueueService } from './bot-update-queue.service';
import { BotWebhookDispatcherService } from './bot-webhook-dispatcher.service';
import { TelegramSerializerService } from './telegram-serializer.service';
import { isProtectedSystemAccount } from '../system-account.util';

@Injectable()
export class BotDeliveryService {
  constructor(
    private readonly prisma: PrismaService,
    private readonly serializer: TelegramSerializerService,
    private readonly webhookDispatcher: BotWebhookDispatcherService,
    private readonly updateQueue: BotUpdateQueueService,
    private readonly callbackRegistry: BotCallbackRegistryService
  ) {}

  async deliverInboundEvent(event: BotInboundQueueMessage) {
    if ((event.eventType ?? 'message') === 'callback_query') {
      await this.deliverCallbackQuery(event);
      return;
    }

    await this.deliverMessage(event);
  }

  private async deliverMessage(event: BotInboundQueueMessage) {
    const [bot, sender] = await Promise.all([
      this.loadBot(event.botId),
      this.loadSender(event.senderUserId)
    ]);

    if (!bot || !sender) return;

    const internalMessage = {
      updateId: event.updateId,
      botId: event.botId,
      senderUserId: sender.id,
      senderDisplayName: sender.displayName,
      senderUsername: sender.username,
      telegramChatId: Number(event.telegramChatId),
      telegramMessageId: event.telegramMessageId,
      text: event.text,
      createdAt: new Date(event.createdAt)
    };

    const update = this.serializer.toTelegramUpdate(internalMessage);
    await this.persistAndDispatch(bot, update);
  }

  private async deliverCallbackQuery(event: BotInboundQueueMessage) {
    const [bot, sender, sourceMessage] = await Promise.all([
      this.loadBot(event.botId),
      this.loadSender(event.senderUserId),
      event.sourceBotMessageId
        ? this.prisma.botMessage.findFirst({
            where: {
              botId: event.botId,
              botChatId: event.botChatId,
              telegramMsgId: event.sourceBotMessageId
            },
            include: {
              botChat: {
                include: {
                  bot: true
                }
              }
            }
          })
        : Promise.resolve(null)
    ]);

    if (!bot || !sender || !sourceMessage || !event.callbackQueryId || !event.callbackData) return;

    const recipient = await this.prisma.user.findUnique({
      where: { id: sender.id },
      select: { displayName: true, username: true }
    });
    if (!recipient) return;

    const telegramMessage = this.serializer.buildBotMessageObject({
      message: sourceMessage,
      bot: sourceMessage.botChat.bot,
      chat: {
        telegramChatId: sourceMessage.botChat.telegramChatId,
        chatType: sourceMessage.botChat.chatType,
        recipientDisplayName: recipient.displayName,
        recipientUsername: recipient.username
      }
    });

    const callbackQueryId = event.callbackQueryId;
    await this.callbackRegistry.register(callbackQueryId, {
      userId: sender.id,
      botId: bot.id,
      callbackData: event.callbackData,
      messageId: sourceMessage.telegramMsgId,
      chatId: Number(event.telegramChatId)
    });

    const update = this.serializer.toTelegramCallbackUpdate({
      updateId: event.updateId,
      botId: event.botId,
      callbackQueryId,
      senderUserId: sender.id,
      senderDisplayName: sender.displayName,
      senderUsername: sender.username,
      telegramChatId: Number(event.telegramChatId),
      callbackData: event.callbackData,
      sourceMessage: telegramMessage,
      createdAt: new Date(event.createdAt)
    });

    await this.persistAndDispatch(bot, update);
  }

  private async persistAndDispatch(
    bot: { id: string; webhookUrl: string | null; webhookSecretToken: string | null },
    update: ReturnType<TelegramSerializerService['toTelegramUpdate']> | ReturnType<TelegramSerializerService['toTelegramCallbackUpdate']>
  ) {
    await this.prisma.botUpdate.upsert({
      where: { botId_updateId: { botId: bot.id, updateId: update.update_id } },
      create: {
        botId: bot.id,
        updateId: update.update_id,
        payload: update as object
      },
      update: {
        payload: update as object
      }
    });

    if (bot.webhookUrl) {
      await this.webhookDispatcher.dispatch(bot.webhookUrl, update, bot.webhookSecretToken);
      return;
    }

    await this.updateQueue.enqueue(bot.id, update);
  }

  private loadBot(botId: string) {
    return this.prisma.bot.findUnique({
      where: { id: botId },
      select: {
        id: true,
        isActive: true,
        webhookUrl: true,
        webhookSecretToken: true
      }
    }).then((bot) => (bot?.isActive ? bot : null));
  }

  private loadSender(senderUserId: string) {
    return this.prisma.user.findUnique({
      where: { id: senderUserId },
      select: { id: true, displayName: true, username: true, deletedAt: true, status: true, isSystemAccount: true, linkedBotId: true }
    }).then((sender) =>
      sender && !sender.deletedAt && sender.status === 'ACTIVE' && !isProtectedSystemAccount(sender) ? sender : null
    );
  }
}
