import { Inject, InternalServerErrorException } from '@nestjs/common';
import { QueryHandler, type IQueryHandler } from '@nestjs/cqrs';
import { DomainException, CacheService, CachePrefix, CacheTTL, LoggerService } from '@modules/shared';
import {
  MARKET_INDEX_REPOSITORY,
  type IMarketIndexRepository,
  type HeatmapDataPoint,
  type WardHeatmapDataPoint,
} from '../../../domain/repositories/market-index.repository';
import { GetHeatmapQuery } from './get-heatmap.query';

export interface HeatmapDto {
  city: string;
  period: string;
  level: 'district' | 'ward';
  dataPoints: HeatmapDataPoint[] | WardHeatmapDataPoint[];
}

@QueryHandler(GetHeatmapQuery)
export class GetHeatmapHandler implements IQueryHandler<GetHeatmapQuery> {
  constructor(
    @Inject(MARKET_INDEX_REPOSITORY) private readonly marketIndexRepo: IMarketIndexRepository,
    private readonly cache: CacheService,
    private readonly logger: LoggerService,
  ) {}

  async execute(query: GetHeatmapQuery): Promise<HeatmapDto> {
    try {
      const cacheKey = CacheService.buildKey(
        CachePrefix.MARKET_HEATMAP,
        query.city,
        query.period,
        query.level,
        query.district ?? 'all',
      );

      const ttl = query.level === 'ward' ? CacheTTL.HEATMAP_WARD : CacheTTL.HEATMAP;

      return this.cache.getOrSet(
        cacheKey,
        async () => {
          if (query.level === 'ward') {
            const dataPoints = await this.marketIndexRepo.getHeatmapWard(
              query.city,
              query.period,
              query.district,
            );
            return { city: query.city, period: query.period, level: 'ward' as const, dataPoints };
          }
          const dataPoints = await this.marketIndexRepo.getHeatmap(query.city, query.period);
          return { city: query.city, period: query.period, level: 'district' as const, dataPoints };
        },
        ttl,
        'heatmap',
      );
    } catch (error) {
      if (error instanceof DomainException) throw error;
      this.logger.error(
        `Failed to truy vấn dữ liệu bản đồ nhiệt: ${error instanceof Error ? error.message : error}`,
        error instanceof Error ? error.stack : undefined,
        this.constructor.name,
      );
      throw new InternalServerErrorException('Không thể truy vấn dữ liệu bản đồ nhiệt. Vui lòng thử lại sau.');
    }
  }
}
