'use strict'

const cli = require('heroku-cli-util')
const co = require('co')

let context
let heroku
let app
let name
let plan
let addon
let service

function noPlanError () {
  throw new Error(`Error: No plan specified.
You need to specify a plan to move ${cli.color.yellow(name)} to.
For example: ${cli.color.blue('heroku addons:upgrade heroku-redis:premium-0')}

${cli.color.cyan('https://devcenter.heroku.com/articles/managing-add-ons')}`)
}

function handlePlanChangeAPIError (err) {
  const {sortBy} = require('lodash')

  if (err.statusCode === 422 && err.body.message && err.body.message.startsWith("Couldn't find either the add-on")) {
    return heroku.get(`/addon-services/${service}/plans`)
      .then((plans) => {
        plans = sortBy(plans, 'price.cents').map((plans) => plans.name)
        throw new Error(`${err.body.message}

Here are the available plans for ${cli.color.yellow(service)}:
${plans.join('\n')}

See more plan information with ${cli.color.blue('heroku addons:plans ' + service)}

${cli.color.cyan('https://devcenter.heroku.com/articles/managing-add-ons')}`)
      })
  }
  throw err
}

function handleAPIError (err) {
  if (err.statusCode === 422 && err.body.id === 'multiple_matches') {
    let example = err.body.message.split(', ')[2] || 'redis-triangular-1234'
    throw new Error(`${err.body.message}

Multiple add-ons match ${cli.color.yellow(name)}${app ? ' on ' + app : ''}
It is not clear which add-on's plan you are trying to change.

Specify the add-on name instead of the name of the add-on service.
For example, instead of: ${cli.color.blue('heroku addons:upgrade ' + context.args.addon + ' ' + (context.args.plan || ''))}
Run this: ${cli.color.blue('heroku addons:upgrade ' + example + ' ' + name + ':' + plan)}
${!app ? 'Alternatively, specify an app to filter by with ' + cli.color.blue('--app') : ''}
${cli.color.cyan('https://devcenter.heroku.com/articles/managing-add-ons')}`)
  }
  throw err
}

function * run (c, h) {
  const resolver = require('../../lib/resolve')
  const util = require('../../lib/util')

  context = c
  heroku = h
  app = context.app
  name = context.args.addon
  plan = context.args.plan

  // called with just one argument in the form of `heroku addons:upgrade heroku-redis:hobby`
  if (!plan && name.indexOf(':') !== -1) {
    let s = name.split(':')
    name = s[0]
    plan = s[1]
  }

  if (!plan) noPlanError()
  // ignore the service part of the plan since we can infer the service based on the add-on
  if (plan.indexOf(':') !== -1) plan = plan.split(':')[1]

  // find the add-on to be changed
  addon = yield resolver.addon(h, app, name).catch((e) => handleAPIError(e))

  service = addon.addon_service.name
  app = addon.app.name
  plan = `${service}:${plan}`
  yield cli.action(`Changing ${cli.color.magenta(addon.name)} on ${cli.color.cyan(app)} from ${cli.color.blue(addon.plan.name)} to ${cli.color.blue(plan)}`, {success: false}, co(function * () {
    addon = yield heroku.request({
      path: `/apps/${app}/addons/${addon.name}`,
      method: 'PATCH',
      body: {plan: {name: plan}},
      headers: {
        'Accept-Expansion': 'plan',
        'X-Heroku-Legacy-Provider-Messages': 'true'
      }
    }).catch((e) => handlePlanChangeAPIError(e))
    cli.action.done(`done, ${cli.color.green(util.formatPrice(addon.plan.price))}`)
    if (addon.provision_message) cli.log(addon.provision_message)
  }))
}

let cmd = {
  topic: 'addons',
  description: 'change add-on plan',
  help: `See available plans with \`heroku addons:plans SERVICE\`.

Note that \`heroku addons:upgrade\` and \`heroku addons:downgrade\` are the same.
Either one can be used to change an add-on plan up or down.

[https://devcenter.heroku.com/articles/managing-add-ons](https://devcenter.heroku.com/articles/managing-add-ons)`,
  examples: [
    `Upgrade an add-on by service name:
$ heroku addons:upgrade heroku-redis:premium-2

Upgrade a specific add-on:
$ heroku addons:upgrade swimming-briskly-123 heroku-redis:premium-2`
  ],
  needsAuth: true,
  wantsApp: true,
  args: [{name: 'addon'}, {name: 'plan', optional: true}],
  run: cli.command({preauth: true}, co.wrap(run))
}

module.exports = [
  Object.assign({command: 'upgrade'}, cmd),
  Object.assign({command: 'downgrade'}, cmd)
]
