import type { SendNotificationCommand } from '../commands/send-notification/send-notification.command';
import { type InquiryReceivedEvent, InquiryReceivedListener } from '../listeners/inquiry-received.listener';

describe('InquiryReceivedListener', () => {
  let listener: InquiryReceivedListener;
  let mockCommandBus: { execute: ReturnType<typeof vi.fn> };
  let mockPrisma: {
    listing: { findUnique: ReturnType<typeof vi.fn> };
    user: { findUnique: ReturnType<typeof vi.fn> };
    notificationPreference: { findFirst: ReturnType<typeof vi.fn> };
  };
  let mockLogger: { log: ReturnType<typeof vi.fn>; warn: ReturnType<typeof vi.fn>; error: ReturnType<typeof vi.fn> };

  const makeEvent = (overrides?: Partial<InquiryReceivedEvent>): InquiryReceivedEvent => ({
    eventName: 'inquiry.received',
    aggregateId: 'inq-1',
    listingId: 'listing-1',
    senderId: 'sender-1',
    message: 'Tôi muốn xem nhà',
    ...overrides,
  });

  beforeEach(() => {
    mockCommandBus = { execute: vi.fn().mockResolvedValue(undefined) };
    mockPrisma = {
      listing: { findUnique: vi.fn() },
      user: { findUnique: vi.fn() },
      notificationPreference: { findFirst: vi.fn() },
    };
    mockLogger = { log: vi.fn(), warn: vi.fn(), error: vi.fn() };

    listener = new InquiryReceivedListener(
      mockCommandBus as any,
      mockPrisma as any,
      mockLogger as any,
    );
  });

  it('sends email to agent when inquiry is received', async () => {
    mockPrisma.listing.findUnique.mockResolvedValue({
      property: { title: 'Căn hộ 3PN Thủ Đức' },
      seller: { id: 'seller-1', email: 'seller@test.com' },
      agent: { user: { id: 'agent-user-1', email: 'agent@test.com' } },
    });
    mockPrisma.user.findUnique.mockResolvedValue({ fullName: 'Nguyễn Văn A', phone: '0901234567' });
    mockPrisma.notificationPreference.findFirst.mockResolvedValue(null);

    await listener.handle(makeEvent());

    // Should send email to agent
    const emailCmd = mockCommandBus.execute.mock.calls[0]![0] as SendNotificationCommand;
    expect(emailCmd.userId).toBe('agent-user-1');
    expect(emailCmd.channel).toBe('EMAIL');
    expect(emailCmd.templateKey).toBe('inquiry.received');
    expect(emailCmd.templateData.senderName).toBe('Nguyễn Văn A');

    // Should also send email to seller (different from agent)
    const sellerCmd = mockCommandBus.execute.mock.calls[1]![0] as SendNotificationCommand;
    expect(sellerCmd.userId).toBe('seller-1');
    expect(sellerCmd.channel).toBe('EMAIL');
  });

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

    await listener.handle(makeEvent());

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

  it('uses fallback sender name when user has no fullName', async () => {
    mockPrisma.listing.findUnique.mockResolvedValue({
      property: { title: 'Nhà phố' },
      seller: { id: 'seller-1', email: 'seller@test.com' },
      agent: null,
    });
    mockPrisma.user.findUnique.mockResolvedValue({ fullName: null, phone: '0901234567' });

    await listener.handle(makeEvent());

    const cmd = mockCommandBus.execute.mock.calls[0]![0] as SendNotificationCommand;
    expect(cmd.templateData.senderName).toBe('0901234567');
  });
});
