import { applyMiddleware, combineReducers, compose, createStore } from 'redux';
import thunk from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension';
import kyberData from './util/kyberData';
import uniswapData from './util/uniswapData';
import getRelDeltaSlippageFree from './util/getRelDeltaSlippageFree';
import { AAVE_THRESHOLD } from './util/constants';
import { cancelFlashLoan, submitFlashLoan } from './util/web3';

const initialState = {
  isInitialized: false,
  /**
   * scenarioState
   * 0 : no arbitrage needed
   * 1 : should sell BAT on Uniswap and buy on Kyber
   * 2 : should cancel previous tx
   * 3 : should sell BAT on Kyber and buy on Uniswap
   */
  scenarioState: 0,
  ethPrivateKey: '',
  infuraKey: '',
  dfuseKey: '',
  ratesIndex: 0,
  uniswapRate: uniswapData[0],
  kyberRate: kyberData[0],
  txs: [],
};

const INITIALIZE = 'INITIALIZE';
const initialize = (ethPrivateKey, infuraKey, dfuseKey) => ({
  type: INITIALIZE,
  payload: { ethPrivateKey, infuraKey, dfuseKey },
});

const UPDATE_RATES = 'UPDATE_RATES';
const updateRates = () => ({
  type: UPDATE_RATES,
  payload: {},
});

const ADD_TX = 'ADD_TX';
const addTx = (hash, sellDaiOnUniswap, cancel) => ({
  type: ADD_TX,
  payload: { hash, sellDaiOnUniswap, cancel },
});

const mainReducer = (state = initialState, action) => {
  let ratesIndex, uniswapRate, kyberRate, txs, relDeltaSlippageFree, shouldSwap, scenarioState;
  switch (action.type) {
    case INITIALIZE:
      return Object.assign({}, state, {
        isInitialized: true,
        ethPrivateKey: action.payload.ethPrivateKey,
        infuraKey: action.payload.infuraKey,
        dfuseKey: action.payload.dfuseKey,
      });
    case ADD_TX:
      txs = state.txs;
      txs.unshift({
        hash: action.payload.hash,
        sellDaiOnUniswap: action.payload.sellDaiOnUniswap,
        cancel: action.payload.cancel,
      });
      return Object.assign({}, state, {
        txs: txs.slice(),
      });
    case UPDATE_RATES:
      ratesIndex = state.ratesIndex + 1;
      uniswapRate =
        ratesIndex < uniswapData.length
          ? uniswapData[ratesIndex]
          : uniswapData[uniswapData.length - 1] + Math.random() * 0.01;
      kyberRate =
        ratesIndex < kyberData.length
          ? kyberData[ratesIndex]
          : kyberData[kyberData.length - 1] + Math.random() * 0.01;

      relDeltaSlippageFree = getRelDeltaSlippageFree(uniswapRate, kyberRate);
      shouldSwap = relDeltaSlippageFree > AAVE_THRESHOLD;
      scenarioState = state.scenarioState;
      if (scenarioState === 0 && shouldSwap && uniswapRate > kyberRate) {
        scenarioState = 1;
        submitFlashLoan(false);
      } else if (scenarioState === 1 && !shouldSwap && uniswapRate > kyberRate) {
        scenarioState = 2;
        cancelFlashLoan();
      } else if (scenarioState === 2 && shouldSwap && uniswapRate < kyberRate) {
        scenarioState = 3;
        submitFlashLoan(true);
      }
      return Object.assign({}, state, {
        scenarioState,
        ratesIndex,
        uniswapRate,
        kyberRate,
      });
    default:
      return state;
  }
};

const reducer = combineReducers({
  main: mainReducer,
});

export function configureStore(initialState = {}) {
  // Middleware and store enhancers
  const enhancers = [applyMiddleware(thunk)];

  let store;
  if (process.env.NODE_ENV === 'development') {
    store = createStore(reducer, initialState, composeWithDevTools(...enhancers));
  } else {
    store = createStore(reducer, initialState, compose(...enhancers));
  }

  return store;
}

export { updateRates, initialize, addTx };