import { InternalServerErrorException } from '@nestjs/common';
import { type IMarketIndexRepository } from '../../../../domain/repositories/market-index.repository';
import { GetMarketHistoryHandler } from '../get-market-history.handler';
import { GetMarketHistoryQuery } from '../get-market-history.query';

describe('GetMarketHistoryHandler', () => {
  let handler: GetMarketHistoryHandler;
  let mockRepo: { getMarketHistory: ReturnType<typeof vi.fn> };
  let mockCache: { getOrSet: ReturnType<typeof vi.fn> };
  let mockLogger: { error: ReturnType<typeof vi.fn> };

  beforeEach(() => {
    mockRepo = { getMarketHistory: vi.fn() };
    mockCache = {
      getOrSet: vi.fn((_key: string, fn: () => Promise<unknown>) => fn()),
    };
    mockLogger = { error: vi.fn() };

    handler = new GetMarketHistoryHandler(
      mockRepo as unknown as IMarketIndexRepository,
      mockCache as any,
      mockLogger as any,
    );
  });

  it('should return market history points for 12m monthly', async () => {
    const points = [
      { date: '2025-05', avgPrice: 50000000, medianPrice: '45000000', listingsCount: 120, inquiriesCount: 0, daysOnMarket: 35 },
      { date: '2025-06', avgPrice: 51000000, medianPrice: '46000000', listingsCount: 130, inquiriesCount: 0, daysOnMarket: 33 },
    ];
    mockRepo.getMarketHistory.mockResolvedValue(points);

    const query = new GetMarketHistoryQuery('HCMC', '12m', 'monthly');
    const result = await handler.execute(query);

    expect(result.city).toBe('HCMC');
    expect(result.points).toEqual(points);
    expect(mockRepo.getMarketHistory).toHaveBeenCalledWith('HCMC', expect.any(Array));
    // Should generate 12 monthly periods
    const calledPeriods = mockRepo.getMarketHistory.mock.calls[0][1] as string[];
    expect(calledPeriods).toHaveLength(12);
  });

  it('should return market history for 6m period', async () => {
    mockRepo.getMarketHistory.mockResolvedValue([]);

    const query = new GetMarketHistoryQuery('HCMC', '6m', 'monthly');
    const result = await handler.execute(query);

    expect(result.city).toBe('HCMC');
    expect(result.points).toEqual([]);
    const calledPeriods = mockRepo.getMarketHistory.mock.calls[0][1] as string[];
    expect(calledPeriods).toHaveLength(6);
  });

  it('should use cache with 6h TTL', async () => {
    mockRepo.getMarketHistory.mockResolvedValue([]);
    const query = new GetMarketHistoryQuery('HCMC', '12m', 'monthly');

    await handler.execute(query);

    expect(mockCache.getOrSet).toHaveBeenCalledWith(
      expect.stringContaining('market_history'),
      expect.any(Function),
      21600,
      'market_history',
    );
  });

  it('should throw InternalServerErrorException on unexpected errors', async () => {
    mockRepo.getMarketHistory.mockRejectedValue(new Error('DB connection lost'));

    const query = new GetMarketHistoryQuery('HCMC', '12m', 'monthly');

    await expect(handler.execute(query)).rejects.toThrow(InternalServerErrorException);
    expect(mockLogger.error).toHaveBeenCalled();
  });
});
