import { cloneDeep, uniq } from 'lodash';
import BigNumber from 'bignumber.js';
import { getRpcUrl } from '../../../shared/constants/network';

const version = 74;

const hexNumberIsGreaterThanZero = (hexNumber) =>
  new BigNumber(hexNumber || '0x0', 16).gt(0);

const DEPRECATED_TEST_NET_CHAINIDS = ['0x3', '0x2a', '0x4'];
const DEPRECATED_TEST_NET_DETAILS = {
  '0x3': {
    rpcUrl: getRpcUrl({ network: 'ropsten' }),
    nickname: 'Ropsten',
    ticker: 'RopstenETH',
  },
  '0x2a': {
    rpcUrl: getRpcUrl({ network: 'kovan' }),
    nickname: 'Kovan',
    ticker: 'KovanETH',
  },
  '0x4': {
    rpcUrl: getRpcUrl({ network: 'rinkeby' }),
    nickname: 'Rinkeby',
    ticker: 'RinkebyETH',
  },
};

/**
 * Migrates the user default but deprecated testnet networks to custom networks, and
 * if the current network is one such network, updates the network provider details so that it
 * will work as a custom rpc
 */
export default {
  version,
  async migrate(originalVersionedData) {
    const versionedData = cloneDeep(originalVersionedData);
    versionedData.meta.version = version;
    const state = versionedData.data;
    const newState = transformState(state);
    versionedData.data = newState;
    return versionedData;
  },
};

function transformState(state) {
  const PreferencesController = state?.PreferencesController || {};
  const preferences = PreferencesController.preferences || {};
  const NetworkController = state?.NetworkController || {};
  const provider = NetworkController?.provider || {};

  const currentlyOnDeprecatedNetwork = DEPRECATED_TEST_NET_CHAINIDS.filter(
    (chainId) => chainId === provider?.chainId,
  );

  // If the user does not want to see test networks, and if the the user is not on a deprecated test network, then
  // no need to migrate the test network data to a custom network
  if (
    !preferences.showTestNetworks &&
    currentlyOnDeprecatedNetwork.length === 0
  ) {
    return state;
  }

  const transactions = state?.TransactionController?.transactions || {};
  const cachedBalances = state.CachedBalancesController?.cachedBalances || {};

  const deprecatedTestnetsOnWhichTheUserHasMadeATransaction = Object.values(
    transactions,
  )
    .filter(({ chainId }) => DEPRECATED_TEST_NET_CHAINIDS.includes(chainId))
    .map(({ chainId }) => chainId);
  const deprecatedTestnetsOnWhichTheUserHasCachedBalance =
    DEPRECATED_TEST_NET_CHAINIDS.filter((chainId) => {
      const cachedBalancesForChain = Object.values(
        cachedBalances[chainId] || {},
      );
      const userHasABalanceGreaterThanZeroOnThisChain =
        cachedBalancesForChain.some(hexNumberIsGreaterThanZero);
      return userHasABalanceGreaterThanZeroOnThisChain;
    });
  const deprecatedTestnetsThatHaveBeenUsed = uniq([
    ...deprecatedTestnetsOnWhichTheUserHasCachedBalance,
    ...deprecatedTestnetsOnWhichTheUserHasMadeATransaction,
    ...currentlyOnDeprecatedNetwork,
  ]);

  const newFrequentRpcListDetail =
    PreferencesController.frequentRpcListDetail ?? [];

  deprecatedTestnetsThatHaveBeenUsed.forEach((chainId) => {
    if (
      !newFrequentRpcListDetail.find(
        (rpcDetails) => rpcDetails.chainId === chainId,
      )
    ) {
      newFrequentRpcListDetail.unshift({
        rpcUrl: DEPRECATED_TEST_NET_DETAILS[chainId].rpcUrl,
        chainId,
        ticker: DEPRECATED_TEST_NET_DETAILS[chainId].ticker,
        nickname: DEPRECATED_TEST_NET_DETAILS[chainId].nickname,
        rpcPrefs: {},
      });
    }
  });

  if (newFrequentRpcListDetail.length) {
    PreferencesController.frequentRpcListDetail = newFrequentRpcListDetail;
  }

  if (currentlyOnDeprecatedNetwork.length) {
    const selectedNetworkChainId = currentlyOnDeprecatedNetwork[0];
    NetworkController.provider = {
      ...NetworkController.provider,
      type: 'rpc',
      rpcUrl: DEPRECATED_TEST_NET_DETAILS[selectedNetworkChainId].rpcUrl,
      chainId: selectedNetworkChainId,
      nickname: DEPRECATED_TEST_NET_DETAILS[selectedNetworkChainId].nickname,
      ticker: DEPRECATED_TEST_NET_DETAILS[selectedNetworkChainId].ticker,
    };
  }

  return {
    ...state,
    PreferencesController: {
      ...PreferencesController,
    },
    NetworkController: {
      ...NetworkController,
    },
  };
}
