import { SavedSearchAlertHandler } from '../../infrastructure/event-handlers/saved-search-alert.handler';

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

  const mockListing = {
    id: 'listing-1',
    sellerId: 'seller-1',
    transactionType: 'SALE',
    priceVND: BigInt(3_000_000_000),
    property: {
      propertyType: 'APARTMENT',
      title: 'Chung cư cao cấp Quận 7',
      district: 'Quan 7',
      city: 'Ho Chi Minh',
      areaM2: 80,
      bedrooms: 2,
    },
  };

  const mockSavedSearch = {
    id: 'saved-1',
    userId: 'user-1',
    name: 'Chung cư Q7',
    filters: { district: 'Quan 7', propertyType: 'APARTMENT' },
    alertEnabled: true,
    lastAlertAt: null,
    user: { id: 'user-1', email: 'user@example.com', fullName: 'Nguyen Van A' },
  };

  beforeEach(() => {
    mockPrisma = {
      listing: {
        findUnique: vi.fn(),
      },
      savedSearch: {
        findMany: vi.fn(),
        update: vi.fn(),
      },
    };
    mockCommandBus = { execute: vi.fn().mockResolvedValue(undefined) };
    mockLogger = { log: vi.fn(), warn: vi.fn() };

    handler = new SavedSearchAlertHandler(mockPrisma, mockCommandBus as any, mockLogger as any);
  });

  it('sends alert when listing matches saved search filters', async () => {
    mockPrisma.listing.findUnique.mockResolvedValue(mockListing);
    mockPrisma.savedSearch.findMany.mockResolvedValue([mockSavedSearch]);
    mockPrisma.savedSearch.update.mockResolvedValue({});

    await handler.handle({ listingId: 'listing-1' });

    expect(mockCommandBus.execute).toHaveBeenCalledTimes(1);
    expect(mockPrisma.savedSearch.update).toHaveBeenCalledWith(
      expect.objectContaining({
        where: { id: 'saved-1' },
        data: { lastAlertAt: expect.any(Date) },
      }),
    );
  });

  it('does not send alert when listing does not match filters', async () => {
    mockPrisma.listing.findUnique.mockResolvedValue(mockListing);
    mockPrisma.savedSearch.findMany.mockResolvedValue([
      {
        ...mockSavedSearch,
        filters: { district: 'Quan 1', propertyType: 'HOUSE' },
      },
    ]);

    await handler.handle({ listingId: 'listing-1' });

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

  it('skips saved search belonging to listing seller', async () => {
    mockPrisma.listing.findUnique.mockResolvedValue(mockListing);
    mockPrisma.savedSearch.findMany.mockResolvedValue([
      { ...mockSavedSearch, userId: 'seller-1' },
    ]);

    await handler.handle({ listingId: 'listing-1' });

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

  it('handles listing not found gracefully', async () => {
    mockPrisma.listing.findUnique.mockResolvedValue(null);

    await handler.handle({ listingId: 'non-existent' });

    expect(mockPrisma.savedSearch.findMany).not.toHaveBeenCalled();
  });

  it('matches price range filters', async () => {
    mockPrisma.listing.findUnique.mockResolvedValue(mockListing);
    mockPrisma.savedSearch.findMany.mockResolvedValue([
      {
        ...mockSavedSearch,
        filters: { priceMin: '2000000000', priceMax: '5000000000' },
      },
    ]);
    mockPrisma.savedSearch.update.mockResolvedValue({});

    await handler.handle({ listingId: 'listing-1' });

    expect(mockCommandBus.execute).toHaveBeenCalledTimes(1);
  });

  it('does not match when price is outside range', async () => {
    mockPrisma.listing.findUnique.mockResolvedValue(mockListing);
    mockPrisma.savedSearch.findMany.mockResolvedValue([
      {
        ...mockSavedSearch,
        filters: { priceMax: '1000000000' },
      },
    ]);

    await handler.handle({ listingId: 'listing-1' });

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