import { constant, times, uniq, zip } from 'lodash';
import BigNumber from 'bignumber.js';
import {
  GasRecommendations,
  EditGasModes,
} from '../../../shared/constants/gas';
import { hexWEIToDecGWEI } from '../../../shared/modules/conversion.utils';
import { Numeric } from '../../../shared/modules/Numeric';
import {
  bnGreaterThan,
  isNullish,
  roundToDecimalPlacesRemovingExtraZeroes,
} from './util';

const TEN_PERCENT_NUMERIC = new Numeric(1.1, 10);

export const gasEstimateGreaterThanGasUsedPlusTenPercent = (
  gasUsed,
  gasFeeEstimates,
  estimate,
) => {
  let { maxFeePerGas: maxFeePerGasInTransaction } = gasUsed;
  maxFeePerGasInTransaction = new BigNumber(
    hexWEIToDecGWEI(addTenPercentAndRound(maxFeePerGasInTransaction)),
  );

  const maxFeePerGasFromEstimate =
    gasFeeEstimates[estimate]?.suggestedMaxFeePerGas;
  return bnGreaterThan(maxFeePerGasFromEstimate, maxFeePerGasInTransaction);
};

/**
 * Simple helper to save on duplication to multiply the supplied wei hex string
 * by 1.10 to get bare minimum new gas fee.
 *
 * @param {string | undefined} hexStringValue - hex value in wei to be incremented
 * @returns {string | undefined} hex value in WEI 10% higher than the param.
 */
export function addTenPercentAndRound(hexStringValue) {
  if (hexStringValue === undefined) {
    return undefined;
  }
  return new Numeric(hexStringValue, 16)
    .times(TEN_PERCENT_NUMERIC)
    .round(0)
    .toPrefixedHexString();
}

export function isMetamaskSuggestedGasEstimate(estimate) {
  return [
    GasRecommendations.high,
    GasRecommendations.medium,
    GasRecommendations.low,
  ].includes(estimate);
}

/**
 * Formats a singular gas fee or a range of gas fees by rounding them to the
 * given precisions and then arranging them as a string.
 *
 * @param {string | [string, string] | null | undefined} feeOrFeeRange - The fee
 * in GWEI or range of fees in GWEI.
 * @param {object} options - The options.
 * @param {number | [number, number]} options.precision - The precision(s) to
 * use when formatting the fee(s).
 * @returns A string which represents the formatted version of the fee or fee
 * range.
 */
export function formatGasFeeOrFeeRange(
  feeOrFeeRange,
  { precision: precisionOrPrecisions = 2 } = {},
) {
  if (
    isNullish(feeOrFeeRange) ||
    (Array.isArray(feeOrFeeRange) && feeOrFeeRange.length === 0)
  ) {
    return null;
  }

  const range = Array.isArray(feeOrFeeRange)
    ? feeOrFeeRange.slice(0, 2)
    : [feeOrFeeRange];
  const precisions = Array.isArray(precisionOrPrecisions)
    ? precisionOrPrecisions.slice(0, 2)
    : times(range.length, constant(precisionOrPrecisions));
  const formattedRange = uniq(
    zip(range, precisions).map(([fee, precision]) => {
      return precision === undefined
        ? fee
        : roundToDecimalPlacesRemovingExtraZeroes(fee, precision);
    }),
  ).join(' - ');

  return `${formattedRange} GWEI`;
}

/**
 * Helper method for determining whether an edit gas mode is either a speed up or cancel transaction
 *
 * @param {string | undefined} editGasMode - One of 'speed-up', 'cancel', 'modify-in-place', or 'swaps'
 * @returns boolean
 */
export function editGasModeIsSpeedUpOrCancel(editGasMode) {
  return (
    editGasMode === EditGasModes.cancel || editGasMode === EditGasModes.speedUp
  );
}