import { Injectable, Logger } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { PrismaService, TransactionClient } from './prisma/prisma.service';
import { TransferOrder } from '../../domain/aggregates/transfer-order.aggregate';
import { TransferOrderMapper } from './mappers/transfer-order.mapper';
import { OutboxEventData } from './repositories/outbox.repository';

/**
 * 工作单元 - 用于管理跨多个聚合根的数据库事务
 */
@Injectable()
export class UnitOfWork {
  constructor(private readonly prisma: PrismaService) {}

  /**
   * 在数据库事务中执行操作
   */
  async executeInTransaction<T>(
    fn: (uow: TransactionalUnitOfWork) => Promise<T>,
    options?: {
      maxWait?: number;
      timeout?: number;
      isolationLevel?: Prisma.TransactionIsolationLevel;
    },
  ): Promise<T> {
    return this.prisma.executeTransaction(async (tx) => {
      const transactionalUow = new TransactionalUnitOfWork(tx);
      return fn(transactionalUow);
    }, options);
  }
}

/**
 * 事务性工作单元 - 在事务上下文中提供聚合根的持久化操作
 *
 * 支持 Outbox Pattern：在同一个事务中保存业务数据和事件数据
 */
export class TransactionalUnitOfWork {
  private readonly logger = new Logger(TransactionalUnitOfWork.name);
  private pendingOutboxEvents: OutboxEventData[] = [];

  constructor(private readonly tx: TransactionClient) {}

  /**
   * 添加 Outbox 事件
   */
  addOutboxEvent(event: OutboxEventData): void {
    this.pendingOutboxEvents.push(event);
  }

  /**
   * 批量添加 Outbox 事件
   */
  addOutboxEvents(events: OutboxEventData[]): void {
    this.pendingOutboxEvents.push(...events);
  }

  /**
   * 提交所有待发送的 Outbox 事件
   */
  async commitOutboxEvents(): Promise<void> {
    if (this.pendingOutboxEvents.length === 0) return;

    await this.tx.outboxEvent.createMany({
      data: this.pendingOutboxEvents.map((event) => ({
        eventType: event.eventType,
        topic: event.topic,
        key: event.key,
        payload: event.payload as Prisma.JsonObject,
        aggregateId: event.aggregateId,
        aggregateType: event.aggregateType,
        status: 'PENDING',
      })),
    });

    this.logger.log(`[OUTBOX] Committed ${this.pendingOutboxEvents.length} outbox events`);
    this.pendingOutboxEvents = [];
  }

  /**
   * 保存转让订单
   */
  async saveTransferOrder(order: TransferOrder): Promise<void> {
    const data = TransferOrderMapper.toPersistence(order);

    if (order.id) {
      await this.tx.transferOrder.update({
        where: { id: order.id },
        data: {
          status: data.status,
          sagaStep: data.sagaStep,
          failReason: data.failReason,
          retryCount: data.retryCount,
          sellerConfirmedAt: data.sellerConfirmedAt,
          paymentFrozenAt: data.paymentFrozenAt,
          treesLockedAt: data.treesLockedAt,
          ownershipTransferredAt: data.ownershipTransferredAt,
          contributionAdjustedAt: data.contributionAdjustedAt,
          statsUpdatedAt: data.statsUpdatedAt,
          paymentSettledAt: data.paymentSettledAt,
          completedAt: data.completedAt,
          cancelledAt: data.cancelledAt,
          rolledBackAt: data.rolledBackAt,
        },
      });
    } else {
      const created = await this.tx.transferOrder.create({
        data: {
          transferOrderNo: data.transferOrderNo,
          sellerUserId: data.sellerUserId,
          sellerAccountSequence: data.sellerAccountSequence,
          buyerUserId: data.buyerUserId,
          buyerAccountSequence: data.buyerAccountSequence,
          sourceOrderNo: data.sourceOrderNo,
          sourceAdoptionId: data.sourceAdoptionId,
          treeCount: data.treeCount,
          contributionPerTree: data.contributionPerTree,
          originalAdoptionDate: data.originalAdoptionDate,
          originalExpireDate: data.originalExpireDate,
          selectedProvince: data.selectedProvince,
          selectedCity: data.selectedCity,
          transferPrice: data.transferPrice,
          platformFeeRate: data.platformFeeRate,
          platformFeeAmount: data.platformFeeAmount,
          sellerReceiveAmount: data.sellerReceiveAmount,
          status: data.status,
          sagaStep: data.sagaStep,
        },
      });
      order.setId(created.id);
    }
  }

  /**
   * 记录状态变更日志
   */
  async logStatusChange(params: {
    transferOrderNo: string;
    fromStatus: string;
    toStatus: string;
    fromSagaStep: string;
    toSagaStep: string;
    operatorType: string;
    operatorId?: string;
    remark?: string;
  }): Promise<void> {
    await this.tx.transferStatusLog.create({
      data: {
        transferOrderNo: params.transferOrderNo,
        fromStatus: params.fromStatus,
        toStatus: params.toStatus,
        fromSagaStep: params.fromSagaStep,
        toSagaStep: params.toSagaStep,
        operatorType: params.operatorType,
        operatorId: params.operatorId,
        remark: params.remark,
      },
    });
  }
}

export const UNIT_OF_WORK = Symbol('UnitOfWork');
