import { InternalServerErrorException } from '@nestjs/common';
import { type CacheService } from '@modules/shared/infrastructure/cache.service';
import { DomainException } from '@modules/shared';
import { ValuationEntity } from '../../domain/entities/valuation.entity';
import { type IValuationRepository } from '../../domain/repositories/valuation.repository';
import { ValuationHistoryHandler } from '../queries/valuation-history/valuation-history.handler';
import { ValuationHistoryQuery } from '../queries/valuation-history/valuation-history.query';

describe('ValuationHistoryHandler', () => {
  let handler: ValuationHistoryHandler;
  let mockRepo: { [K in keyof IValuationRepository]: ReturnType<typeof vi.fn> };
  let mockLogger: { error: ReturnType<typeof vi.fn> };

  const createValuationEntity = (
    id: string,
    propertyId: string,
    estimatedPrice: bigint,
    confidence: number,
    pricePerM2: number,
    modelVersion: string,
    createdAt: Date,
  ): ValuationEntity =>
    new ValuationEntity(
      id,
      { propertyId, estimatedPrice, confidence, pricePerM2, comparables: [], features: {}, modelVersion },
      createdAt,
      createdAt,
    );

  const sampleEntities = [
    createValuationEntity('v1', 'prop-1', 5000000000n, 0.85, 75000000, 'avm-v1.0', new Date('2026-04-01')),
    createValuationEntity('v2', 'prop-1', 5200000000n, 0.88, 78000000, 'avm-v1.1', new Date('2026-03-01')),
    createValuationEntity('v3', 'prop-1', 4800000000n, 0.82, 72000000, 'avm-v1.0', new Date('2026-02-01')),
  ];

  beforeEach(() => {
    mockRepo = {
      findById: vi.fn(),
      findByPropertyId: vi.fn(),
      findLatestByPropertyId: vi.fn(),
      save: vi.fn(),
    };
    mockLogger = { error: vi.fn() };
    const mockCache = {
      getOrSet: vi.fn((_key: string, loader: () => Promise<unknown>) => loader()),
    } as unknown as CacheService;
    handler = new ValuationHistoryHandler(
      mockRepo as any,
      mockCache,
      mockLogger as any,
    );
  });

  it('returns valuation history for a property', async () => {
    mockRepo.findByPropertyId.mockResolvedValue(sampleEntities);

    const query = new ValuationHistoryQuery('prop-1');
    const result = await handler.execute(query);

    expect(result.propertyId).toBe('prop-1');
    expect(result.history).toHaveLength(3);
    expect(result.totalRecords).toBe(3);
    expect(result.history[0]!.estimatedPrice).toBe('5000000000');
    expect(result.history[0]!.confidence).toBe(0.85);
    expect(result.history[0]!.modelVersion).toBe('avm-v1.0');
  });

  it('respects the limit parameter', async () => {
    mockRepo.findByPropertyId.mockResolvedValue(sampleEntities);

    const query = new ValuationHistoryQuery('prop-1', 2);
    const result = await handler.execute(query);

    expect(result.history).toHaveLength(2);
    expect(result.totalRecords).toBe(3);
  });

  it('returns empty history when no valuations exist', async () => {
    mockRepo.findByPropertyId.mockResolvedValue([]);

    const query = new ValuationHistoryQuery('prop-none');
    const result = await handler.execute(query);

    expect(result.propertyId).toBe('prop-none');
    expect(result.history).toHaveLength(0);
    expect(result.totalRecords).toBe(0);
  });

  it('re-throws DomainException directly', async () => {
    const domainError = new DomainException('NOT_FOUND', 'Property not found');
    mockRepo.findByPropertyId.mockRejectedValue(domainError);

    const query = new ValuationHistoryQuery('prop-bad');

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

  it('wraps unexpected errors in InternalServerErrorException', async () => {
    mockRepo.findByPropertyId.mockRejectedValue(new Error('DB connection lost'));

    const query = new ValuationHistoryQuery('prop-1');

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

  it('uses default limit of 50', () => {
    const query = new ValuationHistoryQuery('prop-1');
    expect(query.limit).toBe(50);
  });
});
