import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { KafkaService, PrismaService, WalletServiceClient } from '../../infrastructure';
import { EventAckPublisher } from '../../infrastructure/kafka/event-ack.publisher';
import { TeamStatisticsService } from '../services';
import { UpdateTeamStatisticsCommand } from '../commands';

interface PlantingCreatedEvent {
  eventName: string;
  data: {
    userId: string;
    accountSequence: string; // 跨服务关联标识（优先使用）
    treeCount: number;
    provinceCode: string;
    cityCode: string;
    // 新增：订单信息，用于转发给 reward-service
    orderId: string;
    paidAt: string;
  };
  _outbox?: {
    id: string;
    aggregateId: string;
    eventType: string;
  };
}

/**
 * 认种创建事件处理器
 * 监听 planting-service 发出的认种事件
 *
 * 处理流程：
 * 1. 更新团队统计数据
 * 2. 根据 CONTRACT_SIGNING_ENABLED 配置决定后续流程：
 *    - true（默认）：等待合同签署结果，由 ContractSigningHandler 转发
 *    - false：立即发送 planting.order.paid，走原有奖励分配流程
 *
 * 合同签署流程（CONTRACT_SIGNING_ENABLED=true 时）：
 * - contract.signed → 发送 planting.order.paid → reward-service 正常奖励分配
 * - contract.expired → 发送 planting.order.expired → reward-service 系统账户分配
 */
@Injectable()
export class PlantingCreatedHandler implements OnModuleInit {
  private readonly logger = new Logger(PlantingCreatedHandler.name);
  private readonly contractSigningEnabled: boolean;

  constructor(
    private readonly kafkaService: KafkaService,
    private readonly teamStatisticsService: TeamStatisticsService,
    private readonly eventAckPublisher: EventAckPublisher,
    private readonly prisma: PrismaService,
    private readonly configService: ConfigService,
    private readonly walletService: WalletServiceClient,
  ) {
    // 默认启用合同签署功能
    this.contractSigningEnabled = this.configService.get<string>('CONTRACT_SIGNING_ENABLED', 'true') === 'true';
    this.logger.log(`Contract signing feature: ${this.contractSigningEnabled ? 'ENABLED' : 'DISABLED'}`);
  }

  async onModuleInit() {
    await this.kafkaService.subscribe(
      'referral-service-planting-created',
      ['planting.planting.created'],
      this.handleMessage.bind(this),
    );
    this.logger.log('Subscribed to planting.created events');
  }

  private async handleMessage(topic: string, message: Record<string, unknown>): Promise<void> {
    const event = message as unknown as PlantingCreatedEvent;

    if (event.eventName !== 'planting.created') {
      return;
    }

    // B方案：提取 outbox 信息用于发送确认
    const outboxInfo = event._outbox;
    const eventId = outboxInfo?.aggregateId || 'unknown';

    // 幂等性检查：检查是否已经处理过该事件
    if (eventId !== 'unknown') {
      const existingEvent = await this.prisma.processedEvent.findUnique({
        where: { eventId },
      });

      if (existingEvent) {
        this.logger.log(`Event ${eventId} already processed, skipping`);
        // 仍然发送成功确认，避免发送方继续重试
        if (outboxInfo) {
          await this.eventAckPublisher.sendSuccess(eventId, outboxInfo.eventType);
        }
        return;
      }
    }

    try {
      // 步骤1：更新团队统计
      // 使用 accountSequence 作为跨服务关联标识
      const command = new UpdateTeamStatisticsCommand(
        event.data.accountSequence,
        event.data.treeCount,
        event.data.provinceCode,
        event.data.cityCode,
      );

      await this.teamStatisticsService.handlePlantingEvent(command);

      this.logger.log(
        `Updated team statistics for accountSequence ${event.data.accountSequence}, count: ${event.data.treeCount}`,
      );

      // 步骤2：根据配置决定是否立即发送 planting.order.paid
      if (this.contractSigningEnabled) {
        // 合同签署功能已启用：等待合同签署结果后再发送
        // - contract.signed → 发送 planting.order.paid → 正常奖励分配
        // - contract.expired → 发送 planting.order.expired → 系统账户分配
        this.logger.log(
          `[CONTRACT_SIGNING=ON] Team statistics updated for order ${event.data.orderId}. ` +
          `Waiting for contract signing result (contract.signed or contract.expired) to trigger reward distribution.`,
        );
      } else {
        // 合同签署功能未启用：立即发送 planting.order.paid（原流程）
        await this.publishOrderPaidEvent(event);
        this.logger.log(
          `[CONTRACT_SIGNING=OFF] Published planting.order.paid for order ${event.data.orderId}. ` +
          `Using original reward distribution flow.`,
        );
      }

      // 记录已处理的事件
      if (eventId !== 'unknown') {
        await this.prisma.processedEvent.create({
          data: {
            eventId,
            eventType: outboxInfo?.eventType || 'planting.created',
          },
        });
      }

      // B方案：发送处理成功确认
      if (outboxInfo) {
        await this.eventAckPublisher.sendSuccess(eventId, outboxInfo.eventType);
      }
    } catch (error) {
      this.logger.error(
        `Failed to process planting event for user ${event.data.userId}:`,
        error,
      );

      // B方案：发送处理失败确认
      if (outboxInfo) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        await this.eventAckPublisher.sendFailure(eventId, outboxInfo.eventType, errorMessage);
      }
    }
  }

  /**
   * 确认扣款并发送 planting.order.paid 事件给 reward-service
   *
   * 仅在 CONTRACT_SIGNING_ENABLED=false 时由此方法直接调用
   * CONTRACT_SIGNING_ENABLED=true 时由 ContractSigningHandler 处理
   *
   * 流程：
   * 1. 调用 wallet-service 确认扣款（将冻结资金正式扣除）
   * 2. 发送 planting.order.paid 事件
   */
  private async publishOrderPaidEvent(event: PlantingCreatedEvent): Promise<void> {
    // 步骤1：确认扣款
    const confirmResult = await this.walletService.confirmPlantingDeduction({
      userId: event.data.userId,
      accountSequence: event.data.accountSequence,
      orderId: event.data.orderId,
    });

    if (!confirmResult.success) {
      throw new Error(`确认扣款失败: ${confirmResult.error}`);
    }

    this.logger.log(`Deduction confirmed for order ${event.data.orderId}`);

    // 步骤2：发送 planting.order.paid 事件
    const maxRetries = 3;
    let lastError: Error | null = null;

    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        await this.kafkaService.publish({
          topic: 'planting.order.paid',
          key: event.data.accountSequence,
          value: {
            eventName: 'planting.order.paid',
            data: {
              orderId: event.data.orderId,
              userId: event.data.userId,
              accountSequence: event.data.accountSequence,
              treeCount: event.data.treeCount,
              provinceCode: event.data.provinceCode,
              cityCode: event.data.cityCode,
              paidAt: event.data.paidAt,
            },
          },
        });

        this.logger.log(
          `Published planting.order.paid event for order ${event.data.orderId}, accountSequence ${event.data.accountSequence}`,
        );
        return;
      } catch (error) {
        lastError = error instanceof Error ? error : new Error(String(error));
        this.logger.warn(
          `Failed to publish planting.order.paid event (attempt ${attempt}/${maxRetries}): ${lastError.message}`,
        );

        if (attempt < maxRetries) {
          await this.sleep(1000 * attempt);
        }
      }
    }

    throw new Error(
      `Failed to publish planting.order.paid event after ${maxRetries} attempts: ${lastError?.message}`,
    );
  }

  private sleep(ms: number): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }
}
