const log = require('../../logging')
const InstanceCLI = require('./Instance')
const { getErrorMessage } = require('../../error')
const { waitForState, waitForIntervalMilliSec } = require('../../utils')

const waitForMinutes = 15

async function builder (yargs) {
  yargs
    .positional('instanceId', {
      type: 'string',
      describe: 'Instance id',
      demandOption: true
    })
    .positional('os', {
      type: 'string',
      describe: 'Operating system semantic version (example: \'16.1\' or \'16.1.4\'',
      demandOption: true
    })
    .option('osbuild', {
      type: 'string',
      describe: 'Use a specific build (example: \'16H20\')',
      demandOption: false
    })
    .option('verbose', {
      alias: 'v',
      type: 'boolean',
      describe: 'Console will receive verbose error output',
      demandOption: false
    })
    .option('format', {
      type: 'input',
      describe: 'Output format (default is table) e.g. json',
      string: true,
      choices: ['json', 'csv'],
      demandOption: false
    })
    .option('wait', {
      type: 'boolean',
      describe: 'Wait for the instance to be ready',
      demandOption: false
    })
}

async function handler (argv) {
  try {
    const { os, osbuild } = argv
    const instance = new InstanceCLI(argv)
    await instance.upgrade({ os, osbuild })
    if (argv.wait) {
      const reporter = argv.verbose
        ? (info) => {
            if (argv.verbose) {
              let restoreStatus = ''
              if (info.restoreStatus && info.restoreStatus.stage) {
                restoreStatus = ` -> ${info.restoreStatus.stage}` + (info.restoreStatus.progress ? `(${parseInt(info.restoreStatus.progress)}%)` : '')
              }
              const report = ` - ${info.state}${restoreStatus}`
              if (lastReport !== report) {
                log.info(report)
              }
              lastReport = report
            }
          }
        : () => { /* nop */ }
      let lastReport = ''
      // First wait up to 10 seconds for the update to begin
      let attempts = 10000 / waitForIntervalMilliSec
      await waitForState(argv.instance, instance.info.bind(instance), 'updating', attempts)
      // Then wait up to "waitForMinutes" for the instance to upgrade its OS version or error out
      // Using on-prem cluster ci-1, the upgrade from iOS 16.0 to 16.1 has been taking about 8-1/2 minutes
      // 180 * 5 seconds = 15 minutes
      log.info(`Waiting up to ${waitForMinutes} minutes for upgrade to complete...`)
      attempts = (waitForMinutes * 60 * 1000) / waitForIntervalMilliSec
      await waitForState(argv.instance, instance.info.bind(instance), 'on', attempts, reporter)
    }
    log.info(`Successfully upgraded instance ${argv.instanceId} to ${os}`)
  } catch (err) {
    const errorMessage = getErrorMessage(err)
    log.error(`Upgrade iOS version failed: ${errorMessage}`)
    if (argv.detailedErrors) { log.error(err) }
  }
}

module.exports = {
  builder,
  handler,
  command: 'upgrade <instanceId> <os> [--osbuild <buildid>] [--wait] [--format <\'json\' | \'csv\'>] [--verbose]',
  describe: 'Upgrade iOS version for instance'
}
