import { type IListingRepository } from '@modules/listings/domain/repositories/listing.repository';
import { GetListingHandler } from '../queries/get-listing/get-listing.handler';
import { GetListingQuery } from '../queries/get-listing/get-listing.query';

describe('GetListingHandler', () => {
  let handler: GetListingHandler;
  let mockListingRepo: { [K in keyof IListingRepository]: ReturnType<typeof vi.fn> };
  let mockCache: { getOrSet: ReturnType<typeof vi.fn>; invalidate: ReturnType<typeof vi.fn>; invalidateByPrefix: ReturnType<typeof vi.fn> };

  const mockListingDetail = {
    id: 'listing-1',
    status: 'ACTIVE',
    price: 5_000_000_000n,
    property: { id: 'prop-1', title: 'Căn hộ Q1' },
  };

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

    mockCache = {
      getOrSet: vi.fn(),
      invalidate: vi.fn(),
      invalidateByPrefix: vi.fn(),
    };

    handler = new GetListingHandler(
      mockListingRepo as any,
      mockCache as any,
    );
  });

  it('returns listing detail via cache', async () => {
    mockCache.getOrSet.mockImplementation(async (_key: string, fn: () => Promise<unknown>) => fn());
    mockListingRepo.findByIdWithProperty.mockResolvedValue(mockListingDetail);

    const query = new GetListingQuery('listing-1');
    const result = await handler.execute(query);

    expect(result).toEqual(mockListingDetail);
    expect(mockCache.getOrSet).toHaveBeenCalled();
  });

  it('throws NotFoundException when listing not found', async () => {
    mockCache.getOrSet.mockImplementation(async (_key: string, fn: () => Promise<unknown>) => fn());
    mockListingRepo.findByIdWithProperty.mockResolvedValue(null);

    const query = new GetListingQuery('nonexistent');

    await expect(handler.execute(query)).rejects.toThrow('Listing');
  });

  it('uses cache key with listing id', async () => {
    mockCache.getOrSet.mockImplementation(async (_key: string, fn: () => Promise<unknown>) => fn());
    mockListingRepo.findByIdWithProperty.mockResolvedValue(mockListingDetail);

    await handler.execute(new GetListingQuery('listing-1'));

    expect(mockCache.getOrSet).toHaveBeenCalledWith(
      expect.stringContaining('listing-1'),
      expect.any(Function),
      expect.anything(),
      expect.anything(),
    );
  });
});
