import { Inject, InternalServerErrorException } from '@nestjs/common';
import { QueryHandler, type IQueryHandler } from '@nestjs/cqrs';
import { CacheService, CachePrefix, CacheTTL, DomainException, LoggerService, PrismaService } from '@modules/shared';
import {
  AVM_SERVICE,
  type IAVMService,
  type ValuationComparisonItem,
  type ValuationResult,
} from '../../../domain/services/avm-service';
import { generateConfidenceExplanation } from '../../../infrastructure/services/confidence-explanation.helper';
import { ValuationComparisonQuery } from './valuation-comparison.query';

export interface ValuationComparisonDto {
  properties: ValuationComparisonItem[];
  summary: {
    highestValue: { propertyId: string; estimatedPrice: string } | null;
    lowestValue: { propertyId: string; estimatedPrice: string } | null;
    averagePricePerM2: number;
    averageConfidence: number;
  };
}

@QueryHandler(ValuationComparisonQuery)
export class ValuationComparisonHandler implements IQueryHandler<ValuationComparisonQuery> {
  constructor(
    @Inject(AVM_SERVICE) private readonly avmService: IAVMService,
    private readonly prisma: PrismaService,
    private readonly cache: CacheService,
    private readonly logger: LoggerService,
  ) {}

  async execute(query: ValuationComparisonQuery): Promise<ValuationComparisonDto> {
    try {
      const cacheKey = CacheService.buildKey(
        CachePrefix.VALUATION,
        'compare',
        ...query.propertyIds.slice().sort(),
      );

      return await this.cache.getOrSet(
        cacheKey,
        () => this.buildComparison(query.propertyIds),
        CacheTTL.MARKET_DATA,
        'valuation_comparison',
      );
    } catch (error) {
      if (error instanceof DomainException) throw error;
      this.logger.error(
        `Valuation comparison failed: ${error instanceof Error ? error.message : error}`,
        error instanceof Error ? error.stack : undefined,
        this.constructor.name,
      );
      throw new InternalServerErrorException('Không thể so sánh định giá. Vui lòng thử lại sau.');
    }
  }

  private async buildComparison(propertyIds: string[]): Promise<ValuationComparisonDto> {
    // Fetch property details and valuations in parallel
    const [properties, valuations] = await Promise.all([
      this.prisma.property.findMany({
        where: { id: { in: propertyIds } },
        select: {
          id: true,
          address: true,
          district: true,
          areaM2: true,
          propertyType: true,
        },
      }),
      this.fetchValuations(propertyIds),
    ]);

    const propertyMap = new Map(properties.map((p) => [p.id, p]));
    const valuationMap = new Map(valuations.map((v) => [v.propertyId, v.valuation]));

    const items: ValuationComparisonItem[] = propertyIds.map((propertyId) => {
      const prop = propertyMap.get(propertyId);
      const valuation = valuationMap.get(propertyId) ?? null;

      // Add confidence explanation if we have a valuation
      const enrichedValuation = valuation
        ? { ...valuation, confidenceExplanation: generateConfidenceExplanation(valuation.confidence, valuation.comparables.length) }
        : null;

      return {
        propertyId,
        address: prop?.address ?? '',
        district: prop?.district ?? '',
        areaM2: prop?.areaM2 ?? 0,
        propertyType: prop?.propertyType ?? 'APARTMENT',
        valuation: enrichedValuation,
      };
    });

    // Calculate summary
    const validValuations = items.filter((i) => i.valuation !== null);
    const prices = validValuations.map((i) => ({
      propertyId: i.propertyId,
      price: BigInt(i.valuation!.estimatedPrice),
      priceStr: i.valuation!.estimatedPrice,
    }));

    let highestValue: { propertyId: string; estimatedPrice: string } | null = null;
    let lowestValue: { propertyId: string; estimatedPrice: string } | null = null;

    if (prices.length > 0) {
      const sorted = prices.sort((a, b) => (a.price > b.price ? 1 : a.price < b.price ? -1 : 0));
      const highest = sorted[sorted.length - 1]!;
      const lowest = sorted[0]!;
      highestValue = { propertyId: highest.propertyId, estimatedPrice: highest.priceStr };
      lowestValue = { propertyId: lowest.propertyId, estimatedPrice: lowest.priceStr };
    }

    const averagePricePerM2 = validValuations.length > 0
      ? Math.round(validValuations.reduce((sum, i) => sum + i.valuation!.pricePerM2, 0) / validValuations.length)
      : 0;

    const averageConfidence = validValuations.length > 0
      ? Math.round(validValuations.reduce((sum, i) => sum + i.valuation!.confidence, 0) / validValuations.length * 100) / 100
      : 0;

    return {
      properties: items,
      summary: { highestValue, lowestValue, averagePricePerM2, averageConfidence },
    };
  }

  private async fetchValuations(propertyIds: string[]): Promise<{ propertyId: string; valuation: ValuationResult | null }[]> {
    const results = await Promise.allSettled(
      propertyIds.map(async (propertyId) => {
        const valuation = await this.avmService.estimateValue({ propertyId });
        return { propertyId, valuation };
      }),
    );

    return results.map((result, index) => {
      if (result.status === 'fulfilled') {
        return result.value;
      }
      return { propertyId: propertyIds[index]!, valuation: null };
    });
  }
}
