import { Injectable, Logger } from '@nestjs/common';
import { PoolAccountRepository } from '../../infrastructure/persistence/repositories/pool-account.repository';
import { OutboxRepository } from '../../infrastructure/persistence/repositories/outbox.repository';
import { PoolAccount, PoolAccountType, TransactionType, CounterpartyType } from '@prisma/client';
import Decimal from 'decimal.js';
import { DomainException } from '../../shared/filters/domain-exception.filter';

export interface InitializePoolsInput {
  sharePool: {
    name: string;
    initialBalance?: Decimal;
  };
  blackHolePool: {
    name: string;
    targetBurn: Decimal;
  };
  circulationPool: {
    name: string;
    initialBalance?: Decimal;
  };
}

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

  constructor(
    private readonly poolAccountRepo: PoolAccountRepository,
    private readonly outboxRepo: OutboxRepository,
  ) {}

  /**
   * 初始化三大池账户
   */
  async initializePools(input: InitializePoolsInput): Promise<PoolAccount[]> {
    const pools: PoolAccount[] = [];

    // 积分股池
    const sharePool = await this.poolAccountRepo.create({
      poolType: 'SHARE_POOL',
      name: input.sharePool.name,
    });
    pools.push(sharePool);

    // 如果有初始余额，注入资金
    if (input.sharePool.initialBalance?.greaterThan(0)) {
      await this.poolAccountRepo.updateBalanceWithTransaction(
        'SHARE_POOL',
        input.sharePool.initialBalance,
        {
          transactionType: 'INITIAL_INJECT',
          counterpartyType: 'EXTERNAL',
          memo: `初始注入, 数量${input.sharePool.initialBalance.toFixed(8)}`,
        },
      );
    }

    // 黑洞积分股池
    const blackHolePool = await this.poolAccountRepo.create({
      poolType: 'BLACK_HOLE_POOL',
      name: input.blackHolePool.name,
      targetBurn: input.blackHolePool.targetBurn,
    });
    pools.push(blackHolePool);

    // 流通积分股池
    const circulationPool = await this.poolAccountRepo.create({
      poolType: 'CIRCULATION_POOL',
      name: input.circulationPool.name,
    });
    pools.push(circulationPool);

    if (input.circulationPool.initialBalance?.greaterThan(0)) {
      await this.poolAccountRepo.updateBalanceWithTransaction(
        'CIRCULATION_POOL',
        input.circulationPool.initialBalance,
        {
          transactionType: 'INITIAL_INJECT',
          counterpartyType: 'EXTERNAL',
          memo: `初始注入, 数量${input.circulationPool.initialBalance.toFixed(8)}`,
        },
      );
    }

    // 发布初始化事件
    await this.outboxRepo.create({
      aggregateType: 'PoolAccount',
      aggregateId: 'ALL_POOLS',
      eventType: 'POOLS_INITIALIZED',
      payload: {
        pools: pools.map((p) => ({
          id: p.id,
          type: p.poolType,
          name: p.name,
        })),
        sharePoolInitialBalance: input.sharePool.initialBalance?.toString() || '0',
        blackHoleTargetBurn: input.blackHolePool.targetBurn.toString(),
        circulationPoolInitialBalance: input.circulationPool.initialBalance?.toString() || '0',
        initializedAt: new Date().toISOString(),
      },
    });

    this.logger.log('Pool accounts initialized');
    return pools;
  }

  /**
   * 从积分股池分配（挖矿分配）
   */
  async distributeMiningReward(
    toAccountSeq: string,
    toUserId: string,
    amount: Decimal,
    miningInfo: {
      miningMinute: Date;
      contributionRatio: Decimal;
      referenceId?: string;
    },
  ): Promise<void> {
    const memo = `挖矿分配给用户[${toAccountSeq}], 算力占比${miningInfo.contributionRatio.mul(100).toFixed(4)}%, 分钟${miningInfo.miningMinute.toISOString()}`;

    await this.poolAccountRepo.updateBalanceWithTransaction(
      'SHARE_POOL',
      amount.negated(),
      {
        transactionType: 'MINING_DISTRIBUTE',
        counterpartyType: 'USER',
        counterpartyAccountSeq: toAccountSeq,
        counterpartyUserId: toUserId,
        referenceId: miningInfo.referenceId,
        referenceType: 'MINING_RECORD',
        memo,
        metadata: {
          miningMinute: miningInfo.miningMinute.toISOString(),
          contributionRatio: miningInfo.contributionRatio.toString(),
        },
      },
    );

    await this.outboxRepo.create({
      aggregateType: 'PoolAccount',
      aggregateId: 'SHARE_POOL',
      eventType: 'MINING_DISTRIBUTED',
      payload: {
        toAccountSeq,
        toUserId,
        amount: amount.toString(),
        miningMinute: miningInfo.miningMinute.toISOString(),
        contributionRatio: miningInfo.contributionRatio.toString(),
      },
    });
  }

  /**
   * 用户划入流通池（准备卖出）
   */
  async userTransferToCirculation(
    fromAccountSeq: string,
    fromUserId: string,
    amount: Decimal,
    referenceId?: string,
  ): Promise<void> {
    const memo = `用户[${fromAccountSeq}]划入流通池, 数量${amount.toFixed(8)}`;

    await this.poolAccountRepo.updateBalanceWithTransaction(
      'CIRCULATION_POOL',
      amount,
      {
        transactionType: 'TRANSFER_IN',
        counterpartyType: 'USER',
        counterpartyAccountSeq: fromAccountSeq,
        counterpartyUserId: fromUserId,
        referenceId,
        referenceType: 'TRANSFER',
        memo,
      },
    );

    await this.outboxRepo.create({
      aggregateType: 'PoolAccount',
      aggregateId: 'CIRCULATION_POOL',
      eventType: 'USER_TRANSFER_IN',
      payload: {
        fromAccountSeq,
        fromUserId,
        amount: amount.toString(),
        referenceId,
      },
    });
  }

  /**
   * 用户从流通池划出（买入成功）
   */
  async userTransferFromCirculation(
    toAccountSeq: string,
    toUserId: string,
    amount: Decimal,
    referenceId?: string,
  ): Promise<void> {
    const memo = `用户[${toAccountSeq}]从流通池划出, 数量${amount.toFixed(8)}`;

    await this.poolAccountRepo.updateBalanceWithTransaction(
      'CIRCULATION_POOL',
      amount.negated(),
      {
        transactionType: 'TRANSFER_OUT',
        counterpartyType: 'USER',
        counterpartyAccountSeq: toAccountSeq,
        counterpartyUserId: toUserId,
        referenceId,
        referenceType: 'TRANSFER',
        memo,
      },
    );

    await this.outboxRepo.create({
      aggregateType: 'PoolAccount',
      aggregateId: 'CIRCULATION_POOL',
      eventType: 'USER_TRANSFER_OUT',
      payload: {
        toAccountSeq,
        toUserId,
        amount: amount.toString(),
        referenceId,
      },
    });
  }

  /**
   * 销毁到黑洞池
   */
  async burnToBlackHole(
    amount: Decimal,
    sourceInfo: {
      fromPoolType: PoolAccountType;
      counterpartyAccountSeq?: string;
      counterpartyUserId?: string;
      referenceId?: string;
    },
  ): Promise<void> {
    const result = await this.poolAccountRepo.burnToBlackHole(
      sourceInfo.fromPoolType,
      amount,
      {
        counterpartyType: sourceInfo.counterpartyAccountSeq ? 'USER' : 'POOL',
        counterpartyAccountSeq: sourceInfo.counterpartyAccountSeq,
        counterpartyUserId: sourceInfo.counterpartyUserId,
        memo: `销毁到黑洞, 来源${sourceInfo.fromPoolType}, 数量${amount.toFixed(8)}`,
      },
    );

    await this.outboxRepo.create({
      aggregateType: 'PoolAccount',
      aggregateId: 'BLACK_HOLE_POOL',
      eventType: 'BURN_TO_BLACK_HOLE',
      payload: {
        amount: amount.toString(),
        fromPoolType: sourceInfo.fromPoolType,
        counterpartyAccountSeq: sourceInfo.counterpartyAccountSeq,
        counterpartyUserId: sourceInfo.counterpartyUserId,
        referenceId: sourceInfo.referenceId,
        newBlackHoleBalance: result.blackHolePool.balance.toString(),
        remainingBurn: result.blackHolePool.remainingBurn?.toString(),
      },
    });

    this.logger.log(`Burned ${amount.toFixed(8)} to black hole from ${sourceInfo.fromPoolType}`);
  }

  async findByType(poolType: PoolAccountType): Promise<PoolAccount | null> {
    return this.poolAccountRepo.findByType(poolType);
  }

  async findAll(): Promise<PoolAccount[]> {
    return this.poolAccountRepo.findAll();
  }

  async getPoolStats() {
    return this.poolAccountRepo.getPoolStats();
  }

  async getTransactions(
    poolType: PoolAccountType,
    options?: {
      transactionType?: TransactionType;
      startDate?: Date;
      endDate?: Date;
      limit?: number;
      offset?: number;
    },
  ) {
    return this.poolAccountRepo.getTransactions(poolType, options);
  }
}
