import Decimal from 'decimal.js';
import { Money } from '../value-objects/money.vo';

/**
 * 交易计算领域服务
 *
 * 核心公式：
 * 1. 每分钟销毁量 = 100亿 ÷ (365×4×1440) = 4756.468797564687 进黑洞
 * 2. 积分股价格 = 积分股池的绿积分 ÷ (100.02亿积分股 - 黑洞积分股 - 流通池积分股)
 * 3. 卖出销毁倍数 = (100亿积分股 - 黑洞销毁量) ÷ (200万 - 流通池量)
 * 4. 卖出销毁量 = 卖出积分股 × 倍数
 * 5. 卖出交易额 = (卖出量 + 卖出销毁量) × 积分股价
 * 6. 资产显示 = (账户积分股 + 账户积分股 × 倍数) × 积分股价
 * 7. 资产每秒增长量 = 用户每天分配的积分股 ÷ 24 ÷ 60 ÷ 60
 */
export class TradingCalculatorService {
  // 总积分股数量: 100.02亿 (SHARE_POOL_A: 100亿 + SHARE_POOL_B: 200万)
  static readonly TOTAL_SHARES = new Decimal('10002000000');

  // 目标销毁量: 100亿 (4年销毁完)
  static readonly BURN_TARGET = new Decimal('10000000000');

  // 销毁周期: 4年的分钟数
  static readonly BURN_PERIOD_MINUTES = 365 * 4 * 1440; // 2102400

  // 流通池目标量: 200万
  static readonly CIRCULATION_POOL_TARGET = new Decimal('2000000');

  // 基础每分钟销毁量: 100亿 ÷ (365×4×1440)
  static readonly BASE_MINUTE_BURN_RATE = TradingCalculatorService.BURN_TARGET.dividedBy(
    TradingCalculatorService.BURN_PERIOD_MINUTES,
  );

  /**
   * 计算积分股价格
   * 价格 = 绿积分(股池) ÷ (总积分股 - 黑洞积分股 - 流通池积分股)
   *
   * @param greenPoints 积分股池的绿积分（分子）
   * @param blackHoleAmount 黑洞积分股数量
   * @param circulationPoolAmount 流通池积分股数量
   * @returns 价格
   */
  calculatePrice(
    greenPoints: Money,
    blackHoleAmount: Money,
    circulationPoolAmount: Money,
  ): Money {
    // 有效分母 = 100.02B - 黑洞 - 流通池
    const effectiveDenominator = TradingCalculatorService.TOTAL_SHARES
      .minus(blackHoleAmount.value)
      .minus(circulationPoolAmount.value);

    if (effectiveDenominator.isZero() || effectiveDenominator.isNegative()) {
      return Money.zero();
    }

    // 价格 = 绿积分 / 有效分母
    const price = greenPoints.value.dividedBy(effectiveDenominator);
    return new Money(price);
  }

  /**
   * 计算有效分母
   * 有效分母 = 总积分股 - 黑洞积分股 - 流通池积分股
   */
  calculateEffectiveDenominator(
    blackHoleAmount: Money,
    circulationPoolAmount: Money,
  ): Money {
    const denominator = TradingCalculatorService.TOTAL_SHARES
      .minus(blackHoleAmount.value)
      .minus(circulationPoolAmount.value);

    if (denominator.isNegative()) {
      return Money.zero();
    }

    return new Money(denominator);
  }

  /**
   * 计算卖出销毁倍数
   * 倍数 = (100亿积分股 - 黑洞销毁量) ÷ (200万 - 流通池量)
   *
   * 目的：确保价格不会因为卖出而下跌
   *
   * @param blackHoleAmount 当前黑洞销毁总量
   * @param circulationPoolAmount 当前流通池量
   * @returns 销毁倍数
   */
  calculateSellBurnMultiplier(
    blackHoleAmount: Money,
    circulationPoolAmount: Money,
  ): Decimal {
    // 分子 = 100亿 - 黑洞销毁量
    const numerator = TradingCalculatorService.BURN_TARGET.minus(blackHoleAmount.value);

    // 分母 = 200万 - 流通池量
    const denominator = TradingCalculatorService.CIRCULATION_POOL_TARGET.minus(
      circulationPoolAmount.value,
    );

    // 防止除以零或负数
    if (denominator.isZero() || denominator.isNegative()) {
      // 当流通池已满时，销毁倍数设为最大合理值
      return new Decimal('5'); // 或其他业务定义的最大倍数
    }

    if (numerator.isNegative()) {
      // 当黑洞已满时，不再销毁
      return new Decimal('0');
    }

    return numerator.dividedBy(denominator);
  }

  /**
   * 计算卖出销毁量
   * 卖出销毁量 = 卖出积分股 × 倍数
   *
   * @param sellQuantity 卖出的积分股数量
   * @param burnMultiplier 销毁倍数
   * @returns 需要销毁的数量
   */
  calculateSellBurnAmount(sellQuantity: Money, burnMultiplier: Decimal): Money {
    const burnAmount = sellQuantity.value.times(burnMultiplier);
    return new Money(burnAmount);
  }

  /**
   * 计算卖出交易额
   * 卖出交易额 = (卖出量 + 卖出销毁量) × 积分股价
   *
   * @param sellQuantity 卖出的积分股数量
   * @param burnQuantity 销毁的数量
   * @param price 当前价格
   * @returns 交易额
   */
  calculateSellAmount(sellQuantity: Money, burnQuantity: Money, price: Money): Money {
    const effectiveQuantity = sellQuantity.value.plus(burnQuantity.value);
    const amount = effectiveQuantity.times(price.value);
    return new Money(amount);
  }

  /**
   * 计算资产显示值
   * 资产显示 = (账户积分股 + 账户积分股 × 倍数) × 积分股价
   *
   * @param shareBalance 账户积分股余额
   * @param burnMultiplier 当前销毁倍数
   * @param price 当前价格
   * @returns 显示的资产价值
   */
  calculateDisplayAssetValue(
    shareBalance: Money,
    burnMultiplier: Decimal,
    price: Money,
  ): Money {
    // 有效积分股 = 余额 + 余额 × 倍数 = 余额 × (1 + 倍数)
    const multiplierFactor = new Decimal(1).plus(burnMultiplier);
    const effectiveShares = shareBalance.value.times(multiplierFactor);
    const assetValue = effectiveShares.times(price.value);
    return new Money(assetValue);
  }

  /**
   * 计算资产每秒增长量
   * 资产每秒增长量 = 用户每天分配的积分股 ÷ 24 ÷ 60 ÷ 60
   *
   * @param dailyAllocation 用户每天分配的积分股
   * @returns 每秒增长量
   */
  calculateAssetGrowthPerSecond(dailyAllocation: Money): Money {
    const secondsPerDay = 24 * 60 * 60; // 86400
    const perSecond = dailyAllocation.value.dividedBy(secondsPerDay);
    return new Money(perSecond);
  }

  /**
   * 计算每分钟销毁量
   * 每次卖出后需要重新计算：(100亿 - 黑洞总量) ÷ 剩余分钟
   *
   * @param blackHoleAmount 当前黑洞销毁总量
   * @param remainingMinutes 剩余分钟数
   * @returns 每分钟销毁量
   */
  calculateMinuteBurnRate(blackHoleAmount: Money, remainingMinutes: number): Money {
    if (remainingMinutes <= 0) {
      return Money.zero();
    }

    // 剩余需要销毁的量 = 100亿 - 已销毁量
    const remainingBurn = TradingCalculatorService.BURN_TARGET.minus(blackHoleAmount.value);

    if (remainingBurn.isZero() || remainingBurn.isNegative()) {
      return Money.zero();
    }

    const minuteRate = remainingBurn.dividedBy(remainingMinutes);
    return new Money(minuteRate);
  }

  /**
   * 计算剩余销毁分钟数
   *
   * @param activatedAt 激活时间
   * @returns 剩余分钟数
   */
  calculateRemainingMinutes(activatedAt: Date): number {
    const now = new Date();
    const elapsedMs = now.getTime() - activatedAt.getTime();
    const elapsedMinutes = Math.floor(elapsedMs / (60 * 1000));
    return Math.max(0, TradingCalculatorService.BURN_PERIOD_MINUTES - elapsedMinutes);
  }

  /**
   * 计算卖出后的新价格（验证用）
   * 确保卖出后价格不下跌
   *
   * @param currentPrice 当前价格
   * @param sellQuantity 卖出数量
   * @param burnQuantity 销毁数量
   * @param greenPoints 绿积分
   * @param blackHoleAmount 黑洞数量
   * @param circulationPoolAmount 流通池数量
   * @returns 卖出后的新价格
   */
  calculatePriceAfterSell(
    greenPoints: Money,
    blackHoleAmount: Money,
    circulationPoolAmount: Money,
    sellQuantity: Money,
    burnQuantity: Money,
  ): Money {
    // 卖出后：流通池增加sellQuantity，黑洞增加burnQuantity
    const newBlackHole = blackHoleAmount.add(burnQuantity);
    const newCirculation = circulationPoolAmount.add(sellQuantity);

    return this.calculatePrice(greenPoints, newBlackHole, newCirculation);
  }
}
