import { Injectable, Logger } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { PrismaService, TransactionClient } from './prisma/prisma.service';
import { PlantingOrder } from '../../domain/aggregates/planting-order.aggregate';
import { PlantingPosition } from '../../domain/aggregates/planting-position.aggregate';
import { PoolInjectionBatch } from '../../domain/aggregates/pool-injection-batch.aggregate';
import { PlantingOrderMapper } from './mappers/planting-order.mapper';
import { PlantingPositionMapper } from './mappers/planting-position.mapper';
import { PoolInjectionBatchMapper } from './mappers/pool-injection-batch.mapper';
import { OutboxEventData } from './repositories/outbox.repository';

/**
 * 工作单元 - 用于管理跨多个聚合根的数据库事务
 *
 * 使用示例:
 * ```typescript
 * await this.unitOfWork.executeInTransaction(async (uow) => {
 *   await uow.saveOrder(order);
 *   await uow.savePosition(position);
 *   await uow.saveBatch(batch);
 * });
 * ```
 */
@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 事件（在事务提交时一起保存）
   *
   * @param event 事件数据
   */
  addOutboxEvent(event: OutboxEventData): void {
    this.logger.debug(`[OUTBOX] Adding event to outbox: ${event.eventType} for ${event.aggregateType}:${event.aggregateId}`);
    this.pendingOutboxEvents.push(event);
  }

  /**
   * 批量添加 Outbox 事件
   *
   * @param events 事件数据数组
   */
  addOutboxEvents(events: OutboxEventData[]): void {
    this.logger.debug(`[OUTBOX] Adding ${events.length} events to outbox`);
    this.pendingOutboxEvents.push(...events);
  }

  /**
   * 保存所有待发送的 Outbox 事件
   * 应在事务的最后调用
   */
  async commitOutboxEvents(): Promise<void> {
    if (this.pendingOutboxEvents.length === 0) {
      return;
    }

    this.logger.debug(`[OUTBOX] Committing ${this.pendingOutboxEvents.length} outbox events`);

    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 = [];
  }

  /**
   * 获取待发送的事件数量
   */
  getPendingOutboxEventCount(): number {
    return this.pendingOutboxEvents.length;
  }

  /**
   * 保存认种订单
   */
  async saveOrder(order: PlantingOrder): Promise<void> {
    const { orderData, allocations } = PlantingOrderMapper.toPersistence(order);

    if (order.id) {
      // 更新
      await this.tx.plantingOrder.update({
        where: { id: order.id },
        data: {
          status: orderData.status,
          selectedProvince: orderData.selectedProvince,
          selectedCity: orderData.selectedCity,
          provinceCitySelectedAt: orderData.provinceCitySelectedAt,
          provinceCityConfirmedAt: orderData.provinceCityConfirmedAt,
          poolInjectionBatchId: orderData.poolInjectionBatchId,
          poolInjectionScheduledTime: orderData.poolInjectionScheduledTime,
          poolInjectionActualTime: orderData.poolInjectionActualTime,
          poolInjectionTxHash: orderData.poolInjectionTxHash,
          miningEnabledAt: orderData.miningEnabledAt,
          paidAt: orderData.paidAt,
          fundAllocatedAt: orderData.fundAllocatedAt,
        },
      });

      // 如果有新的资金分配，插入
      if (allocations.length > 0) {
        const existingAllocations = await this.tx.fundAllocation.count({
          where: { orderId: order.id },
        });

        if (existingAllocations === 0) {
          await this.tx.fundAllocation.createMany({
            data: allocations.map((a) => ({
              orderId: order.id!,
              targetType: a.targetType,
              amount: a.amount,
              targetAccountId: a.targetAccountId,
              metadata: a.metadata ?? Prisma.DbNull,
            })),
          });
        }
      }
    } else {
      // 创建
      const created = await this.tx.plantingOrder.create({
        data: {
          orderNo: orderData.orderNo,
          userId: orderData.userId,
          accountSequence: orderData.accountSequence,
          treeCount: orderData.treeCount,
          totalAmount: orderData.totalAmount,
          status: orderData.status,
          selectedProvince: orderData.selectedProvince,
          selectedCity: orderData.selectedCity,
          provinceCitySelectedAt: orderData.provinceCitySelectedAt,
          provinceCityConfirmedAt: orderData.provinceCityConfirmedAt,
          poolInjectionBatchId: orderData.poolInjectionBatchId,
          poolInjectionScheduledTime: orderData.poolInjectionScheduledTime,
          poolInjectionActualTime: orderData.poolInjectionActualTime,
          poolInjectionTxHash: orderData.poolInjectionTxHash,
          miningEnabledAt: orderData.miningEnabledAt,
          paidAt: orderData.paidAt,
          fundAllocatedAt: orderData.fundAllocatedAt,
        },
      });

      order.setId(created.id);
    }
  }

  /**
   * 保存用户持仓
   */
  async savePosition(position: PlantingPosition): Promise<void> {
    const { positionData } = PlantingPositionMapper.toPersistence(position);

    if (position.id) {
      // 更新
      await this.tx.plantingPosition.update({
        where: { id: position.id },
        data: {
          totalTreeCount: positionData.totalTreeCount,
          effectiveTreeCount: positionData.effectiveTreeCount,
          pendingTreeCount: positionData.pendingTreeCount,
          firstMiningStartAt: positionData.firstMiningStartAt,
        },
      });
    } else {
      // 创建
      const created = await this.tx.plantingPosition.create({
        data: {
          userId: positionData.userId,
          totalTreeCount: positionData.totalTreeCount,
          effectiveTreeCount: positionData.effectiveTreeCount,
          pendingTreeCount: positionData.pendingTreeCount,
          firstMiningStartAt: positionData.firstMiningStartAt,
        },
      });

      position.setId(created.id);
    }
  }

  /**
   * 获取或创建用户持仓
   */
  async getOrCreatePosition(userId: bigint): Promise<PlantingPosition> {
    const existing = await this.tx.plantingPosition.findUnique({
      where: { userId },
    });

    if (existing) {
      return PlantingPositionMapper.toDomain(existing);
    }

    // 创建新的持仓
    const position = PlantingPosition.create(userId);
    await this.savePosition(position);
    return position;
  }

  /**
   * 保存底池注入批次
   */
  async saveBatch(batch: PoolInjectionBatch): Promise<void> {
    const data = PoolInjectionBatchMapper.toPersistence(batch);

    if (batch.id) {
      // 更新
      await this.tx.poolInjectionBatch.update({
        where: { id: batch.id },
        data: {
          status: data.status,
          orderCount: data.orderCount,
          totalAmount: data.totalAmount,
          actualInjectionTime: data.actualInjectionTime,
          injectionTxHash: data.injectionTxHash,
        },
      });
    } else {
      // 创建
      const created = await this.tx.poolInjectionBatch.create({
        data: {
          batchNo: data.batchNo,
          status: data.status,
          startDate: data.startDate,
          endDate: data.endDate,
          orderCount: data.orderCount,
          totalAmount: data.totalAmount,
          scheduledInjectionTime: data.scheduledInjectionTime,
          actualInjectionTime: data.actualInjectionTime,
          injectionTxHash: data.injectionTxHash,
        },
      });

      batch.setId(created.id);
    }
  }

  /**
   * 获取或创建当前批次
   */
  async findOrCreateCurrentBatch(): Promise<PoolInjectionBatch> {
    const now = new Date();
    const startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate());

    const existing = await this.tx.poolInjectionBatch.findFirst({
      where: {
        startDate: { lte: now },
        endDate: { gt: now },
        status: 'PENDING',
      },
    });

    if (existing) {
      return PoolInjectionBatchMapper.toDomain(existing);
    }

    // 创建新批次 - PoolInjectionBatch.create 只需要 startDate，会自动计算 endDate
    const batch = PoolInjectionBatch.create(startOfDay);
    await this.saveBatch(batch);
    return batch;
  }
}

export const UNIT_OF_WORK = Symbol('UnitOfWork');
