import { Injectable, Logger } from '@nestjs/common';
import { JsonRpcProvider, Contract } from 'ethers';
import { RpcProviderManager } from '@/domain/services/rpc-provider-manager.service';
import { ChainType, BlockNumber, TokenAmount } from '@/domain/value-objects';
import { ChainTypeEnum } from '@/domain/enums';

// ERC20 Transfer 事件 ABI
const ERC20_TRANSFER_EVENT_ABI = [
  'event Transfer(address indexed from, address indexed to, uint256 value)',
];

// ERC20 balanceOf ABI
const ERC20_BALANCE_ABI = [
  'function balanceOf(address owner) view returns (uint256)',
  'function decimals() view returns (uint8)',
];

export interface TransferEvent {
  txHash: string;
  logIndex: number;
  blockNumber: bigint;
  blockTimestamp: Date;
  from: string;
  to: string;
  value: bigint;
  tokenContract: string;
}

/**
 * EVM 区块链提供者适配器
 *
 * 封装与 EVM 链的交互。通过 RpcProviderManager 获取 provider，
 * 自动上报 RPC 调用的成功/失败状态，实现故障转移。
 */
@Injectable()
export class EvmProviderAdapter {
  private readonly logger = new Logger(EvmProviderAdapter.name);

  constructor(private readonly rpcProviderManager: RpcProviderManager) {}

  /**
   * 获取某条链当前活跃的 provider（由 RpcProviderManager 统一管理）
   */
  private getProvider(chainType: ChainType): JsonRpcProvider {
    return this.rpcProviderManager.getProvider(chainType.value);
  }

  /**
   * 执行 RPC 调用并自动上报成功/失败状态
   *
   * 所有公开方法通过此辅助方法包裹 RPC 调用：
   * - 成功时调用 reportSuccess() 重置失败计时
   * - 失败时调用 reportFailure() 记录失败（超过 3 分钟自动切换端点）
   * - 错误会 re-throw，不影响调用方的错误处理逻辑
   */
  private async executeWithFailover<T>(
    chainType: ChainType,
    operation: () => Promise<T>,
  ): Promise<T> {
    try {
      const result = await operation();
      this.rpcProviderManager.reportSuccess(chainType.value);
      return result;
    } catch (error) {
      this.rpcProviderManager.reportFailure(
        chainType.value,
        error instanceof Error ? error : new Error(String(error)),
      );
      throw error;
    }
  }

  /**
   * 获取当前区块号
   */
  async getCurrentBlockNumber(chainType: ChainType): Promise<BlockNumber> {
    return this.executeWithFailover(chainType, async () => {
      const provider = this.getProvider(chainType);
      const blockNumber = await provider.getBlockNumber();
      return BlockNumber.create(blockNumber);
    });
  }

  /**
   * 获取区块时间戳
   */
  async getBlockTimestamp(chainType: ChainType, blockNumber: BlockNumber): Promise<Date> {
    return this.executeWithFailover(chainType, async () => {
      const provider = this.getProvider(chainType);
      const block = await provider.getBlock(blockNumber.asNumber);
      if (!block) {
        throw new Error(`Block not found: ${blockNumber.toString()}`);
      }
      return new Date(block.timestamp * 1000);
    });
  }

  /**
   * 扫描指定区块范围内的 ERC20 Transfer 事件
   */
  async scanTransferEvents(
    chainType: ChainType,
    fromBlock: BlockNumber,
    toBlock: BlockNumber,
    tokenContract: string,
  ): Promise<TransferEvent[]> {
    return this.executeWithFailover(chainType, async () => {
      const provider = this.getProvider(chainType);
      const contract = new Contract(tokenContract, ERC20_TRANSFER_EVENT_ABI, provider);

      const filter = contract.filters.Transfer();
      const logs = await contract.queryFilter(filter, fromBlock.asNumber, toBlock.asNumber);

      const events: TransferEvent[] = [];

      for (const log of logs) {
        const block = await provider.getBlock(log.blockNumber);
        if (!block) continue;

        const parsedLog = contract.interface.parseLog({
          topics: log.topics as string[],
          data: log.data,
        });

        if (parsedLog) {
          events.push({
            txHash: log.transactionHash,
            logIndex: log.index,
            blockNumber: BigInt(log.blockNumber),
            blockTimestamp: new Date(block.timestamp * 1000),
            from: parsedLog.args[0],
            to: parsedLog.args[1],
            value: parsedLog.args[2],
            tokenContract,
          });
        }
      }

      return events;
    });
  }

  /**
   * 查询 ERC20 代币余额
   */
  async getTokenBalance(
    chainType: ChainType,
    tokenContract: string,
    address: string,
  ): Promise<TokenAmount> {
    return this.executeWithFailover(chainType, async () => {
      const provider = this.getProvider(chainType);
      const contract = new Contract(tokenContract, ERC20_BALANCE_ABI, provider);
      const [balance, decimals] = await Promise.all([
        contract.balanceOf(address),
        contract.decimals(),
      ]);
      return TokenAmount.fromRaw(balance, Number(decimals));
    });
  }

  /**
   * 查询 ERC20 代币的 decimals
   */
  async getTokenDecimals(chainType: ChainType, tokenContract: string): Promise<number> {
    return this.executeWithFailover(chainType, async () => {
      const provider = this.getProvider(chainType);
      const contract = new Contract(tokenContract, ERC20_BALANCE_ABI, provider);
      const decimals = await contract.decimals();
      return Number(decimals);
    });
  }

  /**
   * 查询原生代币余额
   */
  async getNativeBalance(chainType: ChainType, address: string): Promise<TokenAmount> {
    return this.executeWithFailover(chainType, async () => {
      const provider = this.getProvider(chainType);
      const balance = await provider.getBalance(address);
      return TokenAmount.fromRaw(balance, 18);
    });
  }

  /**
   * 广播签名交易
   */
  async broadcastTransaction(chainType: ChainType, signedTx: string): Promise<string> {
    return this.executeWithFailover(chainType, async () => {
      const provider = this.getProvider(chainType);
      const txResponse = await provider.broadcastTransaction(signedTx);
      this.logger.log(`Transaction broadcasted: ${txResponse.hash}`);
      return txResponse.hash;
    });
  }

  /**
   * 等待交易确认
   */
  async waitForTransaction(
    chainType: ChainType,
    txHash: string,
    confirmations: number = 1,
  ): Promise<boolean> {
    return this.executeWithFailover(chainType, async () => {
      const provider = this.getProvider(chainType);
      const receipt = await provider.waitForTransaction(txHash, confirmations);
      return receipt !== null && receipt.status === 1;
    });
  }

  /**
   * 检查交易是否确认
   */
  async isTransactionConfirmed(
    chainType: ChainType,
    txHash: string,
    requiredConfirmations: number,
  ): Promise<boolean> {
    return this.executeWithFailover(chainType, async () => {
      const provider = this.getProvider(chainType);
      const receipt = await provider.getTransactionReceipt(txHash);
      if (!receipt) return false;

      const currentBlock = await provider.getBlockNumber();
      const confirmations = currentBlock - receipt.blockNumber;
      return confirmations >= requiredConfirmations;
    });
  }
}
