#!/usr/bin/env node

'use strict'

/*
  https://github.com/tj/commander.js
*/
const program = require('commander')
const env = require("dotenv")
const path = require('path')
const { logger } = require('./commands/services/utils')
const figlet = require('figlet')
/* GLOBAL ============================== */

let command_found = false

env.config({ path: path.resolve(process.cwd(), ".env") });

const runCommand = async (cmd = {}, command_name, params = {}) => {

  // flatten options
  let options = Object.assign(params, cmd.parent || {}, cmd)

  try {
    let path = `./commands/${command_name}`
    let command = require(path)
    command_found = true

    if (typeof options.version == 'function') {
      options.version = undefined
    }

    let exitCode = await command(options, logger)
    switch (typeof exitCode) {
      case 'object':
        process.stdout.write(JSON.stringify(exitCode, null, 2))
        process.exit(0)
      case 'string':
        process.stdout.write(exitCode)
        process.exit(0)
      default:
        process.exit(exitCode)
    }
  } catch (err) {
    console.error(err)
    process.exit(1)
  }
}

/* DOCKER PUBLISH COMMAND =================================== */
program.command('docker-publish').alias('dp')
  .description('Push a docker image to docker hub.')
  .option('-o, --org <org>', 'Docker org (Default: viewdo')
  .option('-u, --user <user>', 'Docker Hub username')
  .option('-p, --password <password>', 'Docker Hub password')
  .option('-d, --path <path>', 'Path to image context')
  .option('-i, --image <image>', 'Image name (Default: Repository slug')
  .option('-v, --version <version>', 'Image version')
  .option('-b, --build_id <build>', 'Build ID to tag with')
  .option('-t, --tag <tag>', 'Tag (Default: latest)')
  .option('-D, --debug', 'Debug', false)
  .action(async (cmd) => {
    await runCommand(cmd, 'docker-publish')
  })


/* GCR AUTH COMMAND =================================== */
program.command('gcp-auth').alias('gcr-auth')
  .description('Authenticate gcloud with using a keyfile.')
  .option('-k, --keyfile <keyfile>', 'Keyfile with service-accoun.json')
  .option('-p, --project <project>', 'GCR project')
  .option('-D, --debug', 'Debug', false)
  .action(async (cmd) => {
    await runCommand(cmd, 'gcp-auth')
  })

/* GCR PUBLISH COMMAND =================================== */
program.command('gcr-publish').alias('gp')
  .description('Push a docker image to gcr.')
  .option('-k, --keyfile <keyfile>', 'Key file with service-account.json')
  .option('-p, --project <project>', 'GCR project')
  .option('-r, --repository <repository>', 'GCP repository URL')
  .option('-d, --path <path>', 'Path to image context')
  .option('-i, --image <image>', 'Image name (Default: Repository slug')
  .option('-v, --version <version>', 'Image version')
  .option('-b, --build_id <build>', 'Build ID to tag with')
  .option('-t, --tag <tag>', 'Tag (Default: latest)')
  .option('-D, --debug', 'Debug', false)
  .option('-a, --build_args <args>', 'String of build args to pass to build command (i.e. ARG1=value1,ARG2=value2)')
  .action(async (cmd) => {
    await runCommand(cmd, 'gcr-publish')
  })

/* GET NAMESPACE COMMAND =================================== */
program.command('get-namespace <branch>').alias('ns')
  .description('Translate a namespace from the passed in branch.')
  .action(async (branch, cmd) => {
    await runCommand(cmd, 'get-namespace', { branch })
  })

/* GIT TAG COMMAND =================================== */
program.command('git-tag [version]').alias('tag')
  .option('-v, --version <version>', ' Version')
  .option('-b, --build_id <build>', 'Build ID')
  .option('-D, --debug', 'Debug', false)
  .description('Tags git with the version and build info')
  .action(async (version, cmd) => {
    await runCommand(cmd, 'git-tag', { version })
  })

/* GIT VERSION COMMAND =================================== */
program.command('git-version').alias('version')
  .option('-v, --version <version>', ' Version')
  .description('Uses gitverion to write version.json to the clone directory.')
  .action(async (cmd) => {
    await runCommand(cmd, 'git-version')
  })

/* JIRA RELEASE COMMAND =================================== */
program.command('jira-release <project> [version]').alias('release')
  .description('Create a release in Jira with the specified version, and release all done issues.')
  .option('-c, --component', 'Jira Component')
  .option('-D, --debug', 'Debug', false)
  .action(async (project, version, cmd) => {
    await runCommand(cmd, 'jira-release', { project, version })
  })

/* KUBE AUTH COMMAND =================================== */
program.command('kube-auth').alias('auth')
  .description('Runs the steps to authenticate with k8s in bitbucket.')
  .option('-u, --user <user>', 'Kube User')
  .option('-t, --token <token>', 'Kube token')
  .option('-a, --authority <authority>', 'Kube CA')
  .option('-c, --cluster <cluster>', 'Kube cluster')
  .option('-s, --server <server>', 'Kube server')
  .option('-n, --namespace <namespace>', 'Default namespace')
  .action(async (cmd) => {
    await runCommand(cmd, 'kube-auth')
  });

/* KUBE BUILD =================================== */
program.command('kube-build [source] [output]').alias('kb')
  .description('Copies all files from .kube to ./kube and replaces all variables.')
  .option('-v, --version <version>', 'Replace $VERSION')
  .option('-b, --build_id <build>', 'Replace $BUILD_ID')
  .option('-n, --namespace <namespace>', 'Replace $NAMESPACE')
  .option('-B, --branch', 'Replace $BRANCH')
  .action(async (source, destination, cmd) => {
    await runCommand(cmd, 'kube-build', { source, destination })
  })


/* PIPELINE RUN =================================== */
program.command('run-pipeline [pipeline]').alias('pipeline')
  .description('Run any pipeline within this organization.')
  .option('-u, --user <user>', 'Bitbucket User')
  .option('-p, --password <password>', 'Bitbucket Password')
  .option('-r, --repository <repository>', 'Bitbucket repository slug')
  .option('-b, --branch <branch>', 'Git branch to run pipeline on (default: main)', 'main')
  .option('-D, --debug', 'Debug', false)
  .action(async (pipeline, cmd) => {
    await runCommand(cmd, 'pipeline-run', { pipeline })
  })

/* RUN CONFIGURE DNS PIPELINE =================================== */
program.command('run-configure-environments').alias('dns')
  .description('Run the DNS/Environments control pipeline on devops-dns-control main branch.')
  .option('-u, --user <user>', 'Bitbucket User')
  .option('-p, --password <password>', 'Bitbucket Password')
  .option('-D, --debug', 'Debug', false)
  .action(async (cmd) => {
    await runCommand(cmd, 'pipeline-run', {
      repository: 'devops-dns-control',
      pipeline: 'configure_environments',
      branch: 'main'
    })
  })

/* VAR SET COMMAND =================================== */
program.command('variable-set <key> <value>').alias('set-var')
  .option('-u, --user <user>', 'Bitbucket User')
  .option('-p, --password <password>', 'Bitbucket Password')
  .option('-P, --project <project>', 'Repository / project key')
  .option('-w, --workspace', 'Flag to set in the workspace instead of in the project')
  .option('-s, --secure', 'Encrypts the variable')
  .description('Create or update an environment variable in the given repository or workspace')
  .action(async (key, value, cmd) => {
    await runCommand(cmd, 'variable-set', { key, value })
  })

/* VARS GET COMMAND =================================== */
program.command('variables-get').alias('vars-get').alias('get-vars')
  .option('-v, --version <version>', 'Replace $VERSION')
  .option('-b, --build_id <build>', 'Override $BUILD_ID')
  .option('-n, --namespace <namespace>', 'Override $NAMESPACE')
  .option('-s, --branch_slug <slug>', 'Override derived branch slug')
  .description('Creates vars: VERSION, ASSEMBLY_VERSION, NAMESPACE & BUILD_ID (Requires: version.json).')
  .action(async (cmd) => {
    await runCommand(cmd, 'variables-get')
  })


/* VARS REPLACE COMMAND =================================== */
program.command('variables-replace [path]').alias('rv').alias('replace-vars')
  .option('-v, --version <version>', 'Replace $VERSION')
  .option('-b, --build_id <build>', 'Replace $BUILD_ID')
  .option('-n, --namespace <namespace>', 'Replace $NAMESPACE')
  .option('-B, --branch', 'Replace $BRANCH')
  .description('Replaces environment $VARIABLES in text files at a given path (default: /kube)')
  .action(async (path, cmd) => {
    await runCommand(cmd, 'variables-replace', { path })
  })

/* SEND SLACK COMMAND =================================== */
program.command('send-slack <text>').alias('slack')
  .description('Send a slack message to the #delivery-log channel.')
  .option('-c, --channel <channel>', 'Override the channel')
  .option('-u, --url <url>', 'Slack webhook URL')
  .option('-D, --debug', 'Debug', false)
  .action(async (text, cmd) => {
    cmd.text = text
    await runCommand(cmd, 'send-slack')
  })

/* NPM PUBLISH COMMAND =================================== */
program.command('npm-publish').alias('npp')
  .description('Tags and publishes git repository to npm and pushes tags back into the branch.')
  .option('-br, --branch <branch', 'Target branch to publish. Defaults to current branch')
  .option('-b, --build_id <build>', 'Replaces $BUILD_ID')
  .option('-v, --version <version>', 'Replaces $VERSION')
  .option('-e, --email <email>', 'Email with access to npm repository. Replaces $NPM_EMAIL.')
  .option('-r, --registry <registry>', 'Target npm registry. Replaces $NPM_REGISTRY.')
  .option('-nu, --npm_user <npm_user>', 'Npm user with access to npm registry. Replaces $NPM_USER.')
  .option('-nt, --npm_token <npm_token>', 'Npm token for npm user. Replaces $NPM_TOKEN.')
  .option('-bu, --bitbucket_user <bb_user>', 'Bitbucket user with access to git repo. Replaces $BITBUCKET_APP_USERNAME')
  .option('-bt, --bitbucket_token <bb_token>', 'Bitbucket token for user. Replaces $BITBUCKET_APP_TOKEN.')
  .option('-t, --tag_prefix <tag>', 'Additional tags to add to current branch.')
  .action(async (cmd) => {
    await runCommand(cmd, 'npm-publish')
  })

/* HELP COMMAND =================================== */
program.command('help').alias('h')
  .action(() => {
    program.outputHelp()
  })


program.parse(process.argv)

if (!process.argv.slice(2).length) {
  program.outputHelp()
  process.exit(1)
}

program.on('option:verbose', () => {
  process.env.VERBOSE = 'true';
});

// error on unknown commands
if (!command_found) {
  console.error('Invalid command: %s\n', program.args.join(' '));
  program.outputHelp()
  process.exit(1);
}

process.on('unhandledRejection', (reason, p) => {
  console.log('Unhandled Rejection at: Promise', p, 'reason:', reason)
});
