import { Injectable, Logger } from '@nestjs/common';
import { TradingCalculatorService } from '../../domain/services/trading-calculator.service';
import { BlackHoleRepository } from '../../infrastructure/persistence/repositories/black-hole.repository';
import { CirculationPoolRepository } from '../../infrastructure/persistence/repositories/circulation-pool.repository';
import { TradingConfigRepository } from '../../infrastructure/persistence/repositories/trading-config.repository';
import { OutboxRepository } from '../../infrastructure/persistence/repositories/outbox.repository';
import { RedisService } from '../../infrastructure/redis/redis.service';
import { Money } from '../../domain/value-objects/money.vo';
import Decimal from 'decimal.js';
import {
  TradingEventTypes,
  TradingTopics,
  BurnExecutedPayload,
  MinuteBurnExecutedPayload,
} from '../../domain/events/trading.events';

export interface BurnStatus {
  totalBurned: string;
  targetBurn: string;
  remainingBurn: string;
  burnProgress: string; // 百分比
  minuteBurnRate: string;
  remainingMinutes: number;
  lastBurnMinute: Date | null;
}

export interface SellBurnResult {
  burnQuantity: Money;
  burnMultiplier: Decimal;
  newMinuteBurnRate: Money;
}

@Injectable()
export class BurnService {
  private readonly logger = new Logger(BurnService.name);
  private readonly calculator = new TradingCalculatorService();

  constructor(
    private readonly blackHoleRepository: BlackHoleRepository,
    private readonly circulationPoolRepository: CirculationPoolRepository,
    private readonly tradingConfigRepository: TradingConfigRepository,
    private readonly outboxRepository: OutboxRepository,
    private readonly redis: RedisService,
  ) {}

  /**
   * 获取销毁状态
   */
  async getBurnStatus(): Promise<BurnStatus> {
    const [blackHole, config] = await Promise.all([
      this.blackHoleRepository.getBlackHole(),
      this.tradingConfigRepository.getConfig(),
    ]);

    const totalBurned = blackHole?.totalBurned || Money.zero();
    const targetBurn = blackHole?.targetBurn || new Money(TradingCalculatorService.BURN_TARGET);
    const remainingBurn = blackHole?.remainingBurn || targetBurn;

    // 计算进度百分比
    const progress = totalBurned.value.dividedBy(targetBurn.value).times(100);

    // 计算剩余分钟数
    const activatedAt = config?.activatedAt || new Date();
    const remainingMinutes = this.calculator.calculateRemainingMinutes(activatedAt);

    return {
      totalBurned: totalBurned.toFixed(8),
      targetBurn: targetBurn.toFixed(8),
      remainingBurn: remainingBurn.toFixed(8),
      burnProgress: progress.toFixed(4),
      minuteBurnRate: (config?.minuteBurnRate || Money.zero()).toFixed(18),
      remainingMinutes,
      lastBurnMinute: blackHole?.lastBurnMinute || null,
    };
  }

  /**
   * 执行每分钟自动销毁
   */
  async executeMinuteBurn(): Promise<Money> {
    const lockValue = await this.redis.acquireLock('burn:minute:lock', 55);
    if (!lockValue) {
      return Money.zero();
    }

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

      const blackHole = await this.blackHoleRepository.getBlackHole();
      if (!blackHole) {
        return Money.zero();
      }

      // 检查是否已完成销毁目标
      if (blackHole.remainingBurn.isZero()) {
        return Money.zero();
      }

      const currentMinute = new Date();
      currentMinute.setSeconds(0, 0);

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

      // 使用当前配置的每分钟销毁率
      let burnAmount = config.minuteBurnRate;

      // 确保不超过剩余待销毁量
      if (burnAmount.isGreaterThan(blackHole.remainingBurn)) {
        burnAmount = blackHole.remainingBurn;
      }

      if (burnAmount.isZero()) {
        return Money.zero();
      }

      // 记录销毁
      const burnRecord = await this.blackHoleRepository.recordMinuteBurn(currentMinute, burnAmount);

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

      this.logger.log(`Minute burn executed: ${burnAmount.toFixed(8)}`);

      // 发布每分钟销毁事件
      await this.publishMinuteBurnEvent(
        burnRecord.id,
        currentMinute,
        burnAmount,
        blackHole.totalBurned.add(burnAmount),
        blackHole.remainingBurn.subtract(burnAmount),
      );

      return burnAmount;
    } catch (error) {
      this.logger.error('Failed to execute minute burn', error);
      return Money.zero();
    } finally {
      await this.redis.releaseLock('burn:minute:lock', lockValue);
    }
  }

  /**
   * 执行卖出销毁
   * 卖出销毁量 = 卖出积分股 × 倍数
   * 卖出后需要重新计算每分钟销毁量
   */
  async executeSellBurn(
    sellQuantity: Money,
    accountSeq: string,
    orderNo: string,
  ): Promise<SellBurnResult> {
    const [blackHole, circulationPool, config] = await Promise.all([
      this.blackHoleRepository.getBlackHole(),
      this.circulationPoolRepository.getPool(),
      this.tradingConfigRepository.getConfig(),
    ]);

    if (!blackHole || !config) {
      throw new Error('Trading system not initialized');
    }

    const blackHoleAmount = blackHole.totalBurned;
    const circulationPoolAmount = circulationPool?.totalShares || Money.zero();

    // 计算销毁倍数
    const burnMultiplier = this.calculator.calculateSellBurnMultiplier(
      blackHoleAmount,
      circulationPoolAmount,
    );

    // 计算销毁量
    const burnQuantity = this.calculator.calculateSellBurnAmount(sellQuantity, burnMultiplier);

    // 确保销毁量不超过剩余待销毁量
    const actualBurnQuantity = burnQuantity.isGreaterThan(blackHole.remainingBurn)
      ? blackHole.remainingBurn
      : burnQuantity;

    if (!actualBurnQuantity.isZero()) {
      // 记录卖出销毁
      const burnMinute = new Date();
      burnMinute.setSeconds(0, 0);

      const burnRecord = await this.blackHoleRepository.recordSellBurn(
        burnMinute,
        actualBurnQuantity,
        accountSeq,
        orderNo,
      );

      // 重新计算每分钟销毁量
      const newBlackHoleAmount = blackHoleAmount.add(actualBurnQuantity);
      const remainingMinutes = this.calculator.calculateRemainingMinutes(
        config.activatedAt || new Date(),
      );
      const newMinuteBurnRate = this.calculator.calculateMinuteBurnRate(
        newBlackHoleAmount,
        remainingMinutes,
      );

      // 更新配置中的每分钟销毁率
      await this.tradingConfigRepository.updateMinuteBurnRate(newMinuteBurnRate);

      this.logger.log(
        `Sell burn executed: quantity=${actualBurnQuantity.toFixed(8)}, ` +
          `multiplier=${burnMultiplier.toFixed(8)}, newRate=${newMinuteBurnRate.toFixed(18)}`,
      );

      // 发布卖出销毁事件
      await this.publishSellBurnEvent(
        burnRecord.id,
        accountSeq,
        orderNo,
        actualBurnQuantity,
        burnMultiplier,
        blackHole.remainingBurn.subtract(actualBurnQuantity),
      );

      return {
        burnQuantity: actualBurnQuantity,
        burnMultiplier,
        newMinuteBurnRate,
      };
    }

    return {
      burnQuantity: Money.zero(),
      burnMultiplier,
      newMinuteBurnRate: config.minuteBurnRate,
    };
  }

  /**
   * 初始化交易系统（黑洞、配置、积分股池、流通池）
   */
  async initialize(): Promise<void> {
    const [existingConfig, existingBlackHole, existingCirculationPool] =
      await Promise.all([
        this.tradingConfigRepository.getConfig(),
        this.blackHoleRepository.getBlackHole(),
        this.circulationPoolRepository.getPool(),
      ]);

    if (!existingConfig) {
      await this.tradingConfigRepository.initializeConfig();
      this.logger.log('Trading config initialized');
    }

    if (!existingBlackHole) {
      await this.blackHoleRepository.initializeBlackHole(
        new Money(TradingCalculatorService.BURN_TARGET),
      );
      this.logger.log('Black hole initialized');
    }

    // 绿积分（价格分子）现在由做市商的 cashBalance 提供，不再需要独立的 SharePool

    // 初始化流通池
    if (!existingCirculationPool) {
      await this.circulationPoolRepository.initializePool();
      this.logger.log('Circulation pool initialized');
    }
  }

  /**
   * 获取销毁记录
   */
  async getBurnRecords(
    page: number,
    pageSize: number,
    sourceType?: 'MINUTE_BURN' | 'SELL_BURN',
  ): Promise<{
    data: any[];
    total: number;
  }> {
    const result = await this.blackHoleRepository.getBurnRecords(page, pageSize, sourceType);

    return {
      data: result.data.map((r) => ({
        id: r.id,
        burnMinute: r.burnMinute,
        burnAmount: r.burnAmount.toFixed(8),
        remainingTarget: r.remainingTarget.toFixed(8),
        sourceType: r.sourceType,
        sourceAccountSeq: r.sourceAccountSeq,
        sourceOrderNo: r.sourceOrderNo,
        memo: r.memo,
        createdAt: r.createdAt,
      })),
      total: result.total,
    };
  }

  // ==================== 事件发布方法 ====================

  /**
   * 发布每分钟销毁事件
   */
  private async publishMinuteBurnEvent(
    burnRecordId: string,
    burnMinute: Date,
    burnAmount: Money,
    totalBurned: Money,
    remainingTarget: Money,
  ): Promise<void> {
    try {
      const payload: MinuteBurnExecutedPayload = {
        burnRecordId,
        burnMinute: burnMinute.toISOString(),
        burnAmount: burnAmount.toString(),
        totalBurned: totalBurned.toString(),
        remainingTarget: remainingTarget.toString(),
        executedAt: new Date().toISOString(),
      };

      await this.outboxRepository.create({
        aggregateType: 'BurnRecord',
        aggregateId: burnRecordId,
        eventType: TradingEventTypes.MINUTE_BURN_EXECUTED,
        payload,
        topic: TradingTopics.BURNS,
        key: 'minute-burn',
      });

      this.logger.debug(`Published MinuteBurnExecuted event: ${burnAmount.toFixed(8)}`);
    } catch (error) {
      this.logger.error(`Failed to publish MinuteBurnExecuted event: ${error}`);
    }
  }

  /**
   * 发布卖出销毁事件
   */
  private async publishSellBurnEvent(
    burnRecordId: string,
    accountSeq: string,
    orderNo: string,
    burnAmount: Money,
    burnMultiplier: Decimal,
    remainingTarget: Money,
  ): Promise<void> {
    try {
      const payload: BurnExecutedPayload = {
        burnRecordId,
        sourceType: 'SELL',
        sourceAccountSeq: accountSeq,
        sourceOrderNo: orderNo,
        burnAmount: burnAmount.toString(),
        burnMultiplier: burnMultiplier.toString(),
        remainingTarget: remainingTarget.toString(),
        executedAt: new Date().toISOString(),
      };

      await this.outboxRepository.create({
        aggregateType: 'BurnRecord',
        aggregateId: burnRecordId,
        eventType: TradingEventTypes.BURN_EXECUTED,
        payload,
        topic: TradingTopics.BURNS,
        key: accountSeq,
      });

      this.logger.debug(`Published BurnExecuted event for account ${accountSeq}`);
    } catch (error) {
      this.logger.error(`Failed to publish BurnExecuted event: ${error}`);
    }
  }
}
