import { Inject, InternalServerErrorException } from '@nestjs/common';
import { CommandHandler, EventBus, type ICommandHandler } from '@nestjs/cqrs';
import { type PaymentStatus } from '@prisma/client';
import {
  DomainException,
  NotFoundException,
  ValidationException,
  LoggerService,
} from '@modules/shared';
import {
  PAYMENT_REPOSITORY,
  type IPaymentRepository,
} from '../../../domain/repositories/payment.repository';
import { BankTransferService } from '../../../infrastructure/services/bank-transfer.service';
import { ConfirmBankTransferCommand } from './confirm-bank-transfer.command';

export interface ConfirmBankTransferResult {
  paymentId: string;
  status: string;
  confirmedBy: string;
}

@CommandHandler(ConfirmBankTransferCommand)
export class ConfirmBankTransferHandler
  implements ICommandHandler<ConfirmBankTransferCommand>
{
  constructor(
    @Inject(PAYMENT_REPOSITORY)
    private readonly paymentRepo: IPaymentRepository,
    private readonly bankTransferService: BankTransferService,
    private readonly eventBus: EventBus,
    private readonly logger: LoggerService,
  ) {}

  async execute(
    command: ConfirmBankTransferCommand,
  ): Promise<ConfirmBankTransferResult> {
    try {
      // Find the payment first
      const payment = await this.paymentRepo.findById(command.paymentId);
      if (!payment) {
        throw new NotFoundException('Payment', command.paymentId);
      }

      // Ensure it's a bank transfer payment
      if (payment.provider !== 'BANK_TRANSFER') {
        throw new ValidationException(
          `Chỉ có thể xác nhận thanh toán chuyển khoản ngân hàng, nhưng thanh toán này là ${payment.provider}`,
        );
      }

      // Generate signed callback data using the bank transfer service
      const providerTxId = payment.providerTxId ?? `BT-${payment.id}`;
      const signature = this.bankTransferService.generateConfirmationSignature(
        payment.id,
        providerTxId,
        'CONFIRMED',
        command.confirmedBy,
      );

      const callbackData: Record<string, string> = {
        orderId: payment.id,
        providerTxId,
        status: 'CONFIRMED',
        confirmedBy: command.confirmedBy,
        signature,
      };

      if (command.bankReference) {
        callbackData['bankReference'] = command.bankReference;
      }

      // Verify via gateway (ensures signing consistency)
      const verifyResult = this.bankTransferService.verifyCallback(callbackData);
      if (!verifyResult.isValid || !verifyResult.isSuccess) {
        throw new InternalServerErrorException(
          'Lỗi nội bộ khi xác minh chữ ký xác nhận',
        );
      }

      // Atomically update payment status
      const targetStatus: PaymentStatus = 'COMPLETED';
      const updated = await this.paymentRepo.updateIfStatus(
        payment.id,
        ['PENDING', 'PROCESSING'],
        {
          status: targetStatus,
          callbackData: {
            ...verifyResult.rawData,
            confirmedAt: new Date().toISOString(),
          },
        },
      );

      if (!updated) {
        // Payment already in terminal state — return idempotent response
        const existing = await this.paymentRepo.findById(payment.id);
        if (existing && existing.status === 'COMPLETED') {
          this.logger.log(
            `Bank transfer ${payment.id} already confirmed`,
            'ConfirmBankTransferHandler',
          );
          return {
            paymentId: payment.id,
            status: existing.status,
            confirmedBy: command.confirmedBy,
          };
        }

        throw new ValidationException(
          `Không thể xác nhận thanh toán ở trạng thái ${existing?.status ?? 'unknown'}`,
        );
      }

      // Publish domain events
      updated.emitCompleted();
      const events = updated.clearDomainEvents();
      for (const event of events) {
        this.eventBus.publish(event);
      }

      this.logger.log(
        `Bank transfer confirmed: paymentId=${payment.id}, confirmedBy=${command.confirmedBy}`,
        'ConfirmBankTransferHandler',
      );

      return {
        paymentId: updated.id,
        status: updated.status,
        confirmedBy: command.confirmedBy,
      };
    } catch (error) {
      if (error instanceof DomainException) throw error;
      this.logger.error(
        `Failed to confirm bank transfer: ${error instanceof Error ? error.message : error}`,
        error instanceof Error ? error.stack : undefined,
        this.constructor.name,
      );
      throw new InternalServerErrorException(
        'Không thể xác nhận chuyển khoản. Vui lòng thử lại sau',
      );
    }
  }
}
