import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { ethers } from 'ethers';
import Decimal from 'decimal.js';

export interface TransactionResult {
  txHash: string;
  blockNumber: number;
  gasUsed: bigint;
  status: 'success' | 'failed';
}

export interface TokenBalance {
  balance: Decimal;
  decimals: number;
}

@Injectable()
export class KavaBlockchainService implements OnModuleInit {
  private readonly logger = new Logger(KavaBlockchainService.name);
  private provider: ethers.JsonRpcProvider;
  private hotWallet: ethers.Wallet | null = null;
  private blackHoleAddress: string;
  private isConnected = false;

  constructor(private readonly configService: ConfigService) {
    this.blackHoleAddress = this.configService.get<string>(
      'KAVA_BLACK_HOLE_ADDRESS',
      '0x000000000000000000000000000000000000dEaD',
    );
  }

  async onModuleInit() {
    await this.connect();
  }

  private async connect(): Promise<void> {
    try {
      const rpcUrl = this.configService.get<string>('KAVA_RPC_URL', 'https://evm.kava.io');
      const chainId = this.configService.get<number>('KAVA_CHAIN_ID', 2222);

      this.provider = new ethers.JsonRpcProvider(rpcUrl, chainId);

      // Test connection
      const network = await this.provider.getNetwork();
      this.logger.log(`Connected to KAVA network: ${network.chainId}`);

      // Initialize hot wallet if private key is provided
      const privateKey = this.configService.get<string>('KAVA_HOT_WALLET_PRIVATE_KEY');
      if (privateKey) {
        this.hotWallet = new ethers.Wallet(privateKey, this.provider);
        this.logger.log(`Hot wallet initialized: ${this.hotWallet.address}`);
      } else {
        this.logger.warn('No hot wallet private key provided - blockchain operations limited');
      }

      this.isConnected = true;
    } catch (error) {
      this.logger.error('Failed to connect to KAVA blockchain', error);
      this.isConnected = false;
    }
  }

  isReady(): boolean {
    return this.isConnected && this.hotWallet !== null;
  }

  getHotWalletAddress(): string | null {
    return this.hotWallet?.address || null;
  }

  getBlackHoleAddress(): string {
    return this.blackHoleAddress;
  }

  /**
   * 获取账户的原生币余额
   */
  async getBalance(address: string): Promise<Decimal> {
    const balance = await this.provider.getBalance(address);
    return new Decimal(ethers.formatEther(balance));
  }

  /**
   * 获取当前区块号
   */
  async getCurrentBlockNumber(): Promise<number> {
    return this.provider.getBlockNumber();
  }

  /**
   * 获取交易状态
   */
  async getTransactionStatus(txHash: string): Promise<{
    confirmed: boolean;
    blockNumber: number | null;
    confirmations: number;
    status: 'success' | 'failed' | 'pending';
  }> {
    const receipt = await this.provider.getTransactionReceipt(txHash);

    if (!receipt) {
      return {
        confirmed: false,
        blockNumber: null,
        confirmations: 0,
        status: 'pending',
      };
    }

    const currentBlock = await this.getCurrentBlockNumber();
    const confirmations = currentBlock - receipt.blockNumber;

    return {
      confirmed: confirmations >= 1,
      blockNumber: receipt.blockNumber,
      confirmations,
      status: receipt.status === 1 ? 'success' : 'failed',
    };
  }

  /**
   * 发送原生币（KAVA）
   */
  async sendNative(
    toAddress: string,
    amount: Decimal,
  ): Promise<TransactionResult> {
    if (!this.hotWallet) {
      throw new Error('Hot wallet not initialized');
    }

    const tx = await this.hotWallet.sendTransaction({
      to: toAddress,
      value: ethers.parseEther(amount.toString()),
    });

    const receipt = await tx.wait();

    if (!receipt) {
      throw new Error('Transaction failed - no receipt');
    }

    return {
      txHash: receipt.hash,
      blockNumber: receipt.blockNumber,
      gasUsed: receipt.gasUsed,
      status: receipt.status === 1 ? 'success' : 'failed',
    };
  }

  /**
   * 发送 ERC20 代币
   */
  async sendToken(
    tokenAddress: string,
    toAddress: string,
    amount: Decimal,
    decimals: number = 18,
  ): Promise<TransactionResult> {
    if (!this.hotWallet) {
      throw new Error('Hot wallet not initialized');
    }

    const erc20Abi = [
      'function transfer(address to, uint256 amount) returns (bool)',
      'function balanceOf(address account) view returns (uint256)',
      'function decimals() view returns (uint8)',
    ];

    const tokenContract = new ethers.Contract(tokenAddress, erc20Abi, this.hotWallet);

    const amountWei = ethers.parseUnits(amount.toString(), decimals);
    const tx = await tokenContract.transfer(toAddress, amountWei);

    const receipt = await tx.wait();

    if (!receipt) {
      throw new Error('Transaction failed - no receipt');
    }

    return {
      txHash: receipt.hash,
      blockNumber: receipt.blockNumber,
      gasUsed: receipt.gasUsed,
      status: receipt.status === 1 ? 'success' : 'failed',
    };
  }

  /**
   * 销毁到黑洞地址
   */
  async burnToBlackHole(
    tokenAddress: string,
    amount: Decimal,
    decimals: number = 18,
  ): Promise<TransactionResult> {
    return this.sendToken(tokenAddress, this.blackHoleAddress, amount, decimals);
  }

  /**
   * 获取 ERC20 代币余额
   */
  async getTokenBalance(
    tokenAddress: string,
    walletAddress: string,
  ): Promise<TokenBalance> {
    const erc20Abi = [
      'function balanceOf(address account) view returns (uint256)',
      'function decimals() view returns (uint8)',
    ];

    const tokenContract = new ethers.Contract(tokenAddress, erc20Abi, this.provider);

    const [balance, decimals] = await Promise.all([
      tokenContract.balanceOf(walletAddress),
      tokenContract.decimals(),
    ]);

    return {
      balance: new Decimal(ethers.formatUnits(balance, decimals)),
      decimals,
    };
  }

  /**
   * 估算 Gas 费用
   */
  async estimateGas(
    toAddress: string,
    value: Decimal,
    data?: string,
  ): Promise<{ gasLimit: bigint; gasPrice: bigint; estimatedFee: Decimal }> {
    const gasPrice = (await this.provider.getFeeData()).gasPrice || 0n;

    const gasLimit = await this.provider.estimateGas({
      to: toAddress,
      value: ethers.parseEther(value.toString()),
      data: data || '0x',
    });

    const estimatedFee = new Decimal(ethers.formatEther(gasLimit * gasPrice));

    return {
      gasLimit,
      gasPrice,
      estimatedFee,
    };
  }

  /**
   * 验证地址格式
   */
  isValidAddress(address: string): boolean {
    return ethers.isAddress(address);
  }

  /**
   * 监听新区块（用于检测充值）
   */
  onNewBlock(callback: (blockNumber: number) => void): void {
    this.provider.on('block', callback);
  }

  /**
   * 停止监听
   */
  removeAllListeners(): void {
    this.provider.removeAllListeners();
  }
}
