/**
 * WhatsAppQueueService — bulk chunking behaviour.
 *
 * Covers queueBulkWhatsApp's chunking contract (mirror of the bulk-email path): one SQS message
 * per BULK_WHATSAPP_BATCH_SIZE recipients, per-chunk failure isolation, and the disabled/empty
 * short-circuits.
 */

import { WhatsAppQueueService } from './whatsapp-queue.service';
import { SqsClientService } from './sqs-client.service';
import { AppLoggerService } from 'src/common/services/logger.service';
import { QUEUE_CONSTANTS } from '../constants/queue.constants';
import { BulkWhatsAppQueueMessage } from '../interfaces/queue-message.interface';

const QUEUE_URL = 'https://sqs.test/communication';

const buildRecipients = (count: number) =>
  Array.from({ length: count }, (_, index) => ({
    whatsappNumber: `+9198765${String(index).padStart(5, '0')}`,
    customParams: [{ name: 'name', value: `Seeker ${index}` }],
    registrationId: index + 1,
  }));

describe('WhatsAppQueueService — queueBulkWhatsApp', () => {
  let sqsClient: { sendMessage: jest.Mock };
  let logger: Partial<AppLoggerService>;

  const buildService = (enabled = true): WhatsAppQueueService => {
    process.env.AWS_SQS_COMMUNICATION_QUEUE_URL = enabled ? QUEUE_URL : '';
    process.env.ENABLE_COMMUNICATION_QUEUE = enabled ? 'true' : 'false';
    return new WhatsAppQueueService(
      sqsClient as unknown as SqsClientService,
      logger as AppLoggerService,
    );
  };

  const sentMessages = (): BulkWhatsAppQueueMessage[] =>
    sqsClient.sendMessage.mock.calls.map((call) => call[1] as BulkWhatsAppQueueMessage);

  beforeEach(() => {
    sqsClient = { sendMessage: jest.fn().mockResolvedValue('msg-id') };
    logger = { log: jest.fn(), warn: jest.fn(), error: jest.fn(), debug: jest.fn() };
  });

  it('splits recipients into one message per BULK_WHATSAPP_BATCH_SIZE', async () => {
    const batchSize = QUEUE_CONSTANTS.BULK_WHATSAPP_BATCH_SIZE;
    const total = batchSize * 2 + 5;
    const service = buildService();

    const messageIds = await service.queueBulkWhatsApp({
      templateName: 'session_invite',
      broadcastName: 'session_invite',
      recipients: buildRecipients(total),
      templateId: 42,
      createdBy: 7,
    });

    expect(sqsClient.sendMessage).toHaveBeenCalledTimes(3);
    expect(messageIds).toHaveLength(3);

    const messages = sentMessages();
    expect(messages.map((message) => message.data.recipients.length)).toEqual([
      batchSize,
      batchSize,
      5,
    ]);
    // Every recipient is carried exactly once, in order, across the chunks.
    expect(messages.flatMap((message) => message.data.recipients)).toHaveLength(total);
    // Batch-level fields are repeated on each chunk so a chunk is independently processable.
    for (const message of messages) {
      expect(message.subType).toBe(QUEUE_CONSTANTS.COMMUNICATION_TYPES.BULK_WHATSAPP);
      expect(message.data.templateName).toBe('session_invite');
      expect(message.metadata?.templateId).toBe(42);
      expect(message.metadata?.createdBy).toBe(7);
    }
    // Distinct correlation ids so the chunks are traceable apart.
    expect(new Set(messages.map((message) => message.correlationId)).size).toBe(3);
  });

  it('sends a single message when the recipients fit in one chunk', async () => {
    const service = buildService();

    const messageIds = await service.queueBulkWhatsApp({
      templateName: 'session_absent',
      broadcastName: 'session_absent',
      recipients: buildRecipients(3),
      correlationId: 'absent-run',
    });

    expect(sqsClient.sendMessage).toHaveBeenCalledTimes(1);
    expect(messageIds).toEqual(['msg-id']);
    // Un-suffixed: a single chunk keeps the caller's own correlation id.
    expect(sentMessages()[0].correlationId).toBe('absent-run');
  });

  it('keeps sending the remaining chunks when one chunk fails to enqueue', async () => {
    const batchSize = QUEUE_CONSTANTS.BULK_WHATSAPP_BATCH_SIZE;
    sqsClient.sendMessage
      .mockResolvedValueOnce('msg-1')
      .mockRejectedValueOnce(new Error('SQS unavailable'))
      .mockResolvedValueOnce('msg-3');
    const service = buildService();

    const messageIds = await service.queueBulkWhatsApp({
      templateName: 'session_invite',
      broadcastName: 'session_invite',
      recipients: buildRecipients(batchSize * 3),
    });

    expect(sqsClient.sendMessage).toHaveBeenCalledTimes(3);
    expect(messageIds).toEqual(['msg-1', 'msg-3']);
    expect(logger.error).toHaveBeenCalled();
  });

  it('returns an empty list when the queue is disabled', async () => {
    const service = buildService(false);

    const messageIds = await service.queueBulkWhatsApp({
      templateName: 'session_invite',
      broadcastName: 'session_invite',
      recipients: buildRecipients(5),
    });

    expect(messageIds).toEqual([]);
    expect(sqsClient.sendMessage).not.toHaveBeenCalled();
  });

  it('returns an empty list without enqueuing when there are no recipients', async () => {
    const service = buildService();

    expect(
      await service.queueBulkWhatsApp({
        templateName: 'session_invite',
        broadcastName: 'session_invite',
        recipients: [],
      }),
    ).toEqual([]);
    expect(sqsClient.sendMessage).not.toHaveBeenCalled();
  });
});
