import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { MiningAccountRepository } from '../../infrastructure/persistence/repositories/mining-account.repository';
import { MiningConfigRepository } from '../../infrastructure/persistence/repositories/mining-config.repository';
import { PriceSnapshotRepository } from '../../infrastructure/persistence/repositories/price-snapshot.repository';
import { OutboxRepository } from '../../infrastructure/persistence/repositories/outbox.repository';
import { SystemMiningAccountRepository } from '../../infrastructure/persistence/repositories/system-mining-account.repository';
import { PrismaService } from '../../infrastructure/persistence/prisma/prisma.service';
import { UnitOfWork, TransactionClient } from '../../infrastructure/persistence/unit-of-work/unit-of-work';
import { RedisService } from '../../infrastructure/redis/redis.service';
import { MiningCalculatorService } from '../../domain/services/mining-calculator.service';
import { ShareAmount } from '../../domain/value-objects/share-amount.vo';
import Decimal from 'decimal.js';

// 系统账户类型常量
const HEADQUARTERS_ACCOUNT_TYPE = 'HEADQUARTERS';

/**
 * 挖矿分配服务
 * 负责每秒执行挖矿分配
 *
 * 策略：
 * - 每秒：计算并更新账户余额
 * - 每分钟：写入汇总的MiningRecord记录
 *
 * 注意：销毁机制由 trading-service 负责，mining-service 不再执行销毁
 */
@Injectable()
export class MiningDistributionService {
  private readonly logger = new Logger(MiningDistributionService.name);
  private readonly calculator = new MiningCalculatorService();
  private readonly LOCK_KEY = 'mining:distribution:lock';
  private readonly MINUTE_ACCUMULATOR_PREFIX = 'mining:minute:accumulator:';

  constructor(
    private readonly miningAccountRepository: MiningAccountRepository,
    private readonly miningConfigRepository: MiningConfigRepository,
    private readonly priceSnapshotRepository: PriceSnapshotRepository,
    private readonly outboxRepository: OutboxRepository,
    private readonly systemMiningAccountRepository: SystemMiningAccountRepository,
    private readonly prisma: PrismaService,
    private readonly unitOfWork: UnitOfWork,
    private readonly redis: RedisService,
    private readonly configService: ConfigService,
  ) {}

  /**
   * 执行每秒挖矿分配
   * - 每秒更新账户余额
   * - 每分钟写入汇总MiningRecord
   *
   * 重要变更：
   * - 使用全网理论算力作为分母（包含用户有效算力 + 系统账户算力 + 未解锁算力）
   * - 系统账户（运营/省/市）参与挖矿，有独立的挖矿记录
   * - 未解锁算力参与挖矿，收益归总部账户，有明细记录
   */
  async executeSecondDistribution(): Promise<void> {
    // 获取分布式锁（锁定时间900ms）
    const lockValue = await this.redis.acquireLock(this.LOCK_KEY, 0.9);
    if (!lockValue) {
      return;
    }

    try {
      const config = await this.miningConfigRepository.getConfig();
      if (!config || !config.isActive) {
        return;
      }

      const currentSecond = this.getCurrentSecond();
      const currentMinute = this.getCurrentMinute();
      const isMinuteEnd = currentSecond.getSeconds() === 59;

      // 检查是否已处理过这一秒
      const processedKey = `mining:processed:${currentSecond.getTime()}`;
      if (await this.redis.get(processedKey)) {
        return;
      }

      // 使用预计算的每秒分配量
      const secondDistribution = config.secondDistribution;

      if (secondDistribution.isZero()) {
        return;
      }

      // 使用全网理论算力作为分母
      // 理论算力 = 用户有效算力 + 系统账户算力 + 未解锁算力
      const networkTotalContribution = config.networkTotalContribution;
      if (networkTotalContribution.isZero()) {
        // 如果全网理论算力未同步，则回退到用户有效算力
        this.logger.warn('Network total contribution is zero, falling back to user contribution');
        const userContribution = await this.miningAccountRepository.getTotalContribution();
        if (userContribution.isZero()) {
          return;
        }
        // 使用用户有效算力（旧逻辑）
        await this.distributeToUsersOnly(secondDistribution, userContribution, currentSecond, currentMinute, isMinuteEnd, config);
        return;
      }

      let totalDistributed = ShareAmount.zero();
      let userParticipantCount = 0;
      let systemParticipantCount = 0;
      let pendingParticipantCount = 0;

      // 收集需要累积到Redis的数据（事务外执行）
      const redisAccumulateData: Array<{
        accountSequence: string;
        reward: ShareAmount;
        accountContribution: ShareAmount;
      }> = [];

      // 使用统一事务执行所有数据库操作
      const result = await this.unitOfWork.executeInTransaction(
        async (tx) => {
          // 1. 分配给用户账户
          let page = 1;
          const pageSize = 1000;

          while (true) {
            const { data: accounts, total } = await this.miningAccountRepository.findAllWithContribution(page, pageSize);
            if (accounts.length === 0) break;

            for (const account of accounts) {
              const reward = this.calculator.calculateUserMiningReward(
                account.totalContribution,
                networkTotalContribution,
                secondDistribution,
              );

              if (!reward.isZero()) {
                // 每秒更新账户余额
                account.mine(reward, `秒挖矿 ${currentSecond.getTime()}`);
                await this.miningAccountRepository.save(account, tx, { skipContributionUpdate: true });

                // 收集Redis累积数据（事务外执行）
                redisAccumulateData.push({
                  accountSequence: account.accountSequence,
                  reward,
                  accountContribution: account.totalContribution,
                });

                totalDistributed = totalDistributed.add(reward);
                userParticipantCount++;
              }
            }

            if (page * pageSize >= total) break;
            page++;
          }

          // 2. 分配给系统账户（运营/省/市）和待解锁算力 - 复用同一事务
          const systemAndPendingResult = await this.distributeToSystemAndPendingInTx(
            tx,
            currentSecond,
            currentMinute,
            networkTotalContribution,
            secondDistribution,
          );

          return systemAndPendingResult;
        },
        {
          maxWait: 10000, // 10秒等待获取事务
          timeout: 60000, // 60秒事务超时
        },
      );

      totalDistributed = totalDistributed.add(result.systemDistributed);
      totalDistributed = totalDistributed.add(result.pendingDistributed);
      systemParticipantCount = result.systemParticipantCount;
      pendingParticipantCount = result.pendingParticipantCount;

      // 事务成功后，批量累积Redis数据（非事务性操作）
      for (const data of redisAccumulateData) {
        await this.accumulateMinuteData(
          data.accountSequence,
          currentMinute,
          data.reward,
          data.accountContribution,
          networkTotalContribution,
          secondDistribution,
        );
      }

      // 累积系统账户和待解锁算力的Redis数据
      for (const data of result.systemRedisData) {
        await this.accumulateSystemMinuteData(
          data.accountType,
          data.regionCode,
          currentMinute,
          data.reward,
          data.contribution,
          networkTotalContribution,
          secondDistribution,
        );
      }

      // 为 HEADQUARTERS 累积待解锁算力的挖矿数据
      if (result.pendingRedisData.length > 0) {
        const headquartersTotalReward = result.pendingRedisData.reduce(
          (sum, data) => sum.add(data.reward),
          new ShareAmount(0),
        );
        const headquartersTotalContribution = result.pendingRedisData.reduce(
          (sum, data) => sum.add(new ShareAmount(data.pending.amount)),
          new ShareAmount(0),
        );
        await this.accumulateSystemMinuteData(
          HEADQUARTERS_ACCOUNT_TYPE,
          null, // HEADQUARTERS 没有 regionCode
          currentMinute,
          headquartersTotalReward,
          headquartersTotalContribution,
          networkTotalContribution,
          secondDistribution,
        );
      }

      for (const data of result.pendingRedisData) {
        await this.accumulatePendingMinuteData(
          data.pendingId,
          currentMinute,
          data.reward,
          data.pending,
          networkTotalContribution,
          secondDistribution,
        );
      }

      // 每分钟结束时，写入汇总的MiningRecord
      if (isMinuteEnd) {
        await this.writeMinuteRecords(currentMinute);
        await this.writeSystemMinuteRecords(currentMinute);
        await this.writePendingMinuteRecords(currentMinute);
      }

      // 更新配置
      const newRemaining = config.remainingDistribution.subtract(totalDistributed);
      await this.miningConfigRepository.updateRemainingDistribution(newRemaining);

      // 标记已处理（过期时间2秒）
      await this.redis.set(processedKey, '1', 2);

      // 每分钟记录一次日志并发布 CDC 事件
      if (isMinuteEnd) {
        this.logger.log(
          `Minute distribution: total=${totalDistributed.toFixed(8)}, users=${userParticipantCount}, system=${systemParticipantCount}, pending=${pendingParticipantCount}`,
        );

        // 发布 MiningConfigUpdated 事件（供 mining-admin-service 同步）
        await this.publishMiningConfigUpdated(config, newRemaining);
      }
    } catch (error) {
      this.logger.error('Failed to execute second distribution', error);
    } finally {
      await this.redis.releaseLock(this.LOCK_KEY, lockValue);
    }
  }

  /**
   * 仅分配给用户（旧逻辑，当全网理论算力未同步时使用）
   */
  private async distributeToUsersOnly(
    secondDistribution: ShareAmount,
    totalContribution: ShareAmount,
    currentSecond: Date,
    currentMinute: Date,
    isMinuteEnd: boolean,
    config: any,
  ): Promise<void> {
    let page = 1;
    const pageSize = 1000;
    let totalDistributed = ShareAmount.zero();
    let participantCount = 0;

    while (true) {
      const { data: accounts, total } = await this.miningAccountRepository.findAllWithContribution(page, pageSize);
      if (accounts.length === 0) break;

      for (const account of accounts) {
        const reward = this.calculator.calculateUserMiningReward(
          account.totalContribution,
          totalContribution,
          secondDistribution,
        );

        if (!reward.isZero()) {
          account.mine(reward, `秒挖矿 ${currentSecond.getTime()}`);
          await this.miningAccountRepository.save(account, undefined, { skipContributionUpdate: true });

          await this.accumulateMinuteData(
            account.accountSequence,
            currentMinute,
            reward,
            account.totalContribution,
            totalContribution,
            secondDistribution,
          );

          totalDistributed = totalDistributed.add(reward);
          participantCount++;
        }
      }

      if (page * pageSize >= total) break;
      page++;
    }

    if (isMinuteEnd) {
      await this.writeMinuteRecords(currentMinute);
    }

    const newRemaining = config.remainingDistribution.subtract(totalDistributed);
    await this.miningConfigRepository.updateRemainingDistribution(newRemaining);

    const processedKey = `mining:processed:${currentSecond.getTime()}`;
    await this.redis.set(processedKey, '1', 2);

    if (isMinuteEnd) {
      this.logger.log(
        `Minute distribution (fallback): distributed=${totalDistributed.toFixed(8)}, participants=${participantCount}`,
      );
    }
  }

  /**
   * 在单个事务中分配系统账户和待解锁算力的挖矿收益
   * 解决并发事务更新同一行导致的行锁竞争问题
   */
  private async distributeToSystemAndPending(
    currentSecond: Date,
    currentMinute: Date,
    networkTotalContribution: ShareAmount,
    secondDistribution: ShareAmount,
  ): Promise<{
    systemDistributed: ShareAmount;
    pendingDistributed: ShareAmount;
    systemParticipantCount: number;
    pendingParticipantCount: number;
  }> {
    let systemDistributed = ShareAmount.zero();
    let pendingDistributed = ShareAmount.zero();
    let systemParticipantCount = 0;
    let pendingParticipantCount = 0;

    // 预先获取所有需要的数据
    const systemAccounts = await this.systemMiningAccountRepository.findAll();
    const pendingContributions = await this.prisma.pendingContributionMining.findMany({
      where: { isExpired: false },
    });

    // 计算所有系统账户的挖矿奖励
    const systemRewards: Array<{
      accountType: string;
      regionCode: string | null;
      reward: ShareAmount;
      contribution: ShareAmount;
      memo: string;
    }> = [];

    for (const systemAccount of systemAccounts) {
      // 总部账户不直接参与挖矿，它只接收未解锁算力的收益
      if (systemAccount.accountType === HEADQUARTERS_ACCOUNT_TYPE) {
        continue;
      }

      if (systemAccount.totalContribution.isZero()) {
        continue;
      }

      const reward = this.calculator.calculateUserMiningReward(
        systemAccount.totalContribution,
        networkTotalContribution,
        secondDistribution,
      );

      if (!reward.isZero()) {
        systemRewards.push({
          accountType: systemAccount.accountType,
          regionCode: systemAccount.regionCode,
          reward,
          contribution: systemAccount.totalContribution,
          memo: `秒挖矿 ${currentSecond.getTime()}`,
        });
      }
    }

    // 计算所有待解锁算力的挖矿奖励（归总部账户）
    const pendingRewards: Array<{
      pending: any;
      reward: ShareAmount;
      memo: string;
    }> = [];

    for (const pending of pendingContributions) {
      const contribution = new ShareAmount(pending.amount);
      if (contribution.isZero()) continue;

      const reward = this.calculator.calculateUserMiningReward(
        contribution,
        networkTotalContribution,
        secondDistribution,
      );

      if (!reward.isZero()) {
        pendingRewards.push({
          pending,
          reward,
          memo: `未解锁算力挖矿 - 来源:${pending.sourceAccountSequence} 类型:${pending.contributionType} 应归:${pending.wouldBeAccountSequence || '无'}`,
        });
      }
    }

    // 如果没有需要处理的数据，直接返回
    if (systemRewards.length === 0 && pendingRewards.length === 0) {
      return { systemDistributed, pendingDistributed, systemParticipantCount, pendingParticipantCount };
    }

    // 在单个事务中执行所有系统账户和待解锁算力的挖矿
    await this.prisma.$transaction(async (tx) => {
      // 处理系统账户挖矿
      for (const { accountType, regionCode, reward, memo } of systemRewards) {
        await this.systemMiningAccountRepository.mine(accountType as any, regionCode, reward, memo, tx);
        systemDistributed = systemDistributed.add(reward);
        systemParticipantCount++;
      }

      // 处理待解锁算力挖矿（归总部账户）
      // 计算总部账户的总收益
      let headquartersTotal = ShareAmount.zero();
      const headquartersMemos: string[] = [];

      for (const { reward, memo } of pendingRewards) {
        headquartersTotal = headquartersTotal.add(reward);
        headquartersMemos.push(memo);
        pendingDistributed = pendingDistributed.add(reward);
        pendingParticipantCount++;
      }

      // 一次性更新总部账户（而不是每个待解锁算力单独更新）
      if (!headquartersTotal.isZero()) {
        await this.systemMiningAccountRepository.mine(
          HEADQUARTERS_ACCOUNT_TYPE as any,
          null, // HEADQUARTERS 没有 regionCode
          headquartersTotal,
          `秒挖矿 ${currentSecond.getTime()} - 待解锁算力汇总 (${pendingRewards.length}笔)`,
          tx,
        );
      }
    });

    // 事务成功后，累积 Redis 数据（Redis 操作不需要在事务内）
    for (const { accountType, regionCode, reward, contribution } of systemRewards) {
      await this.accumulateSystemMinuteData(
        accountType,
        regionCode,
        currentMinute,
        reward,
        contribution,
        networkTotalContribution,
        secondDistribution,
      );
    }

    // 为 HEADQUARTERS 累积待解锁算力的挖矿数据
    if (pendingRewards.length > 0) {
      const headquartersTotalReward = pendingRewards.reduce(
        (sum, { reward }) => sum.add(reward),
        new ShareAmount(0),
      );
      const headquartersTotalContribution = pendingRewards.reduce(
        (sum, { pending }) => sum.add(new ShareAmount(pending.amount)),
        new ShareAmount(0),
      );
      await this.accumulateSystemMinuteData(
        HEADQUARTERS_ACCOUNT_TYPE,
        null, // HEADQUARTERS 没有 regionCode
        currentMinute,
        headquartersTotalReward,
        headquartersTotalContribution,
        networkTotalContribution,
        secondDistribution,
      );
    }

    for (const { pending, reward } of pendingRewards) {
      await this.accumulatePendingMinuteData(
        pending.id,
        currentMinute,
        reward,
        pending,
        networkTotalContribution,
        secondDistribution,
      );
    }

    return { systemDistributed, pendingDistributed, systemParticipantCount, pendingParticipantCount };
  }

  /**
   * 在给定事务中分配系统账户和待解锁算力的挖矿收益
   * 返回分配结果和需要累积到Redis的数据
   */
  private async distributeToSystemAndPendingInTx(
    tx: TransactionClient,
    currentSecond: Date,
    currentMinute: Date,
    networkTotalContribution: ShareAmount,
    secondDistribution: ShareAmount,
  ): Promise<{
    systemDistributed: ShareAmount;
    pendingDistributed: ShareAmount;
    systemParticipantCount: number;
    pendingParticipantCount: number;
    systemRedisData: Array<{
      accountType: string;
      regionCode: string | null;
      reward: ShareAmount;
      contribution: ShareAmount;
    }>;
    pendingRedisData: Array<{
      pendingId: bigint;
      reward: ShareAmount;
      pending: any;
    }>;
  }> {
    let systemDistributed = ShareAmount.zero();
    let pendingDistributed = ShareAmount.zero();
    let systemParticipantCount = 0;
    let pendingParticipantCount = 0;
    const systemRedisData: Array<{
      accountType: string;
      regionCode: string | null;
      reward: ShareAmount;
      contribution: ShareAmount;
    }> = [];
    const pendingRedisData: Array<{
      pendingId: bigint;
      reward: ShareAmount;
      pending: any;
    }> = [];

    // 预先获取所有需要的数据
    const systemAccounts = await this.systemMiningAccountRepository.findAll();
    const pendingContributions = await tx.pendingContributionMining.findMany({
      where: { isExpired: false },
    });

    // 计算所有系统账户的挖矿奖励
    const systemRewards: Array<{
      accountType: string;
      regionCode: string | null;
      reward: ShareAmount;
      contribution: ShareAmount;
      memo: string;
    }> = [];

    for (const systemAccount of systemAccounts) {
      // 总部账户不直接参与挖矿，它只接收未解锁算力的收益
      if (systemAccount.accountType === HEADQUARTERS_ACCOUNT_TYPE) {
        continue;
      }

      if (systemAccount.totalContribution.isZero()) {
        continue;
      }

      const reward = this.calculator.calculateUserMiningReward(
        systemAccount.totalContribution,
        networkTotalContribution,
        secondDistribution,
      );

      if (!reward.isZero()) {
        systemRewards.push({
          accountType: systemAccount.accountType,
          regionCode: systemAccount.regionCode,
          reward,
          contribution: systemAccount.totalContribution,
          memo: `秒挖矿 ${currentSecond.getTime()}`,
        });
      }
    }

    // 计算所有待解锁算力的挖矿奖励（归总部账户）
    const pendingRewards: Array<{
      pending: any;
      reward: ShareAmount;
      memo: string;
    }> = [];

    for (const pending of pendingContributions) {
      const contribution = new ShareAmount(pending.amount);
      if (contribution.isZero()) continue;

      const reward = this.calculator.calculateUserMiningReward(
        contribution,
        networkTotalContribution,
        secondDistribution,
      );

      if (!reward.isZero()) {
        pendingRewards.push({
          pending,
          reward,
          memo: `未解锁算力挖矿 - 来源:${pending.sourceAccountSequence} 类型:${pending.contributionType} 应归:${pending.wouldBeAccountSequence || '无'}`,
        });
      }
    }

    // 如果没有需要处理的数据，直接返回
    if (systemRewards.length === 0 && pendingRewards.length === 0) {
      return {
        systemDistributed,
        pendingDistributed,
        systemParticipantCount,
        pendingParticipantCount,
        systemRedisData,
        pendingRedisData,
      };
    }

    // 处理系统账户挖矿（复用外部事务）
    for (const { accountType, regionCode, reward, contribution, memo } of systemRewards) {
      await this.systemMiningAccountRepository.mine(accountType as any, regionCode, reward, memo, tx);
      systemDistributed = systemDistributed.add(reward);
      systemParticipantCount++;
      systemRedisData.push({ accountType, regionCode, reward, contribution });
    }

    // 处理待解锁算力挖矿（归总部账户）
    // 计算总部账户的总收益
    let headquartersTotal = ShareAmount.zero();

    for (const { pending, reward } of pendingRewards) {
      headquartersTotal = headquartersTotal.add(reward);
      pendingDistributed = pendingDistributed.add(reward);
      pendingParticipantCount++;
      pendingRedisData.push({ pendingId: pending.id, reward, pending });
    }

    // 一次性更新总部账户（而不是每个待解锁算力单独更新）
    if (!headquartersTotal.isZero()) {
      await this.systemMiningAccountRepository.mine(
        HEADQUARTERS_ACCOUNT_TYPE as any,
        null, // HEADQUARTERS 没有 regionCode
        headquartersTotal,
        `秒挖矿 ${currentSecond.getTime()} - 待解锁算力汇总 (${pendingRewards.length}笔)`,
        tx,
      );
    }

    return {
      systemDistributed,
      pendingDistributed,
      systemParticipantCount,
      pendingParticipantCount,
      systemRedisData,
      pendingRedisData,
    };
  }

  /**
   * 累积每分钟的挖矿数据到Redis
   */
  private async accumulateMinuteData(
    accountSequence: string,
    minuteTime: Date,
    reward: ShareAmount,
    accountContribution: ShareAmount,
    totalContribution: ShareAmount,
    secondDistribution: ShareAmount,
  ): Promise<void> {
    const key = `${this.MINUTE_ACCUMULATOR_PREFIX}${minuteTime.getTime()}:${accountSequence}`;
    const existing = await this.redis.get(key);

    let accumulated: {
      minedAmount: string;
      contributionRatio: string;
      totalContribution: string;
      secondDistribution: string;
      secondCount: number;
    };

    if (existing) {
      accumulated = JSON.parse(existing);
      accumulated.minedAmount = new Decimal(accumulated.minedAmount).plus(reward.value).toString();
      accumulated.secondCount += 1;
      // 更新为最新的贡献比例
      accumulated.contributionRatio = accountContribution.value.dividedBy(totalContribution.value).toString();
      accumulated.totalContribution = totalContribution.value.toString();
      accumulated.secondDistribution = secondDistribution.value.toString();
    } else {
      accumulated = {
        minedAmount: reward.value.toString(),
        contributionRatio: accountContribution.value.dividedBy(totalContribution.value).toString(),
        totalContribution: totalContribution.value.toString(),
        secondDistribution: secondDistribution.value.toString(),
        secondCount: 1,
      };
    }

    // 设置过期时间为2分钟，确保即使处理失败也能清理
    await this.redis.set(key, JSON.stringify(accumulated), 120);
  }

  /**
   * 累积系统账户每分钟的挖矿数据到Redis
   * @param accountType 账户类型（OPERATION/PROVINCE/CITY/HEADQUARTERS）
   * @param regionCode 区域代码（省市代码，如 440000, 440100）
   */
  private async accumulateSystemMinuteData(
    accountType: string,
    regionCode: string | null,
    minuteTime: Date,
    reward: ShareAmount,
    accountContribution: ShareAmount,
    totalContribution: ShareAmount,
    secondDistribution: ShareAmount,
  ): Promise<void> {
    // Redis key 使用 accountType:regionCode 格式区分不同的省市账户
    const keyId = regionCode ? `${accountType}:${regionCode}` : accountType;
    const key = `mining:system:minute:${minuteTime.getTime()}:${keyId}`;
    const existing = await this.redis.get(key);

    let accumulated: {
      accountType: string;
      regionCode: string | null;
      minedAmount: string;
      contributionRatio: string;
      totalContribution: string;
      secondDistribution: string;
      accountContribution: string;
      secondCount: number;
    };

    if (existing) {
      accumulated = JSON.parse(existing);
      accumulated.minedAmount = new Decimal(accumulated.minedAmount).plus(reward.value).toString();
      accumulated.secondCount += 1;
      accumulated.contributionRatio = accountContribution.value.dividedBy(totalContribution.value).toString();
      accumulated.totalContribution = totalContribution.value.toString();
      accumulated.secondDistribution = secondDistribution.value.toString();
      accumulated.accountContribution = accountContribution.value.toString();
    } else {
      accumulated = {
        accountType,
        regionCode,
        minedAmount: reward.value.toString(),
        contributionRatio: accountContribution.value.dividedBy(totalContribution.value).toString(),
        totalContribution: totalContribution.value.toString(),
        secondDistribution: secondDistribution.value.toString(),
        accountContribution: accountContribution.value.toString(),
        secondCount: 1,
      };
    }

    await this.redis.set(key, JSON.stringify(accumulated), 120);
  }

  /**
   * 累积待解锁算力每分钟的挖矿数据到Redis
   */
  private async accumulatePendingMinuteData(
    pendingId: bigint,
    minuteTime: Date,
    reward: ShareAmount,
    pending: any,
    networkTotalContribution: ShareAmount,
    secondDistribution: ShareAmount,
  ): Promise<void> {
    const key = `mining:pending:minute:${minuteTime.getTime()}:${pendingId.toString()}`;
    const existing = await this.redis.get(key);

    const contribution = new Decimal(pending.amount);
    let accumulated: {
      minedAmount: string;
      contributionRatio: string;
      networkTotalContribution: string;
      secondDistribution: string;
      contributionAmount: string;
      sourceAdoptionId: string;
      sourceAccountSequence: string;
      wouldBeAccountSequence: string | null;
      contributionType: string;
      secondCount: number;
    };

    if (existing) {
      accumulated = JSON.parse(existing);
      accumulated.minedAmount = new Decimal(accumulated.minedAmount).plus(reward.value).toString();
      accumulated.secondCount += 1;
      accumulated.contributionRatio = contribution.dividedBy(networkTotalContribution.value).toString();
      accumulated.networkTotalContribution = networkTotalContribution.value.toString();
      accumulated.secondDistribution = secondDistribution.value.toString();
    } else {
      accumulated = {
        minedAmount: reward.value.toString(),
        contributionRatio: contribution.dividedBy(networkTotalContribution.value).toString(),
        networkTotalContribution: networkTotalContribution.value.toString(),
        secondDistribution: secondDistribution.value.toString(),
        contributionAmount: contribution.toString(),
        sourceAdoptionId: pending.sourceAdoptionId.toString(),
        sourceAccountSequence: pending.sourceAccountSequence,
        wouldBeAccountSequence: pending.wouldBeAccountSequence,
        contributionType: pending.contributionType,
        secondCount: 1,
      };
    }

    await this.redis.set(key, JSON.stringify(accumulated), 120);
  }

  /**
   * 写入系统账户每分钟汇总的挖矿记录
   * accountType: OPERATION/PROVINCE/CITY/HEADQUARTERS
   * regionCode: 区域代码（省市代码）
   */
  private async writeSystemMinuteRecords(minuteTime: Date): Promise<void> {
    try {
      const pattern = `mining:system:minute:${minuteTime.getTime()}:*`;
      const keys = await this.redis.keys(pattern);

      for (const key of keys) {
        const data = await this.redis.get(key);
        if (!data) continue;

        const accumulated = JSON.parse(data);
        // 从 accumulated 中获取 accountType 和 regionCode
        const accountType = accumulated.accountType;
        const regionCode = accumulated.regionCode;

        // 使用 repository 的 saveMinuteRecord 方法（支持 upsert）
        await this.systemMiningAccountRepository.saveMinuteRecord(
          accountType as any,
          regionCode,
          minuteTime,
          new ShareAmount(accumulated.contributionRatio),
          new ShareAmount(accumulated.totalContribution),
          new ShareAmount(accumulated.secondDistribution),
          new ShareAmount(accumulated.minedAmount),
        );

        await this.redis.del(key);
      }
    } catch (error) {
      this.logger.error('Failed to write system minute records', error);
    }
  }

  /**
   * 写入待解锁算力每分钟汇总的挖矿记录
   */
  private async writePendingMinuteRecords(minuteTime: Date): Promise<void> {
    try {
      const pattern = `mining:pending:minute:${minuteTime.getTime()}:*`;
      const keys = await this.redis.keys(pattern);

      for (const key of keys) {
        const data = await this.redis.get(key);
        if (!data) continue;

        const accumulated = JSON.parse(data);
        const pendingId = BigInt(key.split(':').pop()!);

        await this.prisma.pendingMiningRecord.create({
          data: {
            pendingContributionId: pendingId,
            miningMinute: minuteTime,
            sourceAdoptionId: BigInt(accumulated.sourceAdoptionId),
            sourceAccountSequence: accumulated.sourceAccountSequence,
            wouldBeAccountSequence: accumulated.wouldBeAccountSequence,
            contributionType: accumulated.contributionType,
            contributionAmount: new Decimal(accumulated.contributionAmount),
            networkTotalContribution: new Decimal(accumulated.networkTotalContribution),
            contributionRatio: new Decimal(accumulated.contributionRatio),
            secondDistribution: new Decimal(accumulated.secondDistribution),
            minedAmount: new Decimal(accumulated.minedAmount),
            allocatedTo: 'HEADQUARTERS',
          },
        });

        await this.redis.del(key);
      }
    } catch (error) {
      this.logger.error('Failed to write pending minute records', error);
    }
  }

  /**
   * 写入每分钟汇总的MiningRecord，并发布事件到 Kafka 通知 mining-wallet-service 扣减池余额
   */
  private async writeMinuteRecords(minuteTime: Date): Promise<void> {
    try {
      // 获取所有该分钟的累积数据
      const pattern = `${this.MINUTE_ACCUMULATOR_PREFIX}${minuteTime.getTime()}:*`;
      const keys = await this.redis.keys(pattern);

      let totalMinuteDistributed = new Decimal(0);
      const distributions: Array<{
        accountSequence: string;
        minedAmount: string;
        contributionRatio: string;
      }> = [];

      for (const key of keys) {
        const data = await this.redis.get(key);
        if (!data) continue;

        const accumulated = JSON.parse(data);
        const accountSequence = key.split(':').pop();

        if (!accountSequence) continue;

        // 写入汇总的MiningRecord
        await this.prisma.miningRecord.create({
          data: {
            accountSequence,
            miningMinute: minuteTime,
            contributionRatio: new Decimal(accumulated.contributionRatio),
            totalContribution: new Decimal(accumulated.totalContribution),
            secondDistribution: new Decimal(accumulated.secondDistribution),
            minedAmount: new Decimal(accumulated.minedAmount),
          },
        });

        // 累计该分钟的总分配量
        totalMinuteDistributed = totalMinuteDistributed.plus(accumulated.minedAmount);
        distributions.push({
          accountSequence,
          minedAmount: accumulated.minedAmount,
          contributionRatio: accumulated.contributionRatio,
        });

        // 删除已处理的累积数据
        await this.redis.del(key);
      }

      // 发布事件到 Kafka，通知 mining-wallet-service 扣减池余额
      if (!totalMinuteDistributed.isZero()) {
        await this.outboxRepository.create({
          aggregateType: 'MiningDistribution',
          aggregateId: minuteTime.toISOString(),
          eventType: 'MINING_MINUTE_DISTRIBUTED',
          topic: 'mining.distribution.completed',
          key: minuteTime.toISOString(),
          payload: {
            miningMinute: minuteTime.toISOString(),
            totalDistributed: totalMinuteDistributed.toFixed(18),
            participantCount: distributions.length,
            // 只记录汇总数据，不记录每个用户的明细（减少消息大小）
          },
        });

        this.logger.debug(
          `Published MINING_MINUTE_DISTRIBUTED event: minute=${minuteTime.toISOString()}, total=${totalMinuteDistributed.toFixed(8)}`,
        );
      }
    } catch (error) {
      this.logger.error('Failed to write minute records', error);
    }
  }

  /**
   * 获取当前秒（向下取整，去掉毫秒）
   */
  private getCurrentSecond(): Date {
    const now = new Date();
    now.setMilliseconds(0);
    return now;
  }

  /**
   * 获取当前分钟（向下取整，去掉秒和毫秒）
   */
  private getCurrentMinute(): Date {
    const now = new Date();
    now.setSeconds(0);
    now.setMilliseconds(0);
    return now;
  }

  // ===========================================================================
  // CDC 事件发布（供 mining-admin-service 同步）
  // ===========================================================================

  /**
   * 发布 MiningConfigUpdated 事件
   * 每分钟发布一次，同步挖矿配置到 mining-admin-service
   */
  private async publishMiningConfigUpdated(
    config: any,
    newRemaining: ShareAmount,
  ): Promise<void> {
    try {
      await this.outboxRepository.create({
        aggregateType: 'MiningConfig',
        aggregateId: config.id,
        eventType: 'MiningConfigUpdated',
        payload: {
          totalShares: config.totalShares?.toString() || '0',
          distributionPool: config.distributionPool?.toString() || '0',
          remainingDistribution: newRemaining?.toString() || '0',
          halvingPeriodYears: config.halvingPeriodYears || 2,
          currentEra: config.currentEra || 1,
          secondDistribution: config.secondDistribution?.toString() || '0',
          isActive: config.isActive || false,
          activatedAt: config.activatedAt?.toISOString() || null,
        },
      });
      this.logger.debug('Published MiningConfigUpdated event');
    } catch (error) {
      this.logger.error('Failed to publish MiningConfigUpdated event', error);
    }
  }

  /**
   * 发布 MiningAccountUpdated 事件
   * 在用户账户更新后发布，同步账户数据到 mining-admin-service
   */
  private async publishMiningAccountUpdated(
    accountSequence: string,
    totalMined: string,
    availableBalance: string,
    frozenBalance: string,
    totalContribution: string,
  ): Promise<void> {
    try {
      await this.outboxRepository.create({
        aggregateType: 'MiningAccount',
        aggregateId: accountSequence,
        eventType: 'MiningAccountUpdated',
        payload: {
          accountSequence,
          totalMined,
          availableBalance,
          frozenBalance,
          totalContribution,
        },
      });
    } catch (error) {
      this.logger.error(`Failed to publish MiningAccountUpdated event for ${accountSequence}`, error);
    }
  }
}
