import { Injectable, Logger, Inject, OnModuleInit } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import {
  BlockScannerService,
  DepositEvent,
} from '@/infrastructure/blockchain/block-scanner.service';
import { EvmProviderAdapter } from '@/infrastructure/blockchain/evm-provider.adapter';
import { EventPublisherService } from '@/infrastructure/kafka/event-publisher.service';
import { AddressCacheService } from '@/infrastructure/redis/address-cache.service';
import { ConfirmationPolicyService } from '@/domain/services/confirmation-policy.service';
import { ChainConfigService } from '@/domain/services/chain-config.service';
import { Erc20TransferService } from '@/domain/services/erc20-transfer.service';
import {
  DEPOSIT_TRANSACTION_REPOSITORY,
  IDepositTransactionRepository,
} from '@/domain/repositories/deposit-transaction.repository.interface';
import {
  MONITORED_ADDRESS_REPOSITORY,
  IMonitoredAddressRepository,
} from '@/domain/repositories/monitored-address.repository.interface';
import {
  BLOCK_CHECKPOINT_REPOSITORY,
  IBlockCheckpointRepository,
} from '@/domain/repositories/block-checkpoint.repository.interface';
import {
  OUTBOX_EVENT_REPOSITORY,
  IOutboxEventRepository,
} from '@/domain/repositories/outbox-event.repository.interface';
import { DepositTransaction } from '@/domain/aggregates/deposit-transaction';
import { ChainType, TxHash, EvmAddress, TokenAmount, BlockNumber } from '@/domain/value-objects';
import { DepositConfirmedEvent } from '@/domain/events';

/**
 * 充值检测服务
 * 负责扫描区块链、检测充值、更新确认状态
 */
@Injectable()
export class DepositDetectionService implements OnModuleInit {
  private readonly logger = new Logger(DepositDetectionService.name);

  constructor(
    private readonly blockScanner: BlockScannerService,
    private readonly evmProvider: EvmProviderAdapter,
    private readonly eventPublisher: EventPublisherService,
    private readonly addressCache: AddressCacheService,
    private readonly confirmationPolicy: ConfirmationPolicyService,
    private readonly chainConfig: ChainConfigService,
    private readonly transferService: Erc20TransferService,
    @Inject(DEPOSIT_TRANSACTION_REPOSITORY)
    private readonly depositRepo: IDepositTransactionRepository,
    @Inject(MONITORED_ADDRESS_REPOSITORY)
    private readonly monitoredAddressRepo: IMonitoredAddressRepository,
    @Inject(BLOCK_CHECKPOINT_REPOSITORY)
    private readonly checkpointRepo: IBlockCheckpointRepository,
    @Inject(OUTBOX_EVENT_REPOSITORY)
    private readonly outboxRepo: IOutboxEventRepository,
  ) {}

  async onModuleInit() {
    // 初始化地址缓存
    await this.initializeAddressCache();
    this.logger.log('DepositDetectionService initialized');
  }

  /**
   * 初始化地址缓存
   */
  private async initializeAddressCache(): Promise<void> {
    for (const chainTypeEnum of this.chainConfig.getSupportedChains()) {
      const chainType = ChainType.fromEnum(chainTypeEnum);
      const addresses = await this.monitoredAddressRepo.getAllActiveAddresses(chainType);
      await this.addressCache.reloadCache(chainType, addresses);
      this.logger.log(`Loaded ${addresses.length} addresses for ${chainTypeEnum} into cache`);
    }
  }

  /**
   * 定时扫描区块（每5秒）
   */
  @Cron(CronExpression.EVERY_5_SECONDS)
  async scanBlocks(): Promise<void> {
    for (const chainTypeEnum of this.chainConfig.getSupportedChains()) {
      try {
        await this.scanChain(ChainType.fromEnum(chainTypeEnum));
      } catch (error) {
        this.logger.error(`Error scanning ${chainTypeEnum}:`, error);
        await this.checkpointRepo.recordError(
          ChainType.fromEnum(chainTypeEnum),
          error instanceof Error ? error.message : 'Unknown error',
        );
      }
    }
  }

  /**
   * 扫描单条链
   */
  private async scanChain(chainType: ChainType): Promise<void> {
    // 检查缓存是否为空，如果为空则自动从数据库重新加载
    const cacheCount = await this.addressCache.getCount(chainType);
    if (cacheCount === 0) {
      this.logger.warn(`Address cache empty for ${chainType}, reloading from database...`);
      const addresses = await this.monitoredAddressRepo.getAllActiveAddresses(chainType);
      await this.addressCache.reloadCache(chainType, addresses);
      this.logger.log(`Reloaded ${addresses.length} addresses for ${chainType} into cache`);
    }

    // 获取上次扫描位置
    let lastBlock = await this.checkpointRepo.getLastScannedBlock(chainType);

    if (!lastBlock) {
      // 首次扫描，从当前区块开始
      const currentBlock = await this.evmProvider.getCurrentBlockNumber(chainType);
      lastBlock = currentBlock.subtract(10); // 从10个块前开始
      await this.checkpointRepo.initializeIfNotExists(chainType, lastBlock);
    }

    // 执行扫描
    const { deposits, newLastBlock } = await this.blockScanner.executeScan(chainType, lastBlock);

    // 处理检测到的充值
    for (const deposit of deposits) {
      await this.processDeposit(deposit);
    }

    // 更新检查点
    if (newLastBlock.isGreaterThan(lastBlock)) {
      await this.checkpointRepo.updateCheckpoint(chainType, newLastBlock);
    }
  }

  /**
   * 处理检测到的充值
   */
  private async processDeposit(event: DepositEvent): Promise<void> {
    const txHash = TxHash.create(event.txHash);

    // 检查是否已处理
    if (await this.depositRepo.existsByTxHash(txHash)) {
      this.logger.debug(`Deposit already processed: ${event.txHash}`);
      return;
    }

    const chainType = ChainType.fromEnum(event.chainType);

    // 过滤从热钱包发出的转账（内部转账/提现），避免重复入账
    const hotWalletAddress = this.transferService.getHotWalletAddress(event.chainType);
    if (hotWalletAddress && event.from.toLowerCase() === hotWalletAddress.toLowerCase()) {
      this.logger.debug(`Skipping hot wallet outgoing transfer: ${event.txHash} from ${event.from}`);
      return;
    }

    // 查找监控地址以获取用户ID或系统账户信息
    const monitoredAddress = await this.monitoredAddressRepo.findByChainAndAddress(
      chainType,
      EvmAddress.fromUnchecked(event.to),
    );

    if (!monitoredAddress || !monitoredAddress.id) {
      this.logger.warn(`Monitored address not found: ${event.to}`);
      return;
    }

    // 获取代币的实际 decimals（USDT 通常是 6 位，而不是 18 位）
    const tokenDecimals = await this.evmProvider.getTokenDecimals(chainType, event.tokenContract);

    // 创建充值记录 - 用户地址
    const deposit = DepositTransaction.create({
      chainType,
      txHash,
      fromAddress: EvmAddress.fromUnchecked(event.from),
      toAddress: EvmAddress.fromUnchecked(event.to),
      tokenContract: EvmAddress.fromUnchecked(event.tokenContract),
      amount: TokenAmount.fromRaw(event.value, tokenDecimals),
      blockNumber: BlockNumber.create(event.blockNumber),
      blockTimestamp: event.blockTimestamp,
      logIndex: event.logIndex,
      addressId: monitoredAddress.id,
      accountSequence: monitoredAddress.accountSequence,
      userId: monitoredAddress.userId,
    });

    // 保存
    await this.depositRepo.save(deposit);

    // 发布事件
    for (const domainEvent of deposit.domainEvents) {
      await this.eventPublisher.publish(domainEvent);
    }
    deposit.clearDomainEvents();

    this.logger.log(
      `User deposit saved: ${txHash.toShort()} -> ${event.to} (${deposit.amount.formatted} USDT)`,
    );
  }

  /**
   * 定时更新确认状态（每30秒）
   */
  @Cron(CronExpression.EVERY_30_SECONDS)
  async updateConfirmations(): Promise<void> {
    for (const chainTypeEnum of this.chainConfig.getSupportedChains()) {
      try {
        await this.updateChainConfirmations(ChainType.fromEnum(chainTypeEnum));
      } catch (error) {
        this.logger.error(`Error updating confirmations for ${chainTypeEnum}:`, 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) {
      deposit.updateConfirmations(currentBlock, requiredConfirmations);

      await this.depositRepo.save(deposit);

      // 处理领域事件
      for (const event of deposit.domainEvents) {
        if (event instanceof DepositConfirmedEvent) {
          // 重要事件写入 outbox，保证可靠投递
          await this.outboxRepo.create({
            eventType: event.eventType,
            aggregateId: deposit.id?.toString() || deposit.txHash.toString(),
            aggregateType: 'DepositTransaction',
            payload: event.toPayload(),
          });
          this.logger.log(
            `Deposit confirmed event saved to outbox: ${deposit.txHash.toShort()} (${deposit.confirmations} confirmations)`,
          );
        } else {
          // 非关键事件直接发送（如 DepositDetectedEvent）
          await this.eventPublisher.publish(event);
        }
      }
      deposit.clearDomainEvents();

      if (deposit.isConfirmed) {
        this.logger.log(
          `Deposit confirmed: ${deposit.txHash.toShort()} (${deposit.confirmations} confirmations)`,
        );
      }
    }
  }
}
