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 { BlackHoleRepository } from '../../infrastructure/persistence/repositories/black-hole.repository';
import { PriceSnapshotRepository } from '../../infrastructure/persistence/repositories/price-snapshot.repository';
import { PrismaService } from '../../infrastructure/persistence/prisma/prisma.service';
import { RedisService } from '../../infrastructure/redis/redis.service';
import { MiningCalculatorService } from '../../domain/services/mining-calculator.service';
import { MiningAccountAggregate } from '../../domain/aggregates/mining-account.aggregate';
import { ShareAmount } from '../../domain/value-objects/share-amount.vo';
import { Price } from '../../domain/value-objects/price.vo';

/**
 * 挖矿分配服务
 * 负责每分钟执行挖矿分配和销毁
 */
@Injectable()
export class MiningDistributionService {
  private readonly logger = new Logger(MiningDistributionService.name);
  private readonly calculator = new MiningCalculatorService();
  private readonly LOCK_KEY = 'mining:distribution:lock';

  constructor(
    private readonly miningAccountRepository: MiningAccountRepository,
    private readonly miningConfigRepository: MiningConfigRepository,
    private readonly blackHoleRepository: BlackHoleRepository,
    private readonly priceSnapshotRepository: PriceSnapshotRepository,
    private readonly prisma: PrismaService,
    private readonly redis: RedisService,
    private readonly configService: ConfigService,
  ) {}

  /**
   * 执行每分钟挖矿分配
   */
  async executeMinuteDistribution(): Promise<void> {
    // 获取分布式锁
    const lockValue = await this.redis.acquireLock(this.LOCK_KEY, 55);
    if (!lockValue) {
      this.logger.debug('Another instance is processing distribution');
      return;
    }

    try {
      const config = await this.miningConfigRepository.getConfig();
      if (!config || !config.isActive) {
        this.logger.debug('Mining is not active');
        return;
      }

      const currentMinute = this.getCurrentMinute();

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

      // 计算每分钟分配量
      const remainingMinutes = this.calculator.calculateRemainingMinutes(
        config.eraStartDate,
        MiningCalculatorService.HALVING_PERIOD_MINUTES,
      );

      const minuteDistribution = this.calculator.calculateMinuteDistribution(
        config.remainingDistribution,
        config.currentEra,
        remainingMinutes,
      );

      if (minuteDistribution.isZero()) {
        this.logger.debug('No distribution available');
        return;
      }

      // 获取有算力的账户
      const totalContribution = await this.miningAccountRepository.getTotalContribution();
      if (totalContribution.isZero()) {
        this.logger.debug('No contribution available');
        return;
      }

      // 分批处理账户
      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,
            minuteDistribution,
          );

          if (!reward.isZero()) {
            account.mine(reward, `分钟挖矿 ${currentMinute.toISOString()}`);
            await this.miningAccountRepository.save(account);

            // 保存挖矿记录
            await this.prisma.miningRecord.create({
              data: {
                accountSequence: account.accountSequence,
                miningMinute: currentMinute,
                contributionRatio: account.totalContribution.value.dividedBy(totalContribution.value),
                totalContribution: totalContribution.value,
                minuteDistribution: minuteDistribution.value,
                minedAmount: reward.value,
              },
            });

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

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

      // 执行销毁
      const burnAmount = await this.executeBurn(currentMinute);

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

      // 保存分钟统计
      await this.prisma.minuteMiningStat.create({
        data: {
          minute: currentMinute,
          totalContribution: totalContribution.value,
          totalDistributed: totalDistributed.value,
          participantCount,
          burnAmount: burnAmount.value,
        },
      });

      // 保存价格快照
      await this.savePriceSnapshot(currentMinute);

      // 标记已处理
      await this.redis.set(processedKey, '1', 120);

      this.logger.log(
        `Minute distribution completed: distributed=${totalDistributed.toFixed(8)}, ` +
          `participants=${participantCount}, burned=${burnAmount.toFixed(8)}`,
      );
    } catch (error) {
      this.logger.error('Failed to execute minute distribution', error);
      throw error;
    } finally {
      await this.redis.releaseLock(this.LOCK_KEY, lockValue);
    }
  }

  /**
   * 执行销毁
   */
  private async executeBurn(burnMinute: Date): Promise<ShareAmount> {
    const blackHole = await this.blackHoleRepository.getBlackHole();
    if (!blackHole) {
      return ShareAmount.zero();
    }

    if (blackHole.remainingBurn.isZero()) {
      return ShareAmount.zero();
    }

    const config = await this.miningConfigRepository.getConfig();
    if (!config) {
      return ShareAmount.zero();
    }

    // 计算剩余销毁分钟数（使用整个挖矿周期）
    const totalBurnMinutes = 10 * 365 * 24 * 60; // 10年
    const remainingMinutes = this.calculator.calculateRemainingBurnMinutes(
      config.activatedAt || new Date(),
      totalBurnMinutes,
    );

    const burnAmount = this.calculator.calculateMinuteBurn(
      blackHole.targetBurn,
      blackHole.totalBurned,
      remainingMinutes,
    );

    if (!burnAmount.isZero()) {
      await this.blackHoleRepository.recordBurn(burnMinute, burnAmount);
    }

    return burnAmount;
  }

  /**
   * 保存价格快照
   */
  private async savePriceSnapshot(snapshotTime: Date): Promise<void> {
    const blackHole = await this.blackHoleRepository.getBlackHole();

    // 获取流通池数据（需要从 trading-service 获取，这里简化处理）
    const circulationPool = ShareAmount.zero(); // TODO: 从 trading-service 获取

    // 获取股池数据（初始为分配池，实际需要计算）
    const config = await this.miningConfigRepository.getConfig();
    const sharePool = config?.distributionPool || ShareAmount.zero();

    const burnedAmount = blackHole?.totalBurned || ShareAmount.zero();

    const price = this.calculator.calculatePrice(sharePool, burnedAmount, circulationPool);

    const effectiveDenominator = MiningCalculatorService.TOTAL_SHARES.value
      .minus(burnedAmount.value)
      .minus(circulationPool.value);

    await this.priceSnapshotRepository.saveSnapshot({
      snapshotTime,
      price,
      sharePool,
      blackHoleAmount: burnedAmount,
      circulationPool,
      effectiveDenominator: new ShareAmount(effectiveDenominator),
    });
  }

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