#!/usr/bin/env python
"""HighlyProbable-cli.

Usage:
  hpcli login
  hpcli create-account
  hpcli create-project [<template-type>] [<project-name>]
  hpcli config-project (<project-name>)
  hpcli run [<project-name>] [<custom-docker-image>] [--use-remote=<use-remote>]
  hpcli remote deploy [<project-name>]
  hpcli remote upload [<project-name>]
  hpcli remote create [<project-name>]
  hpcli remote start [<project-name>]
  hpcli remote stop [<project-name>]
  hpcli remote ping [<project-name>]

Options:
  -h --help     Show this screen.
  --use-remote=<use-remote>  Use a remote url for the code source eg: s3://foo.bar.baz [default: None]

Templates:
- python3-generic
"""
from docopt import docopt
from hpcli.hpclilib.common import DOCKER_REPO_URLS, big_print, get_project_root
from hpcli.hpclilib.docker import Docker
from hpcli.hpclilib.templates import copy_template
from hpcli.hpclilib.login import load_credentials, interactive_login, is_logged_in
from hpcli.hpclilib.upload import MicroServiceUploader
from hpcli.sdk.microservice_defn import MicroServiceDefinition
from hpcli.sdk.microservice_manager import MicroServiceManager
from hpcli.sdk.base import API, MS_URL_ROOT
from requests.compat import urljoin
import time


def panic(reason):
    print('\033[91m' + reason + '\033[0m')
    exit(1)


def warn(reason, hint=""):
    print('\033[93m' + reason + '\033[0m')
    if hint:
        warn(hint, "")


def user_input_choices(input_str, choices):
    try:
        while True:
            print(input_str)
            for number, choice in enumerate(choices):
                print("%i) %s" % (number, choice))
            user_input = input('> ')
            try:
                user_input_int = int(user_input)
                assert(user_input_int in range(len(choices)))
                return choices[user_input_int]
            except:
                warn('Invalid Selection, Please try again')
    except KeyboardInterrupt:
        panic('')


def user_input_loop(input_str, failure_reason, transform_fn, choices=None):
    try:
        while True:
            user_input = input(input_str)
            try:
                transform_fn(user_input)
                break
            except:
                warn(failure_reason)
        return transform_fn(user_input)
    except KeyboardInterrupt:
        panic('')


def validate_project_name(project_name):
    assert(project_name != '')
    for x in ['%', ':', '/', '#', '@', '!', '&', '*', ' ']:
        assert(x not in project_name)
    return project_name


def print_templates():
    counter = 0
    choices = []
    for project_type in DOCKER_REPO_URLS:
        choices.append(project_type)
        print('%i) %s' % (counter, project_type))
    return choices


def create_project(arguments, config):
    if not Docker.docker_is_running(''):
        panic('Docker must be running to create a project. Please check if docker is running, or you can download docker from here: https://www.docker.com/get-docker')

    if not arguments['<template-type>']:
        print('To make a Microservice, please choose a template below (0,1,2..):')
        choices = print_templates()
        arguments['<template-type>'] = user_input_loop(
            '> ', 'Invalid selection, please try again',
            lambda x: choices[int(x)]
        )
    aux_details = {}
    if not arguments['<project-name>']:
        arguments['<project-name>'] = user_input_loop('Now lets give that project a name:\n> ',
                                                      'Project names cannot special characters',
                                                      validate_project_name)
    if 'friendly_name' not in arguments:
        aux_details['friendly_name'] = user_input_loop(
            'What should we call the project on highlyprobable.com?\n> ',
            'Website project names should not be empty, longer than 100 character:',
            lambda x: x if x and len(x) <= 100 else False
        )
    if 'category' not in arguments:
        aux_details['category'] = user_input_choices(
            'Please choose a category for your project\n>',
            ['TRIAL::JustTesting', 'NLP::Classifier', 'IMG::Classifier', 'OTHER']
        )

    if 'description' not in arguments:
        aux_details['description'] = user_input_loop(
            'Now all we need is a quick description\n> ',
            '',
            lambda x: x if x else ''
        )

    project_type = arguments['<template-type>']

    if project_type not in DOCKER_REPO_URLS:
        print('Specified template %s does not exist!' % (project_type))
        print('Available project templates:')
        for project_type in DOCKER_REPO_URLS:
            print('- ' + project_type)
        return
    config = load_credentials(
        get_project_root(arguments), arguments['<project-name>'])
    config = {**config, **aux_details}
    Docker(arguments, config).download_image()
    copy_template(arguments, config)
    MicroServiceDefinition(arguments, config).write_to_file()
    big_print('All done! Try it out with `hpcli run %s`' %
              (arguments['<project-name>']))


def run_project(arguments, config):
    Docker(arguments, config).run()


def pretty_create_microservice(arguments, config):
    m = MicroServiceManager(arguments, config)
    big_print('Creating Microservice...')
    print(m.create_microservice())


def pretty_ping_service(url, config):
    if not url and ('id' in config and 'microservice-name' in config):
        url = urljoin(MS_URL_ROOT, config['id'][0:6]) + '/'
        url = urljoin(url, config['microservice-name'])
        print(url)
    elif not url:
        panic('Cannot find a suitable url, did you start the project?')

    api = API('', config=config, full_url_override=url)
    attempts = 1
    print('Checking if service is up (attempt %s)' % (str(attempts)))
    try:
        while True:
            a = api.get({})
            print(a)
            if a == "200 OK":
                break
            attempts += 1
            time.sleep(6)
            print('Checking if service is up (attempt %s)' % (str(attempts)))
    except KeyboardInterrupt:
        panic('')
    big_print(
        'Your microservice is active! You can access your microservice at %s' %
        (url))


def pretty_start_microservice(arguments, config):
    m = MicroServiceManager(arguments, config)
    big_print('Adding Microservice to Cluster...')
    a = m.start_microservice()
    print(a)
    url = None
    if 'path' in a and a['message'] == 'success':
        url = a['path']
    big_print('Microservice boot in progress (2 min) (Safe to CTRL+C)...')
    print('Waiting for service boot (30s)...')
    time.sleep(30)
    pretty_ping_service(url, config)


def pretty_stop_microservice(arguments, config):
    m = MicroServiceManager(arguments, config)
    big_print('Removing the Microservice from the Cluster...')
    m.stop_microservice()


def pretty_upload_microservice(arguments, config):
    m = MicroServiceUploader(arguments, config)
    big_print('Uploading Microservice...')
    print(m.upload())


def main():
    arguments = docopt(__doc__, version='HighlyProbable-cli v0.1')

    try:
        if arguments['login']:
            if interactive_login():
                print('You have successfully logged in!')
            else:
                panic('There was an error logging you in. Please try again.')
            exit(0)

        if not is_logged_in():
            warn('You must be logged in to use hpcli.',
                 hint='Hint: you can login with `hpcli login`')

        if arguments['create-project']:
            create_project(arguments, {})

        config = load_credentials(
            get_project_root(arguments), arguments['<project-name>'])

        if arguments['run']:
            run_project(arguments, config)

        if arguments['remote']:
            if arguments['deploy']:
                pretty_create_microservice(arguments, config)
                pretty_upload_microservice(arguments, config)
                pretty_start_microservice(arguments, config)

            if arguments['create']:
                pretty_create_microservice(arguments, config)

            if arguments['start']:
                pretty_start_microservice(arguments, config)

            if arguments['stop']:
                pretty_stop_microservice(arguments, config)

            if arguments['upload']:
                pretty_upload_microservice(arguments, config)

            if arguments['ping']:
                pretty_ping_service(None, config)

            print('=' * 80)
    except Exception as e:
        panic(str(e))

if __name__ == '__main__':
    main()
