import { Inject, InternalServerErrorException } from '@nestjs/common';
import { QueryHandler, IQueryHandler } from '@nestjs/cqrs';
import { DomainException, CacheService, CachePrefix, CacheTTL, LoggerService } from '@modules/shared';
import { ListingDetailData } from '../../../domain/repositories/listing-read.dto';
import { LISTING_REPOSITORY, IListingRepository } from '../../../domain/repositories/listing.repository';
import { GetListingQuery } from './get-listing.query';

/** @deprecated Use ListingDetailData from listing-read.dto instead */
export type ListingDetailDto = ListingDetailData;

@QueryHandler(GetListingQuery)
export class GetListingHandler implements IQueryHandler<GetListingQuery> {
  constructor(
    @Inject(LISTING_REPOSITORY) private readonly listingRepo: IListingRepository,
    private readonly cache: CacheService,
    private readonly logger: LoggerService,
  ) {}

  /**
   * Returns listing detail or null when not found.
   * The controller is responsible for mapping null to a 404 HttpException.
   */
  async execute(query: GetListingQuery): Promise<ListingDetailData | null> {
    try {
      const cacheKey = CacheService.buildKey(CachePrefix.LISTING, query.listingId);

      // Check cache first
      const cached = await this.cache.getOrSet<ListingDetailData | null>(
        cacheKey,
        async () => {
          const result = await this.listingRepo.findByIdWithProperty(query.listingId);
          if (!result) {
            // Signal to skip caching by throwing; we catch it below
            throw new ListingNotFoundSignal();
          }
          return result;
        },
        CacheTTL.LISTING_DETAIL,
        'listing',
      );

      return cached;
    } catch (error) {
      // Not-found: return null without caching so subsequent requests can find a newly-created listing
      if (error instanceof ListingNotFoundSignal) return null;
      if (error instanceof DomainException) throw error;
      this.logger.error(
        `Failed to get listing ${query.listingId}: ${error instanceof Error ? error.message : error}`,
        error instanceof Error ? error.stack : undefined,
        this.constructor.name,
      );
      throw new InternalServerErrorException('Không thể lấy thông tin tin đăng');
    }
  }
}

/** Internal signal to avoid caching not-found results. Not a domain exception. */
class ListingNotFoundSignal extends Error {
  constructor() {
    super('listing_not_found');
    this.name = 'ListingNotFoundSignal';
  }
}
