import { JsonRpcProvider, TransactionReceipt, ethers } from 'ethers';

import { ethWallet } from '../../libs/auth';
import { RemoteSigner } from '../../libs/remote-signer';
import { getApproveFunction } from './contract';
import erc20Abi from './erc20-abi.json';

export async function fetchErc20Meta(provider: JsonRpcProvider, contractAddress: string) {
  const contract = new ethers.Contract(contractAddress, erc20Abi, provider);

  // @ts-ignore
  const [name, symbol, decimal] = await Promise.all([contract.name(), contract.symbol(), contract.decimals()]);

  return { name, symbol, decimal: typeof decimal.toNumber === 'function' ? decimal.toNumber() : Number(decimal) };
}

export async function fetchErc20Balance(provider: JsonRpcProvider, contractAddress: string, account: string) {
  const contract = new ethers.Contract(contractAddress, erc20Abi, provider);

  // @ts-ignore
  const balance = await contract.balanceOf(account);
  return balance.toString();
}

export async function fetchErc20Allowance(
  provider: JsonRpcProvider,
  contractAddress: string,
  owner: string,
  spender: string
) {
  const contract = new ethers.Contract(contractAddress, erc20Abi, provider);

  // @ts-ignore
  const allowance = await contract.allowance(owner, spender);
  return allowance.toString();
}

export async function fetchEtherBalance(provider: JsonRpcProvider, account: string) {
  const balance = await provider.getBalance(account);
  return balance.toString();
}

export function encodeErc20Transfer(to: string, amount: string) {
  const iface = new ethers.Interface(erc20Abi);
  return iface.encodeFunctionData('transfer', [to, amount]);
}

export function encodeErc20Approve(spender: string, allowance: string, action: string = 'approve') {
  const iface = new ethers.Interface(erc20Abi);
  return iface.encodeFunctionData(action, [spender, allowance] as any);
}

export function encodeTransferItx(to: string, amount: string, contract?: string) {
  // Sending ERC20
  if (contract) {
    return {
      to: contract,
      value: '0',
      gasLimit: '120000',
      data: encodeErc20Transfer(to, amount),
    };
  }

  // Sending Ether
  return {
    to,
    value: amount,
    gasLimit: '21000',
    data: '',
  };
}

export async function encodeApproveItx(
  provider: JsonRpcProvider,
  spender: string,
  allowance: string,
  contract: string
) {
  const action = await getApproveFunction(provider, contract);
  const result = {
    to: contract,
    value: '0',
    gasLimit: '120000', // FIXME: make me dynamic
    data: encodeErc20Approve(spender, allowance, action),
  };
  return result;
}

// capture payments
export async function transferErc20FromUser(
  provider: JsonRpcProvider,
  contractAddress: string,
  user: string,
  amount: string
): Promise<TransactionReceipt> {
  const signer = new RemoteSigner(ethWallet, provider);
  const contract = new ethers.Contract(contractAddress, erc20Abi, signer);

  // @ts-ignore
  const res = await contract.transferFrom(user, ethWallet.address, amount);

  // Wait for the transaction to be mined
  const receipt = await res.wait();
  return receipt;
}

// do refunds
export async function sendErc20ToUser(
  provider: JsonRpcProvider,
  contractAddress: string,
  user: string,
  amount: string
): Promise<TransactionReceipt> {
  const signer = new RemoteSigner(ethWallet, provider);
  const contract = new ethers.Contract(contractAddress, erc20Abi, signer);

  // @ts-ignore
  const res = await contract.transfer(user, amount);

  // Wait for the transaction to be mined
  const receipt = await res.wait();
  return receipt;
}
