import { type IListingRepository } from '@modules/listings/domain/repositories/listing.repository';
import { type CacheService } from '@modules/shared';
import { SearchListingsHandler } from '../queries/search-listings/search-listings.handler';
import { SearchListingsQuery } from '../queries/search-listings/search-listings.query';

describe('SearchListingsHandler', () => {
  let handler: SearchListingsHandler;
  let mockListingRepo: { [K in keyof IListingRepository]: ReturnType<typeof vi.fn> };
  let mockCacheService: { [K in keyof CacheService]: ReturnType<typeof vi.fn> };

  beforeEach(() => {
    mockListingRepo = {
      findById: vi.fn(),
      findByIdWithProperty: vi.fn(),
      save: vi.fn(),
      update: vi.fn(),
      search: vi.fn(),
      findByStatus: vi.fn(),
      findBySellerId: vi.fn(),
    };

    mockCacheService = {
      getOrSet: vi.fn((_key, loader) => loader()),
      invalidate: vi.fn(),
      invalidateByPrefix: vi.fn(),
      onModuleInit: vi.fn(),
    };

    handler = new SearchListingsHandler(mockListingRepo as any, mockCacheService as any);
  });

  it('searches with all filters', async () => {
    const mockResult = {
      data: [{ id: 'listing-1', status: 'ACTIVE' }],
      total: 1,
      page: 1,
      limit: 20,
      totalPages: 1,
    };
    mockListingRepo.search.mockResolvedValue(mockResult);

    const query = new SearchListingsQuery(
      'ACTIVE', 'SALE', 'APARTMENT', 'TP. Hồ Chí Minh', 'Quận 1',
      1_000_000_000n, 10_000_000_000n, 50, 200, 2,
    );
    const result = await handler.execute(query);

    expect(result).toEqual(mockResult);
    expect(mockListingRepo.search).toHaveBeenCalledWith({
      status: 'ACTIVE',
      transactionType: 'SALE',
      propertyType: 'APARTMENT',
      city: 'TP. Hồ Chí Minh',
      district: 'Quận 1',
      minPrice: 1_000_000_000n,
      maxPrice: 10_000_000_000n,
      minArea: 50,
      maxArea: 200,
      bedrooms: 2,
      page: 1,
      limit: 20,
    });
  });

  it('searches with no filters (defaults)', async () => {
    const mockResult = { data: [], total: 0, page: 1, limit: 20, totalPages: 0 };
    mockListingRepo.search.mockResolvedValue(mockResult);

    const query = new SearchListingsQuery();
    const result = await handler.execute(query);

    expect(result).toEqual(mockResult);
    expect(mockListingRepo.search).toHaveBeenCalledWith(
      expect.objectContaining({ page: 1, limit: 20 }),
    );
  });

  it('passes custom pagination', async () => {
    const mockResult = { data: [], total: 100, page: 5, limit: 10, totalPages: 10 };
    mockListingRepo.search.mockResolvedValue(mockResult);

    const query = new SearchListingsQuery(
      undefined, undefined, undefined, undefined, undefined,
      undefined, undefined, undefined, undefined, undefined,
      5, 10,
    );
    const result = await handler.execute(query);

    expect(result.page).toBe(5);
    expect(mockListingRepo.search).toHaveBeenCalledWith(
      expect.objectContaining({ page: 5, limit: 10 }),
    );
  });
});
