import { Processor, WorkerHost } from '@nestjs/bullmq';
import { Logger } from '@nestjs/common';
import { Job } from 'bullmq';
import { Prisma } from '@prisma/client';
import { PrismaService } from '../../../prisma/prisma.service';
import { BotManagerService } from '../services/bot-manager.service';
import { TelegramApiService } from '../services/telegram-api.service';

export interface BroadcastBatchJobData {
  broadcastId: string;
  botId: string;
  message: {
    text: string;
    image?: string;
    buttons?: Array<{ text: string; url?: string }>;
  };
  subscribers: Array<{ id: string; chatId: string }>;
  batchIndex: number;
}

@Processor('telegram-bot-broadcast')
export class BroadcastProcessor extends WorkerHost {
  private readonly logger = new Logger(BroadcastProcessor.name);

  constructor(
    private readonly prisma: PrismaService,
    private readonly botManager: BotManagerService,
    private readonly telegramApi: TelegramApiService,
  ) {
    super();
  }

  async process(job: Job<BroadcastBatchJobData>): Promise<void> {
    const { broadcastId, botId, message, subscribers, batchIndex } = job.data;

    // Check if broadcast was cancelled
    const broadcast = await this.prisma.tgBotBroadcast.findUnique({
      where: { id: broadcastId },
    });
    if (!broadcast || broadcast.status === 'CANCELLED') {
      this.logger.log(`Broadcast ${broadcastId} cancelled, skipping batch ${batchIndex}`);
      return;
    }

    const bot = await this.botManager.getWithToken(botId);
    let sent = 0;
    let failed = 0;

    // Build reply markup
    let reply_markup: Record<string, unknown> | undefined;
    if (message.buttons?.length) {
      reply_markup = this.telegramApi.buildInlineKeyboard(
        message.buttons.map((b) => ({
          text: b.text,
          ...(b.url ? { url: b.url } : {}),
        })),
      );
    }

    for (const sub of subscribers) {
      try {
        const sentMsg = await this.telegramApi.sendMessage(bot.token, {
          chat_id: sub.chatId,
          text: message.text,
          parse_mode: 'HTML',
          reply_markup,
        });

        // Log message
        await this.prisma.tgBotMessage.create({
          data: {
            botId,
            subscriberId: sub.id,
            direction: 'OUT',
            text: message.text,
            nodeId: `broadcast_${broadcastId}`,
            telegramMsgId: sentMsg.message_id,
            payload: Prisma.JsonNull,
          },
        });

        sent++;
      } catch (err: unknown) {
        const errMsg = err instanceof Error ? err.message : String(err);
        failed++;

        // If blocked by user — mark as unsubscribed
        if (
          errMsg.includes('blocked') ||
          errMsg.includes('deactivated') ||
          errMsg.includes('chat not found')
        ) {
          await this.prisma.tgBotSubscriber
            .update({
              where: { id: sub.id },
              data: { isSubscribed: false, blockedAt: new Date() },
            })
            .catch(() => {});
        }

        this.logger.warn(`Broadcast ${broadcastId}: failed to send to ${sub.chatId}: ${errMsg}`);
      }

      // Rate limit: ~30 msg/sec
      await this.sleep(35);
    }

    // Update counters
    await this.prisma.tgBotBroadcast.update({
      where: { id: broadcastId },
      data: {
        sentCount: { increment: sent },
        failedCount: { increment: failed },
      },
    });

    // Check if this was the last batch
    const updated = await this.prisma.tgBotBroadcast.findUnique({
      where: { id: broadcastId },
    });
    if (updated && updated.sentCount + updated.failedCount >= updated.totalCount) {
      await this.prisma.tgBotBroadcast.update({
        where: { id: broadcastId },
        data: { status: 'COMPLETED', completedAt: new Date() },
      });
      this.logger.log(
        `Broadcast ${broadcastId} completed: ${updated.sentCount} sent, ${updated.failedCount} failed`,
      );
    }
  }

  private sleep(ms: number): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }
}
