import { type CacheService } from '@modules/shared/infrastructure/cache.service';
import { type IAVMService, type ValuationResult } from '../../domain/services/avm-service';
import { GetValuationHandler } from '../queries/get-valuation/get-valuation.handler';
import { GetValuationQuery } from '../queries/get-valuation/get-valuation.query';

describe('GetValuationHandler', () => {
  let handler: GetValuationHandler;
  let mockAvm: { [K in keyof IAVMService]: ReturnType<typeof vi.fn> };

  const sampleResult: ValuationResult = {
    estimatedPrice: '5000000000',
    confidence: 0.85,
    pricePerM2: 75000000,
    comparables: [
      {
        propertyId: 'prop-1',
        address: '123 Nguyễn Huệ',
        district: 'Quận 1',
        priceVND: '4800000000',
        pricePerM2: 72000000,
        areaM2: 66.7,
        propertyType: 'APARTMENT',
        distanceMeters: 350,
        soldAt: '2026-03-01T00:00:00.000Z',
      },
    ],
    modelVersion: 'avm-v1.0',
  };

  beforeEach(() => {
    mockAvm = {
      estimateValue: vi.fn(),
      getComparables: vi.fn(),
    };
    const mockCache = {
      getOrSet: vi.fn((_key: string, loader: () => Promise<unknown>) => loader()),
    } as unknown as CacheService;
    handler = new GetValuationHandler(mockAvm as any, mockCache);
  });

  it('returns valuation by propertyId', async () => {
    mockAvm.estimateValue.mockResolvedValue(sampleResult);

    const query = new GetValuationQuery('prop-123');
    const result = await handler.execute(query);

    expect(result.estimatedPrice).toBe('5000000000');
    expect(result.confidence).toBe(0.85);
    expect(result.comparables).toHaveLength(1);
    expect(mockAvm.estimateValue).toHaveBeenCalledWith({
      propertyId: 'prop-123',
      latitude: undefined,
      longitude: undefined,
      areaM2: undefined,
      propertyType: undefined,
    });
  });

  it('returns valuation by coordinates', async () => {
    mockAvm.estimateValue.mockResolvedValue(sampleResult);

    const query = new GetValuationQuery(undefined, 10.762, 106.66, 80, 'APARTMENT');
    const result = await handler.execute(query);

    expect(result.estimatedPrice).toBe('5000000000');
    expect(mockAvm.estimateValue).toHaveBeenCalledWith({
      propertyId: undefined,
      latitude: 10.762,
      longitude: 106.66,
      areaM2: 80,
      propertyType: 'APARTMENT',
    });
  });

  it('returns zero confidence when insufficient comparables', async () => {
    const lowConfidence: ValuationResult = {
      estimatedPrice: '0',
      confidence: 0,
      pricePerM2: 0,
      comparables: [],
      modelVersion: 'avm-v1.0',
    };
    mockAvm.estimateValue.mockResolvedValue(lowConfidence);

    const query = new GetValuationQuery('prop-remote');
    const result = await handler.execute(query);

    expect(result.estimatedPrice).toBe('0');
    expect(result.confidence).toBe(0);
  });
});
