#!/usr/bin/env python

import os
import sys

import click

import hokusai

CONTEXT_SETTINGS = {
  'terminal_width': 120,
  'max_content_width': 120,
  'help_option_names': ['-h', '--help']
}

def select_context(staging, production):
  if staging and production is None:
    return 'staging'
  if production and staging is None:
    return 'production'
  hokusai.lib.common.print_red("Invoke with either --staging OR --production")
  sys.exit(-1)

@click.group()
def cli():
  pass

@cli.command(context_settings=CONTEXT_SETTINGS)
def version():
  """
  Print Hokusai's version
  """
  hokusai.version()

@cli.command(context_settings=CONTEXT_SETTINGS)
@click.option('--kubectl-version', type=click.STRING, required=True, help='The version of kubectl to install')
@click.option('--s3-bucket', type=click.STRING, required=True, help="The S3 bucket name containing your org's kubectl config file")
@click.option('--s3-key', type=click.STRING, required=True, help="The S3 key of your org's kubectl config file")
@click.option('--platform', type=click.Choice(['darwin', 'linux']), default='darwin', help='The platform OS (default: darwin)')
@click.option('--install-to', type=click.STRING, default='/usr/local/bin', help='Install kubectl to (default: /usr/local/bin)')
@click.option('--install-config-to', type=click.STRING, default=os.path.join(os.environ.get('HOME'), '.kube'), help='Install kubectl config to (default: ~/.kube)')
def configure(kubectl_version, s3_bucket, s3_key, platform, install_to, install_config_to):
  """
  Install and configure kubectl
  """
  hokusai.configure(kubectl_version, s3_bucket, s3_key, platform, install_to, install_config_to)

@cli.command(context_settings=CONTEXT_SETTINGS)
@click.option('--aws-account-id', type=click.STRING, required=True, envvar='AWS_ACCOUNT_ID', help='Your AWS account ID (default: $AWS_ACCOUNT_ID)')
@click.option('--project-type', type=click.Choice(['ruby-rack', 'ruby-rails', 'nodejs', 'elixir', 'python-wsgi']), required=True, help='The type of project')
@click.option('--project-name', type=click.STRING, default=os.path.basename(os.getcwd()), help='The project name (default: name of current directory)')
@click.option('--aws-ecr-region', type=click.STRING, default='us-east-1', envvar='AWS_DEFAULT_REGION', help='Your AWS ECR region (default: $AWS_REGION or \'us-east-1\')')
@click.option('--port', type=click.INT, default=80, help='The port of the service (default: 80)')
def setup(aws_account_id, project_type, project_name, aws_ecr_region, port):
  """
  Set up Hokusai for the current project
  """
  hokusai.setup(aws_account_id, project_type, project_name, aws_ecr_region, port)

@cli.command(context_settings=CONTEXT_SETTINGS)
def check():
  """
  Check Hokusai dependencies and configuration
  """
  hokusai.check()

@cli.command(context_settings=CONTEXT_SETTINGS)
@click.argument('action', type=click.STRING)
@click.option('-s', '--skip-build', type=click.BOOL, is_flag=True, help='Do not build the project while launching the stack')
@click.option('-f', '--follow', type=click.BOOL, is_flag=True, help="Follow stack output when running 'start' or 'logs'")
@click.option('-v', '--verbose', type=click.BOOL, is_flag=True, help='Verbose output')
def dev(action, skip_build, follow, verbose):
  """Manage the development stack

  Actions: [start|stop||status|logs|shell|clean]

  start - Start the development stack defined in ./hokusai/development.yml

  stop - Stop the development stack defined in ./hokusai/development.yml

  status - Print the status of the development stack

  logs - Print logs from the development stack

  shell - Attach a shell session to the stack's primary project container

  clean - Stop and remove all containers in the stack"""
  hokusai.lib.common.set_output(verbose)
  if action == 'start':
    hokusai.dev_start(skip_build, follow)
  elif action == 'stop':
    hokusai.dev_stop()
  elif action == 'status':
    hokusai.dev_status()
  elif action == 'logs':
    hokusai.dev_logs(follow)
  elif action == 'shell':
    hokusai.dev_shell()
  elif action == 'clean':
    hokusai.dev_clean()
  else:
    hokusai.lib.common.print_red("Invalid action %s" % action)
    sys.exit(-1)

@cli.command(context_settings=CONTEXT_SETTINGS)
@click.option('-v', '--verbose', type=click.BOOL, is_flag=True, help='Verbose output')
def test(verbose):
  """Boot the test stack and run the test suite

  Return the exit code of the container with the name 'project-name' in hokusai/config.yml"""
  hokusai.lib.common.set_output(verbose)
  hokusai.test()

@cli.command(context_settings=CONTEXT_SETTINGS)
@click.option('--tag', type=click.STRING, help='The tag to push (default: the value of `git rev-parse HEAD`)')
@click.option('--force', type=click.BOOL, is_flag=True, help='Push even if working directory is not clean')
@click.option('--overwrite', type=click.BOOL, is_flag=True, help='Push even if the tag already exists')
@click.option('-v', '--verbose', type=click.BOOL, is_flag=True, help='Verbose output')
def push(tag, force, overwrite, verbose):
  """
  Build and push an image to the project's remote repo tagged as the git SHA1 of HEAD
  """
  hokusai.lib.common.set_output(verbose)
  hokusai.push(tag, force, overwrite)

@cli.command(context_settings=CONTEXT_SETTINGS)
@click.option('-v', '--verbose', type=click.BOOL, is_flag=True, help='Verbose output')
def images(verbose):
  """
  Print images and tags in the project's remote repo
  """
  hokusai.lib.common.set_output(verbose)
  hokusai.images()

@cli.command(context_settings=CONTEXT_SETTINGS)
@click.argument('action', type=click.STRING)
@click.argument('env_vars', type=click.STRING, nargs=-1)
@click.option('--staging', type=click.BOOL, is_flag=True, help='Target staging')
@click.option('--production', type=click.BOOL, is_flag=True, help='Target production')
@click.option('-v', '--verbose', type=click.BOOL, is_flag=True, help='Verbose output')
def env(action, env_vars, staging, production, verbose):
  """Actions: [get|set|unset]

  create - Create the Kubernetes configmap object `{project_name}-environment`

  get - Print environment variables stored on the Kubernetes server

  set - Set environment variables - each of {ENV_VARS} must be in of form 'KEY=VALUE'

  unset - Unset environment variables - each of {ENV_VARS} must be of the form 'KEY'

  delete - Delete the Kubernetes configmap object `{project_name}-environment`"""
  hokusai.lib.common.set_output(verbose)
  context = select_context(staging, production)
  if action == 'create':
    hokusai.create_env(context)
  elif action == 'get':
    hokusai.get_env(context, env_vars)
  elif action == 'set':
    hokusai.set_env(context, env_vars)
  elif action == 'unset':
    hokusai.unset_env(context, env_vars)
  elif action == 'delete':
    hokusai.delete_env(context)
  else:
    hokusai.lib.common.print_red("Invalid action %s" % action)
    sys.exit(-1)

@cli.command(context_settings=CONTEXT_SETTINGS)
@click.argument('action', type=click.STRING)
@click.option('--staging', type=click.BOOL, is_flag=True, help='Target staging')
@click.option('--production', type=click.BOOL, is_flag=True, help='Target production')
@click.option('-v', '--verbose', type=click.BOOL, is_flag=True, help='Verbose output')
def stack(action, staging, production, verbose):
  """  Actions: [create|update|delete|status]

  create - create the Kubernetes stack defined in ./hokusai/{staging/production}.yml

  update - update the Kubernetes stack defined in ./hokusai/{staging/production}.yml

  delete - delete the Kubernetes stack defined in ./hokusai/{staging/production}.yml

  status - print the Kubernetes stack status defined in ./hokusai/{staging/production}.yml"""
  hokusai.lib.common.set_output(verbose)
  context = select_context(staging, production)
  if action == 'create':
    hokusai.stack_create(context)
  elif action == 'update':
    hokusai.stack_update(context)
  elif action == 'delete':
    hokusai.stack_delete(context)
  elif action == 'status':
    hokusai.stack_status(context)
  else:
    hokusai.lib.common.print_red("Invalid action %s" % action)
    sys.exit(-1)

@cli.command(context_settings=CONTEXT_SETTINGS)
@click.argument('tag', type=click.STRING)
@click.option('--migration', type=click.STRING, help='Run a migration before deploying')
@click.option('--staging', type=click.BOOL, is_flag=True, help='Target staging')
@click.option('--production', type=click.BOOL, is_flag=True, help='Target production')
@click.option('-v', '--verbose', type=click.BOOL, is_flag=True, help='Verbose output')
def deploy(tag, migration, staging, production, verbose):
  """
  Update the project's deployment(s) to reference a named image tag and update the tag (staging/production) to reference the same image
  """
  hokusai.lib.common.set_output(verbose)
  context = select_context(staging, production)
  hokusai.deploy(context, tag, migration)

@cli.command(context_settings=CONTEXT_SETTINGS)
@click.option('--staging', type=click.BOOL, is_flag=True, help='Target staging')
@click.option('--production', type=click.BOOL, is_flag=True, help='Target production')
@click.option('-v', '--verbose', type=click.BOOL, is_flag=True, help='Verbose output')
def refresh(staging, production, verbose):
  """
  Refresh the project's deployment(s) by terminating the currently running pods
  """
  hokusai.lib.common.set_output(verbose)
  context = select_context(staging, production)
  hokusai.refresh(context)

@cli.command(context_settings=CONTEXT_SETTINGS)
@click.option('--staging', type=click.BOOL, is_flag=True, help='Target staging')
@click.option('--production', type=click.BOOL, is_flag=True, help='Target production')
@click.option('-v', '--verbose', type=click.BOOL, is_flag=True, help='Verbose output')
def history(staging, production, verbose):
  """
  Print the project's deployment history in terms of revision number,
  creation time, container name and image tag
  """
  hokusai.lib.common.set_output(verbose)
  context = select_context(staging, production)
  hokusai.history(context)

@cli.command(context_settings=CONTEXT_SETTINGS)
@click.option('--migration', type=click.STRING, help='Run a migration before deploying')
@click.option('-v', '--verbose', type=click.BOOL, is_flag=True, help='Verbose output')
def promote(migration, verbose):
  """
  Update the project's deployment(s) on production with the image tag currently deployed on staging
  and update the production tag to reference the same image
  """
  hokusai.lib.common.set_output(verbose)
  hokusai.promote(migration)

@cli.command(context_settings=CONTEXT_SETTINGS)
@click.option('-v', '--verbose', type=click.BOOL, is_flag=True, help='Verbose output')
def diff(verbose):
  """
  Print a git diff between the tag currently deployed on production
  and the tag currently deployed on staging
  """
  hokusai.lib.common.set_output(verbose)
  hokusai.diff()

@cli.command(context_settings=CONTEXT_SETTINGS)
@click.argument('command', type=click.STRING)
@click.option('--staging', type=click.BOOL, is_flag=True, help='Target staging')
@click.option('--production', type=click.BOOL, is_flag=True, help='Target production')
@click.option('--tty', type=click.BOOL, is_flag=True, help='Attach the terminal')
@click.option('--tag', type=click.STRING, help='The image tag to run (defaults to either staging or production)')
@click.option('--env', type=click.STRING, multiple=True, help='Environment variables in the form of "KEY=VALUE"')
@click.option('-v', '--verbose', type=click.BOOL, is_flag=True, help='Verbose output')
def run(command, staging, production, tty, tag, env, verbose):
  """
  Launch a new container and run a command
  """
  hokusai.lib.common.set_output(verbose)
  context = select_context(staging, production)
  hokusai.run(context, command, tty, tag, env)

@cli.command(context_settings=CONTEXT_SETTINGS)
@click.option('--staging', type=click.BOOL, is_flag=True, help='Target staging')
@click.option('--production', type=click.BOOL, is_flag=True, help='Target production')
@click.option('-t', '--timestamps', type=click.BOOL, is_flag=True, help='Include timestamps')
@click.option('-n', '--nlines', type=click.STRING, help='Num lines to follow')
@click.option('-f', '--follow', type=click.BOOL, is_flag=True, help='Follow logs')
@click.option('-v', '--verbose', type=click.BOOL, is_flag=True, help='Verbose output')
def logs(staging, production, timestamps, nlines, follow, verbose):
  """
  Get container logs
  """
  hokusai.lib.common.set_output(verbose)
  context = select_context(staging, production)
  hokusai.logs(context, timestamps, nlines, follow)

if __name__ == '__main__':
  cli(obj={})
