// Native
const os = require('os')

// Packages
const {stringify: stringifyQuery} = require('querystring')
const chalk = require('chalk')
const fetch = require('node-fetch')
const {validate} = require('email-validator')
const readEmail = require('email-prompt')
const ora = require('ora')

// Ours
const pkg = require('./pkg')
const ua = require('./ua')
const cfg = require('./cfg')
const info = require('./utils/output/info')
const promptBool = require('./utils/input/prompt-bool')

async function getVerificationData(url, email) {
  const tokenName = `Now CLI ${os.platform()}-${os.arch()} ${pkg.version} (${os.hostname()})`
  const data = JSON.stringify({email, tokenName})
  const res = await fetch(`${url}/now/registration`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Content-Length': Buffer.byteLength(data),
      'User-Agent': ua
    },
    body: data
  })

  if (res.status !== 200) {
    throw new Error('Verification error')
  }

  const body = await res.json()
  return body
}

async function verify(url, email, verificationToken) {
  const query = {
    email,
    token: verificationToken
  }

  const res = await fetch(`${url}/now/registration/verify?${stringifyQuery(query)}`, {
    headers: {'User-Agent': ua}
  })
  const body = await res.json()
  return body.token
}

function sleep(ms) {
  return new Promise(resolve => {
    setTimeout(resolve, ms)
  })
}

async function register(url, {retryEmail = false} = {}) {
  let email
  try {
    email = await readEmail({invalid: retryEmail})
  } catch (err) {
    process.stdout.write('\n')
    throw err
  }

  process.stdout.write('\n')

  info(`By continuing, you declare that you agree with ${chalk.bold('https://zeit.co/terms')} and ${chalk.bold('https://zeit.co/privacy.')}`)
  if (!await promptBool('Continue?')) {
    info('Aborted.')
    process.exit() // eslint-disable-line unicorn/no-process-exit
  }

  if (!validate(email)) {
    return register(url, {retryEmail: true})
  }

  const {token, securityCode} = await getVerificationData(url, email)
  console.log(`> Please follow the link sent to ${chalk.bold(email)} to log in.`)

  if (securityCode) {
    console.log(`> Verify that the provided security code in the email matches ${chalk.cyan(chalk.bold(securityCode))}.`)
  }

  process.stdout.write('\n')

  const spinner = ora({
    text: 'Waiting for confirmation...',
    color: 'black'
  }).start()

  let final

  do {
    await sleep(2500)

    try {
      final = await verify(url, email, token)
    } catch (err) {}
  } while (!final)

  spinner.text = 'Confirmed email address!'
  spinner.stopAndPersist('✔')

  process.stdout.write('\n')

  return {email, token: final}
}

module.exports = async function (url) {
  const loginData = await register(url)
  cfg.merge(loginData)
  return loginData.token
}