import { Inject, InternalServerErrorException } from '@nestjs/common';
import { QueryHandler, type IQueryHandler } from '@nestjs/cqrs';
import { DomainException, LoggerService, NotFoundException } from '@modules/shared';
import { PROPERTY_REPOSITORY, type IPropertyRepository } from '../../../domain/repositories/property.repository';
import { DUPLICATE_DETECTOR, type DuplicateCandidate, type IDuplicateDetector } from '../../../domain/services/duplicate-detector';
import { GetPropertyDuplicatesQuery } from './get-property-duplicates.query';

export interface PropertyDuplicateItem {
  listingId: string;
  propertyId: string;
  title: string;
  address: string;
  district: string;
  distanceMeters: number;
  addressMatch: boolean;
  sameAgent: boolean;
}

export interface GetPropertyDuplicatesResult {
  propertyId: string;
  radiusMeters: number;
  candidates: PropertyDuplicateItem[];
}

const DEFAULT_RADIUS_METERS = 50;

@QueryHandler(GetPropertyDuplicatesQuery)
export class GetPropertyDuplicatesHandler implements IQueryHandler<GetPropertyDuplicatesQuery> {
  constructor(
    @Inject(PROPERTY_REPOSITORY) private readonly propertyRepo: IPropertyRepository,
    @Inject(DUPLICATE_DETECTOR) private readonly duplicateDetector: IDuplicateDetector,
    private readonly logger: LoggerService,
  ) {}

  async execute(query: GetPropertyDuplicatesQuery): Promise<GetPropertyDuplicatesResult> {
    try {
      const property = await this.propertyRepo.findById(query.propertyId);
      if (!property) throw new NotFoundException('Property', query.propertyId);

      const radiusMeters = query.radiusMeters ?? DEFAULT_RADIUS_METERS;

      // Admin lookup is not scoped to a single agent — we surface every
      // nearby candidate (including from other agents) so moderators can
      // judge cross-agent collisions. `sameAgent` is therefore always
      // computed against `undefined` and will be `false`.
      const candidates: DuplicateCandidate[] = await this.duplicateDetector.findDuplicates({
        excludePropertyId: property.id,
        latitude: property.location.latitude,
        longitude: property.location.longitude,
        addressNormalized: property.address.normalized,
        propertyType: property.propertyType,
        radiusMeters,
      });

      return {
        propertyId: property.id,
        radiusMeters,
        candidates: candidates.map((c) => ({
          listingId: c.listingId,
          propertyId: c.propertyId,
          title: c.title,
          address: c.address,
          district: c.district,
          distanceMeters: c.distanceMeters,
          addressMatch: c.addressMatch,
          sameAgent: c.sameAgent,
        })),
      };
    } catch (error) {
      if (error instanceof DomainException) throw error;
      this.logger.error(
        `Failed to find duplicates for property ${query.propertyId}: ${error instanceof Error ? error.message : error}`,
        error instanceof Error ? error.stack : undefined,
        this.constructor.name,
      );
      throw new InternalServerErrorException('Không thể tìm tin đăng trùng lặp');
    }
  }
}
