import { Injectable, Logger } from '@nestjs/common';
import { UnallocatedContributionRepository, UnallocatedContribution } from '../../infrastructure/persistence/repositories/unallocated-contribution.repository';
import { ContributionAccountRepository } from '../../infrastructure/persistence/repositories/contribution-account.repository';
import { ContributionRecordRepository } from '../../infrastructure/persistence/repositories/contribution-record.repository';
import { OutboxRepository } from '../../infrastructure/persistence/repositories/outbox.repository';
import { UnitOfWork } from '../../infrastructure/persistence/unit-of-work/unit-of-work';
import { ContributionRecordAggregate } from '../../domain/aggregates/contribution-record.aggregate';
import { ContributionSourceType } from '../../domain/aggregates/contribution-account.aggregate';
import { ContributionAmount } from '../../domain/value-objects/contribution-amount.vo';
import { DistributionRate } from '../../domain/value-objects/distribution-rate.vo';
import { ContributionRecordSyncedEvent } from '../../domain/events';

/**
 * 奖励补发服务
 * 当用户解锁新的奖励档位时，补发之前所有认种对应的奖励
 */
@Injectable()
export class BonusClaimService {
  private readonly logger = new Logger(BonusClaimService.name);

  constructor(
    private readonly unallocatedContributionRepository: UnallocatedContributionRepository,
    private readonly contributionAccountRepository: ContributionAccountRepository,
    private readonly contributionRecordRepository: ContributionRecordRepository,
    private readonly outboxRepository: OutboxRepository,
    private readonly unitOfWork: UnitOfWork,
  ) {}

  /**
   * 检查并处理奖励补发
   * 当用户的直推认种人数变化时调用
   * @param accountSequence 用户账号
   * @param previousCount 之前的直推认种人数
   * @param newCount 新的直推认种人数
   */
  async checkAndClaimBonus(
    accountSequence: string,
    previousCount: number,
    newCount: number,
  ): Promise<void> {
    // 检查是否达到新的解锁条件
    const tiersToClaimList: number[] = [];

    // T2: 直推≥2人认种时解锁
    if (previousCount < 2 && newCount >= 2) {
      tiersToClaimList.push(2);
    }

    // T3: 直推≥4人认种时解锁
    if (previousCount < 4 && newCount >= 4) {
      tiersToClaimList.push(3);
    }

    if (tiersToClaimList.length === 0) {
      return;
    }

    this.logger.log(
      `User ${accountSequence} unlocked bonus tiers: ${tiersToClaimList.join(', ')} ` +
        `(directReferralAdoptedCount: ${previousCount} -> ${newCount})`,
    );

    // 检查是否已在事务中（被 ContributionCalculationService 调用时）
    // 如果已在事务中，直接执行，避免嵌套事务导致超时
    if (this.unitOfWork.isInTransaction()) {
      for (const tier of tiersToClaimList) {
        await this.claimBonusTier(accountSequence, tier);
      }
    } else {
      // 独立调用时，开启新事务
      await this.unitOfWork.executeInTransaction(async () => {
        for (const tier of tiersToClaimList) {
          await this.claimBonusTier(accountSequence, tier);
        }
      });
    }
  }

  /**
   * 补发指定档位的奖励
   */
  private async claimBonusTier(accountSequence: string, bonusTier: number): Promise<void> {
    // 1. 查询待领取的记录
    const pendingRecords = await this.unallocatedContributionRepository.findPendingBonusByAccountSequence(
      accountSequence,
      bonusTier,
    );

    if (pendingRecords.length === 0) {
      this.logger.debug(`No pending T${bonusTier} bonus records for ${accountSequence}`);
      return;
    }

    this.logger.log(
      `Claiming ${pendingRecords.length} T${bonusTier} bonus records for ${accountSequence}`,
    );

    // 2. 创建贡献值记录
    const contributionRecords: ContributionRecordAggregate[] = [];
    for (const pending of pendingRecords) {
      const record = new ContributionRecordAggregate({
        accountSequence: accountSequence,
        sourceType: ContributionSourceType.TEAM_BONUS,
        sourceAdoptionId: pending.sourceAdoptionId,
        sourceAccountSequence: pending.sourceAccountSequence,
        treeCount: 0, // 补发记录不记录树数
        baseContribution: new ContributionAmount(0),
        distributionRate: DistributionRate.BONUS_PER,
        bonusTier: bonusTier,
        amount: pending.amount,
        effectiveDate: pending.effectiveDate,
        expireDate: pending.expireDate,
      });
      contributionRecords.push(record);
    }

    // 3. 保存贡献值记录
    const savedRecords = await this.contributionRecordRepository.saveMany(contributionRecords);

    // 4. 更新用户的贡献值账户
    let totalAmount = new ContributionAmount(0);
    for (const pending of pendingRecords) {
      totalAmount = new ContributionAmount(totalAmount.value.plus(pending.amount.value));
    }

    await this.contributionAccountRepository.updateContribution(
      accountSequence,
      ContributionSourceType.TEAM_BONUS,
      totalAmount,
      null,
      bonusTier,
    );

    // 5. 标记待领取记录为已分配
    const pendingIds = pendingRecords.map((r) => r.id);
    await this.unallocatedContributionRepository.claimBonusRecords(pendingIds, accountSequence);

    // 6. 发布事件到 Kafka（通过 Outbox）
    await this.publishBonusClaimEvents(accountSequence, savedRecords, pendingRecords);

    this.logger.log(
      `Claimed T${bonusTier} bonus for ${accountSequence}: ` +
        `${pendingRecords.length} records, total amount: ${totalAmount.value.toString()}`,
    );
  }

  /**
   * 发布补发事件
   */
  private async publishBonusClaimEvents(
    accountSequence: string,
    savedRecords: ContributionRecordAggregate[],
    pendingRecords: UnallocatedContribution[],
  ): Promise<void> {
    // 1. 发布贡献值记录同步事件（用于 mining-admin-service CDC）
    for (const record of savedRecords) {
      const event = new ContributionRecordSyncedEvent(
        record.id!,
        record.accountSequence,
        record.sourceType,
        record.sourceAdoptionId,
        record.sourceAccountSequence,
        record.treeCount,
        record.baseContribution.value.toString(),
        record.distributionRate.value.toString(),
        record.levelDepth,
        record.bonusTier,
        record.amount.value.toString(),
        record.effectiveDate,
        record.expireDate,
        record.isExpired,
        record.createdAt,
      );

      await this.outboxRepository.save({
        aggregateType: ContributionRecordSyncedEvent.AGGREGATE_TYPE,
        aggregateId: record.id!.toString(),
        eventType: ContributionRecordSyncedEvent.EVENT_TYPE,
        payload: event.toPayload(),
      });
    }

    // 2. 发布补发事件到 mining-wallet-service
    const userContributions = savedRecords.map((record, index) => ({
      accountSequence: record.accountSequence,
      contributionType: 'TEAM_BONUS',
      amount: record.amount.value.toString(),
      bonusTier: record.bonusTier,
      effectiveDate: record.effectiveDate.toISOString(),
      expireDate: record.expireDate.toISOString(),
      sourceAdoptionId: record.sourceAdoptionId.toString(),
      sourceAccountSequence: record.sourceAccountSequence,
      isBackfill: true, // 标记为补发
    }));

    const eventId = `bonus-claim-${accountSequence}-${Date.now()}`;
    const payload = {
      eventType: 'BonusClaimed',
      eventId,
      timestamp: new Date().toISOString(),
      payload: {
        accountSequence,
        bonusTier: savedRecords[0]?.bonusTier,
        claimedCount: savedRecords.length,
        userContributions,
      },
    };

    await this.outboxRepository.save({
      eventType: 'BonusClaimed',
      topic: 'contribution.bonus.claimed',
      key: accountSequence,
      payload,
      aggregateId: accountSequence,
      aggregateType: 'ContributionAccount',
    });
  }
}
