import { Injectable } from '@nestjs/common';
import { type PropertyType } from '@prisma/client';
import { PrismaService, LoggerService } from '@modules/shared';
import {
  type IPriceValidator,
  type PriceValidationParams,
  type PriceValidationResult,
} from '../../domain/services/price-validator';

/**
 * Default price ranges per m2 (VND) by property type when no market data exists.
 * These are conservative ranges for the Vietnamese real estate market.
 */
const DEFAULT_RANGES: Record<PropertyType, { min: number; max: number }> = {
  APARTMENT: { min: 15_000_000, max: 200_000_000 },
  TOWNHOUSE: { min: 20_000_000, max: 500_000_000 },
  VILLA: { min: 50_000_000, max: 1_000_000_000 },
  LAND: { min: 5_000_000, max: 800_000_000 },
  OFFICE: { min: 10_000_000, max: 300_000_000 },
  SHOPHOUSE: { min: 30_000_000, max: 600_000_000 },
  // Phòng trọ: priced per month (1M–10M VND), stored as total price (not per-m²).
  // Range reflects typical HCMC room rental market 2024-2026.
  ROOM_RENTAL: { min: 1_000_000, max: 10_000_000 },
  // Condotel: mixed-use hotel/condo; higher-end per-m² due to resort factor.
  CONDOTEL: { min: 20_000_000, max: 300_000_000 },
  // Serviced apartment: furnished with hotel-style services; premium over standard apartments.
  SERVICED_APARTMENT: { min: 20_000_000, max: 250_000_000 },
};

/** Multiplier to widen default ranges for suspicious-but-not-invalid detection */
const SUSPICIOUS_MULTIPLIER = 0.5;

@Injectable()
export class PrismaPriceValidator implements IPriceValidator {
  constructor(
    private readonly prisma: PrismaService,
    private readonly logger: LoggerService,
  ) {}

  async validate(params: PriceValidationParams): Promise<PriceValidationResult> {
    const { priceVND, areaM2, propertyType, district } = params;
    const pricePerM2 = areaM2 > 0 ? Number(priceVND) / areaM2 : 0;

    // Try to get market data from recent listings in the same district + property type
    const marketRange = await this.getMarketRange(propertyType, district);

    const minPricePerM2 = marketRange?.min ?? DEFAULT_RANGES[propertyType].min;
    const maxPricePerM2 = marketRange?.max ?? DEFAULT_RANGES[propertyType].max;

    // Price is invalid if it's zero or negative area
    if (pricePerM2 <= 0) {
      return {
        isValid: false,
        isSuspicious: true,
        reason: 'Giá hoặc diện tích không hợp lệ',
        pricePerM2,
        minPricePerM2,
        maxPricePerM2,
      };
    }

    // Too low — likely a data entry error
    const suspiciousLow = minPricePerM2 * SUSPICIOUS_MULTIPLIER;
    if (pricePerM2 < suspiciousLow) {
      return {
        isValid: true,
        isSuspicious: true,
        reason: `Giá ${Math.round(pricePerM2).toLocaleString()} VND/m² thấp hơn nhiều so với thị trường (${Math.round(minPricePerM2).toLocaleString()} - ${Math.round(maxPricePerM2).toLocaleString()} VND/m²)`,
        pricePerM2,
        minPricePerM2,
        maxPricePerM2,
      };
    }

    // Too high — likely a data entry error
    const suspiciousHigh = maxPricePerM2 * (1 + (1 - SUSPICIOUS_MULTIPLIER));
    if (pricePerM2 > suspiciousHigh) {
      return {
        isValid: true,
        isSuspicious: true,
        reason: `Giá ${Math.round(pricePerM2).toLocaleString()} VND/m² cao hơn nhiều so với thị trường (${Math.round(minPricePerM2).toLocaleString()} - ${Math.round(maxPricePerM2).toLocaleString()} VND/m²)`,
        pricePerM2,
        minPricePerM2,
        maxPricePerM2,
      };
    }

    return {
      isValid: true,
      isSuspicious: false,
      pricePerM2,
      minPricePerM2,
      maxPricePerM2,
    };
  }

  /**
   * Get min/max price per m2 from active listings in the same district + property type.
   * Uses P10/P90 percentiles to exclude outliers.
   */
  private async getMarketRange(
    propertyType: PropertyType,
    district: string,
  ): Promise<{ min: number; max: number } | null> {
    try {
      const rows = await this.prisma.$queryRaw<{ min_price: number; max_price: number }[]>`
        SELECT
          PERCENTILE_CONT(0.10) WITHIN GROUP (ORDER BY l."priceVND"::float8 / p."areaM2") AS min_price,
          PERCENTILE_CONT(0.90) WITHIN GROUP (ORDER BY l."priceVND"::float8 / p."areaM2") AS max_price
        FROM "Listing" l
        JOIN "Property" p ON p.id = l."propertyId"
        WHERE p."propertyType" = ${propertyType}::"PropertyType"
          AND p.district = ${district}
          AND l.status = 'ACTIVE'
          AND p."areaM2" > 0
          AND l."createdAt" > NOW() - INTERVAL '6 months'
      `;

      if (rows.length > 0 && rows[0]!.min_price && rows[0]!.max_price) {
        return { min: rows[0]!.min_price, max: rows[0]!.max_price };
      }
      return null;
    } catch {
      this.logger.warn('Failed to fetch market range, using defaults', 'PrismaPriceValidator');
      return null;
    }
  }
}
