import { Tx } from "../client";
import { bnOrZero } from "@dojima-wallet/utils";
import axios from "axios";
import { BigNumberish } from "ethers";

import {
  ETHTransactionInfo,
  GasOracleResponse,
  TokenBalanceParam,
  TokenTransactionInfo,
  TransactionHistoryParam,
} from "./types";
import {
  filterSelfTxs,
  getTxFromEthTransaction,
  getTxFromTokenTransaction,
} from "./utils";

const getApiKeyQueryParameter = (apiKey?: string): string =>
  !!apiKey ? `&apiKey=${apiKey}` : "";

/**
 * SafeGasPrice, ProposeGasPrice And FastGasPrice returned in string-Gwei
 *
 * @see https://etherscan.io/apis#gastracker
 *
 * @param {string} baseUrl The etherscan node url.
 * @param {string} apiKey The etherscan API key. (optional)
 * @returns {GasOracleResponse} LastBlock, SafeGasPrice, ProposeGasPrice, FastGasPrice
 */
export const getGasOracle = async (
  baseUrl: string,
  apiKey?: string
): Promise<GasOracleResponse> => {
  const url = baseUrl + "/api?module=gastracker&action=gasoracle";

  return (await axios.get(url + getApiKeyQueryParameter(apiKey))).data.result;
};

export const getGoerliGasOracle = async (
  baseUrl: string,
  apiKey?: string
): Promise<string> => {
  const url = baseUrl + "/api?module=proxy&action=eth_gasPrice";

  return (await axios.get(url + getApiKeyQueryParameter(apiKey))).data.result;
};

/**
 * Get token balance
 *
 * @see https://etherscan.io/apis#tokens
 *
 * @param {string} baseUrl The etherscan node url.
 * @param {string} address The address.
 * @param {string} assetAddress The token contract address.
 * @param {string} apiKey The etherscan API key. (optional)
 * @returns {BigNumberish} The token balance
 */
export const getTokenBalance = async ({
  baseUrl,
  address,
  assetAddress,
  apiKey,
}: TokenBalanceParam & {
  baseUrl: string;
  apiKey?: string;
}): Promise<BigNumberish> => {
  const url =
    baseUrl +
    `/api?module=account&action=tokenbalance&contractaddress=${assetAddress}&address=${address}`;

  return (await axios.get(url + getApiKeyQueryParameter(apiKey))).data.result;
};

/**
 * Get ETH transaction history
 *
 * @see https://etherscan.io/apis#accounts
 *
 * @param {string} baseUrl The etherscan node url.
 * @param {string} address The address.
 * @param {TransactionHistoryParam} params The search options.
 * @param {string} apiKey The etherscan API key. (optional)
 * @returns {ETHTransactionInfo[]} The ETH transaction history
 */
export const getETHTransactionHistory = async ({
  baseUrl,
  address,
  page,
  offset,
  startblock,
  endblock,
  apiKey,
}: TransactionHistoryParam & { baseUrl: string; apiKey?: string }): Promise<
  Tx[]
> => {
  let url =
    baseUrl +
    `/api?module=account&action=txlist&sort=desc` +
    getApiKeyQueryParameter(apiKey);
  if (address) url += `&address=${address}`;
  if (offset) url += `&offset=${offset}`;
  if (page) url += `&page=${page}`;
  if (startblock) url += `&startblock=${startblock}`;
  if (endblock) url += `&endblock=${endblock}`;

  const result = (await axios.get(url)).data.result;
  if (JSON.stringify(result).includes("Invalid API Key"))
    throw new Error("Invalid API Key");
  if (typeof result !== "object") throw new Error(result);

  return filterSelfTxs<ETHTransactionInfo>(result)
    .filter((tx) => !bnOrZero(tx.value).isZero())
    .map(getTxFromEthTransaction);
};

/**
 * Get token transaction history
 *
 * @see https://etherscan.io/apis#accounts
 *
 * @param {string} baseUrl The etherscan node url.
 * @param {string} address The address.
 * @param {TransactionHistoryParam} params The search options.
 * @param {string} apiKey The etherscan API key. (optional)
 * @returns {Tx[]} The token transaction history
 */
export const getTokenTransactionHistory = async ({
  baseUrl,
  address,
  assetAddress,
  page,
  offset,
  startblock,
  endblock,
  apiKey,
}: TransactionHistoryParam & { baseUrl: string; apiKey?: string }): Promise<
  Tx[]
> => {
  let url =
    baseUrl +
    `/api?module=account&action=tokentx&sort=desc` +
    getApiKeyQueryParameter(apiKey);
  if (address) url += `&address=${address}`;
  if (assetAddress) url += `&contractaddress=${assetAddress}`;
  if (offset) url += `&offset=${offset}`;
  if (page) url += `&page=${page}`;
  if (startblock) url += `&startblock=${startblock}`;
  if (endblock) url += `&endblock=${endblock}`;

  const result = (await axios.get(url)).data.result;
  if (JSON.stringify(result).includes("Invalid API Key"))
    throw new Error("Invalid API Key");

  return filterSelfTxs<TokenTransactionInfo>(result)
    .filter((tx) => !bnOrZero(tx.value).isZero())
    .reduce((acc, cur) => {
      const tx = getTxFromTokenTransaction(cur);
      return tx ? [...acc, tx] : acc;
    }, [] as Tx[]);
};
