import { Inject, InternalServerErrorException } from '@nestjs/common';
import { CommandHandler, EventBus, type ICommandHandler } from '@nestjs/cqrs';
import {
  DomainException,
  ForbiddenException,
  type IPaymentInitiator,
  LoggerService,
  NotFoundException,
  PAYMENT_INITIATOR,
  ValidationException,
} from '@modules/shared';
import { FeaturedListingPaymentRequestedEvent } from '../../../domain/events/featured-listing-payment-requested.event';
import { LISTING_REPOSITORY, type IListingRepository } from '../../../domain/repositories/listing.repository';
import { type FeaturePackage, FeatureListingCommand } from './feature-listing.command';

const PACKAGE_PRICES: Record<FeaturePackage, bigint> = {
  '3_days': 99_000n,
  '7_days': 199_000n,
  '30_days': 499_000n,
};

export interface FeatureListingResult {
  paymentId: string;
  paymentUrl: string;
  providerTxId: string;
  package_: FeaturePackage;
  priceVND: string;
}

@CommandHandler(FeatureListingCommand)
export class FeatureListingHandler implements ICommandHandler<FeatureListingCommand> {
  constructor(
    @Inject(LISTING_REPOSITORY) private readonly listingRepo: IListingRepository,
    @Inject(PAYMENT_INITIATOR) private readonly paymentInitiator: IPaymentInitiator,
    private readonly eventBus: EventBus,
    private readonly logger: LoggerService,
  ) {}

  async execute(command: FeatureListingCommand): Promise<FeatureListingResult> {
    try {
      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 price = PACKAGE_PRICES[command.package_];
      if (!price) {
        throw new ValidationException('Gói không hợp lệ', { package: command.package_ });
      }

      // Emit domain event BEFORE payment initiation so audit/analytics listeners
      // see the request even if the downstream payment gateway fails.
      this.eventBus.publish(
        new FeaturedListingPaymentRequestedEvent(
          command.listingId,
          command.userId,
          command.package_,
          price,
          command.provider,
        ),
      );

      // Cross-module call goes through the shared `IPaymentInitiator` port
      // (payments registers the adapter). No direct dependency on payments
      // application-layer commands — see A-10.
      const paymentResult = await this.paymentInitiator.initiate({
        userId: command.userId,
        provider: command.provider,
        type: 'FEATURED_LISTING',
        amountVND: price,
        description: `Đẩy tin nổi bật ${command.package_.replace('_', ' ')} - Listing ${command.listingId}`,
        returnUrl: command.returnUrl,
        ipAddress: command.ipAddress,
        transactionId: command.listingId,
        idempotencyKey: `feature_${command.listingId}_${Date.now()}`,
      });

      this.logger.log(
        `Featured listing payment created: listing=${command.listingId}, package=${command.package_}, payment=${paymentResult.paymentId}`,
        'FeatureListingHandler',
      );

      return {
        ...paymentResult,
        package_: command.package_,
        priceVND: price.toString(),
      };
    } catch (error) {
      if (error instanceof DomainException) throw error;
      this.logger.error(
        `Failed to create featured listing payment: ${error instanceof Error ? error.message : error}`,
        error instanceof Error ? error.stack : undefined,
        this.constructor.name,
      );
      throw new InternalServerErrorException('Không thể tạo thanh toán đẩy tin nổi bật');
    }
  }
}
