import {
  conversionRateSelector,
  currentCurrencySelector,
  unconfirmedTransactionsHashSelector,
  getNativeCurrency,
} from '../../selectors/confirm-transaction'

import {
  getValueFromWeiHex,
  getTransactionFee,
  getHexStorageTotal,
  getHexGasTotal,
  addFiat,
  addEth,
  increaseLastGasPrice,
  hexGreaterThan,
} from '../../helpers/utils/confirm-tx.util'

import { getTokenData, sumHexes } from '../../helpers/utils/transactions.util'

import { conversionUtil } from '../../helpers/utils/conversion-util'
import { addHexPrefix } from 'cfx-util'

// Actions
const createActionType = (action) => `metamask/confirm-transaction/${action}`

const UPDATE_TX_DATA = createActionType('UPDATE_TX_DATA')
const CLEAR_TX_DATA = createActionType('CLEAR_TX_DATA')
const UPDATE_TOKEN_DATA = createActionType('UPDATE_TOKEN_DATA')
const CLEAR_TOKEN_DATA = createActionType('CLEAR_TOKEN_DATA')
const UPDATE_METHOD_DATA = createActionType('UPDATE_METHOD_DATA')
const CLEAR_METHOD_DATA = createActionType('CLEAR_METHOD_DATA')
const CLEAR_CONFIRM_TRANSACTION = createActionType('CLEAR_CONFIRM_TRANSACTION')
const UPDATE_TRANSACTION_AMOUNTS = createActionType(
  'UPDATE_TRANSACTION_AMOUNTS'
)
const UPDATE_TRANSACTION_FEES = createActionType('UPDATE_TRANSACTION_FEES')
const UPDATE_TRANSACTION_TOTALS = createActionType('UPDATE_TRANSACTION_TOTALS')
const UPDATE_TOKEN_PROPS = createActionType('UPDATE_TOKEN_PROPS')
const UPDATE_NONCE = createActionType('UPDATE_NONCE')
const UPDATE_TO_SMART_CONTRACT = createActionType('UPDATE_TO_SMART_CONTRACT')
const FETCH_DATA_START = createActionType('FETCH_DATA_START')
const FETCH_DATA_END = createActionType('FETCH_DATA_END')

// Initial state
const initState = {
  txData: {},
  tokenData: {},
  methodData: {},
  tokenProps: {
    tokenDecimals: '',
    tokenSymbol: '',
  },
  fiatTransactionAmount: '',
  fiatTransactionFee: '',
  fiatTransactionTotal: '',
  ethTransactionAmount: '',
  ethTransactionFee: '',
  ethTransactionTotal: '',
  hexTransactionAmount: '',
  hexTransactionFee: '',
  hexTransactionTotal: '',
  nonce: '',
  toSmartContract: false,
  fetchingData: false,
}

// Reducer
export default function reducer (state = initState, action = {}) {
  switch (action.type) {
    case UPDATE_TX_DATA:
      return {
        ...state,
        txData: {
          ...action.payload,
        },
      }
    case CLEAR_TX_DATA:
      return {
        ...state,
        txData: {},
      }
    case UPDATE_TOKEN_DATA:
      return {
        ...state,
        tokenData: {
          ...action.payload,
        },
      }
    case CLEAR_TOKEN_DATA:
      return {
        ...state,
        tokenData: {},
      }
    case UPDATE_METHOD_DATA:
      return {
        ...state,
        methodData: {
          ...action.payload,
        },
      }
    case CLEAR_METHOD_DATA:
      return {
        ...state,
        methodData: {},
      }
    case UPDATE_TRANSACTION_AMOUNTS:
      const {
        fiatTransactionAmount,
        ethTransactionAmount,
        hexTransactionAmount,
      } = action.payload
      return {
        ...state,
        fiatTransactionAmount:
          fiatTransactionAmount || state.fiatTransactionAmount,
        ethTransactionAmount:
          ethTransactionAmount || state.ethTransactionAmount,
        hexTransactionAmount:
          hexTransactionAmount || state.hexTransactionAmount,
      }
    case UPDATE_TRANSACTION_FEES:
      const {
        fiatTransactionFee,
        ethTransactionFee,
        hexTransactionFee,
      } = action.payload
      return {
        ...state,
        fiatTransactionFee: fiatTransactionFee || state.fiatTransactionFee,
        ethTransactionFee: ethTransactionFee || state.ethTransactionFee,
        hexTransactionFee: hexTransactionFee || state.hexTransactionFee,
      }
    case UPDATE_TRANSACTION_TOTALS:
      const {
        fiatTransactionTotal,
        ethTransactionTotal,
        hexTransactionTotal,
      } = action.payload
      return {
        ...state,
        fiatTransactionTotal:
          fiatTransactionTotal || state.fiatTransactionTotal,
        ethTransactionTotal: ethTransactionTotal || state.ethTransactionTotal,
        hexTransactionTotal: hexTransactionTotal || state.hexTransactionTotal,
      }
    case UPDATE_TOKEN_PROPS:
      const { tokenSymbol = '', tokenDecimals = '' } = action.payload
      return {
        ...state,
        tokenProps: {
          ...state.tokenProps,
          tokenSymbol,
          tokenDecimals,
        },
      }
    case UPDATE_NONCE:
      return {
        ...state,
        nonce: action.payload,
      }
    case UPDATE_TO_SMART_CONTRACT:
      return {
        ...state,
        toSmartContract: action.payload,
      }
    case FETCH_DATA_START:
      return {
        ...state,
        fetchingData: true,
      }
    case FETCH_DATA_END:
      return {
        ...state,
        fetchingData: false,
      }
    case CLEAR_CONFIRM_TRANSACTION:
      return initState
    default:
      return state
  }
}

// Action Creators
export function updateTxData (txData) {
  return {
    type: UPDATE_TX_DATA,
    payload: txData,
  }
}

export function clearTxData () {
  return {
    type: CLEAR_TX_DATA,
  }
}

export function updateTokenData (tokenData) {
  return {
    type: UPDATE_TOKEN_DATA,
    payload: tokenData,
  }
}

export function clearTokenData () {
  return {
    type: CLEAR_TOKEN_DATA,
  }
}

export function updateMethodData (methodData) {
  return {
    type: UPDATE_METHOD_DATA,
    payload: methodData,
  }
}

export function clearMethodData () {
  return {
    type: CLEAR_METHOD_DATA,
  }
}

export function updateTransactionAmounts (amounts) {
  return {
    type: UPDATE_TRANSACTION_AMOUNTS,
    payload: amounts,
  }
}

export function updateTransactionFees (fees) {
  return {
    type: UPDATE_TRANSACTION_FEES,
    payload: fees,
  }
}

export function updateTransactionTotals (totals) {
  return {
    type: UPDATE_TRANSACTION_TOTALS,
    payload: totals,
  }
}

export function updateTokenProps (tokenProps) {
  return {
    type: UPDATE_TOKEN_PROPS,
    payload: tokenProps,
  }
}

export function updateNonce (nonce) {
  return {
    type: UPDATE_NONCE,
    payload: nonce,
  }
}

export function updateToSmartContract (toSmartContract) {
  return {
    type: UPDATE_TO_SMART_CONTRACT,
    payload: toSmartContract,
  }
}

export function setFetchingData (isFetching) {
  return {
    type: isFetching ? FETCH_DATA_START : FETCH_DATA_END,
  }
}

export function updateGasAndCollateralAndCalculate ({ gasLimit, gasPrice, storageLimit }) {
  storageLimit = addHexPrefix(storageLimit)
  gasLimit = addHexPrefix(gasLimit)
  gasPrice = addHexPrefix(gasPrice)
  return (dispatch, getState) => {
    const {
      confirmTransaction: { txData },
    } = getState()
    const newTxData = {
      ...txData,
      txParams: {
        ...txData.txParams,
        storageLimit,
        gas: gasLimit,
        gasPrice,
      },
    }

    dispatch(updateTxDataAndCalculate(newTxData))
  }
}

function increaseFromLastGasPrice (txData) {
  const { lastGasPrice, txParams: { gasPrice: previousGasPrice } = {} } = txData

  // Set the minimum to a 10% increase from the lastGasPrice.
  const minimumGasPrice = increaseLastGasPrice(lastGasPrice)
  const gasPriceBelowMinimum = hexGreaterThan(minimumGasPrice, previousGasPrice)
  const gasPrice =
    !previousGasPrice || gasPriceBelowMinimum
      ? minimumGasPrice
      : previousGasPrice

  return {
    ...txData,
    txParams: {
      ...txData.txParams,
      gasPrice,
    },
  }
}

export function updateTxDataAndCalculate (txData) {
  return (dispatch, getState) => {
    const state = getState()
    const currentCurrency = currentCurrencySelector(state)
    const conversionRate = conversionRateSelector(state)
    const nativeCurrency = getNativeCurrency(state)

    dispatch(updateTxData(txData))

    const {
      txParams: { value = '0x0', gas: gasLimit = '0x0', gasPrice = '0x0', storageLimit = '0x0' } = {},
    } = txData

    const fiatTransactionAmount = getValueFromWeiHex({
      value,
      fromCurrency: nativeCurrency,
      toCurrency: currentCurrency,
      conversionRate,
      numberOfDecimals: 2,
    })
    const ethTransactionAmount = getValueFromWeiHex({
      value,
      fromCurrency: nativeCurrency,
      toCurrency: nativeCurrency,
      conversionRate,
      numberOfDecimals: 6,
    })

    dispatch(
      updateTransactionAmounts({
        fiatTransactionAmount,
        ethTransactionAmount,
        hexTransactionAmount: value,
      })
    )

    const hexTransactionFee = getHexGasTotal({ gasLimit, gasPrice })
    const hexTransactionCollateral = getHexStorageTotal({ storageLimit })

    const fiatTransactionFee = getTransactionFee({
      value: hexTransactionFee,
      fromCurrency: nativeCurrency,
      toCurrency: currentCurrency,
      numberOfDecimals: 2,
      conversionRate,
    })
    const ethTransactionFee = getTransactionFee({
      value: hexTransactionFee,
      fromCurrency: nativeCurrency,
      toCurrency: nativeCurrency,
      numberOfDecimals: 6,
      conversionRate,
    })

    dispatch(
      updateTransactionFees({
        fiatTransactionFee,
        ethTransactionFee,
        hexTransactionFee,
      })
    )

    const fiatTransactionTotal = addFiat(
      fiatTransactionFee,
      fiatTransactionAmount
    )
    const ethTransactionTotal = addEth(ethTransactionFee, ethTransactionAmount)
    const hexTransactionTotal = sumHexes(value, hexTransactionFee)

    dispatch(
      updateTransactionTotals({
        fiatTransactionTotal,
        ethTransactionTotal,
        hexTransactionTotal,
        hexTransactionCollateral,
      })
    )
  }
}

export function setTransactionToConfirm (transactionId) {
  return (dispatch, getState) => {
    const state = getState()
    const unconfirmedTransactionsHash = unconfirmedTransactionsHashSelector(
      state
    )
    const transaction = unconfirmedTransactionsHash[transactionId]

    if (!transaction) {
      console.error(`Transaction with id ${transactionId} not found`)
      return
    }

    if (transaction.txParams) {
      const { lastGasPrice } = transaction
      const txData = lastGasPrice
        ? increaseFromLastGasPrice(transaction)
        : transaction
      dispatch(updateTxDataAndCalculate(txData))

      const { txParams } = transaction

      if (txParams.data) {
        const { data } = txParams

        const tokenData = getTokenData(data)
        dispatch(updateTokenData(tokenData))
      }

      if (txParams.nonce) {
        const nonce = conversionUtil(txParams.nonce, {
          fromNumericBase: 'hex',
          toNumericBase: 'dec',
        })

        dispatch(updateNonce(nonce))
      }
    } else {
      dispatch(updateTxData(transaction))
    }
  }
}

export function clearConfirmTransaction () {
  return {
    type: CLEAR_CONFIRM_TRANSACTION,
  }
}
