import { CachePrefix, CacheService } from '@modules/shared/infrastructure/cache.service';
import { ListingApprovedEventHandler } from '../event-handlers/listing-approved.handler';

describe('ListingApprovedEventHandler', () => {
  let handler: ListingApprovedEventHandler;
  let mockIndexer: { indexListing: ReturnType<typeof vi.fn>; removeListing: ReturnType<typeof vi.fn> };
  let mockCache: {
    invalidate: ReturnType<typeof vi.fn>;
    invalidateByPrefix: ReturnType<typeof vi.fn>;
  };
  let mockLogger: { log: ReturnType<typeof vi.fn>; warn: ReturnType<typeof vi.fn>; error: ReturnType<typeof vi.fn> };

  beforeEach(() => {
    mockIndexer = {
      indexListing: vi.fn().mockResolvedValue(undefined),
      removeListing: vi.fn().mockResolvedValue(undefined),
    };
    mockCache = {
      invalidate: vi.fn().mockResolvedValue(undefined),
      invalidateByPrefix: vi.fn().mockResolvedValue(undefined),
    };
    mockLogger = {
      log: vi.fn(),
      warn: vi.fn(),
      error: vi.fn(),
    };
    handler = new ListingApprovedEventHandler(mockIndexer as any, mockCache as any, mockLogger as any);
  });

  it('indexes listing and invalidates cache on listing.approved event', async () => {
    await handler.handle({ listingId: 'listing-1' });

    expect(mockIndexer.indexListing).toHaveBeenCalledWith('listing-1');
    expect(mockCache.invalidate).toHaveBeenCalledWith(
      CacheService.buildKey(CachePrefix.LISTING, 'listing-1'),
    );
    expect(mockCache.invalidateByPrefix).toHaveBeenCalledWith(CachePrefix.SEARCH);
    expect(mockCache.invalidateByPrefix).toHaveBeenCalledWith(CachePrefix.GEO_SEARCH);
    expect(mockCache.invalidateByPrefix).toHaveBeenCalledWith(CachePrefix.MARKET_DISTRICT);
    expect(mockLogger.log).toHaveBeenCalled();
  });

  it('indexes listing and invalidates cache on listing.updated event', async () => {
    await handler.handleUpdate({ listingId: 'listing-2' });

    expect(mockIndexer.indexListing).toHaveBeenCalledWith('listing-2');
    expect(mockCache.invalidateByPrefix).toHaveBeenCalledWith(CachePrefix.SEARCH);
  });

  it('removes listing and invalidates cache on listing.deactivated event', async () => {
    await handler.handleDeactivation({ listingId: 'listing-3' });

    expect(mockIndexer.removeListing).toHaveBeenCalledWith('listing-3');
    expect(mockCache.invalidateByPrefix).toHaveBeenCalledWith(CachePrefix.SEARCH);
  });

  it('removes listing and invalidates cache on listing.sold event', async () => {
    await handler.handleSold({ listingId: 'listing-4' });

    expect(mockIndexer.removeListing).toHaveBeenCalledWith('listing-4');
    expect(mockCache.invalidate).toHaveBeenCalledWith(
      CacheService.buildKey(CachePrefix.LISTING, 'listing-4'),
    );
    expect(mockCache.invalidateByPrefix).toHaveBeenCalledWith(CachePrefix.SEARCH);
    expect(mockCache.invalidateByPrefix).toHaveBeenCalledWith(CachePrefix.GEO_SEARCH);
    expect(mockCache.invalidateByPrefix).toHaveBeenCalledWith(CachePrefix.MARKET_DISTRICT);
    expect(mockCache.invalidateByPrefix).toHaveBeenCalledWith(CachePrefix.MARKET_REPORT);
    expect(mockCache.invalidateByPrefix).toHaveBeenCalledWith(CachePrefix.MARKET_HEATMAP);
    expect(mockCache.invalidateByPrefix).toHaveBeenCalledWith(CachePrefix.MARKET_TREND);
  });

  it('invalidates all analytics cache prefixes on listing events', async () => {
    await handler.handle({ listingId: 'listing-5' });

    const prefixCalls = mockCache.invalidateByPrefix.mock.calls.map(
      (c: [string]) => c[0],
    );
    expect(prefixCalls).toContain(CachePrefix.MARKET_DISTRICT);
    expect(prefixCalls).toContain(CachePrefix.MARKET_REPORT);
    expect(prefixCalls).toContain(CachePrefix.MARKET_HEATMAP);
    expect(prefixCalls).toContain(CachePrefix.MARKET_TREND);
  });
});
