import { createSelector } from 'reselect'
import { ipcRenderer } from 'electron'
import { bech32 } from '../utils'

// ------------------------------------
// Constants
// ------------------------------------
export const GET_DESCRIBE_NETWORK = 'GET_DESCRIBE_NETWORK'
export const RECEIVE_DESCRIBE_NETWORK = 'RECEIVE_DESCRIBE_NETWORK'

export const GET_QUERY_ROUTES = 'GET_QUERY_ROUTES'
export const RECEIVE_QUERY_ROUTES = 'RECEIVE_QUERY_ROUTES'

export const SET_CURRENT_ROUTE = 'SET_CURRENT_ROUTE'

export const SET_CURRENT_CHANNEL = 'SET_CURRENT_CHANNEL'

export const SET_CURRENT_TAB = 'SET_CURRENT_TAB'

export const SET_CURRENT_PEER = 'SET_CURRENT_PEER'

export const UPDATE_PAY_REQ = 'UPDATE_PAY_REQ'
export const RESET_PAY_REQ = 'RESET_PAY_REQ'

export const UPDATE_SELECTED_PEERS = 'UPDATE_SELECTED_PEERS'
export const CLEAR_SELECTED_PEERS = 'CLEAR_SELECTED_PEERS'

export const UPDATE_SELECTED_CHANNELS = 'UPDATE_SELECTED_CHANNELS'
export const CLEAR_SELECTED_CHANNELS = 'CLEAR_SELECTED_CHANNELS'

export const GET_INFO_AND_QUERY_ROUTES = 'GET_INFO_AND_QUERY_ROUTES'
export const RECEIVE_INFO_AND_QUERY_ROUTES = 'RECEIVE_INFO_AND_QUERY_ROUTES'
export const CLEAR_QUERY_ROUTES = 'CLEAR_QUERY_ROUTES'

// ------------------------------------
// Actions
// ------------------------------------
export function getDescribeNetwork() {
  return {
    type: GET_DESCRIBE_NETWORK
  }
}

export function getQueryRoutes(pubkey) {
  return {
    type: GET_QUERY_ROUTES,
    pubkey
  }
}

export function setCurrentRoute(route) {
  return {
    type: SET_CURRENT_ROUTE,
    route
  }
}

export function setCurrentChannel(selectedChannel) {
  return {
    type: SET_CURRENT_CHANNEL,
    selectedChannel
  }
}

export function setCurrentTab(currentTab) {
  return {
    type: SET_CURRENT_TAB,
    currentTab
  }
}

export function setCurrentPeer(currentPeer) {
  return {
    type: SET_CURRENT_PEER,
    currentPeer
  }
}

export function updatePayReq(pay_req) {
  return {
    type: UPDATE_PAY_REQ,
    pay_req
  }
}

export function resetPayReq() {
  return {
    type: RESET_PAY_REQ
  }
}

export function updateSelectedPeers(peer) {
  return {
    type: UPDATE_SELECTED_PEERS,
    peer
  }
}

export function updateSelectedChannels(channel) {
  return {
    type: UPDATE_SELECTED_CHANNELS,
    channel
  }
}

export function getInvoiceAndQueryRoutes() {
  return {
    type: GET_INFO_AND_QUERY_ROUTES
  }
}

export function clearQueryRoutes() {
  return {
    type: CLEAR_QUERY_ROUTES
  }
}

export function clearSelectedPeers() {
  return {
    type: CLEAR_SELECTED_PEERS
  }
}

export function clearSelectedChannels() {
  return {
    type: CLEAR_SELECTED_CHANNELS
  }
}

// Send IPC event for describeNetwork
export const fetchDescribeNetwork = () => (dispatch) => {
  dispatch(getDescribeNetwork())
  ipcRenderer.send('lnd', { msg: 'describeNetwork' })
}

// Receive IPC event for describeNetwork
export const receiveDescribeNetwork = (event, { nodes, edges }) => dispatch =>
  dispatch({ type: RECEIVE_DESCRIBE_NETWORK, nodes, edges })

export const queryRoutes = (pubkey, amount) => (dispatch) => {
  dispatch(getQueryRoutes(pubkey))
  ipcRenderer.send('lnd', { msg: 'queryRoutes', data: { pubkey, amount } })
}

export const receiveQueryRoutes = (event, { routes }) => dispatch => dispatch({ type: RECEIVE_QUERY_ROUTES, routes })

// take a payreq and query routes for it
export const fetchInvoiceAndQueryRoutes = payreq => (dispatch) => {
  dispatch(getInvoiceAndQueryRoutes())
  ipcRenderer.send('lnd', { msg: 'getInvoiceAndQueryRoutes', data: { payreq } })
}

export const receiveInvoiceAndQueryRoutes = (event, { routes }) => dispatch =>
  dispatch({ type: RECEIVE_INFO_AND_QUERY_ROUTES, routes })

// ------------------------------------
// Action Handlers
// ------------------------------------
const ACTION_HANDLERS = {
  [GET_DESCRIBE_NETWORK]: state => ({ ...state, networkLoading: true }),
  [RECEIVE_DESCRIBE_NETWORK]: (state, { nodes, edges }) => ({
    ...state, networkLoading: false, nodes, edges
  }),

  [GET_QUERY_ROUTES]: (state, { pubkey }) => ({ ...state, networkLoading: true, selectedNode: { pubkey, routes: [], currentRoute: {} } }),
  [RECEIVE_QUERY_ROUTES]: (state, { routes }) => (
    {
      ...state,
      networkLoading: false,
      selectedNode: { pubkey: state.selectedNode.pubkey, routes, currentRoute: routes[0] }
    }
  ),

  [SET_CURRENT_ROUTE]: (state, { route }) => ({ ...state, currentRoute: route }),

  [SET_CURRENT_CHANNEL]: (state, { selectedChannel }) => ({ ...state, selectedChannel }),

  [SET_CURRENT_TAB]: (state, { currentTab }) => ({ ...state, currentTab }),

  [SET_CURRENT_PEER]: (state, { currentPeer }) => ({ ...state, currentPeer }),

  [UPDATE_PAY_REQ]: (state, { pay_req }) => ({ ...state, pay_req }),
  [RESET_PAY_REQ]: state => ({ ...state, pay_req: '' }),

  [GET_INFO_AND_QUERY_ROUTES]: state => ({ ...state, fetchingInvoiceAndQueryingRoutes: true }),
  [RECEIVE_INFO_AND_QUERY_ROUTES]: (state, { routes }) => ({ ...state, fetchingInvoiceAndQueryingRoutes: false, payReqRoutes: routes }),
  [CLEAR_QUERY_ROUTES]: state => ({ ...state, payReqRoutes: [], currentRoute: {} }),

  [UPDATE_SELECTED_PEERS]: (state, { peer }) => {
    let selectedPeers

    if (state.selectedPeers.includes(peer)) {
      selectedPeers = state.selectedPeers.filter(selectedPeer => selectedPeer.pub_key !== peer.pub_key)
    }

    if (!state.selectedPeers.includes(peer)) {
      selectedPeers = [...state.selectedPeers, peer]
    }

    return {
      ...state, selectedPeers
    }
  },
  [CLEAR_SELECTED_PEERS]: state => ({ ...state, selectedPeers: [] }),

  [UPDATE_SELECTED_CHANNELS]: (state, { channel }) => {
    let selectedChannels

    if (state.selectedChannels.includes(channel)) {
      selectedChannels = state.selectedChannels.filter(selectedChannel => selectedChannel.chan_id !== channel.chan_id)
    }

    if (!state.selectedChannels.includes(channel)) {
      selectedChannels = [...state.selectedChannels, channel]
    }

    return {
      ...state, selectedChannels
    }
  },
  [CLEAR_SELECTED_CHANNELS]: state => ({ ...state, selectedChannels: [] })
}

// ------------------------------------
// Selectors
// ------------------------------------
const networkSelectors = {}
const selectedPeersSelector = state => state.network.selectedPeers
const selectedChannelsSelector = state => state.network.selectedChannels
const payReqSelector = state => state.network.pay_req
const currentRouteSelector = state => state.network.currentRoute

// networkSelectors.currentRouteHopChanIds = createSelector(
//   currentRouteSelector,
//   (currentRoute) => {
//     if (!currentRoute.hops) { return [] }

//     return currentRoute.hops.map(hop => hop.chan_id)
//   }
// )

networkSelectors.selectedPeerPubkeys = createSelector(
  selectedPeersSelector,
  peers => peers.map(peer => peer.pub_key)
)

networkSelectors.selectedChannelIds = createSelector(
  selectedChannelsSelector,
  channels => channels.map(channel => channel.chan_id)
)

networkSelectors.payReqIsLn = createSelector(
  payReqSelector,
  (input) => {
    if (!input.startsWith('ln')) { return false }

    try {
      bech32.decode(input)
      return true
    } catch (e) {
      return false
    }
  }
)

networkSelectors.currentRouteChanIds = createSelector(
  currentRouteSelector,
  (route) => {
    if (!route.hops || !route.hops.length) { return [] }

    return route.hops.map(hop => hop.chan_id)
  }
)

export { networkSelectors }

// ------------------------------------
// Initial State
// ------------------------------------
const initialState = {
  networkLoading: false,
  nodes: [],
  edges: [],
  selectedChannel: {},

  currentTab: 1,

  currentPeer: {},
  currentRoute: {},

  fetchingInvoiceAndQueryingRoutes: false,
  pay_req: '',
  payReqRoutes: [],
  selectedPeers: [],
  selectedChannels: []
}


// ------------------------------------
// Reducer
// ------------------------------------
export default function activityReducer(state = initialState, action) {
  const handler = ACTION_HANDLERS[action.type]

  return handler ? handler(state, action) : state
}