import { NOTIFICATION_RPC } from '@app/contracts';
import { FOLLOW_REQUEST_APPROVED_EVENT, FollowRequestApprovedEvent } from '@app/events';
import { ConfigService } from '@nestjs/config';
import { of } from 'rxjs';
import { NotificationConsumer } from '../../../apps/notification-service/src/notification.consumer';
import { NotificationServiceClient } from './notification-service.client';
import { NotificationsGateway } from './notifications.gateway';

const remoteConfig = () => ({
  get: jest.fn((key: string, fallback?: string) => ({
    NOTIFICATION_SERVICE_ENABLED: 'true',
    RABBITMQ_URL: 'amqp://rabbit',
    NOTIFICATION_RPC_TIMEOUT_MS: '1000',
    NOTIFICATION_RPC_RETRIES: '0',
  })[key] ?? fallback),
}) as unknown as ConfigService;

describe('Gateway to notification-service contract', () => {
  it('keeps the gateway result identical while using the shared RMQ pattern', async () => {
    const expected = { items: [{ _id: 'notification-1' }], total: 1, unreadCount: 1 };
    const service = new NotificationServiceClient(
      remoteConfig(),
      { getMine: jest.fn() } as never,
      { emitCreated: jest.fn(), emitUnreadCount: jest.fn() } as never,
    );
    const transport = { send: jest.fn().mockReturnValue(of({ result: expected })), close: jest.fn() };
    (service as unknown as { client: unknown }).client = transport;

    await expect(service.getMine('user-1', { page: 1 } as never, 'request-1')).resolves.toBe(expected);
    expect(transport.send).toHaveBeenCalledWith(
      NOTIFICATION_RPC.list,
      expect.objectContaining({ data: { recipientId: 'user-1', query: { page: 1 } } }),
    );
    await service.onModuleDestroy();
  });

  it('uses only the local service and never sends to RabbitMQ when the flag is false', async () => {
    const expected = { items: [], total: 0, unreadCount: 0 };
    const local = { getMine: jest.fn().mockResolvedValue(expected) };
    const service = new NotificationServiceClient(
      { get: jest.fn((_key: string, fallback?: string) => fallback) } as unknown as ConfigService,
      local as never,
      { emitCreated: jest.fn(), emitUnreadCount: jest.fn() } as never,
    );
    const transport = { send: jest.fn(), connect: jest.fn(), close: jest.fn() };
    (service as unknown as { client: unknown }).client = transport;

    await expect(service.getMine('user-1', {} as never)).resolves.toBe(expected);
    expect(local.getMine).toHaveBeenCalledTimes(1);
    expect(transport.send).not.toHaveBeenCalled();
    expect(transport.connect).not.toHaveBeenCalled();
    await service.onModuleDestroy();
  });

  it('delivers follow approval from the consumer to each compatible Socket.IO event once', async () => {
    jest.spyOn(console, 'log').mockImplementation(() => undefined);
    const event: FollowRequestApprovedEvent = {
      eventId: 'event-socket-1',
      eventType: FOLLOW_REQUEST_APPROVED_EVENT,
      occurredAt: '2026-08-03T10:00:00.000Z',
      payload: { requestId: 'request-1', actorId: 'actor-1', recipientId: 'recipient-1' },
    };
    const notification = { _id: 'notification-1', type: 'follow_request_approved' };
    const consumer = new NotificationConsumer({
      createFollowRequestApprovedNotification: jest.fn().mockResolvedValue(notification),
      getUnreadCounts: jest.fn().mockResolvedValue({ total: 1, followRequests: 1 }),
    } as never);
    const channel = { ack: jest.fn(), nack: jest.fn() };
    const response = await consumer.followRequestApproved(event, {
      getChannelRef: () => channel,
      getMessage: () => ({ properties: { headers: {} } }),
    } as never);

    const gateway = new NotificationsGateway({} as never, {} as never, {} as never);
    const roomEmitter = { emit: jest.fn() };
    (gateway as unknown as { server: unknown }).server = {
      to: jest.fn().mockReturnValue(roomEmitter),
    };
    const client = new NotificationServiceClient(remoteConfig(), {} as never, gateway);
    const transport = { send: jest.fn().mockReturnValue(of(response)), close: jest.fn() };
    (client as unknown as { client: unknown }).client = transport;

    await client.publishFollowRequestApproved(event);

    expect(roomEmitter.emit).toHaveBeenCalledWith('notification_created', notification);
    expect(roomEmitter.emit).toHaveBeenCalledWith('notification:new', notification);
    expect(roomEmitter.emit.mock.calls.filter(([name]) => name === 'notification_created')).toHaveLength(1);
    expect(roomEmitter.emit.mock.calls.filter(([name]) => name === 'notification:new')).toHaveLength(1);
    await client.onModuleDestroy();
    jest.restoreAllMocks();
  });
});
