'use strict';

var _defineProperty = require('@babel/runtime/helpers/defineProperty');
var get = require('../external/lodash-es/get.js');
var set = require('../external/lodash-es/set.js');
var minterjsUtil = require('minterjs-util');
var getCoinInfo = require('./get-coin-info.js');
var utils = require('../utils.js');

function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }

/**
 * @param {MinterApiInstance} apiInstance
 * @param {import('axios').AxiosRequestConfig} [factoryAxiosOptions]
 * @return {ReplaceCoinSymbolInstance}
 */
function ReplaceCoinSymbol(apiInstance, factoryAxiosOptions) {
  var replaceCoinSymbolByPath = ReplaceCoinSymbolByPath(apiInstance, factoryAxiosOptions);
  /**
   * @typedef {Function} ReplaceCoinSymbolInstance
   * @param {TxParams} txParams
   * @param {import('axios').AxiosRequestConfig} [axiosOptions]
   * @return {Promise<TxParams>}
   */
  return function replaceCoinSymbol(txParams, axiosOptions) {
    var pathList = getTxParamsPathList(txParams);
    return replaceCoinSymbolByPath(txParams, pathList, txParams.chainId, axiosOptions);
  };
}

/**
 * @param {MinterApiInstance} apiInstance
 * @param {import('axios').AxiosRequestConfig} [factoryAxiosOptions]
 * @return {ReplaceCoinSymbolByPathInstance}
 */
function ReplaceCoinSymbolByPath(apiInstance, factoryAxiosOptions) {
  var replaceParamsByPath = ReplaceParamsByPath(apiInstance, factoryAxiosOptions);
  /**
   * @typedef {Function} ReplaceCoinSymbolByPathInstance
   * @param {object} txParams
   * @param {Array<string>} pathList
   * @param {number} [chainId]
   * @param {import('axios').AxiosRequestConfig} [axiosOptions]
   * @return {Promise<object>}
   */
  return function replaceCoinSymbolByPath(txParams, pathList) {
    var chainId = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : apiInstance.defaults.chainId;
    var axiosOptions = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : undefined;
    return replaceParamsByPath(txParams, pathList, replacer, chainId, axiosOptions);

    /**
     * @param {string} symbolValue
     * @param {number|string} chainId
     * @param {MinterApiInstance} apiInstance
     * @param {import('axios').AxiosRequestConfig} axiosOptions
     */
    // eslint-disable-next-line no-shadow, unicorn/consistent-function-scoping
    function replacer(symbolValue, chainId, apiInstance, axiosOptions) {
      if (utils.isCoinSymbol(symbolValue)) {
        return _getCoinId(symbolValue, chainId, apiInstance, axiosOptions);
      } else {
        return Promise.resolve(symbolValue);
      }
    }
  };
}

/**
 * Replace id with symbol or symbol with id
 * Shares same interface as _getCoinSymbol or _getCoinId
 * @function CoinIdSymbolReplacer
 * @param {number|string} inputValue
 * @param {number} [chainId]
 * @param {MinterApiInstance} apiInstance
 * @param {import('axios').AxiosRequestConfig} [axiosOptions]
 * @return {Promise<number|string>}
 */

/**
 * @param {MinterApiInstance} apiInstance
 * @param {import('axios').AxiosRequestConfig} [factoryAxiosOptions]
 * @return {ReplaceParamsByPathInstance}
 */
function ReplaceParamsByPath(apiInstance, factoryAxiosOptions) {
  /**
   * @typedef {Function} ReplaceParamsByPathInstance
   * @param {object} txParams
   * @param {Array<string>} pathList
   * @param {CoinIdSymbolReplacer} replacer
   * @param {number} [chainId]
   * @param {import('axios').AxiosRequestConfig} [axiosOptions]
   * @return {Promise<object>}
   */
  return function replaceParamsByPath(txParams, pathList, replacer) {
    var chainId = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : apiInstance.defaults.chainId;
    var axiosOptions = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : undefined;
    axiosOptions = _objectSpread(_objectSpread({}, factoryAxiosOptions), axiosOptions);
    var promiseList = {};
    pathList.forEach(function (path) {
      return fillPath(path);
    });
    var promiseArray = Object.values(promiseList);
    return Promise.all(promiseArray).then(function () {
      return txParams;
    });

    /**
     * Fill promiseList by path and replace txParams value by path
     * @param {string} path
     */
    function fillPath(path) {
      var inputValue = get(txParams, path);
      // coinInfo promise may be used by multiple patchers
      if (!promiseList[inputValue]) {
        promiseList[inputValue] = replacer(inputValue, chainId, apiInstance, axiosOptions);
      }
      // append txParams patcher
      promiseList[inputValue] = promiseList[inputValue].then(function (outputValue) {
        set(txParams, path, outputValue);
        return outputValue;
      });
    }
  };
}

/**
 * @param {MinterApiInstance} apiInstance
 * @param {import('axios').AxiosRequestConfig} [factoryAxiosOptions]
 * @return {GetCoinIdInstance}
 */
function GetCoinId(apiInstance, factoryAxiosOptions) {
  /**
   * @typedef {Function} GetCoinIdInstance
   * @param {string|Array<string>} symbol
   * @param {number} [chainId]
   * @param {import('axios').AxiosRequestConfig} [axiosOptions]
   * @return {Promise<number|Array<number>>}
   */
  return function getCoinId(symbol) {
    var chainId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : apiInstance.defaults.chainId;
    var axiosOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined;
    axiosOptions = _objectSpread(_objectSpread({}, factoryAxiosOptions), axiosOptions);
    return processArrayByPromise(_getCoinId, symbol, chainId, apiInstance, axiosOptions);
  };
}

/**
 * @template T
 * @param {function(value, ...otherArgs): Promise<T>} fn
 * @param {*|Array<*>} value
 * @param {...object} otherArgs
 * @return {Promise<T|Array<T>>}
 */
function processArrayByPromise(fn, value) {
  for (var _len = arguments.length, otherArgs = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
    otherArgs[_key - 2] = arguments[_key];
  }
  if (Array.isArray(value)) {
    var valueList = value;
    var promiseList = valueList.map(function (valueItem) {
      return fn.apply(void 0, [valueItem].concat(otherArgs));
    });
    return Promise.all(promiseList);
  } else {
    return fn.apply(void 0, [value].concat(otherArgs));
  }
}

/**
 * @param {string} symbol
 * @param {number} [chainId]
 * @param {MinterApiInstance} apiInstance
 * @param {import('axios').AxiosRequestConfig} [axiosOptions]
 * @return {Promise<number>}
 * @private
 */
function _getCoinId(symbol, chainId, apiInstance, axiosOptions) {
  if (utils.isCoinId(symbol)) {
    return Promise.resolve(symbol);
  }
  if (utils.isCoinSymbol(symbol)) {
    if (utils.isBaseCoinSymbol(chainId, symbol)) {
      return Promise.resolve(0);
    } else {
      var getCoinInfo$1 = getCoinInfo(apiInstance);
      return getCoinInfo$1(symbol, axiosOptions).then(function (coinInfo) {
        return coinInfo.id;
      });
    }
  } else {
    return Promise.reject(new Error('Invalid coin symbol'));
  }
}

/**
 * @param {MinterApiInstance} apiInstance
 * @param {import('axios').AxiosRequestConfig} [factoryAxiosOptions]
 * @return {ReplaceCoinIdInstance}
 */
function ReplaceCoinId(apiInstance, factoryAxiosOptions) {
  var replaceCoinIdByPath = ReplaceCoinIdByPath(apiInstance, factoryAxiosOptions);
  /**
   * @typedef {Function} ReplaceCoinIdInstance
   * @param {TxParams} txParams
   * @param {import('axios').AxiosRequestConfig} [axiosOptions]
   * @return {Promise<TxParams>}
   */
  return function replaceCoinId(txParams, axiosOptions) {
    var pathList = getTxParamsPathList(txParams);
    return replaceCoinIdByPath(txParams, pathList, txParams.chainId, axiosOptions);
  };
}

/**
 * @param {MinterApiInstance} apiInstance
 * @param {import('axios').AxiosRequestConfig} [factoryAxiosOptions]
 * @return {ReplaceCoinIdByPathInstance}
 */
function ReplaceCoinIdByPath(apiInstance, factoryAxiosOptions) {
  var replaceParamsByPath = ReplaceParamsByPath(apiInstance, factoryAxiosOptions);
  /**
   * @typedef {Function} ReplaceCoinIdByPathInstance
   * @param {object} txParams
   * @param {Array<string>} pathList
   * @param {number} [chainId]
   * @param {import('axios').AxiosRequestConfig} [axiosOptions]
   * @return {Promise<object>}
   */
  return function replaceCoinIdByPath(txParams, pathList) {
    var chainId = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : apiInstance.defaults.chainId;
    var axiosOptions = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : undefined;
    return replaceParamsByPath(txParams, pathList, replacer, chainId, axiosOptions);

    /**
     * @param {number|any} idValue
     * @param {number} [chainId]
     * @param {MinterApiInstance} apiInstance
     * @param {import('axios').AxiosRequestConfig} [axiosOptions]
     * @return {Promise<string>}
     */
    // eslint-disable-next-line no-shadow, unicorn/consistent-function-scoping
    function replacer(idValue, chainId, apiInstance, axiosOptions) {
      if (utils.isCoinId(idValue)) {
        return _getCoinSymbol(idValue, chainId, apiInstance, axiosOptions);
      } else {
        return Promise.resolve(idValue);
      }
    }
  };
}

/**
 * @param {MinterApiInstance} apiInstance
 * @param {import('axios').AxiosRequestConfig} [factoryAxiosOptions]
 * @return {GetCoinSymbolInstance}
 */
function GetCoinSymbol(apiInstance, factoryAxiosOptions) {
  /**
   * @typedef {Function} GetCoinSymbolInstance
   * @param {number|string|Array<number|string>} id
   * @param {number} [chainId]
   * @param {import('axios').AxiosRequestConfig} [axiosOptions]
   * @return {Promise<string|Array<string>>}
   */
  return function getCoinSymbol(id) {
    var chainId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : apiInstance.defaults.chainId;
    var axiosOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined;
    axiosOptions = _objectSpread(_objectSpread({}, factoryAxiosOptions), axiosOptions);
    return processArrayByPromise(_getCoinSymbol, id, chainId, apiInstance, axiosOptions);
  };
}

/**
 * @param {number|string} id
 * @param {number} [chainId]
 * @param {MinterApiInstance} apiInstance
 * @param {import('axios').AxiosRequestConfig} [axiosOptions]
 * @return {Promise<string>}
 * @private
 */
function _getCoinSymbol(id, chainId, apiInstance, axiosOptions) {
  if (utils.isCoinSymbol(id)) {
    return Promise.resolve(id);
  }
  if (utils.isCoinId(id)) {
    if (Number.parseInt(id, 10) === 0 && chainId) {
      return Promise.resolve(utils.getBaseCoinSymbol(chainId));
    } else {
      var getCoinInfo$1 = getCoinInfo(apiInstance);
      return getCoinInfo$1(id, axiosOptions).then(function (coinInfo) {
        return coinInfo.symbol;
      });
    }
  } else {
    return Promise.reject(new Error('Invalid coin id'));
  }
}

/**
 * @param {TxParams} txParams
 * @return {Array<string>}
 */
function getTxParamsPathList(txParams) {
  var pathList = [];
  pathList.push('gasCoin');
  var txType = minterjsUtil.normalizeTxType(txParams.type);
  switch (txType) {
    case minterjsUtil.TX_TYPE.SEND:
    case minterjsUtil.TX_TYPE.DECLARE_CANDIDACY:
    case minterjsUtil.TX_TYPE.DELEGATE:
    case minterjsUtil.TX_TYPE.UNBOND:
    case minterjsUtil.TX_TYPE.MOVE_STAKE:
    case minterjsUtil.TX_TYPE.MINT_TOKEN:
    case minterjsUtil.TX_TYPE.BURN_TOKEN:
    case minterjsUtil.TX_TYPE.LOCK:
    case minterjsUtil.TX_TYPE.VOTE_COMMISSION:
      {
        pathList.push('data.coin');
        break;
      }
    case minterjsUtil.TX_TYPE.ADD_LIMIT_ORDER:
    case minterjsUtil.TX_TYPE.SELL:
    case minterjsUtil.TX_TYPE.SELL_ALL:
    case minterjsUtil.TX_TYPE.BUY:
      {
        pathList.push('data.coinToSell', 'data.coinToBuy');
        break;
      }
    case minterjsUtil.TX_TYPE.CREATE_SWAP_POOL:
    case minterjsUtil.TX_TYPE.ADD_LIQUIDITY:
    case minterjsUtil.TX_TYPE.REMOVE_LIQUIDITY:
      {
        pathList.push('data.coin0', 'data.coin1');
        break;
      }
    case minterjsUtil.TX_TYPE.MULTISEND:
      {
        var _txParams$data$list;
        (_txParams$data$list = txParams.data.list) === null || _txParams$data$list === void 0 ? void 0 : _txParams$data$list.forEach(function (item, index) {
          pathList.push("data.list[".concat(index, "].coin"));
        });
        break;
      }
    case minterjsUtil.TX_TYPE.BUY_SWAP_POOL:
    case minterjsUtil.TX_TYPE.SELL_SWAP_POOL:
    case minterjsUtil.TX_TYPE.SELL_ALL_SWAP_POOL:
      {
        var _txParams$data$coins;
        (_txParams$data$coins = txParams.data.coins) === null || _txParams$data$coins === void 0 ? void 0 : _txParams$data$coins.forEach(function (item, index) {
          pathList.push("data.coins[".concat(index, "]"));
        });
        break;
      }
  }
  return pathList;
}

exports.GetCoinId = GetCoinId;
exports.GetCoinSymbol = GetCoinSymbol;
exports.ReplaceCoinId = ReplaceCoinId;
exports.ReplaceCoinIdByPath = ReplaceCoinIdByPath;
exports.ReplaceCoinSymbol = ReplaceCoinSymbol;
exports.ReplaceCoinSymbolByPath = ReplaceCoinSymbolByPath;
exports.ReplaceParamsByPath = ReplaceParamsByPath;
