import { Inject, InternalServerErrorException } from '@nestjs/common';
import { CommandHandler, type ICommandHandler } from '@nestjs/cqrs';
import { DomainException, NotFoundException, ValidationException, LoggerService } from '@modules/shared';
import {
  PAYMENT_REPOSITORY,
  type IPaymentRepository,
} from '../../../domain/repositories/payment.repository';
import {
  PAYMENT_GATEWAY_FACTORY,
  type IPaymentGatewayFactory,
} from '../../../infrastructure/services/payment-gateway.interface';
import { RefundPaymentCommand } from './refund-payment.command';

export interface RefundPaymentResult {
  paymentId: string;
  refundTxId: string | null;
  success: boolean;
}

@CommandHandler(RefundPaymentCommand)
export class RefundPaymentHandler implements ICommandHandler<RefundPaymentCommand> {
  constructor(
    @Inject(PAYMENT_REPOSITORY)
    private readonly paymentRepo: IPaymentRepository,
    @Inject(PAYMENT_GATEWAY_FACTORY)
    private readonly gatewayFactory: IPaymentGatewayFactory,
    private readonly logger: LoggerService,
  ) {}

  async execute(command: RefundPaymentCommand): Promise<RefundPaymentResult> {
    try {
      const payment = await this.paymentRepo.findById(command.paymentId);
      if (!payment) {
        throw new NotFoundException('Payment', command.paymentId);
      }

      if (payment.status !== 'COMPLETED') {
        throw new ValidationException('Chỉ có thể hoàn tiền cho thanh toán đã hoàn tất');
      }

      if (!payment.providerTxId) {
        throw new ValidationException('Không có mã giao dịch từ nhà cung cấp');
      }

      const gateway = this.gatewayFactory.getGateway(payment.provider);
      const result = await gateway.refund({
        providerTxId: payment.providerTxId,
        amountVND: payment.amount.value,
        reason: command.reason,
      });

      if (result.success) {
        const refundResult = payment.markRefunded();
        if (refundResult.isErr) {
          throw refundResult.unwrapErr();
        }
        await this.paymentRepo.update(payment);
      }

      this.logger.log(
        `Refund ${result.success ? 'successful' : 'failed'} for payment ${command.paymentId}`,
        'RefundPaymentHandler',
      );

      return {
        paymentId: command.paymentId,
        refundTxId: result.refundTxId,
        success: result.success,
      };
    } catch (error) {
      if (error instanceof DomainException) throw error;
      this.logger.error(
        `Failed to refund payment: ${error instanceof Error ? error.message : error}`,
        error instanceof Error ? error.stack : undefined,
        this.constructor.name,
      );
      throw new InternalServerErrorException('Không thể hoàn tiền thanh toán. Vui lòng thử lại sau');
    }
  }
}
