import { type CommandBus } from '@nestjs/cqrs';
import { type LoggerService } from '@modules/shared/infrastructure/logger.service';
import { type PrismaService } from '@modules/shared/infrastructure/prisma.service';
import { QuotaExceededEvent } from '@modules/subscriptions/domain/events/quota-exceeded.event';
import { SendNotificationCommand } from '../commands/send-notification/send-notification.command';
import { QuotaExceededListener } from '../listeners/quota-exceeded.listener';

describe('QuotaExceededListener', () => {
  let listener: QuotaExceededListener;
  let mockCommandBus: { execute: ReturnType<typeof vi.fn> };
  let mockPrisma: any;
  let mockLogger: { log: ReturnType<typeof vi.fn>; warn: ReturnType<typeof vi.fn> };

  beforeEach(() => {
    mockCommandBus = { execute: vi.fn() };
    mockPrisma = {
      user: {
        findUnique: vi.fn(),
      },
    };
    mockLogger = { log: vi.fn(), warn: vi.fn() };

    listener = new QuotaExceededListener(
      mockCommandBus as unknown as CommandBus,
      mockPrisma as unknown as PrismaService,
      mockLogger as unknown as LoggerService,
    );
  });

  it('sends email notification when user has email', async () => {
    mockPrisma.user.findUnique.mockResolvedValue({
      id: 'user-1',
      email: 'user@example.com',
    });
    mockCommandBus.execute.mockResolvedValue({});

    const event = new QuotaExceededEvent('user-1', 'listings_created', 3, 3);
    await listener.handle(event);

    expect(mockCommandBus.execute).toHaveBeenCalledWith(
      new SendNotificationCommand(
        'user-1',
        'EMAIL',
        'quota.exceeded',
        { metric: 'listings_created', limit: 3, used: 3 },
        'user@example.com',
      ),
    );
  });

  it('skips notification when user has no email', async () => {
    mockPrisma.user.findUnique.mockResolvedValue({
      id: 'user-1',
      email: null,
    });

    const event = new QuotaExceededEvent('user-1', 'listings_created', 3, 3);
    await listener.handle(event);

    expect(mockCommandBus.execute).not.toHaveBeenCalled();
  });

  it('skips notification when user not found', async () => {
    mockPrisma.user.findUnique.mockResolvedValue(null);

    const event = new QuotaExceededEvent('user-99', 'analytics_queries', 0, 0);
    await listener.handle(event);

    expect(mockCommandBus.execute).not.toHaveBeenCalled();
  });

  it('handles analytics_queries metric', async () => {
    mockPrisma.user.findUnique.mockResolvedValue({
      id: 'user-2',
      email: 'investor@example.com',
    });
    mockCommandBus.execute.mockResolvedValue({});

    const event = new QuotaExceededEvent('user-2', 'analytics_queries', 100, 100);
    await listener.handle(event);

    expect(mockCommandBus.execute).toHaveBeenCalledWith(
      expect.objectContaining({
        templateKey: 'quota.exceeded',
        templateData: { metric: 'analytics_queries', limit: 100, used: 100 },
      }),
    );
  });
});
