// ignore_for_file: depend_on_referenced_packages

import 'dart:typed_data';
import 'package:flutter_wallet/tools/YSNetWork.dart';
import 'package:flutter_wallet/tools/YSTools.dart';
import 'package:web3dart/web3dart.dart';
import 'dart:math';
import 'package:http/http.dart';
import 'package:intl/intl.dart';
import 'dart:convert';
import 'package:flutter/services.dart' show rootBundle;

const int ysdecimals = 18;

class Wbe3Api {
  late Wbe3Api? wbe3api;
  late Web3Client? client = Web3Client(YSData().rpc, Client());
  late DeployedContract contract;
  late String _contractAddress;
  late int decimals = 18;
//使用单例模式，
  Future<Wbe3Api?> getInstances() async {
    try {
      wbe3api = Wbe3Api();
      client = Web3Client(YSData().rpc, Client());
      return wbe3api;
    } catch (error) {
      LogUtil.d(error);
      return null;
    }
  }

  //获取主链余额
  static Future<String> getBalance(String address) async {
    try {
      final client = Web3Client(YSData().rpc, Client());
      EtherAmount? amount =
      await client.getBalance(EthereumAddress.fromHex(address));
      BigInt? available = amount.getInWei;
      String blance = (available/ BigInt.from(pow(10, ysdecimals))).toString();
      LogUtil.d("$address=====$blance");
      await client.dispose();
      return blance;
    } catch (err) {
      LogUtil.d(' 余额错误: ${err.toString()}');
      return '0.0';
    }
  }

  //获取主链矿工费，如果矿工费给的不够高，那就无法交易
  Future<String> getdefaultEthfee() async {
    EtherAmount? gasprice = await client?.getGasPrice();
    LogUtil.d("==${_tofee(BigInt.from(21000), gasprice!)}");
    return _tofee(BigInt.from(21000), gasprice);
  }
  /// 获取手续费
  /// gaslimit :最小gas,合约需要算出来，主链币则默认为21000
  /// gasprice：gas价格
  String _tofee(BigInt gaslimit, EtherAmount gasprice) {
    var fee = gaslimit * gasprice.getInWei;
    var result = fee / BigInt.from(pow(10, ysdecimals));
    return result.toString();
  }
  //判断以太坊地址是否正确
  Future<bool> getIsGCAddress(String maddress) async {
    try {//
      EthereumAddress address = EthereumAddress.fromHex(maddress);
      LogUtil.d(address);
      return true;
    } catch (e) {
      return false;
    }
  }

  /// 发起普通交易
  /// fromaddress 发送地址
  /// toaddress  接收地址
  /// privatekey 私钥
  /// fee  手续费
  /// value  数量
  Future<Object?> signETHTransaction(String fromaddress, String toaddress,
      String privatekey, String value,String gas,{Uint8List? data,String? fee}) async {
    // LogUtil.d('发送地址:$fromaddress\n接收地址:$toaddress\n金额:$value\n服务费:$gas');
    try {
      // LogUtil.d(YSData().rpc);
      final credentials = EthPrivateKey.fromHex(privatekey);
      // EthereumAddress from = EthereumAddress.fromHex(fromaddress);
      final receiver = EthereumAddress.fromHex(toaddress);
      // EtherAmount? gasprice = await client?.getGasPrice();
      EtherAmount? gasprice = await client?.getGasPrice();
      if(fee!=null){
        gasprice = EtherAmount.fromInt(EtherUnit.wei,int.parse(fee));
      }
      BigInt gaslimit = BigInt.from(double.parse(gas) * pow(10, 0));
      final networkId = await client?.getNetworkId();
      BigInt amount = tokenInt(value, decimals);
      EtherAmount amountValue = EtherAmount.fromBigInt(EtherUnit.wei, amount);
      if(value=='0'){
        amountValue = EtherAmount.fromInt(EtherUnit.wei,0);
      }
      // BigInt gaslimit = BigInt.from(double.parse(fee)* pow(10, decimals));
      // LogUtil.d(
      //   '发送地址:$fromaddress\n'
      //   '接收地址:$toaddress\n'
      //   '金额:${EtherAmount.fromBigInt(EtherUnit.wei, amount)}\n'
      //   'gasPrice:$gasprice\n'
      //   'maxGas:${gaslimit.toInt()}'
      // );
      var transaction = data!=null?Transaction(
        to: receiver,
        gasPrice: gasprice,
        maxGas: gaslimit.toInt(),
        value: amountValue,
        data: data
      ):Transaction(
        to: receiver,
        gasPrice: gasprice,
        maxGas: gaslimit.toInt(),
        value: amountValue,
      );
      var txHash = await client?.sendTransaction(
        credentials,
        transaction,
        chainId: networkId,
      );
      LogUtil.d('transferhash====${txHash??''}');
      await client?.dispose();
      return txHash;
    } catch (error) {
      LogUtil.d(error);
      ysFlutterToast('$error');
      return '';
    }
  }

  /// 通过精度格式化 传入的数量
  /// value 数量
  /// decimals 精度（保留小数位）
  BigInt tokenInt(String? value, int decimals) {
    if (value==null) {
      return BigInt.zero;
    }
    double? v = 0;
    try {
      if (value.contains(',') || value.contains('.')) {
        v = NumberFormat(",##0.${"0" * decimals}").parse(value) as double?;

      } else {
        v = double.parse(value);
      }
    } catch (err) {
      LogUtil.d('Fmt.tokenInt() error: ${err.toString()}');
    }
    return BigInt.from(v! * pow(10, decimals));
  }

  /// 判断是否合约地址
  /// contractaddress 合约地址
  Future<bool> getIsContractAddress(String contractaddress) async {
    if (contractaddress.length != 42) {
      return false;
    } else {
      EthereumAddress address = EthereumAddress.fromHex(contractaddress);
      var respons = await client?.getCode(address);
      LogUtil.d("respons ====$respons");
      return respons!.isNotEmpty ? true : false;
    }
  }

  /// 设置全局合约，读取abi
  Future<bool> setContaract(String contractaddress,{int decimal = 0}) async {
    _contractAddress = contractaddress;
    contract = await fromAssets('images/contract.json', _contractAddress);
    // decimals = decimal;
    // LogUtil.d(_contractAddress);
    // decimals = decimal;
    // return true;
    // LogUtil.d('setContaract=========$_contractAddress');
    var result = await getContractInfo('decimals');
    if('$result'.contains('RangeError')){
      decimals = decimal;
      return true;
    }else{
      var result = await getContractInfo('decimals');
      decimals = int.parse(result.toString());
      return true;
    }
    return false;
  }

  /// 将合约格式化
  Future<DeployedContract> fromAssets(String path, String contractAddress) async {
    final contractJson = jsonDecode(await rootBundle.loadString(path));
    DeployedContract value = DeployedContract(ContractAbi.fromJson(jsonEncode(contractJson['abi']),contractJson['contractName'] as String),EthereumAddress.fromHex(contractAddress));
    return value;
  }

  /// 读取合约基本信息decimals和symbol
  adppContaractDecimals(String contractaddress) async {
    // int dappDecimals = 18;
    _contractAddress = contractaddress;
    contract = await fromAssets('images/contract.json', _contractAddress);
    decimals = 0;
    // try {
    //   contract = await fromAssets('images/contract.json', contractaddress);
    //   var dec = await getContractInfo('decimals');
    //   var type = await getContractInfo('symbol');
    //   decimals = int.parse(dec.toString());
    //   LogUtil.d("adpp精度获取==$decimals");
    //   return {'decimals': decimals, 'coin': type};
    // } catch (error) {
    //   return {'decimals': decimals, 'coin': "ETH"};
    // }
  }

  /// 获取合约基本信息
  /// functionname 方法名
  /// decimals：合约精度，小数位
  /// name 名字
  /// symbol  和名字一致
  Future<String?> getContractInfo(String functionname) async {
    try {
      final response = await client?.call(
        contract: contract,
        function: contract.function(functionname),
        params: [],
      );
      // LogUtil.d('getContractInfo${response?.first.toString()}');
      return response?.first.toString();
    } catch (error) {
      // LogUtil.d('getContractInfo2$error');
      return error.toString();
    }
  }

  //获取合约余额
  Future<String> getTokenBalance(String madress,String contractAddress) async {
    // LogUtil.d(YSData().rpc);
    bool isCheck = await setContaract(contractAddress);
    if(isCheck){
      EthereumAddress adress = EthereumAddress.fromHex(madress);
      final response = await client?.call(
        contract: contract,
        function: contract.function('balanceOf'),
        params: [adress],
      );
      // LogUtil.d('response=====${contract.address}');
      // LogUtil.d(response.toString());
      double blanceD = response?.first / BigInt.from(pow(10, decimals));
      // String blance = blanceD.toStringAsFixed(10);
      LogUtil.d('$madress====$blanceD');
      return '$blanceD';
    }
    return '0.0';
  }

  //转出手续费
  Future<String> getTransferFee(
      String fromaddress, String toaddress, String value) async {
    BigInt amount = tokenInt(value, decimals);
    EthereumAddress receiver = EthereumAddress.fromHex(toaddress);
    return await getCommonFee(fromaddress, "transfer", [receiver, amount]);
  }

  //兑换手续费
  Future<String> getexchangeFee(String fromaddress, String toaddress, String value) async {
    BigInt amount = tokenInt(value, decimals);
    return await getCommonFee(fromaddress, "exchange", [amount]);
  }

/*
   * 获取合约手续费通用方法
   * fromaddress 发送地址
   * functionname 方法名
   * parameters  合约参数
   */
  Future<String> getCommonFee(
      String from, String functionname, List<dynamic> parameters) async {

    EthereumAddress fromaddress = EthereumAddress.fromHex(from);
    final gasprice = await client?.getGasPrice();
    LogUtil.d('gasprice$gasprice');
    var transaction = Transaction.callContract(
        contract: contract,
        function: contract.function(functionname),
        parameters: parameters,
        gasPrice: gasprice,
        from: fromaddress);
    BigInt? gaslimit = BigInt.from(21000);
    LogUtil.d('检测gaslimit${transaction.data} $gaslimit');
    try {
      LogUtil.d("gaslimit ====${transaction.from}====${transaction.to}====${transaction.data}====${EtherAmount.zero()}");
      gaslimit = await client?.estimateGas(
          sender: transaction.from,
          to: transaction.to,
          data: transaction.data,
          value: EtherAmount.zero()
      );
      // gaslimit = await client?.estimateGas(
      //     sender: fromaddress,
      //     to: EthereumAddress.fromHex(_contractAddress),
      //     data: transaction.data,
      //     value: EtherAmount.zero());
      LogUtil.d('gaslimit ====$gaslimit');
      return _tofee(gaslimit!, gasprice!);
    } catch (error) {
      LogUtil.d(error.toString());
      return 'error$error';
    }
  }

  /// 发起合约转账
  /// fromaddress 发送地址
  /// toaddress  接收地址
  /// privatekey 私钥
  /// value  数量
  Future<Object> tokenTransfer(String fromaddress, String toaddress,
      String privatekey, String fee, String value,String gas) async {
    LogUtil.d('发送地址:$fromaddress\n接收地址:$toaddress\n金额:$value\n服务费:$fee');
    BigInt amount = tokenInt(value, decimals);
    EthereumAddress receiver = EthereumAddress.fromHex(toaddress);
    return await signContractTransaction2(fromaddress, privatekey, fee, "transfer", [receiver, amount],gas);
  }
  Future<Object> tokenTransfer2(String fromaddress, String toaddress,
      String privatekey, String fee, String value,String gas) async {
    LogUtil.d('发送地址:$fromaddress\n接收地址:$toaddress\n金额:$value\n服务费:$fee');
    BigInt amount = tokenInt(value, decimals);
    EthereumAddress receiver = EthereumAddress.fromHex(toaddress);
    return await signContractTransaction3(fromaddress, privatekey, fee, "transfer", [receiver, amount],gas);
  }

  /// 发起兑换交易
  /// fromaddress 发送地址
  /// privatekey 私钥
  /// value  数量
  Future<Object> tokenExchange(String fromaddress, String privatekey, String fee, String value, String gas,String toaddress,String rpc,String towalletaddress,String fromtokenaddress) async {
    BigInt amount = tokenInt(value, decimals);
    // LogUtil.d(contract.address.hexEip55);
    // String changeStr = await getexchangeFee(fromaddress, contract.address.hexEip55, value);
    // LogUtil.d(changeStr);
    // return '';
    // return await signContractTransaction3(fromaddress, privatekey, fee, "exchange", [amount],gas);
    LogUtil.d('tokenExchange============$fromaddress===============$toaddress');
    bool isContract = await getIsContractAddress(fromtokenaddress);
    bool isWBNB = toaddress=='0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c'&&isContract==false;

    // final myAddress = EthereumAddress.fromHex(fromaddress);
    final toWalletAddress = EthereumAddress.fromHex(towalletaddress);
    // 要交换的代币合约地址，例如：CAKE
    final tokenIn = EthereumAddress.fromHex(isContract?fromtokenaddress:'0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c');
    // 要接收的代币合约地址，例如：USDT
    final tokenOut = EthereumAddress.fromHex(toaddress);
    client = Web3Client(rpc, Client());

    contract = await fromAssets('asset/exchange.json', isWBNB?'0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c':YSData().pancakeAddress);
    //swapExactETHForTokens swapExactTokensForTokens

    String funName = 'swapExactETHForTokens';
    if(isContract){
      funName = 'swapExactTokensForTokens';
    }
    if(isWBNB){
      funName = 'deposit';
    }
    List<dynamic> parameters = isWBNB?[]:[
      amount, // 要交换的代币数量（注意小数位数）
      if(funName=='swapExactTokensForTokens')BigInt.from(0), // 接收的最少代币数量
      funName=='swapExactTokensForTokens'?[tokenIn, tokenOut]:[tokenIn,tokenOut], // 路径
      toWalletAddress, // 接收地址
      BigInt.from((DateTime.now().millisecondsSinceEpoch / 1000).floor() + 60 * 20), // 截止时间
    ];
    LogUtil.d('tokenExchange=======$funName======$isContract====$parameters');
    return await signContractTransaction4(fromaddress, privatekey, fee, funName,parameters ,gas,amount: (isContract==false)?amount:null);
  }

/*
   * 发起合约交易
   * fromaddress 发送地址
   * privatekey 私钥
   * functionname 合約调用用方法
   * parameters  合約参數
   */
  Future<Object> signContractTransaction4(String from, String privatekey,String fee, String functionname, List<dynamic> parameters, String gas,{BigInt? amount}) async {
    // bool isCheck = await setContaract(YSData().contAddress);
    LogUtil.d('amount======$amount');
    if(true){
      try {
        final credentials = EthPrivateKey.fromHex(privatekey);
        EthereumAddress fromaddress = EthereumAddress.fromHex(from);
        final networkId = await client?.getNetworkId();
        final gasprice = await client?.getGasPrice();
        // LogUtil.d(gasprice);
        // 设置交易参数
        final transaction = Transaction.callContract(
          contract: contract,
          function: contract.function(functionname),
          parameters: parameters,
          from: fromaddress,
          gasPrice: gasprice,
          maxGas: 200000,
          value: amount==null?EtherAmount.zero():EtherAmount.inWei(amount),
        );
        // return '';
        var txHash = await client?.sendTransaction(
          credentials,
          transaction,
          chainId: networkId,
        );
        LogUtil.d('hash====${txHash!}');
        await client?.dispose();
        return txHash;
      } catch (error) {
        LogUtil.d('signContractTransaction error========$error');
        ysFlutterToast('$error');
        return '';
      }
    }
    return '';
  }
  Future<Object> signContractTransaction(String from, String privatekey,String fee, String functionname, List<dynamic> parameters, String gas) async {
    // bool isCheck = await setContaract(YSData().contAddress);
    LogUtil.d('decimals======$decimals');
    if(true){
      try {
        EthereumAddress fromaddress = EthereumAddress.fromHex(from);
        final credentials = EthPrivateKey.fromHex(privatekey);
        final networkId = await client?.getNetworkId();
        final gasprice = await client?.getGasPrice();
        BigInt gaslimit = BigInt.from(double.parse(gas) * pow(10, 0));
        // LogUtil.d('decimals======${gaslimit.toInt()}');
        // BigInt feeValue = BigInt.from(double.parse(fee) * pow(10, decimals));
        LogUtil.d("gaslimit ====${gaslimit.toInt()}\n$parameters\n$gasprice");
        var transaction = Transaction.callContract(
            contract: contract,
            function: contract.function(functionname),
            parameters: parameters,
            from: fromaddress,
            gasPrice: gasprice,
            maxGas: gaslimit.toInt());
        var txHash = await client?.sendTransaction(
          credentials,
          transaction,
          chainId: networkId,
        );
        LogUtil.d('hash====${txHash!}');
        await client?.dispose();
        
        return txHash;
      } catch (error) {
        LogUtil.d('signContractTransaction error========$error');
        ysFlutterToast('$error');
        return '';
      }
    }
    return '';
  }

  Future<Object> signContractTransaction2(String from, String privatekey,String fee, String functionname, List<dynamic> parameters, String gas) async {
    // bool isCheck = await setContaract(YSData().contAddress);
    LogUtil.d('decimals======$decimals');
    if(true){
      try {
        EthereumAddress fromaddress = EthereumAddress.fromHex(from);
        final credentials = EthPrivateKey.fromHex(privatekey);
        final networkId = await client?.getNetworkId();
        final gasprice = EtherAmount.fromInt(EtherUnit.wei,int.parse(fee));
        BigInt gaslimit = BigInt.from(double.parse(gas) * pow(10, 0));
        // LogUtil.d('decimals======${gaslimit.toInt()}');
        // BigInt feeValue = BigInt.from(double.parse(fee) * pow(10, decimals));
        LogUtil.d("gaslimit ====${gaslimit.toInt()}\n$parameters\n$gasprice");
        var transaction = Transaction.callContract(
            contract: contract,
            function: contract.function(functionname),
            parameters: parameters,
            from: fromaddress,
            gasPrice: gasprice,
            maxGas: gaslimit.toInt());
        var txHash = await client?.sendTransaction(
          credentials,
          transaction,
          chainId: networkId,
        );
        LogUtil.d('hash====${txHash!}');
        await client?.dispose();
        return txHash;
      } catch (error) {
        LogUtil.d('signContractTransaction error========$error');
        ysFlutterToast('$error');
        return '';
      }
    }
    return '';
  }

  Future<Object> signContractTransaction3(String from, String privatekey,String fee, String functionname, List<dynamic> parameters, String gas) async {
    // bool isCheck = await setContaract(YSData().contAddress);
    LogUtil.d('decimals======$decimals');
    if(true){
      try {
        EthereumAddress fromaddress = EthereumAddress.fromHex(from);
        final credentials = EthPrivateKey.fromHex(privatekey);
        final networkId = await client?.getNetworkId();
        final gasprice = EtherAmount.fromInt(EtherUnit.wei,int.parse(fee));
        BigInt gaslimit = BigInt.from(double.parse(gas) * pow(10, 0));
        // LogUtil.d('decimals======${gaslimit.toInt()}');
        // BigInt feeValue = BigInt.from(double.parse(fee) * pow(10, decimals));
        LogUtil.d("gaslimit ====${gaslimit.toInt()}\n$parameters\n$gasprice");
        var transaction = Transaction.callContract(
            contract: contract,
            function: contract.function(functionname),
            parameters: parameters,
            from: fromaddress,
            gasPrice: gasprice,
            maxGas: gaslimit.toInt());
        LogUtil.d(YSData().rpc);
        LogUtil.d(transaction.data);
        return '';
        var txHash = await client?.sendTransaction(
          credentials,
          transaction,
          chainId: networkId,
        );
        LogUtil.d('hash====${txHash!}');
        await client?.dispose();
        return txHash;
      } catch (error) {
        LogUtil.d('signContractTransaction error========$error');
        ysFlutterToast('$error');
        return '';
      }
    }
    return '';
  }

  /// 获取交易状态
  /// txHash 交易hash
  Future<bool?> getTranferstate(String txHash) async {
    try {
      var transactionReceipt = await client?.getTransactionReceipt(txHash);
      if (transactionReceipt != null) {
        LogUtil.d("交易状态: ${transactionReceipt.status}");
        await client?.dispose();
        return transactionReceipt.status;
      }
      return false;
    } catch (error) {
      LogUtil.d("状态error: $error");
      return false;
    }
  }


}

//格式化小数点
String formatFour(String values) {
  double value = double.tryParse(values)! / pow(10, ysdecimals);
  String newvalue = value.toStringAsFixed(8);
  return newvalue.substring(0, newvalue.indexOf('.') + 7);
}