import { describe, it, expect, vi, beforeEach } from 'vitest';
import { NotFoundException } from '@modules/shared';
import { type IPropertyRepository } from '../../domain/repositories/property.repository';
import { type IDuplicateDetector } from '../../domain/services/duplicate-detector';
import { GetPropertyDuplicatesHandler } from '../queries/get-property-duplicates/get-property-duplicates.handler';
import { GetPropertyDuplicatesQuery } from '../queries/get-property-duplicates/get-property-duplicates.query';

describe('GetPropertyDuplicatesHandler', () => {
  let handler: GetPropertyDuplicatesHandler;
  let mockPropertyRepo: { [K in keyof IPropertyRepository]: ReturnType<typeof vi.fn> };
  let mockDetector: { [K in keyof IDuplicateDetector]: ReturnType<typeof vi.fn> };

  beforeEach(() => {
    mockPropertyRepo = {
      findById: vi.fn(),
      save: vi.fn(),
      update: vi.fn(),
      addMedia: vi.fn(),
      findMediaByPropertyId: vi.fn(),
      deleteMedia: vi.fn(),
      countMediaByPropertyId: vi.fn(),
    };
    mockDetector = { findDuplicates: vi.fn().mockResolvedValue([]) };

    const mockLogger = { log: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn(), verbose: vi.fn() };

    handler = new GetPropertyDuplicatesHandler(
      mockPropertyRepo as any,
      mockDetector as any,
      mockLogger as any,
    );
  });

  it('throws NotFoundException when property is missing', async () => {
    mockPropertyRepo.findById.mockResolvedValue(null);

    await expect(
      handler.execute(new GetPropertyDuplicatesQuery('nope')),
    ).rejects.toBeInstanceOf(NotFoundException);
  });

  it('invokes the detector with the property location/normalized address (no agent scope)', async () => {
    const fakeProperty = {
      id: 'prop-1',
      propertyType: 'APARTMENT',
      location: { latitude: 10.7769, longitude: 106.7009 },
      address: { normalized: '123 nguyen hue quan 1' },
    };
    mockPropertyRepo.findById.mockResolvedValue(fakeProperty);
    mockDetector.findDuplicates.mockResolvedValue([
      {
        listingId: 'listing-1',
        propertyId: 'prop-2',
        title: 'Dup',
        address: '123 Nguyễn Huệ',
        district: 'Quận 1',
        distanceMeters: 20,
        addressMatch: true,
        sameAgent: false,
        propertyType: 'APARTMENT',
      },
    ]);

    const result = await handler.execute(new GetPropertyDuplicatesQuery('prop-1', 75));

    expect(mockDetector.findDuplicates).toHaveBeenCalledWith(
      expect.objectContaining({
        excludePropertyId: 'prop-1',
        latitude: 10.7769,
        longitude: 106.7009,
        addressNormalized: '123 nguyen hue quan 1',
        propertyType: 'APARTMENT',
        radiusMeters: 75,
      }),
    );
    expect(result.radiusMeters).toBe(75);
    expect(result.candidates).toHaveLength(1);
    expect(result.candidates[0]!.addressMatch).toBe(true);
  });

  it('defaults to 50m radius when none provided', async () => {
    mockPropertyRepo.findById.mockResolvedValue({
      id: 'prop-1',
      propertyType: 'APARTMENT',
      location: { latitude: 10.77, longitude: 106.7 },
      address: { normalized: 'x' },
    });

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

    expect(result.radiusMeters).toBe(50);
    expect(mockDetector.findDuplicates).toHaveBeenCalledWith(
      expect.objectContaining({ radiusMeters: 50 }),
    );
  });
});
