import { Inject, InternalServerErrorException } from '@nestjs/common';
import { CommandHandler, type ICommandHandler } from '@nestjs/cqrs';
import { DomainException, NotFoundException, ValidationException, CacheService, CachePrefix, PrismaService, LoggerService } from '@modules/shared';
import {
  SUBSCRIPTION_REPOSITORY,
  type ISubscriptionRepository,
} from '../../../domain/repositories/subscription.repository';
import { MeterUsageCommand } from './meter-usage.command';

export interface MeterUsageResult {
  usageRecordId: string;
  metric: string;
  count: number;
  periodStart: Date;
  periodEnd: Date;
}

@CommandHandler(MeterUsageCommand)
export class MeterUsageHandler implements ICommandHandler<MeterUsageCommand> {
  constructor(
    @Inject(SUBSCRIPTION_REPOSITORY)
    private readonly subscriptionRepo: ISubscriptionRepository,
    private readonly prisma: PrismaService,
    private readonly cache: CacheService,
    private readonly logger: LoggerService,
  ) {}

  async execute(command: MeterUsageCommand): Promise<MeterUsageResult> {
    try {
      if (command.count <= 0) {
        throw new ValidationException('Số lượng phải lớn hơn 0');
      }

      const subscription = await this.subscriptionRepo.findByUserId(command.userId);
      if (!subscription) {
        throw new NotFoundException('Subscription', command.userId);
      }

      if (!subscription.isActive()) {
        throw new ValidationException('Subscription không ở trạng thái hoạt động');
      }

      // Atomic upsert using the @@unique constraint to prevent race conditions
      const usageRecord = await this.prisma.usageRecord.upsert({
        where: {
          subscriptionId_metric_periodStart_periodEnd: {
            subscriptionId: subscription.id,
            metric: command.metric,
            periodStart: subscription.currentPeriodStart,
            periodEnd: subscription.currentPeriodEnd,
          },
        },
        update: {
          count: { increment: command.count },
        },
        create: {
          subscriptionId: subscription.id,
          metric: command.metric,
          count: command.count,
          periodStart: subscription.currentPeriodStart,
          periodEnd: subscription.currentPeriodEnd,
        },
      });

      // Invalidate cached quota for this user + metric
      await this.cache.invalidate(
        CacheService.buildKey(CachePrefix.USER_QUOTA, command.userId, command.metric),
      );

      this.logger.log(
        `Usage metered: subscription=${subscription.id}, metric=${command.metric}, count=${command.count}`,
        'MeterUsageHandler',
      );

      return {
        usageRecordId: usageRecord.id,
        metric: usageRecord.metric,
        count: usageRecord.count,
        periodStart: usageRecord.periodStart,
        periodEnd: usageRecord.periodEnd,
      };
    } catch (error) {
      if (error instanceof DomainException) throw error;
      this.logger.error(
        `Failed to ghi nhận mức sử dụng: ${error instanceof Error ? error.message : error}`,
        error instanceof Error ? error.stack : undefined,
        this.constructor.name,
      );
      throw new InternalServerErrorException('Không thể ghi nhận mức sử dụng. Vui lòng thử lại sau.');
    }
  }
}
