text: // @flow
  import axios from 'axios'
  import { createActions } from 'spunky'
  import { get, map } from 'lodash-es'
  
  import { getDefaultTokens } from '../core/nep5'
  import { getSettings } from './settingsActions'
  import { ASSETS } from '../core/constants'
  import { toFixedDecimals } from '../core/formatters'
  
  const getPriceApiSymbolExceptions = (tokens: Array<TokenItemType>) => {
    const directMap = {}
    const reverseMap = {}
  
    map(tokens, token => {
      if (token.symbol !== undefined && token.cryptocompareSymbol !== undefined) {
        directMap[token.symbol] = token.cryptocompareSymbol
        // $FlowFixMe
        reverseMap[token.cryptocompareSymbol] = token.symbol
      }
    })
  
    return { direct: directMap, reverse: reverseMap }
  }
  
  function mapPrices(pricingData: Array<any>, currency) {
    const upperCasedCurrency = currency.toUpperCase()
    return pricingData.reduce(
      (accum: Object, price: { currency: { upperCasedCurrency: Object } }) => {
        const priceInSelectedCurrency = price[upperCasedCurrency]
        if (price && priceInSelectedCurrency) {
          // eslint-disable-next-line
          accum[priceInSelectedCurrency.FROMSYMBOL] = parseFloat(
            priceInSelectedCurrency.PRICE,
          )
        }
        return accum
      },
      {},
    )
  }
  
  const apiCall = async url => {
    const priceDataResponse = await axios.get(url).catch(error => {
      console.error(`Unable to retrieve prices from the api ${url}`, error)
      return undefined
    })
  
    if (priceDataResponse !== undefined && priceDataResponse !== null) {
      return Object.values(priceDataResponse.data.RAW)
    }
  
    return []
  }
  
  const apiCallWrapper = async (url, currency) => {
    const pricingArray = await apiCall(url)
    if (pricingArray !== []) {
      return mapPrices(pricingArray, currency)
    }
    return {}
  }
  
  async function getPrices() {
    try {
      const tokens = await getDefaultTokens()
      const PRICE_API_SYMBOL_EXCEPTIONS = getPriceApiSymbolExceptions(tokens)
  
      const settings = await getSettings()
      const { currency } = settings
      const joinedTokens = tokens
        .map((token: TokenItemType) =>
          // We pass to the cryptocompare api the token cryptocompareSymbol
          // if they are available. Otherwise we pass the token symbol.
          get(PRICE_API_SYMBOL_EXCEPTIONS.direct, token.symbol, token.symbol),
        )
        .concat([ASSETS.NEO, ASSETS.GAS])
        .join(',')
  
      const url = `https://min-api.cryptocompare.com/data/pricemultifull?fsyms=${joinedTokens}&tsyms=${currency.toUpperCase()}`
      const prices = await apiCallWrapper(url, currency)
  
      // We need to perform a second api call because there may be token that have price listed only in relationship to NEO.
      // As of now, (27.07.2019), for instance NEX price can't be retrieve via the first api call above.
      // Therefore we need a second api call with currency NEO.
      const url2 = `https://min-api.cryptocompare.com/data/pricemultifull?fsyms=${joinedTokens}&tsyms=NEO`
      const neoPrices = await apiCallWrapper(url2, 'NEO')
  
      // Build final prices map
      Object.entries(neoPrices).forEach(([key, value]) => {
        if (!(key in prices)) {
          prices[key] = toFixedDecimals(parseFloat(value) * prices.NEO, 2)
        }
      })
  
      // Within the neon-wallet app, we use the token's symbol to retrieve the token's price.
      // Therefore if any key in the price object is a "cryptocompare" symbol,
      // we have to replace it by the corresponding token's symbol.
      Object.entries(prices).forEach(([key, value]) => {
        prices[get(PRICE_API_SYMBOL_EXCEPTIONS.reverse, key, key)] = value
      })
  
      return prices
    } catch (error) {
      console.error('An error occurred getting price data', { error })
      return {}
    }
  }
  
  export const ID = 'prices'
  
  export default createActions(ID, () => () => getPrices())
  