import { Injectable, Inject, Logger } from '@nestjs/common';
import {
  IPlantingOrderRepository,
  PLANTING_ORDER_REPOSITORY,
} from '../../domain/repositories/planting-order.repository.interface';
import {
  IPlantingPositionRepository,
  PLANTING_POSITION_REPOSITORY,
} from '../../domain/repositories/planting-position.repository.interface';
import {
  IPoolInjectionBatchRepository,
  POOL_INJECTION_BATCH_REPOSITORY,
} from '../../domain/repositories/pool-injection-batch.repository.interface';
import { WalletServiceClient } from '../../infrastructure/external/wallet-service.client';
import { OutboxRepository, OutboxEventData } from '../../infrastructure/persistence/repositories/outbox.repository';
import { BatchStatus } from '../../domain/value-objects/batch-status.enum';
import { PlantingOrder } from '../../domain/aggregates/planting-order.aggregate';

@Injectable()
export class PoolInjectionService {
  private readonly logger = new Logger(PoolInjectionService.name);

  constructor(
    @Inject(PLANTING_ORDER_REPOSITORY)
    private readonly orderRepository: IPlantingOrderRepository,
    @Inject(PLANTING_POSITION_REPOSITORY)
    private readonly positionRepository: IPlantingPositionRepository,
    @Inject(POOL_INJECTION_BATCH_REPOSITORY)
    private readonly batchRepository: IPoolInjectionBatchRepository,
    private readonly walletService: WalletServiceClient,
    private readonly outboxRepository: OutboxRepository,
  ) {}

  /**
   * 处理到期的底池注入批次
   * 此方法应由定时任务调用
   */
  async processScheduledBatches(): Promise<void> {
    const batches =
      await this.batchRepository.findScheduledBatchesReadyForInjection();

    for (const batch of batches) {
      try {
        await this.processBatch(batch.id!);
      } catch (error) {
        this.logger.error(
          `Failed to process batch ${batch.batchNo}`,
          error,
        );
      }
    }
  }

  /**
   * 处理单个批次的底池注入
   */
  async processBatch(batchId: bigint): Promise<void> {
    const batch = await this.batchRepository.findById(batchId);
    if (!batch) {
      throw new Error('批次不存在');
    }

    if (batch.status !== BatchStatus.SCHEDULED) {
      throw new Error('批次状态不正确');
    }

    this.logger.log(`Processing batch ${batch.batchNo}`);

    // 标记为注入中
    batch.startInjection();
    await this.batchRepository.save(batch);

    try {
      // 调用钱包服务注入底池
      const result = await this.walletService.injectToPool(
        batch.batchNo,
        batch.totalAmount,
      );

      // 完成注入
      batch.completeInjection(result.txHash);
      await this.batchRepository.save(batch);

      // 更新批次内所有订单状态
      const orders = await this.orderRepository.findByBatchId(batchId);
      this.logger.log(`[BATCH] Processing ${orders.length} orders in batch ${batch.batchNo}`);

      for (const order of orders) {
        this.logger.debug(`[BATCH] Processing order ${order.orderNo}`);

        order.confirmPoolInjection(result.txHash);
        await this.orderRepository.save(order);

        // 开启挖矿
        order.enableMining();
        await this.orderRepository.save(order);

        // 更新用户持仓为有效
        const position = await this.positionRepository.findByUserId(
          order.userId,
        );
        if (position) {
          position.activateMining(order.treeCount.value);
          await this.positionRepository.save(position);
        }

        // 保存 MiningEnabled 事件到 Outbox（使用 Outbox Pattern）
        await this.saveMiningEnabledEventsToOutbox(order);
      }

      this.logger.log(
        `[BATCH] ✓ Batch ${batch.batchNo} processed successfully, txHash: ${result.txHash}, orders: ${orders.length}`,
      );
    } catch (error) {
      this.logger.error(`Failed to inject batch ${batch.batchNo}`, error);
      throw error;
    }
  }

  /**
   * 安排待处理批次的注入时间
   * 批次结束后自动安排
   */
  async schedulePendingBatches(): Promise<void> {
    const batches = await this.batchRepository.findByStatus(BatchStatus.PENDING);

    const now = new Date();
    for (const batch of batches) {
      // 如果批次已过期，安排注入
      if (batch.endDate < now) {
        const scheduledTime = new Date();
        scheduledTime.setMinutes(scheduledTime.getMinutes() + 5);

        batch.schedule(scheduledTime);
        await this.batchRepository.save(batch);

        this.logger.log(
          `Batch ${batch.batchNo} scheduled for injection at ${scheduledTime}`,
        );
      }
    }
  }

  /**
   * 获取批次状态
   */
  async getBatchStatus(
    batchId: bigint,
  ): Promise<{
    batchNo: string;
    status: string;
    orderCount: number;
    totalAmount: number;
    scheduledInjectionTime: Date | null;
    actualInjectionTime: Date | null;
    injectionTxHash: string | null;
  } | null> {
    const batch = await this.batchRepository.findById(batchId);
    if (!batch) {
      return null;
    }

    return {
      batchNo: batch.batchNo,
      status: batch.status,
      orderCount: batch.orderCount,
      totalAmount: batch.totalAmount,
      scheduledInjectionTime: batch.scheduledInjectionTime,
      actualInjectionTime: batch.actualInjectionTime,
      injectionTxHash: batch.injectionTxHash,
    };
  }

  /**
   * 保存挖矿开启事件到 Outbox
   *
   * 使用 Outbox Pattern 保证事件发布的可靠性
   * 事件由 OutboxPublisherService 轮询发布到 Kafka
   */
  private async saveMiningEnabledEventsToOutbox(order: PlantingOrder): Promise<void> {
    this.logger.debug(`[OUTBOX] Saving MiningEnabled events to outbox for order ${order.orderNo}`);

    const outboxEvents: OutboxEventData[] = [];

    // 将领域事件转换为 Outbox 事件
    for (const domainEvent of order.domainEvents) {
      outboxEvents.push({
        eventType: domainEvent.type,
        topic: 'planting-events',
        key: order.userId.toString(),
        payload: {
          eventName: domainEvent.type,
          aggregateId: domainEvent.aggregateId,
          occurredAt: domainEvent.occurredAt,
          data: domainEvent.data,
        },
        aggregateId: order.orderNo,
        aggregateType: 'PlantingOrder',
      });
    }

    if (outboxEvents.length > 0) {
      // 使用 PrismaService 事务保存（这里简化处理，直接保存）
      // 注意：理想情况下应该在 processBatch 的事务中一起保存
      await this.outboxRepository.saveEvents(outboxEvents);

      this.logger.log(`[OUTBOX] ✓ Saved ${outboxEvents.length} MiningEnabled events to outbox for order ${order.orderNo}`);
    }

    // 清除领域事件
    order.clearDomainEvents();
  }
}
