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 {
  sharePoolA: {
    name: string;
    initialBalance?: Decimal;
  };
  sharePoolB: {
    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[] = [];

    // 积分股池A - 初始100亿
    const sharePoolA = await this.poolAccountRepo.create({
      poolType: 'SHARE_POOL_A',
      name: input.sharePoolA.name,
    });
    pools.push(sharePoolA);

    if (input.sharePoolA.initialBalance?.greaterThan(0)) {
      await this.poolAccountRepo.updateBalanceWithTransaction(
        'SHARE_POOL_A',
        input.sharePoolA.initialBalance,
        {
          transactionType: 'INITIAL_INJECT',
          counterpartyType: 'EXTERNAL',
          memo: `初始注入积分股池A, 数量${input.sharePoolA.initialBalance.toFixed(8)}`,
        },
      );
    }

    // 积分股池B - 初始200万
    const sharePoolB = await this.poolAccountRepo.create({
      poolType: 'SHARE_POOL_B',
      name: input.sharePoolB.name,
    });
    pools.push(sharePoolB);

    if (input.sharePoolB.initialBalance?.greaterThan(0)) {
      await this.poolAccountRepo.updateBalanceWithTransaction(
        'SHARE_POOL_B',
        input.sharePoolB.initialBalance,
        {
          transactionType: 'INITIAL_INJECT',
          counterpartyType: 'EXTERNAL',
          memo: `初始注入积分股池B, 数量${input.sharePoolB.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,
        })),
        sharePoolAInitialBalance: input.sharePoolA.initialBalance?.toString() || '0',
        sharePoolBInitialBalance: input.sharePoolB.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;
  }

  /**
   * 从积分股池分配（挖矿分配）
   * 挖矿只从 SHARE_POOL_B (200万) 分配
   */
  async distributeMiningReward(
    toAccountSeq: string,
    toUserId: string,
    amount: Decimal,
    miningInfo: {
      miningMinute: Date;
      contributionRatio: Decimal;
      referenceId?: string;
    },
  ): Promise<void> {
    const sourcePool: PoolAccountType = 'SHARE_POOL_B';

    const memo = `挖矿分配给用户[${toAccountSeq}], 贡献值占比${miningInfo.contributionRatio.mul(100).toFixed(4)}%, 分钟${miningInfo.miningMinute.toISOString()}`;

    await this.poolAccountRepo.updateBalanceWithTransaction(
      sourcePool,
      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: sourcePool,
      eventType: 'MINING_DISTRIBUTED',
      payload: {
        toAccountSeq,
        toUserId,
        amount: amount.toString(),
        miningMinute: miningInfo.miningMinute.toISOString(),
        contributionRatio: miningInfo.contributionRatio.toString(),
      },
    });
  }

  /**
   * 获取积分股池总余量 (SHARE_POOL_A + SHARE_POOL_B)
   */
  async getSharePoolTotalBalance(): Promise<{
    poolA: string;
    poolB: string;
    total: string;
  }> {
    const [poolA, poolB] = await Promise.all([
      this.poolAccountRepo.findByType('SHARE_POOL_A'),
      this.poolAccountRepo.findByType('SHARE_POOL_B'),
    ]);

    const balanceA = poolA ? new Decimal(poolA.balance.toString()) : new Decimal(0);
    const balanceB = poolB ? new Decimal(poolB.balance.toString()) : new Decimal(0);
    const total = balanceA.plus(balanceB);

    return {
      poolA: balanceA.toFixed(8),
      poolB: balanceB.toFixed(8),
      total: total.toFixed(8),
    };
  }

  /**
   * 从积分股池B扣减（挖矿分配汇总）
   * 由 Kafka 消费者调用，处理 mining-service 发布的挖矿分配事件
   */
  async deductFromSharePoolB(
    amount: Decimal,
    miningInfo: {
      miningMinute: Date;
      participantCount: number;
      referenceId?: string;
    },
  ): Promise<void> {
    const sourcePool: PoolAccountType = 'SHARE_POOL_B';

    const memo = `挖矿分配扣减, 分钟${miningInfo.miningMinute.toISOString()}, 参与人数${miningInfo.participantCount}, 总量${amount.toFixed(8)}`;

    await this.poolAccountRepo.updateBalanceWithTransaction(
      sourcePool,
      amount.negated(), // 负数表示扣减
      {
        transactionType: 'MINING_DISTRIBUTE',
        counterpartyType: 'SYSTEM_ACCOUNT',
        referenceId: miningInfo.referenceId,
        referenceType: 'MINING_MINUTE_SUMMARY',
        memo,
        metadata: {
          miningMinute: miningInfo.miningMinute.toISOString(),
          participantCount: miningInfo.participantCount,
          totalDistributed: amount.toString(),
        },
      },
    );

    this.logger.log(
      `Deducted ${amount.toFixed(8)} from SHARE_POOL_B for minute ${miningInfo.miningMinute.toISOString()}`,
    );
  }

  /**
   * 从积分股池A扣减（手工补发挖矿）
   * 由 Kafka 消费者调用，处理 mining-service 发布的手工补发事件
   */
  async deductFromSharePoolA(
    amount: Decimal,
    info: {
      referenceId: string;
      referenceType: string;
      counterpartyAccountSeq?: string;
      memo: string;
    },
  ): Promise<void> {
    const sourcePool: PoolAccountType = 'SHARE_POOL_A';

    await this.poolAccountRepo.updateBalanceWithTransaction(
      sourcePool,
      amount.negated(), // 负数表示扣减
      {
        transactionType: 'MINING_DISTRIBUTE',
        counterpartyType: 'USER',
        counterpartyAccountSeq: info.counterpartyAccountSeq,
        referenceId: info.referenceId,
        referenceType: info.referenceType,
        memo: info.memo,
        metadata: {
          referenceId: info.referenceId,
          referenceType: info.referenceType,
          amount: amount.toString(),
        },
      },
    );

    this.logger.log(
      `Deducted ${amount.toFixed(8)} from SHARE_POOL_A for ${info.referenceType}`,
    );
  }

  /**
   * 从积分股池A扣减（销毁）
   * 由 Kafka 消费者调用，处理 trading-service 发布的销毁事件
   * 销毁的积分股从 100亿的 SHARE_POOL_A 中扣减
   */
  async deductFromSharePoolAForBurn(
    amount: Decimal,
    burnInfo: {
      burnMinute: Date;
      burnRecordId: string;
      sourceType: 'MINUTE_BURN' | 'SELL_BURN';
      sourceAccountSeq?: string;
      sourceOrderNo?: string;
    },
  ): Promise<void> {
    const sourcePool: PoolAccountType = 'SHARE_POOL_A';

    const memo =
      burnInfo.sourceType === 'MINUTE_BURN'
        ? `每分钟销毁扣减, 分钟${burnInfo.burnMinute.toISOString()}, 销毁量${amount.toFixed(8)}`
        : `卖出销毁扣减, 账户${burnInfo.sourceAccountSeq}, 订单${burnInfo.sourceOrderNo}, 销毁量${amount.toFixed(8)}`;

    await this.poolAccountRepo.updateBalanceWithTransaction(
      sourcePool,
      amount.negated(), // 负数表示扣减
      {
        transactionType: 'BURN',
        counterpartyType: 'POOL',
        counterpartyPoolType: 'BLACK_HOLE_POOL',
        referenceId: burnInfo.burnRecordId,
        referenceType: burnInfo.sourceType,
        memo,
        metadata: {
          burnMinute: burnInfo.burnMinute.toISOString(),
          burnRecordId: burnInfo.burnRecordId,
          sourceType: burnInfo.sourceType,
          sourceAccountSeq: burnInfo.sourceAccountSeq,
          sourceOrderNo: burnInfo.sourceOrderNo,
          burnAmount: amount.toString(),
        },
      },
    );

    // 同时增加黑洞池的余额
    await this.poolAccountRepo.updateBalanceWithTransaction(
      'BLACK_HOLE_POOL',
      amount, // 正数表示增加
      {
        transactionType: 'BURN',
        counterpartyType: 'POOL',
        counterpartyPoolType: 'SHARE_POOL_A',
        referenceId: burnInfo.burnRecordId,
        referenceType: burnInfo.sourceType,
        memo: `销毁入账, 来源${sourcePool}, ${memo}`,
        metadata: {
          burnMinute: burnInfo.burnMinute.toISOString(),
          burnRecordId: burnInfo.burnRecordId,
          sourceType: burnInfo.sourceType,
          sourceAccountSeq: burnInfo.sourceAccountSeq,
          sourceOrderNo: burnInfo.sourceOrderNo,
          burnAmount: amount.toString(),
        },
      },
    );

    this.logger.log(
      `Burned ${amount.toFixed(8)} from SHARE_POOL_A to BLACK_HOLE_POOL (${burnInfo.sourceType})`,
    );
  }

  /**
   * 用户划入流通池（准备卖出）
   */
  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);
  }

  // ============ 区块链充值/提现 ============

  /**
   * 区块链充值入账
   * 由 Kafka 消费者调用，处理 mining-blockchain-service 发布的充值确认事件
   */
  async blockchainDeposit(
    poolType: PoolAccountType,
    amount: Decimal,
    metadata: {
      txHash: string;
      fromAddress: string;
      blockNumber: string;
      depositId: string;
    },
  ): Promise<void> {
    const poolName = poolType === 'SHARE_POOL_A' ? '100亿销毁池' : '200万挖矿池';

    const memo = `区块链充值入账, ${poolName}, 金额${amount.toFixed(8)} fUSDT, 交易哈希${metadata.txHash.slice(0, 10)}..., 来源${metadata.fromAddress.slice(0, 10)}...`;

    await this.poolAccountRepo.updateBalanceWithTransaction(
      poolType,
      amount,
      {
        transactionType: 'BLOCKCHAIN_DEPOSIT',
        counterpartyType: 'EXTERNAL',
        counterpartyAddress: metadata.fromAddress,
        referenceId: metadata.depositId,
        referenceType: 'BLOCKCHAIN_DEPOSIT',
        txHash: metadata.txHash,
        memo,
        metadata: {
          txHash: metadata.txHash,
          fromAddress: metadata.fromAddress,
          blockNumber: metadata.blockNumber,
          depositId: metadata.depositId,
          amount: amount.toString(),
        },
      },
    );

    this.logger.log(
      `Blockchain deposit: ${amount.toFixed(8)} fUSDT to ${poolType} (tx: ${metadata.txHash.slice(0, 10)}...)`,
    );
  }

  /**
   * 区块链提现扣减
   * 由 Admin API 调用，管理员发起提现操作
   */
  async blockchainWithdraw(
    poolType: PoolAccountType,
    amount: Decimal,
    metadata: {
      txHash: string;
      toAddress: string;
      blockNumber?: string;
    },
  ): Promise<void> {
    const poolName = poolType === 'SHARE_POOL_A' ? '100亿销毁池' : '200万挖矿池';

    const memo = `区块链提现, ${poolName}, 金额${amount.toFixed(8)} fUSDT, 目标${metadata.toAddress.slice(0, 10)}..., 交易哈希${metadata.txHash.slice(0, 10)}...`;

    await this.poolAccountRepo.updateBalanceWithTransaction(
      poolType,
      amount.negated(),
      {
        transactionType: 'BLOCKCHAIN_WITHDRAW',
        counterpartyType: 'EXTERNAL',
        counterpartyAddress: metadata.toAddress,
        referenceType: 'BLOCKCHAIN_WITHDRAW',
        txHash: metadata.txHash,
        memo,
        metadata: {
          txHash: metadata.txHash,
          toAddress: metadata.toAddress,
          blockNumber: metadata.blockNumber,
          amount: amount.toString(),
        },
      },
    );

    this.logger.log(
      `Blockchain withdraw: ${amount.toFixed(8)} fUSDT from ${poolType} (tx: ${metadata.txHash.slice(0, 10)}...)`,
    );
  }

  /**
   * 中心化充值（管理员手动调整余额）
   */
  async centralizedDeposit(
    poolType: PoolAccountType,
    amount: Decimal,
    adminId?: string,
  ): Promise<void> {
    if (amount.isZero() || amount.isNegative()) {
      throw new DomainException('充值金额必须大于0', 'INVALID_AMOUNT');
    }

    const poolName = poolType === 'SHARE_POOL_A' ? '100亿销毁池' : '200万挖矿池';
    const memo = `中心化充值, ${poolName}, 金额${amount.toFixed(8)}, 操作员${adminId || 'admin'}`;

    await this.poolAccountRepo.updateBalanceWithTransaction(
      poolType,
      amount,
      {
        transactionType: 'ADJUSTMENT',
        counterpartyType: 'SYSTEM_ACCOUNT',
        referenceType: 'ADMIN_DEPOSIT',
        memo,
        metadata: {
          type: 'centralized_deposit',
          amount: amount.toString(),
          adminId: adminId || 'admin',
        },
      },
    );

    this.logger.log(`Centralized deposit: ${amount.toFixed(8)} to ${poolType} by ${adminId || 'admin'}`);
  }
}
