import { Inject, InternalServerErrorException } from '@nestjs/common';
import { CommandHandler, CommandBus, type ICommandHandler, QueryBus } from '@nestjs/cqrs';
import {
  DomainException,
  ForbiddenException,
  NotFoundException,
  ValidationException,
  LoggerService,
  PrismaService,
} from '@modules/shared';
import {
  CheckQuotaQuery,
  MeterUsageCommand,
  type QuotaCheckResult,
} from '@modules/subscriptions';
import { LISTING_REPOSITORY, type IListingRepository } from '../../../domain/repositories/listing.repository';
import {
  type PromoteFeaturedDuration,
  PROMOTE_FEATURED_DURATION_VALUES,
  PromoteFeaturedListingCommand,
} from './promote-featured-listing.command';

export const FEATURED_LISTINGS_PROMOTED_METRIC = 'featured_listings_promoted';

export interface PromoteFeaturedListingResult {
  listingId: string;
  featuredUntil: string;
  durationDays: PromoteFeaturedDuration;
  quotaRemaining: number | null;
}

@CommandHandler(PromoteFeaturedListingCommand)
export class PromoteFeaturedListingHandler
  implements ICommandHandler<PromoteFeaturedListingCommand>
{
  constructor(
    @Inject(LISTING_REPOSITORY) private readonly listingRepo: IListingRepository,
    private readonly prisma: PrismaService,
    private readonly commandBus: CommandBus,
    private readonly queryBus: QueryBus,
    private readonly logger: LoggerService,
  ) {}

  async execute(command: PromoteFeaturedListingCommand): Promise<PromoteFeaturedListingResult> {
    try {
      if (!PROMOTE_FEATURED_DURATION_VALUES.includes(command.durationDays)) {
        throw new ValidationException('Thời lượng không hợp lệ', {
          durationDays: command.durationDays,
          allowed: PROMOTE_FEATURED_DURATION_VALUES,
        });
      }

      const listing = await this.listingRepo.findById(command.listingId);
      if (!listing) {
        throw new NotFoundException('Listing', command.listingId);
      }

      if (listing.sellerId !== command.userId && listing.agentId !== command.userId) {
        throw new ForbiddenException('Chỉ người bán hoặc môi giới mới có thể đẩy tin nổi bật');
      }

      if (listing.status !== 'ACTIVE') {
        throw new ValidationException('Chỉ tin đăng đang hoạt động mới có thể đẩy nổi bật', {
          status: listing.status,
        });
      }

      const quota: QuotaCheckResult = await this.queryBus.execute(
        new CheckQuotaQuery(command.userId, FEATURED_LISTINGS_PROMOTED_METRIC),
      );

      if (!quota.allowed) {
        throw new ForbiddenException(
          `Đã dùng hết lượt đẩy tin nổi bật trong gói (${quota.used}/${quota.limit}). Vui lòng nâng cấp gói để tiếp tục.`,
        );
      }

      const now = new Date();
      const baseDate =
        listing.featuredUntil && listing.featuredUntil > now ? listing.featuredUntil : now;
      const featuredUntil = new Date(
        baseDate.getTime() + command.durationDays * 24 * 60 * 60 * 1000,
      );

      const durationToPackage: Record<number, string> = { 3: '3_days', 7: '7_days', 30: '30_days' };

      await this.prisma.listing.update({
        where: { id: command.listingId },
        data: {
          featuredUntil,
          featuredPackage: durationToPackage[command.durationDays] ?? `${command.durationDays}_days`,
        },
      });

      await this.commandBus.execute(
        new MeterUsageCommand(command.userId, FEATURED_LISTINGS_PROMOTED_METRIC, 1),
      );

      const newRemaining = quota.remaining === null ? null : Math.max(0, quota.remaining - 1);

      this.logger.log(
        `Featured listing promoted via entitlement: listing=${command.listingId}, user=${command.userId}, until=${featuredUntil.toISOString()}, days=${command.durationDays}`,
        'PromoteFeaturedListingHandler',
      );

      return {
        listingId: command.listingId,
        featuredUntil: featuredUntil.toISOString(),
        durationDays: command.durationDays,
        quotaRemaining: newRemaining,
      };
    } catch (error) {
      if (error instanceof DomainException) throw error;
      this.logger.error(
        `Failed to promote featured listing: ${error instanceof Error ? error.message : error}`,
        error instanceof Error ? error.stack : undefined,
        this.constructor.name,
      );
      throw new InternalServerErrorException('Không thể đẩy tin nổi bật');
    }
  }
}
