import { Injectable, Logger, Inject } from '@nestjs/common';
import {
  ITransferOrderRepository,
  TRANSFER_ORDER_REPOSITORY,
} from '../../domain/repositories/transfer-order.repository.interface';
import { TransferOrderStatus } from '../../domain/value-objects/transfer-order-status.enum';
import { SagaStep } from '../../domain/value-objects/saga-step.enum';
import { UnitOfWork, UNIT_OF_WORK } from '../../infrastructure/persistence/unit-of-work';
import { EventPublisherService } from '../../infrastructure/kafka/event-publisher.service';
import { WalletServiceClient } from '../../infrastructure/external/wallet-service.client';
import { DomainEvent } from '../../domain/events/domain-event.interface';

/**
 * Saga 编排器
 * 负责推进转让订单的各个步骤
 *
 * 状态机流转：
 * SELLER_CONFIRMED → 冻结买方资金 → PAYMENT_FROZEN
 * PAYMENT_FROZEN → 锁定卖方树 → TREES_LOCKED
 * TREES_LOCKED → 变更所有权 → OWNERSHIP_TRANSFERRED
 * OWNERSHIP_TRANSFERRED → 调整算力 → CONTRIBUTION_ADJUSTED
 * CONTRIBUTION_ADJUSTED → 更新团队统计 → STATS_UPDATED
 * STATS_UPDATED → 结算资金 → PAYMENT_SETTLED → COMPLETED
 */
@Injectable()
export class SagaOrchestratorService {
  private readonly logger = new Logger(SagaOrchestratorService.name);

  constructor(
    @Inject(TRANSFER_ORDER_REPOSITORY)
    private readonly transferOrderRepo: ITransferOrderRepository,
    @Inject(UNIT_OF_WORK)
    private readonly unitOfWork: UnitOfWork,
    private readonly eventPublisher: EventPublisherService,
    private readonly walletClient: WalletServiceClient,
  ) {}

  /**
   * 推进 Saga：卖方确认后 → 冻结买方资金
   */
  async proceedAfterSellerConfirm(transferOrderNo: string): Promise<void> {
    this.logger.log(`[SAGA] Proceeding after seller confirm: ${transferOrderNo}`);

    const order = await this.transferOrderRepo.findByTransferOrderNo(transferOrderNo);
    if (!order || order.status !== TransferOrderStatus.SELLER_CONFIRMED) return;

    try {
      // 调用 wallet-service 冻结买方资金
      await this.walletClient.freezePayment({
        accountSequence: order.buyerAccountSequence,
        amount: order.transferPrice,
        transferOrderNo: order.transferOrderNo,
        reason: `树转让冻结 - ${order.transferOrderNo}`,
      });

      order.markPaymentFrozen();

      await this.saveAndPublish(order, 'SYSTEM', '买方资金冻结成功');

      // 继续下一步：发送锁定树事件
      await this.proceedToLockTrees(order.transferOrderNo);
    } catch (error) {
      this.logger.error(`[SAGA] Failed to freeze payment for ${transferOrderNo}:`, error);
      await this.handleFailure(transferOrderNo, `冻结资金失败: ${(error as Error).message}`);
    }
  }

  /**
   * 推进 Saga：资金冻结后 → 锁定卖方树
   */
  async proceedToLockTrees(transferOrderNo: string): Promise<void> {
    this.logger.log(`[SAGA] Proceeding to lock trees: ${transferOrderNo}`);

    const order = await this.transferOrderRepo.findByTransferOrderNo(transferOrderNo);
    if (!order || order.status !== TransferOrderStatus.PAYMENT_FROZEN) return;

    // 发送 Kafka 事件给 planting-service 锁定树
    await this.eventPublisher.publish('transfer.trees.lock', transferOrderNo, {
      transferOrderNo: order.transferOrderNo,
      sellerUserId: order.sellerUserId.toString(),
      sourceOrderNo: order.sourceOrderNo,
      treeCount: order.treeCount,
      sellerAccountSequence: order.sellerAccountSequence,
    });
  }

  /**
   * 收到树锁定确认 → 变更所有权
   */
  async handleTreesLockedAck(transferOrderNo: string): Promise<void> {
    this.logger.log(`[SAGA] Trees locked ack: ${transferOrderNo}`);

    const order = await this.transferOrderRepo.findByTransferOrderNo(transferOrderNo);
    if (!order || order.status !== TransferOrderStatus.PAYMENT_FROZEN) return;

    order.markTreesLocked();
    await this.saveAndPublish(order, 'SYSTEM', '卖方树锁定成功');

    // 继续：发送所有权变更事件
    await this.eventPublisher.publish('transfer.ownership.execute', transferOrderNo, {
      transferOrderNo: order.transferOrderNo,
      sellerUserId: order.sellerUserId.toString(),
      sellerAccountSequence: order.sellerAccountSequence,
      buyerUserId: order.buyerUserId.toString(),
      buyerAccountSequence: order.buyerAccountSequence,
      sourceOrderNo: order.sourceOrderNo,
      treeCount: order.treeCount,
      contributionPerTree: order.contributionPerTree,
      selectedProvince: order.selectedProvince,
      selectedCity: order.selectedCity,
      originalAdoptionDate: order.originalAdoptionDate.toISOString(),
      originalExpireDate: order.originalExpireDate.toISOString(),
    });
  }

  /**
   * 收到所有权变更完成确认 → 等待算力调整
   */
  async handleOwnershipTransferred(transferOrderNo: string): Promise<void> {
    this.logger.log(`[SAGA] Ownership transferred: ${transferOrderNo}`);

    const order = await this.transferOrderRepo.findByTransferOrderNo(transferOrderNo);
    if (!order || order.status !== TransferOrderStatus.TREES_LOCKED) return;

    order.markOwnershipTransferred();
    await this.saveAndPublish(order, 'SYSTEM', '所有权变更完成');

    // planting-service 发出的 planting.ownership.removed / planting.ownership.added
    // 会被 contribution-service 和 referral-service 各自消费
    // 它们处理完后会发送确认事件回来
  }

  /**
   * 收到算力调整完成确认
   */
  async handleContributionAdjusted(transferOrderNo: string): Promise<void> {
    this.logger.log(`[SAGA] Contribution adjusted: ${transferOrderNo}`);

    const order = await this.transferOrderRepo.findByTransferOrderNo(transferOrderNo);
    if (!order || order.status !== TransferOrderStatus.OWNERSHIP_TRANSFERRED) return;

    order.markContributionAdjusted();
    await this.saveAndPublish(order, 'SYSTEM', '算力调整完成');
  }

  /**
   * 收到团队统计更新完成确认
   */
  async handleStatsUpdated(transferOrderNo: string): Promise<void> {
    this.logger.log(`[SAGA] Stats updated: ${transferOrderNo}`);

    const order = await this.transferOrderRepo.findByTransferOrderNo(transferOrderNo);
    if (!order || order.status !== TransferOrderStatus.CONTRIBUTION_ADJUSTED) return;

    order.markStatsUpdated();
    await this.saveAndPublish(order, 'SYSTEM', '团队统计更新完成');

    // 继续：结算资金
    await this.proceedToSettlePayment(order.transferOrderNo);
  }

  /**
   * 结算资金 → 完成
   */
  async proceedToSettlePayment(transferOrderNo: string): Promise<void> {
    this.logger.log(`[SAGA] Proceeding to settle payment: ${transferOrderNo}`);

    const order = await this.transferOrderRepo.findByTransferOrderNo(transferOrderNo);
    if (!order || order.status !== TransferOrderStatus.STATS_UPDATED) return;

    try {
      await this.walletClient.settleTransferPayment({
        buyerAccountSequence: order.buyerAccountSequence,
        sellerAccountSequence: order.sellerAccountSequence,
        freezeId: order.transferOrderNo, // 使用订单号作为冻结标识
        sellerReceiveAmount: order.sellerReceiveAmount,
        platformFeeAmount: order.platformFeeAmount,
        transferOrderNo: order.transferOrderNo,
      });

      order.markPaymentSettled();
      await this.saveAndPublish(order, 'SYSTEM', '资金结算完成');

      // 完成转让
      order.complete();
      await this.saveAndPublish(order, 'SYSTEM', '转让完成');
    } catch (error) {
      this.logger.error(`[SAGA] Failed to settle payment for ${transferOrderNo}:`, error);
      await this.handleFailure(transferOrderNo, `资金结算失败: ${(error as Error).message}`);
    }
  }

  /**
   * 处理失败 → 开始补偿
   */
  async handleFailure(transferOrderNo: string, reason: string): Promise<void> {
    this.logger.error(`[SAGA] Handling failure for ${transferOrderNo}: ${reason}`);

    const order = await this.transferOrderRepo.findByTransferOrderNo(transferOrderNo);
    if (!order) return;

    order.markFailed(reason);

    await this.saveAndPublish(order, 'SYSTEM', `转让失败: ${reason}`);

    // 如果需要补偿，开始补偿流程
    if (order.status === TransferOrderStatus.ROLLING_BACK) {
      await this.executeCompensation(order.transferOrderNo);
    }
  }

  /**
   * 执行补偿操作
   */
  private async executeCompensation(transferOrderNo: string): Promise<void> {
    const order = await this.transferOrderRepo.findByTransferOrderNo(transferOrderNo);
    if (!order || order.status !== TransferOrderStatus.ROLLING_BACK) return;

    if (order.sagaStep === SagaStep.COMPENSATE_UNLOCK_TREES) {
      // 发送解锁树事件
      await this.eventPublisher.publish('transfer.trees.unlock', transferOrderNo, {
        transferOrderNo: order.transferOrderNo,
        sourceOrderNo: order.sourceOrderNo,
        treeCount: order.treeCount,
      });
    } else if (order.sagaStep === SagaStep.COMPENSATE_UNFREEZE) {
      // 解冻资金
      try {
        await this.walletClient.unfreezePayment({
          accountSequence: order.buyerAccountSequence,
          freezeId: order.transferOrderNo,
          transferOrderNo: order.transferOrderNo,
          reason: `转让取消解冻 - ${order.transferOrderNo}`,
        });

        order.markRolledBack();
        await this.saveAndPublish(order, 'SYSTEM', '补偿完成：资金已解冻');
      } catch (error) {
        this.logger.error(`[SAGA] Failed to unfreeze for ${transferOrderNo}:`, error);
      }
    }
  }

  /**
   * 收到树解锁确认 → 继续补偿
   */
  async handleTreesUnlockedAck(transferOrderNo: string): Promise<void> {
    const order = await this.transferOrderRepo.findByTransferOrderNo(transferOrderNo);
    if (!order || order.status !== TransferOrderStatus.ROLLING_BACK) return;

    order.advanceCompensation();

    await this.saveAndPublish(order, 'SYSTEM', '补偿：树已解锁');

    // 如果还需要解冻资金
    if (order.status === TransferOrderStatus.ROLLING_BACK) {
      await this.executeCompensation(order.transferOrderNo);
    }
  }

  /**
   * 保存订单并发布事件
   */
  private async saveAndPublish(
    order: any,
    operatorType: string,
    remark: string,
  ): Promise<void> {
    await this.unitOfWork.executeInTransaction(async (uow) => {
      await uow.saveTransferOrder(order);

      const events = order.getDomainEvents() as DomainEvent[];
      if (events.length > 0) {
        const lastEvent = events[events.length - 1];
        if (lastEvent.type === 'TransferStatusChanged') {
          const data = lastEvent.data as any;
          await uow.logStatusChange({
            transferOrderNo: order.transferOrderNo,
            fromStatus: data.fromStatus,
            toStatus: data.toStatus,
            fromSagaStep: data.fromSagaStep,
            toSagaStep: data.toSagaStep,
            operatorType,
            remark,
          });
        }
      }

      const outboxEvents = events.map((e: DomainEvent) => ({
        eventType: e.type,
        topic: this.getTopicForEvent(e.type),
        key: e.aggregateId,
        payload: e.data,
        aggregateId: e.aggregateId,
        aggregateType: e.aggregateType,
      }));
      uow.addOutboxEvents(outboxEvents);
      await uow.commitOutboxEvents();
    });

    order.clearDomainEvents();
  }

  private getTopicForEvent(eventType: string): string {
    const topicMap: Record<string, string> = {
      TransferOrderCreated: 'transfer.order.created',
      TransferStatusChanged: 'transfer.status.changed',
      TransferCompleted: 'transfer.order.completed',
    };
    return topicMap[eventType] || 'transfer.events';
  }
}
