//                         This is a work in progress
//             The goal is to provide a cli which allow interact
//                with device & libcore for faster iterations

require('babel-polyfill')
require('babel-register')

const chalk = require('chalk')
const inquirer = require('inquirer')
const path = require('path')
const TransportNodeHid = require('@ledgerhq/hw-transport-node-hid').default

const { serializeAccounts, encodeAccount, decodeAccount } = require('../src/reducers/accounts')
const { doSignAndBroadcast } = require('../src/commands/libcoreSignAndBroadcast')

const coreHelper = require('../src/helpers/libcore')
const withLibcore = require('../src/helpers/withLibcore').default

if (!process.env.LEDGER_LIVE_SQLITE_PATH) {
  throw new Error('you must define process.env.LEDGER_LIVE_SQLITE_PATH first')
}

const LOCAL_DIRECTORY_PATH = path.resolve(process.env.LEDGER_LIVE_SQLITE_PATH, '../')

gimmeDeviceAndLibCore(async ({ device, core, njsWalletPool }) => {
  const raw = require(path.join(LOCAL_DIRECTORY_PATH, 'accounts.json')) // eslint-disable-line import/no-dynamic-require
  const accounts = serializeAccounts(raw.data)
  const accountToUse = await chooseAccount('Which account to use?', accounts)
  await actionLoop({ account: accountToUse, accounts, core, njsWalletPool, device })
  process.exit(0)
})

async function actionLoop(props) {
  try {
    const { account, accounts, core, njsWalletPool, device } = props
    const actionToDo = await chooseAction(`What do you want to do with [${account.name}] ?`)
    if (actionToDo === 'send funds') {
      const transport = await TransportNodeHid.open(device.path)
      const accountToReceive = await chooseAccount('To which account?', accounts)
      const receiveAddress = await getFreshAddress({
        account: accountToReceive,
        core,
        njsWalletPool,
      })
      console.log(`the receive address is ${receiveAddress}`)
      const rawAccount = encodeAccount(account)
      console.log(`trying to sign and broadcast...`)
      const rawOp = await doSignAndBroadcast({
        account: rawAccount,
        transaction: {
          amount: 4200000,
          recipient: receiveAddress,
          feePerByte: 16,
          isRBF: false,
        },
        deviceId: device.path,
        core,
        transport,
      })
      console.log(rawOp)
    } else if (actionToDo === 'sync') {
      console.log(`\nLaunch sync...\n`)
      const rawAccount = encodeAccount(account)
      const syncedAccount = await coreHelper.syncAccount({ rawAccount, core, njsWalletPool })
      console.log(`\nEnd sync...\n`)
      console.log(`updated account: `, displayAccount(syncedAccount, 'red'))
    } else if (actionToDo === 'quit') {
      return true
    }
  } catch (err) {
    console.log(`x Something went wrong`)
    console.log(err)
    process.exit(1)
  }
  return actionLoop(props)
}

async function chooseInList(msg, list, formatItem = i => i) {
  const choices = list.map(formatItem)
  const { choice } = await inquirer.prompt([
    {
      type: 'list',
      name: 'choice',
      message: msg,
      choices,
    },
  ])
  const index = choices.indexOf(choice)
  return list[index]
}

async function chooseAction(msg) {
  return chooseInList(msg, ['sync', 'send funds', 'quit'])
}

function chooseAccount(msg, accounts) {
  return chooseInList(msg, accounts, acc => displayAccount(acc))
}

async function gimmeDeviceAndLibCore(cb) {
  withLibcore((core, njsWalletPool) => {
    TransportNodeHid.listen({
      error: () => {},
      complete: () => {},
      next: async e => {
        if (!e.device) {
          return
        }
        if (e.type === 'add') {
          const { device } = e
          cb({ device, core, njsWalletPool })
        }
      },
    })
  })
}

function displayAccount(acc, color = null) {
  const isRawAccount = typeof acc.lastSyncDate === 'string'
  if (isRawAccount) {
    acc = decodeAccount(acc)
  }
  const str = `[${acc.name}] ${acc.isSegwit ? '' : '(legacy) '}${acc.unit.code} ${acc.balance} - ${
    acc.operations.length
  } txs`
  return color ? chalk[color](str) : str
}

async function getFreshAddress({ account, core, njsWalletPool }) {
  const njsAccount = await coreHelper.getNJSAccount({ account, njsWalletPool })
  const unsub = await core.syncAccount(njsAccount)
  unsub()
  const rawAddresses = await njsAccount.getFreshPublicAddresses()
  return rawAddresses[0]
}