import { Injectable, Logger, ConflictException, BadRequestException } from '@nestjs/common';
import { PrismaService } from '../../infrastructure/persistence/prisma/prisma.service';
import { MiningConfigRepository } from '../../infrastructure/persistence/repositories/mining-config.repository';
import { SystemMiningAccountRepository } from '../../infrastructure/persistence/repositories/system-mining-account.repository';
import { ShareAmount } from '../../domain/value-objects/share-amount.vo';
import Decimal from 'decimal.js';

/**
 * Excel 中的单行数据
 */
export interface BatchMiningItem {
  accountSequence: string;    // 注册ID (用户账号序列号)
  treeCount: number;          // 认种量（棵）
  miningStartDate: string;    // 挖矿开始时间
  batch: number;              // 批次
  preMineDays: number;        // 授权提前挖的天数（该批次比后续批次提前的天数）
  totalMiningDays?: number;   // 总挖矿天数（从挖矿开始日期到今天）
  remark?: string;            // 备注
}

/**
 * 批量补发请求
 */
export interface BatchMiningRequest {
  items: BatchMiningItem[];
  operatorId: string;
  operatorName: string;
  reason: string;
}

/**
 * 单个用户的补发结果
 */
export interface BatchMiningItemResult {
  accountSequence: string;
  batch: number;
  treeCount: number;
  userContribution: string;           // 用户算力 (70% 个人算力)
  networkContribution: string;        // 计算时的全网算力
  contributionRatio: string;          // 算力占比
  preMineDays: number;
  totalSeconds: string;
  amount: string;                     // 补发金额
  success: boolean;
  error?: string;
}

/**
 * 批量补发响应
 */
export interface BatchMiningResult {
  success: boolean;
  batchId: string;
  totalUsers: number;
  successCount: number;
  failedCount: number;
  totalAmount: string;
  operationAmount: string;      // 运营账户12%金额
  headquartersAmount: string;   // 总部账户18%金额
  results: BatchMiningItemResult[];
  message: string;
}

/**
 * 批量补发预览结果
 */
export interface BatchMiningPreviewResult {
  canExecute: boolean;
  alreadyExecuted: boolean;
  totalBatches: number;
  totalUsers: number;
  batches: {
    batch: number;
    users: {
      accountSequence: string;
      treeCount: number;
      preMineDays: number;
      userContribution: string;
      networkContribution: string;
      contributionRatio: string;
      totalSeconds: string;
      estimatedAmount: string;
    }[];
    batchTotalContribution: string;
    cumulativeNetworkContribution: string;
    batchTotalAmount: string;
  }[];
  grandTotalAmount: string;
  operationAmount: string;      // 运营账户12%金额
  headquartersAmount: string;   // 总部账户18%金额
  message: string;
  calculatedStartDate: string;  // 计算使用的起始日期 (YYYY-MM-DD)
}

// 常量
const DEFAULT_MINING_START_DATE = '2025-11-08';  // 默认起始日期（找不到有效数据时使用）
const BASE_CONTRIBUTION_PER_TREE = new Decimal('22617');  // 每棵树的基础算力
const SECONDS_PER_DAY = 86400;
// 每天产出的70%分给补发用户
const DAILY_DISTRIBUTION_RATIO = new Decimal('0.70');
// 每天产出的12%分给运营账户
const OPERATION_DISTRIBUTION_RATIO = new Decimal('0.12');
// 每天产出的18%分给总部账户
const HEADQUARTERS_DISTRIBUTION_RATIO = new Decimal('0.18');

/**
 * 挖矿阶段信息
 */
interface MiningPhase {
  phaseNumber: number;
  startDate: Date;
  endDate: Date;
  daysInPhase: number;
  participatingContribution: Decimal;  // 该阶段参与用户的总算力
  participatingBatches: number[];  // 参与该阶段的批次号
}

/**
 * 批量补发挖矿服务
 *
 * 核心逻辑（分阶段计算）:
 * 1. 根据各批次的挖矿开始时间，划分挖矿阶段
 * 2. 每天产出的70%固定分给当前参与的用户，12%分给运营账户，18%分给总部账户
 * 3. 用户算力 = 认种棵数 × 基础算力/棵
 * 4. 用户在每个阶段的收益 = 每日产出 × 70% × 阶段天数 × (用户算力 / 当前参与总算力)
 * 5. 用户总收益 = 用户参与的各阶段收益之和
 * 6. 运营/总部收益 = 每日产出 × 对应比例 × 总挖矿天数
 */
@Injectable()
export class BatchMiningService {
  private readonly logger = new Logger(BatchMiningService.name);

  constructor(
    private readonly prisma: PrismaService,
    private readonly miningConfigRepository: MiningConfigRepository,
    private readonly systemMiningAccountRepository: SystemMiningAccountRepository,
  ) {}

  /**
   * 检查批量补发是否已经执行过
   */
  async hasExecuted(): Promise<boolean> {
    this.logger.log('[hasExecuted] 检查批量补发是否已执行...');
    const record = await this.prisma.batchMiningExecution.findFirst();
    this.logger.log(`[hasExecuted] 结果: ${!!record}`);
    return !!record;
  }

  /**
   * 预览批量补发（计算但不执行）
   *
   * 分阶段计算逻辑：
   * - 阶段1（3天）：只有批次1在挖，批次1独占全部产出
   * - 阶段2（2天）：批次1+2一起挖，按算力比例分
   * - 阶段3（1天）：批次1+2+3一起挖，按算力比例分
   * - 阶段4（剩余天数）：所有批次一起挖到今天
   */
  async preview(items: BatchMiningItem[]): Promise<BatchMiningPreviewResult> {
    this.logger.log(`[preview] 开始预览批量补发, 共 ${items.length} 条数据`);
    this.logger.debug(`[preview] 数据样例: ${JSON.stringify(items.slice(0, 3))}`);

    // 检查是否已执行过
    const alreadyExecuted = await this.hasExecuted();
    if (alreadyExecuted) {
      const existing = await this.prisma.batchMiningExecution.findFirst();
      return {
        canExecute: false,
        alreadyExecuted: true,
        totalBatches: 0,
        totalUsers: 0,
        batches: [],
        grandTotalAmount: '0',
        operationAmount: '0',
        headquartersAmount: '0',
        message: `批量补发已于 ${existing?.executedAt?.toISOString()} 执行过，操作人: ${existing?.operatorName}`,
        calculatedStartDate: DEFAULT_MINING_START_DATE,
      };
    }

    // 获取挖矿配置
    this.logger.log('[preview] 获取挖矿配置...');
    const config = await this.miningConfigRepository.getConfig();
    if (!config) {
      this.logger.error('[preview] 挖矿配置不存在');
      throw new BadRequestException('挖矿配置不存在');
    }

    const secondDistribution = config.secondDistribution.value;
    this.logger.log(`[preview] 每秒分配量: ${secondDistribution.toString()}`);

    // 按批次分组并排序
    const batchGroups = this.groupByBatch(items);
    const sortedBatches = Array.from(batchGroups.keys()).sort((a, b) => a - b);
    this.logger.log(`[preview] 分组完成, 共 ${sortedBatches.length} 个批次: ${sortedBatches.join(', ')}`);

    // 计算每个批次的算力
    const batchContributions = new Map<number, Decimal>();
    for (const batchNum of sortedBatches) {
      const batchItems = batchGroups.get(batchNum)!;
      let batchTotal = new Decimal(0);
      for (const item of batchItems) {
        batchTotal = batchTotal.plus(this.calculateUserContribution(item.treeCount));
      }
      batchContributions.set(batchNum, batchTotal);
      this.logger.log(`[preview] 批次${batchNum}算力: ${batchTotal.toFixed(2)}`);
    }

    // 计算每个用户在每个批次中的算力
    // key: `${accountSequence}-${batch}`, value: 该用户在该批次的算力
    const userBatchContributions = new Map<string, Decimal>();
    for (const item of items) {
      const key = `${item.accountSequence}-${item.batch}`;
      const contribution = this.calculateUserContribution(item.treeCount);
      const existing = userBatchContributions.get(key);
      if (existing) {
        userBatchContributions.set(key, existing.plus(contribution));
      } else {
        userBatchContributions.set(key, contribution);
      }
    }

    // 计算每个用户的总算力（用于显示）
    const userContributions = new Map<string, Decimal>();
    for (const item of items) {
      const contribution = this.calculateUserContribution(item.treeCount);
      const existing = userContributions.get(item.accountSequence);
      if (existing) {
        userContributions.set(item.accountSequence, existing.plus(contribution));
      } else {
        userContributions.set(item.accountSequence, contribution);
      }
    }

    // 获取计算起始日期（从第一批次用户的挖矿开始时间获取，找不到则使用默认值）
    const calculatedStartDate = this.getCalculatedStartDate(items);
    this.logger.log(`[preview] 计算起始日期: ${calculatedStartDate}`);

    // 定义挖矿阶段
    // 阶段1: 批次1独挖 preMineDays 天
    // 阶段2: 批次1+2共挖 preMineDays 天
    // ...依次类推
    // 最后阶段: 所有批次共挖（剩余天数）
    const phases = this.buildMiningPhases(items, sortedBatches, batchContributions, calculatedStartDate);
    this.logger.log(`[preview] 挖矿阶段: ${JSON.stringify(phases.map(p => ({
      phase: p.phaseNumber,
      days: p.daysInPhase,
      batches: p.participatingBatches,
      participatingContribution: p.participatingContribution.toFixed(2)
    })))}`);

    // 每天补发额度 = 日产出 × 70%（用户）/ 12%（运营）/ 18%（总部）
    const dailyDistribution = secondDistribution.times(SECONDS_PER_DAY);
    const dailyAllocation = dailyDistribution.times(DAILY_DISTRIBUTION_RATIO);
    const dailyOperationAllocation = dailyDistribution.times(OPERATION_DISTRIBUTION_RATIO);
    const dailyHeadquartersAllocation = dailyDistribution.times(HEADQUARTERS_DISTRIBUTION_RATIO);
    this.logger.log(`[preview] 每日产出: ${dailyDistribution.toFixed(8)}, 用户70%: ${dailyAllocation.toFixed(8)}, 运营12%: ${dailyOperationAllocation.toFixed(8)}, 总部18%: ${dailyHeadquartersAllocation.toFixed(8)}`);

    // 计算总挖矿天数（用于系统账户分配计算）
    const totalMiningDays = phases.reduce((sum, p) => sum + p.daysInPhase, 0);

    // 计算每个用户在各阶段的收益
    // 使用 Set 记录已处理的用户，避免重复计算
    const userAmounts = new Map<string, Decimal>();
    for (const item of items) {
      userAmounts.set(item.accountSequence, new Decimal(0));
    }

    for (const phase of phases) {
      // 该阶段的总补发额 = 每日补发额度 × 阶段天数
      const phaseAllocation = dailyAllocation.times(phase.daysInPhase);

      // 用 Set 记录本阶段已处理的用户，避免重复
      const processedInPhase = new Set<string>();

      for (const item of items) {
        // 检查该用户的批次是否参与此阶段，且该用户在本阶段尚未处理
        if (phase.participatingBatches.includes(item.batch) && !processedInPhase.has(item.accountSequence)) {
          processedInPhase.add(item.accountSequence);

          // 计算用户在该阶段参与的批次中的总算力
          let userPhaseContribution = new Decimal(0);
          for (const batch of phase.participatingBatches) {
            const key = `${item.accountSequence}-${batch}`;
            const batchContrib = userBatchContributions.get(key);
            if (batchContrib) {
              userPhaseContribution = userPhaseContribution.plus(batchContrib);
            }
          }

          // 用户收益 = 阶段补发额 × (用户在该阶段的算力 / 当前参与总算力)
          const ratio = userPhaseContribution.dividedBy(phase.participatingContribution);
          const phaseAmount = phaseAllocation.times(ratio);

          const currentAmount = userAmounts.get(item.accountSequence)!;
          userAmounts.set(item.accountSequence, currentAmount.plus(phaseAmount));
        }
      }
    }

    // 构建返回结果
    const batchResults: BatchMiningPreviewResult['batches'] = [];

    // 计算用户总算力（用于显示）
    let totalUserContribution = new Decimal(0);
    for (const contribution of batchContributions.values()) {
      totalUserContribution = totalUserContribution.plus(contribution);
    }
    this.logger.log(`[preview] 用户总算力: ${totalUserContribution.toFixed(2)}`);

    // 计算 grandTotalAmount：所有用户的收益总和（去重）
    let grandTotalAmount = new Decimal(0);
    const processedUsers = new Set<string>();
    for (const [accountSequence, amount] of userAmounts) {
      if (!processedUsers.has(accountSequence)) {
        processedUsers.add(accountSequence);
        grandTotalAmount = grandTotalAmount.plus(amount);
      }
    }
    this.logger.log(`[preview] 计算的总金额: ${grandTotalAmount.toFixed(8)} (${processedUsers.size}个用户)`);

    for (const batchNum of sortedBatches) {
      const batchItems = batchGroups.get(batchNum)!;
      const batchContribution = batchContributions.get(batchNum)!;

      const users: BatchMiningPreviewResult['batches'][0]['users'] = [];
      let batchTotalAmount = new Decimal(0);
      // 跟踪本批次已处理的用户，避免重复累加金额
      const processedUsersInBatch = new Set<string>();

      for (const item of batchItems) {
        const userContribution = userContributions.get(item.accountSequence)!;
        const amount = userAmounts.get(item.accountSequence)!;
        // 显示用：用户算力占总用户算力的比例
        const ratio = userContribution.dividedBy(totalUserContribution);

        users.push({
          accountSequence: item.accountSequence,
          treeCount: item.treeCount,
          preMineDays: item.preMineDays,
          userContribution: userContribution.toFixed(10),
          networkContribution: totalUserContribution.toFixed(10),  // 显示总用户算力
          contributionRatio: ratio.toFixed(18),
          totalSeconds: (item.preMineDays * SECONDS_PER_DAY).toString(),
          estimatedAmount: amount.toFixed(8),
        });

        // 只在第一次遇到该用户时累加金额
        if (!processedUsersInBatch.has(item.accountSequence)) {
          processedUsersInBatch.add(item.accountSequence);
          batchTotalAmount = batchTotalAmount.plus(amount);
        }
      }

      // 计算到当前批次的累计算力
      let cumulativeContribution = new Decimal(0);
      for (const b of sortedBatches) {
        cumulativeContribution = cumulativeContribution.plus(batchContributions.get(b)!);
        if (b === batchNum) break;
      }

      batchResults.push({
        batch: batchNum,
        users,
        batchTotalContribution: batchContribution.toFixed(10),
        cumulativeNetworkContribution: cumulativeContribution.toFixed(10),
        batchTotalAmount: batchTotalAmount.toFixed(8),
      });
    }

    // 计算运营和总部账户的补发金额
    const operationAmount = dailyOperationAllocation.times(totalMiningDays);
    const headquartersAmount = dailyHeadquartersAllocation.times(totalMiningDays);

    const result: BatchMiningPreviewResult = {
      canExecute: true,
      alreadyExecuted: false,
      totalBatches: sortedBatches.length,
      totalUsers: items.length,
      batches: batchResults,
      grandTotalAmount: grandTotalAmount.toFixed(8),
      operationAmount: operationAmount.toFixed(8),
      headquartersAmount: headquartersAmount.toFixed(8),
      message: `预览成功: ${sortedBatches.length} 个批次, ${items.length} 个用户, 用户补发 ${grandTotalAmount.toFixed(8)}, 运营 ${operationAmount.toFixed(8)}, 总部 ${headquartersAmount.toFixed(8)}`,
      calculatedStartDate,
    };
    this.logger.log(`[preview] 预览完成: ${result.message}, 起始日期: ${calculatedStartDate}`);
    return result;
  }

  /**
   * 构建挖矿阶段
   *
   * 业务规则：
   * - 总挖矿天数 = 从第一批用户开始挖矿到今天的自然天数
   * - preMineDays 表示该批次比下一批次提前开始的天数
   * - 阶段1: 只有批次1，持续批次1的 preMineDays 天
   * - 阶段2: 批次1+2，持续批次2的 preMineDays 天
   * - ...
   * - 最后阶段: 所有批次一起挖，持续 (总天数 - 前面各阶段天数之和) 天
   */
  private buildMiningPhases(
    items: BatchMiningItem[],
    sortedBatches: number[],
    batchContributions: Map<number, Decimal>,
    startDateStr: string,  // 计算起始日期 (YYYY-MM-DD)
  ): MiningPhase[] {
    const phases: MiningPhase[] = [];

    if (sortedBatches.length === 0) {
      return phases;
    }

    // 计算总挖矿天数：从起始日期到今天
    const miningStartDate = new Date(startDateStr);
    miningStartDate.setHours(0, 0, 0, 0);
    const today = new Date();
    today.setHours(0, 0, 0, 0);
    const totalMiningDays = Math.floor((today.getTime() - miningStartDate.getTime()) / (1000 * 60 * 60 * 24));
    this.logger.log(`[buildMiningPhases] 总挖矿天数: ${totalMiningDays} (从${startDateStr}到今天)`);

    // 获取每个批次的提前天数
    const batchPreMineDays = new Map<number, number>();
    for (const item of items) {
      if (!batchPreMineDays.has(item.batch)) {
        batchPreMineDays.set(item.batch, item.preMineDays);
      }
    }

    this.logger.log(`[buildMiningPhases] 各批次提前天数: ${JSON.stringify(Object.fromEntries(batchPreMineDays))}`);

    let currentPhase = 1;
    let participatingBatches: number[] = [];
    let usedDays = 0;

    // 阶段划分：
    // - 阶段1到N-1: 各批次依次加入，每个阶段持续该批次的 preMineDays 天
    // - 最后阶段: 所有批次一起挖剩余天数
    for (let i = 0; i < sortedBatches.length; i++) {
      const currentBatch = sortedBatches[i];
      const currentPreMineDays = batchPreMineDays.get(currentBatch) || 0;
      const isLastBatch = i === sortedBatches.length - 1;

      // 当前批次加入参与列表
      participatingBatches.push(currentBatch);

      // 计算该阶段参与用户的总算力
      let participatingContribution = new Decimal(0);
      for (const batch of participatingBatches) {
        participatingContribution = participatingContribution.plus(batchContributions.get(batch) || 0);
      }

      let daysInPhase: number;
      if (isLastBatch) {
        // 最后阶段：所有批次一起挖剩余天数
        daysInPhase = totalMiningDays - usedDays;
        this.logger.log(`[buildMiningPhases] 最后阶段: 剩余天数 = ${totalMiningDays} - ${usedDays} = ${daysInPhase}`);
      } else {
        // 非最后阶段：使用该批次的 preMineDays
        daysInPhase = currentPreMineDays;
      }

      if (daysInPhase > 0) {
        phases.push({
          phaseNumber: currentPhase,
          startDate: new Date(),
          endDate: new Date(),
          daysInPhase,
          participatingContribution,
          participatingBatches: [...participatingBatches],
        });

        this.logger.log(`[buildMiningPhases] 阶段${currentPhase}: ${daysInPhase}天, 批次[${participatingBatches.join(',')}], 参与算力=${participatingContribution.toFixed(2)}`);
        currentPhase++;
        usedDays += daysInPhase;
      }
    }

    // 验证
    const expectedDailyAllocation = new Decimal('958.904109589041');
    const expectedTotal = expectedDailyAllocation.times(usedDays);
    this.logger.log(`[buildMiningPhases] 实际使用天数: ${usedDays}, 预期总金额: ${expectedTotal.toFixed(2)} (${usedDays}天 × 958.904/天)`);

    return phases;
  }

  /**
   * 执行批量补发
   */
  async execute(request: BatchMiningRequest): Promise<BatchMiningResult> {
    const { items, operatorId, operatorName, reason } = request;
    this.logger.log(`[execute] 开始执行批量补发, 操作人: ${operatorName}, 共 ${items.length} 条数据`);
    this.logger.log(`[execute] 原因: ${reason}`);

    // 检查是否已执行过
    const alreadyExecuted = await this.hasExecuted();
    if (alreadyExecuted) {
      const existing = await this.prisma.batchMiningExecution.findFirst();
      throw new ConflictException(
        `批量补发已于 ${existing?.executedAt?.toISOString()} 执行过，不能重复执行。操作人: ${existing?.operatorName}`
      );
    }

    // 获取挖矿配置
    const config = await this.miningConfigRepository.getConfig();
    if (!config) {
      throw new BadRequestException('挖矿配置不存在');
    }

    const secondDistribution = config.secondDistribution.value;
    const now = new Date();

    // 预检查: 确保系统账户存在
    await this.systemMiningAccountRepository.ensureSystemAccountsExist();
    this.logger.log('[execute] 系统账户预检查通过');

    // 按批次分组并排序
    const batchGroups = this.groupByBatch(items);
    const sortedBatches = Array.from(batchGroups.keys()).sort((a, b) => a - b);

    // 计算每个批次的算力
    const batchContributions = new Map<number, Decimal>();
    for (const batchNum of sortedBatches) {
      const batchItems = batchGroups.get(batchNum)!;
      let batchTotal = new Decimal(0);
      for (const item of batchItems) {
        batchTotal = batchTotal.plus(this.calculateUserContribution(item.treeCount));
      }
      batchContributions.set(batchNum, batchTotal);
    }

    // 计算每个用户在每个批次中的算力
    const userBatchContributions = new Map<string, Decimal>();
    for (const item of items) {
      const key = `${item.accountSequence}-${item.batch}`;
      const contribution = this.calculateUserContribution(item.treeCount);
      const existing = userBatchContributions.get(key);
      if (existing) {
        userBatchContributions.set(key, existing.plus(contribution));
      } else {
        userBatchContributions.set(key, contribution);
      }
    }

    // 计算每个用户的总算力（用于记录）
    const userContributions = new Map<string, Decimal>();
    for (const item of items) {
      const contribution = this.calculateUserContribution(item.treeCount);
      const existing = userContributions.get(item.accountSequence);
      if (existing) {
        userContributions.set(item.accountSequence, existing.plus(contribution));
      } else {
        userContributions.set(item.accountSequence, contribution);
      }
    }

    // 获取计算起始日期
    const calculatedStartDate = this.getCalculatedStartDate(items);
    this.logger.log(`[execute] 计算起始日期: ${calculatedStartDate}`);

    // 构建挖矿阶段
    const phases = this.buildMiningPhases(items, sortedBatches, batchContributions, calculatedStartDate);

    // 每天补发额度 = 日产出 × 70%（用户）/ 12%（运营）/ 18%（总部）
    const dailyDistribution = secondDistribution.times(SECONDS_PER_DAY);
    const dailyAllocation = dailyDistribution.times(DAILY_DISTRIBUTION_RATIO);
    const dailyOperationAllocation = dailyDistribution.times(OPERATION_DISTRIBUTION_RATIO);
    const dailyHeadquartersAllocation = dailyDistribution.times(HEADQUARTERS_DISTRIBUTION_RATIO);

    // 计算总挖矿天数（用于系统账户分配）
    const totalMiningDays = phases.reduce((sum, p) => sum + p.daysInPhase, 0);
    const operationTotalAmount = dailyOperationAllocation.times(totalMiningDays);
    const headquartersTotalAmount = dailyHeadquartersAllocation.times(totalMiningDays);
    this.logger.log(`[execute] 系统账户分配: 运营=${operationTotalAmount.toFixed(8)}, 总部=${headquartersTotalAmount.toFixed(8)}, 总挖矿天数=${totalMiningDays}`);

    // 计算每个用户在各阶段的收益
    const userAmounts = new Map<string, Decimal>();
    for (const item of items) {
      userAmounts.set(item.accountSequence, new Decimal(0));
    }

    for (const phase of phases) {
      const phaseAllocation = dailyAllocation.times(phase.daysInPhase);

      // 用 Set 记录本阶段已处理的用户，避免重复
      const processedInPhase = new Set<string>();

      for (const item of items) {
        if (phase.participatingBatches.includes(item.batch) && !processedInPhase.has(item.accountSequence)) {
          processedInPhase.add(item.accountSequence);

          // 计算用户在该阶段参与的批次中的总算力
          let userPhaseContribution = new Decimal(0);
          for (const batch of phase.participatingBatches) {
            const key = `${item.accountSequence}-${batch}`;
            const batchContrib = userBatchContributions.get(key);
            if (batchContrib) {
              userPhaseContribution = userPhaseContribution.plus(batchContrib);
            }
          }

          const ratio = userPhaseContribution.dividedBy(phase.participatingContribution);
          const phaseAmount = phaseAllocation.times(ratio);

          const currentAmount = userAmounts.get(item.accountSequence)!;
          userAmounts.set(item.accountSequence, currentAmount.plus(phaseAmount));
        }
      }
    }

    // 计算总用户算力（用于记录）
    let totalUserContribution = new Decimal(0);
    for (const contribution of batchContributions.values()) {
      totalUserContribution = totalUserContribution.plus(contribution);
    }

    const results: BatchMiningItemResult[] = [];
    let successCount = 0;
    let failedCount = 0;
    let totalAmount = new Decimal(0);

    // 计算每个用户在每个阶段的收益（用于创建阶段记录）
    // key: `${accountSequence}-${phaseNumber}`, value: { amount, userPhaseContribution, phaseContribution, daysInPhase }
    const userPhaseAmounts = new Map<string, {
      amount: Decimal;
      userPhaseContribution: Decimal;
      phaseContribution: Decimal;
      daysInPhase: number;
    }>();

    for (const phase of phases) {
      const phaseAllocation = dailyAllocation.times(phase.daysInPhase);
      const processedInPhase = new Set<string>();

      for (const item of items) {
        if (phase.participatingBatches.includes(item.batch) && !processedInPhase.has(item.accountSequence)) {
          processedInPhase.add(item.accountSequence);

          // 计算用户在该阶段参与的批次中的总算力
          let userPhaseContribution = new Decimal(0);
          for (const batch of phase.participatingBatches) {
            const key = `${item.accountSequence}-${batch}`;
            const batchContrib = userBatchContributions.get(key);
            if (batchContrib) {
              userPhaseContribution = userPhaseContribution.plus(batchContrib);
            }
          }

          const ratio = userPhaseContribution.dividedBy(phase.participatingContribution);
          const phaseAmount = phaseAllocation.times(ratio);

          const phaseKey = `${item.accountSequence}-${phase.phaseNumber}`;
          userPhaseAmounts.set(phaseKey, {
            amount: phaseAmount,
            userPhaseContribution,
            phaseContribution: phase.participatingContribution,
            daysInPhase: phase.daysInPhase,
          });
        }
      }
    }

    // 使用事务执行所有操作
    const batchId = await this.prisma.$transaction(async (tx) => {
      // 1. 创建批量执行记录（用于防重复）
      const execution = await tx.batchMiningExecution.create({
        data: {
          operatorId,
          operatorName,
          reason,
          totalUsers: items.length,
          totalBatches: sortedBatches.length,
          executedAt: now,
        },
      });

      // 2. 处理每个用户（创建账户、更新余额、创建交易记录）
      const processedUsers = new Set<string>();
      for (const item of items) {
        // 每个用户只处理一次（账户和交易记录）
        if (processedUsers.has(item.accountSequence)) {
          continue;
        }
        processedUsers.add(item.accountSequence);

        try {
          const userContribution = userContributions.get(item.accountSequence)!;
          const amount = userAmounts.get(item.accountSequence)!;
          const manualAmount = new ShareAmount(amount);

          // 查找或创建挖矿账户
          let account = await tx.miningAccount.findUnique({
            where: { accountSequence: item.accountSequence },
          });

          if (!account) {
            account = await tx.miningAccount.create({
              data: {
                accountSequence: item.accountSequence,
                totalMined: new Decimal(0),
                availableBalance: new Decimal(0),
                frozenBalance: new Decimal(0),
                totalContribution: userContribution,
              },
            });
          }

          const balanceBefore = new Decimal(account.availableBalance);
          const balanceAfter = balanceBefore.plus(manualAmount.value);
          const totalMinedAfter = new Decimal(account.totalMined).plus(manualAmount.value);

          // 更新账户余额
          await tx.miningAccount.update({
            where: { accountSequence: item.accountSequence },
            data: {
              totalMined: totalMinedAfter,
              availableBalance: balanceAfter,
              totalContribution: userContribution,
              updatedAt: now,
            },
          });

          // 创建交易明细记录（总金额）
          const description = `批量补发挖矿收益 - 批次:${item.batch} - 认种棵数:${item.treeCount} - 共${phases.length}个阶段 - 操作人:${operatorName} - ${reason}`;

          await tx.miningTransaction.create({
            data: {
              accountSequence: item.accountSequence,
              type: 'BATCH_MINING',
              amount: manualAmount.value,
              balanceBefore,
              balanceAfter,
              referenceId: execution.id,
              referenceType: 'BATCH_MINING',
              memo: description,
            },
          });

          // 3. 为该用户创建每个阶段的明细记录
          for (const phase of phases) {
            // 检查该用户是否参与此阶段
            if (!phase.participatingBatches.includes(item.batch)) {
              continue;
            }

            const phaseKey = `${item.accountSequence}-${phase.phaseNumber}`;
            const phaseData = userPhaseAmounts.get(phaseKey);
            if (!phaseData) continue;

            const ratio = phaseData.userPhaseContribution.dividedBy(phaseData.phaseContribution);
            const totalSeconds = BigInt(phaseData.daysInPhase * SECONDS_PER_DAY);

            await tx.batchMiningRecord.create({
              data: {
                executionId: execution.id,
                accountSequence: item.accountSequence,
                batch: item.batch,
                phase: phase.phaseNumber,
                treeCount: item.treeCount,
                daysInPhase: phaseData.daysInPhase,
                preMineDays: item.preMineDays,
                userContribution: phaseData.userPhaseContribution,
                networkContribution: phaseData.phaseContribution,
                contributionRatio: ratio,
                totalSeconds,
                amount: phaseData.amount,
                remark: `阶段${phase.phaseNumber}: ${phaseData.daysInPhase}天, 参与批次[${phase.participatingBatches.join(',')}]`,
              },
            });
          }

          // 发布事件到 Kafka（每个用户一个事件，包含总金额）
          await tx.outboxEvent.create({
            data: {
              aggregateType: 'BatchMining',
              aggregateId: execution.id,
              eventType: 'BATCH_MINING_COMPLETED',
              topic: 'mining.batch-mining.completed',
              key: item.accountSequence,
              payload: {
                eventId: `${execution.id}-${item.accountSequence}`,
                executionId: execution.id,
                accountSequence: item.accountSequence,
                batch: item.batch,
                amount: manualAmount.value.toString(),
                treeCount: item.treeCount,
                totalPhases: phases.length,
                userContribution: userContribution.toString(),
                operatorId,
                operatorName,
                reason,
              },
              status: 'PENDING',
            },
          });

          const ratio = userContribution.dividedBy(totalUserContribution);
          results.push({
            accountSequence: item.accountSequence,
            batch: item.batch,
            treeCount: item.treeCount,
            userContribution: userContribution.toFixed(10),
            networkContribution: totalUserContribution.toFixed(10),
            contributionRatio: ratio.toFixed(18),
            preMineDays: item.preMineDays,
            totalSeconds: (phases.length * SECONDS_PER_DAY).toString(),
            amount: manualAmount.value.toFixed(8),
            success: true,
          });

          successCount++;
          totalAmount = totalAmount.plus(manualAmount.value);

        } catch (error: any) {
          this.logger.error(`Failed to process ${item.accountSequence}: ${error.message}`);
          results.push({
            accountSequence: item.accountSequence,
            batch: item.batch,
            treeCount: item.treeCount,
            userContribution: '0',
            networkContribution: '0',
            contributionRatio: '0',
            preMineDays: item.preMineDays,
            totalSeconds: '0',
            amount: '0',
            success: false,
            error: error.message,
          });
          failedCount++;
        }
      }

      // 系统账户补发 (30%部分)
      // 运营账户 12%
      if (!operationTotalAmount.isZero()) {
        const operationMemo = `批量补发挖矿 - 运营账户12%份额 - ${totalMiningDays}天 × ${dailyOperationAllocation.toFixed(8)}/天 - 操作人:${operatorName} - ${reason}`;
        await this.systemMiningAccountRepository.mine(
          'OPERATION',
          null,
          new ShareAmount(operationTotalAmount),
          operationMemo,
          tx,
          execution.id,
          'BATCH_MINING',
        );
        this.logger.log(`[execute] 运营账户补发完成: ${operationTotalAmount.toFixed(8)}`);
      }

      // 总部账户 18%
      if (!headquartersTotalAmount.isZero()) {
        const headquartersMemo = `批量补发挖矿 - 总部账户18%份额 - ${totalMiningDays}天 × ${dailyHeadquartersAllocation.toFixed(8)}/天 - 操作人:${operatorName} - ${reason}`;
        await this.systemMiningAccountRepository.mine(
          'HEADQUARTERS',
          null,
          new ShareAmount(headquartersTotalAmount),
          headquartersMemo,
          tx,
          execution.id,
          'BATCH_MINING',
        );
        this.logger.log(`[execute] 总部账户补发完成: ${headquartersTotalAmount.toFixed(8)}`);
      }

      // 更新执行记录
      await tx.batchMiningExecution.update({
        where: { id: execution.id },
        data: {
          successCount,
          failedCount,
          totalAmount,
          operationAmount: operationTotalAmount,
          headquartersAmount: headquartersTotalAmount,
        },
      });

      return execution.id;
    }, {
      timeout: 120000, // 2分钟超时
    });

    this.logger.log(
      `Batch mining executed: batchId=${batchId}, total=${items.length}, success=${successCount}, failed=${failedCount}, 用户=${totalAmount.toFixed(8)}, 运营=${operationTotalAmount.toFixed(8)}, 总部=${headquartersTotalAmount.toFixed(8)}`,
    );

    return {
      success: failedCount === 0,
      batchId,
      totalUsers: items.length,
      successCount,
      failedCount,
      totalAmount: totalAmount.toFixed(8),
      operationAmount: operationTotalAmount.toFixed(8),
      headquartersAmount: headquartersTotalAmount.toFixed(8),
      results,
      message: `批量补发完成: 成功 ${successCount} 个, 失败 ${failedCount} 个, 用户 ${totalAmount.toFixed(8)}, 运营 ${operationTotalAmount.toFixed(8)}, 总部 ${headquartersTotalAmount.toFixed(8)}`,
    };
  }

  /**
   * 获取批量补发执行记录
   */
  async getExecution(): Promise<any | null> {
    this.logger.log('[getExecution] 获取批量补发执行记录...');
    const execution = await this.prisma.batchMiningExecution.findFirst({
      include: {
        records: {
          orderBy: [{ batch: 'asc' }, { accountSequence: 'asc' }],
        },
      },
    });

    if (!execution) {
      this.logger.log('[getExecution] 未找到执行记录');
      return null;
    }

    this.logger.log(`[getExecution] 找到执行记录: id=${execution.id}, records=${execution.records.length}`);
    return {
      id: execution.id,
      operatorId: execution.operatorId,
      operatorName: execution.operatorName,
      reason: execution.reason,
      totalUsers: execution.totalUsers,
      totalBatches: execution.totalBatches,
      successCount: execution.successCount,
      failedCount: execution.failedCount,
      totalAmount: execution.totalAmount?.toString() || '0',
      executedAt: execution.executedAt.toISOString(),
      records: execution.records.map((r) => ({
        id: r.id,
        accountSequence: r.accountSequence,
        batch: r.batch,
        treeCount: r.treeCount,
        preMineDays: r.preMineDays,
        userContribution: r.userContribution.toString(),
        networkContribution: r.networkContribution.toString(),
        contributionRatio: r.contributionRatio.toString(),
        totalSeconds: r.totalSeconds.toString(),
        amount: r.amount.toString(),
        remark: r.remark,
        createdAt: r.createdAt.toISOString(),
      })),
    };
  }

  /**
   * 按批次分组
   */
  private groupByBatch(items: BatchMiningItem[]): Map<number, BatchMiningItem[]> {
    const groups = new Map<number, BatchMiningItem[]>();
    for (const item of items) {
      const batch = item.batch;
      if (!groups.has(batch)) {
        groups.set(batch, []);
      }
      groups.get(batch)!.push(item);
    }
    return groups;
  }

  /**
   * 计算用户算力
   * 用户算力 = 认种棵数 × 基础算力/棵
   * 注意：70%的比例在全网算力计算时体现（用户算力/0.7=全网算力）
   */
  private calculateUserContribution(treeCount: number): Decimal {
    return BASE_CONTRIBUTION_PER_TREE.times(treeCount);
  }

  /**
   * 获取计算起始日期
   * 从第一批次用户的挖矿开始时间中获取最早的日期
   * 如果找不到有效数据，返回默认日期 2025-11-08
   */
  private getCalculatedStartDate(items: BatchMiningItem[]): string {
    // 找出批次1的用户
    const batch1Items = items.filter(item => item.batch === 1);

    if (batch1Items.length === 0) {
      this.logger.warn(`[getCalculatedStartDate] 未找到批次1的用户，使用默认日期: ${DEFAULT_MINING_START_DATE}`);
      return DEFAULT_MINING_START_DATE;
    }

    // 解析并找出最早的日期
    let earliestDate: Date | null = null;
    let earliestDateStr = '';

    for (const item of batch1Items) {
      const dateStr = item.miningStartDate;
      if (!dateStr) continue;

      const parsed = this.parseDate(dateStr);
      if (parsed && (!earliestDate || parsed < earliestDate)) {
        earliestDate = parsed;
        earliestDateStr = dateStr;
      }
    }

    if (!earliestDate) {
      this.logger.warn(`[getCalculatedStartDate] 批次1用户的挖矿开始日期均无效，使用默认日期: ${DEFAULT_MINING_START_DATE}`);
      return DEFAULT_MINING_START_DATE;
    }

    // 格式化为 YYYY-MM-DD
    const year = earliestDate.getFullYear();
    const month = String(earliestDate.getMonth() + 1).padStart(2, '0');
    const day = String(earliestDate.getDate()).padStart(2, '0');
    const formattedDate = `${year}-${month}-${day}`;

    this.logger.log(`[getCalculatedStartDate] 从批次1用户中找到最早日期: ${formattedDate} (原始值: ${earliestDateStr})`);
    return formattedDate;
  }

  /**
   * 解析日期字符串
   * 支持格式: 2025.11.8, 2025-11-08, 2025/11/8
   */
  private parseDate(dateStr: string): Date | null {
    if (!dateStr) return null;

    const formats = [
      /^(\d{4})\.(\d{1,2})\.(\d{1,2})$/,  // 2025.11.8
      /^(\d{4})-(\d{1,2})-(\d{1,2})$/,    // 2025-11-08
      /^(\d{4})\/(\d{1,2})\/(\d{1,2})$/,  // 2025/11/8
    ];

    for (const format of formats) {
      const match = dateStr.match(format);
      if (match) {
        const year = parseInt(match[1], 10);
        const month = parseInt(match[2], 10) - 1;
        const day = parseInt(match[3], 10);
        const date = new Date(year, month, day);
        date.setHours(0, 0, 0, 0);
        return date;
      }
    }

    return null;
  }
}
