import { Injectable, Logger, Inject, OnModuleInit } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Cron, CronExpression } from '@nestjs/schedule';
import { EvmProviderAdapter, TransferEvent } from '@/infrastructure/blockchain/evm-provider.adapter';
import { ConfirmationPolicyService } from '@/domain/services/confirmation-policy.service';
import { ChainConfigService } from '@/domain/services/chain-config.service';
import {
  POOL_ACCOUNT_DEPOSIT_REPOSITORY,
  IPoolAccountDepositRepository,
  PoolAccountDepositDto,
  POOL_ACCOUNT_CHECKPOINT_REPOSITORY,
  IPoolAccountCheckpointRepository,
} from '@/domain/repositories/pool-account-deposit.repository.interface';
import {
  OUTBOX_EVENT_REPOSITORY,
  IOutboxEventRepository,
} from '@/domain/repositories/outbox-event.repository.interface';
import { ChainType, BlockNumber } from '@/domain/value-objects';
import { ChainTypeEnum, PoolAccountType, PoolAccountDepositStatus } from '@/domain/enums';
import { PoolAccountDepositConfirmedEvent } from '@/domain/events';

interface PoolWalletConfig {
  poolType: PoolAccountType;
  walletAddress: string;
  name: string;
}

/**
 * 池账户充值检测服务
 *
 * 负责扫描区块链、检测100亿销毁池和200万挖矿池的 fUSDT 充值、更新确认状态
 * 当充值达到确认数后，通过 Kafka 通知 mining-wallet-service 进行入账
 */
@Injectable()
export class PoolAccountDepositDetectionService implements OnModuleInit {
  private readonly logger = new Logger(PoolAccountDepositDetectionService.name);
  private readonly scanBatchSize: number;
  private readonly fUsdtContract: string;
  private readonly poolWalletConfigs: PoolWalletConfig[];
  private isEnabled: boolean = false;

  constructor(
    private readonly configService: ConfigService,
    private readonly evmProvider: EvmProviderAdapter,
    private readonly confirmationPolicy: ConfirmationPolicyService,
    private readonly chainConfig: ChainConfigService,
    @Inject(POOL_ACCOUNT_DEPOSIT_REPOSITORY)
    private readonly depositRepo: IPoolAccountDepositRepository,
    @Inject(POOL_ACCOUNT_CHECKPOINT_REPOSITORY)
    private readonly checkpointRepo: IPoolAccountCheckpointRepository,
    @Inject(OUTBOX_EVENT_REPOSITORY)
    private readonly outboxRepo: IOutboxEventRepository,
  ) {
    this.scanBatchSize = this.configService.get<number>('blockchain.scanBatchSize', 100);
    this.fUsdtContract = this.configService.get<string>('blockchain.kava.fUsdtContract', '');

    const burnPoolAddress = this.configService.get<string>('blockchain.burnPool.walletAddress', '');
    const miningPoolAddress = this.configService.get<string>('blockchain.miningPool.walletAddress', '');

    this.poolWalletConfigs = [
      {
        poolType: PoolAccountType.BURN_POOL,
        walletAddress: burnPoolAddress,
        name: '100亿销毁池',
      },
      {
        poolType: PoolAccountType.MINING_POOL,
        walletAddress: miningPoolAddress,
        name: '200万挖矿池',
      },
    ];
  }

  async onModuleInit() {
    const validConfigs = this.poolWalletConfigs.filter((c) => c.walletAddress);

    if (validConfigs.length === 0 || !this.fUsdtContract) {
      this.logger.warn('[INIT] 没有有效的池钱包配置或 fUSDT 合约未配置，池账户充值监控功能已禁用');
      this.isEnabled = false;
      return;
    }

    this.poolWalletConfigs.forEach((c) => {
      if (!c.walletAddress) {
        this.logger.warn(`[INIT] ${c.name} 钱包地址未配置，该池充值监控已禁用`);
      }
    });

    this.isEnabled = true;
    this.logger.log(`[INIT] PoolAccountDepositDetectionService initialized`);
    this.logger.log(`[INIT] fUSDT 合约: ${this.fUsdtContract}`);
    validConfigs.forEach((c) => {
      this.logger.log(`[INIT] ${c.name}: 钱包=${c.walletAddress}`);
    });
  }

  /**
   * 定时扫描区块（每5秒）
   */
  @Cron(CronExpression.EVERY_5_SECONDS)
  async scanBlocks(): Promise<void> {
    if (!this.isEnabled) return;

    const chainType = ChainType.fromEnum(ChainTypeEnum.KAVA);

    for (const walletConfig of this.poolWalletConfigs) {
      if (!walletConfig.walletAddress) continue;

      try {
        await this.scanPoolDeposits(chainType, walletConfig);
      } catch (error) {
        this.logger.error(`Error scanning ${walletConfig.name} deposits:`, error);
        await this.checkpointRepo.recordError(
          chainType,
          walletConfig.poolType,
          error instanceof Error ? error.message : 'Unknown error',
        );
      }
    }
  }

  /**
   * 扫描单个池钱包的充值
   */
  private async scanPoolDeposits(chainType: ChainType, walletConfig: PoolWalletConfig): Promise<void> {
    let lastBlock = await this.checkpointRepo.getLastScannedBlock(chainType, walletConfig.poolType);

    if (!lastBlock) {
      const currentBlock = await this.evmProvider.getCurrentBlockNumber(chainType);
      lastBlock = currentBlock.subtract(10);
      await this.checkpointRepo.initializeIfNotExists(chainType, walletConfig.poolType, lastBlock);
    }

    const currentBlock = await this.evmProvider.getCurrentBlockNumber(chainType);

    const fromBlock = lastBlock.add(1);
    let toBlock = fromBlock.add(this.scanBatchSize - 1);

    if (toBlock.isGreaterThan(currentBlock)) {
      toBlock = currentBlock;
    }

    if (fromBlock.isGreaterThan(currentBlock)) {
      return;
    }

    this.logger.debug(
      `Scanning ${walletConfig.name}: blocks ${fromBlock} to ${toBlock}`,
    );

    // 扫描 fUSDT Transfer 事件
    const events = await this.evmProvider.scanTransferEvents(
      chainType,
      fromBlock,
      toBlock,
      this.fUsdtContract,
    );

    // 过滤出充值到池钱包的交易
    const deposits = events.filter(
      (e) => e.to.toLowerCase() === walletConfig.walletAddress.toLowerCase(),
    );

    for (const deposit of deposits) {
      await this.processDeposit(chainType, walletConfig, deposit);
    }

    if (toBlock.isGreaterThan(lastBlock)) {
      await this.checkpointRepo.updateCheckpoint(chainType, walletConfig.poolType, toBlock);
    }
  }

  /**
   * 处理检测到的充值
   */
  private async processDeposit(
    chainType: ChainType,
    walletConfig: PoolWalletConfig,
    event: TransferEvent,
  ): Promise<void> {
    if (await this.depositRepo.existsByTxHashAndLogIndex(event.txHash, event.logIndex)) {
      this.logger.debug(`Pool deposit already processed: ${event.txHash}:${event.logIndex}`);
      return;
    }

    const tokenDecimals = await this.evmProvider.getTokenDecimals(
      chainType,
      this.fUsdtContract,
    );

    const divisor = BigInt(10 ** tokenDecimals);
    const integerPart = event.value / divisor;
    const fractionalPart = event.value % divisor;
    const amountFormatted = `${integerPart}.${fractionalPart.toString().padStart(tokenDecimals, '0')}`;

    const deposit: PoolAccountDepositDto = {
      chainType: chainType.toString(),
      txHash: event.txHash,
      fromAddress: event.from,
      toAddress: event.to,
      poolType: walletConfig.poolType,
      tokenContract: this.fUsdtContract,
      amount: event.value,
      amountFormatted,
      blockNumber: event.blockNumber,
      blockTimestamp: event.blockTimestamp,
      logIndex: event.logIndex,
      confirmations: 0,
      status: PoolAccountDepositStatus.DETECTED,
    };

    await this.depositRepo.save(deposit);

    this.logger.log(
      `[DEPOSIT] ${walletConfig.name} fUSDT deposit detected: ${event.txHash.slice(0, 10)}... -> ${walletConfig.walletAddress.slice(0, 10)}... (${amountFormatted})`,
    );
  }

  /**
   * 定时更新确认状态（每30秒）
   */
  @Cron(CronExpression.EVERY_30_SECONDS)
  async updateConfirmations(): Promise<void> {
    if (!this.isEnabled) return;

    const chainType = ChainType.fromEnum(ChainTypeEnum.KAVA);

    try {
      await this.updateChainConfirmations(chainType);
    } catch (error) {
      this.logger.error(`Error updating pool account confirmations:`, error);
    }
  }

  /**
   * 更新确认状态
   */
  private async updateChainConfirmations(chainType: ChainType): Promise<void> {
    const pendingDeposits = await this.depositRepo.findPendingConfirmation(chainType);
    if (pendingDeposits.length === 0) return;

    const currentBlock = await this.evmProvider.getCurrentBlockNumber(chainType);
    const requiredConfirmations = this.confirmationPolicy.getRequiredConfirmations(chainType);

    for (const deposit of pendingDeposits) {
      const confirmations = Number(currentBlock.value - deposit.blockNumber);
      const isConfirmed = confirmations >= requiredConfirmations;

      const newStatus = isConfirmed
        ? PoolAccountDepositStatus.CONFIRMED
        : PoolAccountDepositStatus.CONFIRMING;

      await this.depositRepo.updateConfirmations(deposit.id!, confirmations, newStatus);

      if (isConfirmed && deposit.status !== PoolAccountDepositStatus.CONFIRMED) {
        const event = new PoolAccountDepositConfirmedEvent({
          depositId: deposit.id!.toString(),
          chainType: deposit.chainType,
          txHash: deposit.txHash,
          fromAddress: deposit.fromAddress,
          toAddress: deposit.toAddress,
          poolType: deposit.poolType as 'BURN_POOL' | 'MINING_POOL',
          tokenContract: deposit.tokenContract,
          amount: deposit.amount.toString(),
          amountFormatted: deposit.amountFormatted,
          confirmations,
          blockNumber: deposit.blockNumber.toString(),
          blockTimestamp: deposit.blockTimestamp.toISOString(),
        });

        // 写入 outbox，保证可靠投递
        await this.outboxRepo.create({
          eventType: event.eventType,
          aggregateId: deposit.id!.toString(),
          aggregateType: 'PoolAccountDeposit',
          payload: event.toPayload(),
        });

        this.logger.log(
          `[CONFIRMED] Pool deposit confirmed: ${deposit.txHash.slice(0, 10)}... (${confirmations} confirmations, ${deposit.poolType})`,
        );
      }
    }
  }
}
