import { HttpStatus, Inject, InternalServerErrorException } from '@nestjs/common';
import { CommandHandler, EventBus, type ICommandHandler } from '@nestjs/cqrs';
import { DomainException, ErrorCode, LoggerService } from '@modules/shared';
import { ESCROW_REPOSITORY, type IEscrowRepository } from '../../../domain/repositories/escrow.repository';
import { ORDER_REPOSITORY, type IOrderRepository } from '../../../domain/repositories/order.repository';
import { HoldEscrowCommand } from './hold-escrow.command';

export interface HoldEscrowResult {
  escrowId: string;
  status: string;
  heldAt: string;
}

@CommandHandler(HoldEscrowCommand)
export class HoldEscrowHandler implements ICommandHandler<HoldEscrowCommand> {
  constructor(
    @Inject(ORDER_REPOSITORY) private readonly orderRepo: IOrderRepository,
    @Inject(ESCROW_REPOSITORY) private readonly escrowRepo: IEscrowRepository,
    private readonly eventBus: EventBus,
    private readonly logger: LoggerService,
  ) {}

  async execute(command: HoldEscrowCommand): Promise<HoldEscrowResult> {
    try {
      const order = await this.orderRepo.findById(command.orderId);
      if (!order) {
        throw new DomainException(ErrorCode.ORDER_NOT_FOUND, 'Đơn hàng không tồn tại', HttpStatus.NOT_FOUND);
      }

      const escrow = await this.escrowRepo.findByOrderId(command.orderId);
      if (!escrow) {
        throw new DomainException(ErrorCode.ESCROW_NOT_FOUND, 'Ký quỹ không tồn tại', HttpStatus.NOT_FOUND);
      }

      // Hold escrow
      const holdResult = escrow.hold();
      if (holdResult.isErr) throw holdResult.unwrapErr();

      // Transition order
      const orderResult = order.markEscrowHeld();
      if (orderResult.isErr) throw orderResult.unwrapErr();

      await this.escrowRepo.update(escrow);
      await this.orderRepo.update(order);

      for (const event of escrow.clearDomainEvents()) {
        this.eventBus.publish(event);
      }

      this.logger.log(`Escrow held: id=${escrow.id}, order=${command.orderId}`, 'HoldEscrowHandler');

      return {
        escrowId: escrow.id,
        status: escrow.status,
        heldAt: escrow.heldAt?.toISOString() ?? '',
      };
    } catch (error) {
      if (error instanceof DomainException) throw error;
      this.logger.error(
        `Failed to hold escrow: ${error instanceof Error ? error.message : error}`,
        error instanceof Error ? error.stack : undefined,
        this.constructor.name,
      );
      throw new InternalServerErrorException('Không thể giữ ký quỹ. Vui lòng thử lại sau');
    }
  }
}
