/* eslint-disable linebreak-style */
const algosdk = require('algosdk')
const { exit } = require('process')
const readline = require('readline')
const PricecasterLib = require('../lib/pricecaster')
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
})
const spawnSync = require('child_process').spawnSync
const fs = require('fs')

function ask (questionText) {
  return new Promise((resolve) => {
    rl.question(questionText, input => resolve(input))
  })
}

let globalMnemo = ''

function signCallback (sender, tx) {
  const txSigned = tx.signTxn(algosdk.mnemonicToSecretKey(globalMnemo).sk)
  return txSigned
}

async function startOp (algodClient, fromAddress, gexpTime, gkeys) {
  console.log('Compiling programs ...\n')
  let out = spawnSync('python', ['teal/wormhole/pyteal/vaa-processor.py'])
  console.log(out.output.toString())
  out = spawnSync('python', ['teal/wormhole/pyteal/pricekeeper-v2.py'])
  console.log(out.output.toString())

  const pclib = new PricecasterLib.PricecasterLib(algodClient)
  console.log('Creating VAA Processor...')
  let txId = await pclib.createVaaProcessorApp(fromAddress, gexpTime, 0, gkeys.join(''), signCallback)
  console.log('txId: ' + txId)
  let txResponse = await pclib.waitForTransactionResponse(txId)
  const appId = pclib.appIdFromCreateAppResponse(txResponse)
  console.log('Deployment App Id: %d', appId)
  pclib.setAppId('vaaProcessor', appId)

  console.log('Creating Pricekeeper V2...')
  txId = await pclib.createPricekeeperApp(fromAddress, appId, signCallback)
  console.log('txId: ' + txId)
  txResponse = await pclib.waitForTransactionResponse(txId)
  const pkAppId = pclib.appIdFromCreateAppResponse(txResponse)
  console.log('Deployment App Id: %d', pkAppId)
  pclib.setAppId('pricekeeper', pkAppId)

  console.log('Setting VAA Processor authid parameter...')
  txId = await pclib.setAuthorizedAppId(fromAddress, pkAppId, signCallback)
  console.log('txId: ' + txId)
  txResponse = await pclib.waitForTransactionResponse(txId)

  console.log('Compiling verify VAA stateless code...')
  out = spawnSync('python', ['teal/wormhole/pyteal/vaa-verify.py'])
  console.log(out.output.toString())

  spawnSync('python', ['teal/wormhole/pyteal/vaa-verify.py', appId])
  const program = fs.readFileSync('teal/wormhole/build/vaa-verify.teal', 'utf8')
  const compiledVerifyProgram = await pclib.compileProgram(program)
  console.log('Stateless program address: ', compiledVerifyProgram.hash)

  console.log('Setting VAA Processor stateless code...')
  const txid = await pclib.setVAAVerifyProgramHash(fromAddress, compiledVerifyProgram.hash, signCallback)
  console.log('txId: ' + txId)
  await pclib.waitForTransactionResponse(txid)

  const dt = Date.now().toString()
  const resultsFileName = 'DEPLOY-' + dt
  const binaryFileName = 'VAA-VERIFY-' + dt + '.BIN'

  console.log(`Writing deployment results file ${resultsFileName}...`)
  fs.writeFileSync(resultsFileName, `vaaProcessorAppId: ${appId}\npriceKeeperV2AppId: ${pkAppId}\nvaaVerifyProgramHash: '${compiledVerifyProgram.hash}'`)

  console.log(`Writing stateless code binary file ${binaryFileName}...`)
  fs.writeFileSync(binaryFileName, compiledVerifyProgram.bytes)
}

(async () => {
  console.log('\nPricecaster v2 Apps Deployment Tool')
  console.log('Copyright (c) Randlabs Inc,  2021-22\n')

  if (process.argv.length !== 7) {
    console.log('Usage: deploy <glistfile> <from> <network>\n')
    console.log('where:\n')
    console.log('glistfile              File containing the initial list of guardians')
    console.log('gexptime               Guardian set expiration time')
    console.log('from                   Deployer account')
    console.log('network                Testnet, betanet or mainnet')
    console.log('keyfile                Secret file containing signing key mnemonic')
    console.log('\n- File must contain one guardian key per line, formatted in hex, without hex prefix.')
    console.log('\n- Deployment process will generate one DEPLOY-xxxx file with application Ids, stateless hash, and')
    console.log('  a VAA-VERIFY-XXXX.bin with stateless compiled bytes, to use with backend configuration')
    exit(0)
  }

  const listfile = process.argv[2]
  const gexpTime = process.argv[3]
  const fromAddress = process.argv[4]
  const network = process.argv[5]
  const keyfile = process.argv[6]

  const config = { server: '', apiToken: '', port: '' }
  if (network === 'betanet') {
    config.server = 'https://api.betanet.algoexplorer.io'
  } else if (network === 'mainnet') {
    config.server = 'https://api.algoexplorer.io'
  } else if (network === 'testnet') {
    config.server = 'https://api.testnet.algoexplorer.io'
  } else {
    console.error('Invalid network: ' + network)
    exit(1)
  }

  const fileDataStr = fs.readFileSync(listfile).toString()
  const gkeys = fileDataStr.match(/[^\r\n]+/g)
  if (!algosdk.isValidAddress(fromAddress)) {
    console.error('Invalid deployer address: ' + fromAddress)
    exit(1)
  }

  const algodClient = new algosdk.Algodv2(config.apiToken, config.server, config.port)

  console.log('Parameters for deployment: ')
  console.log('From: ' + fromAddress)
  console.log('Network: ' + network)
  console.log('Guardian expiration time: ' + gexpTime)
  console.log(`Guardian Keys: (${gkeys.length}) ` + gkeys)
  const answer = await ask('\nEnter YES to confirm parameters, anything else to abort. ')
  if (answer !== 'YES') {
    console.warn('Aborted by user.')
    exit(1)
  }
  globalMnemo = fs.readFileSync(keyfile).toString()
  try {
    await startOp(algodClient, fromAddress, gexpTime, gkeys)
  } catch (e) {
    console.error('(!) Deployment Failed: ' + e.toString())
  }
  console.log('Bye.')
  exit(0)
})()
