"""
Description
  The profile management system is used to create multiple profiles for
  different Pennsieve accounts or organizations. The user can also set/unset
  global settings which apply to all profiles.

  Settings are loaded in the following order, with each tier overriding the
  last:

    1. Global settings are loaded first
    2. Default profile, if configured
    3. Command line arguments, e.g.: --profile=<name>
    4. Environment variables, e.g.: PENNSIEVE_API_TOKEN

Usage:
  pennsieve-profile create [<name>]
  pennsieve-profile show [<name>]
  pennsieve-profile delete [<name>] [-f | --force]
  pennsieve-profile list [-c | --contents]
  pennsieve-profile set-default [<name>]
  pennsieve-profile unset-default [-f | --force]
  pennsieve-profile status
  pennsieve-profile set <key> <value> [-f | --force] [--profile=<name>]
  pennsieve-profile unset <key> [-f | --force] [--profile=<name>]
  pennsieve-profile keys [--profile=<name>]
  pennsieve-profile version
  pennsieve-profile help

Options:
  -c --contents       List profiles with contents. Also lists global settings, if any
  -f --force          Attempt action without prompting for confirmation. Use with care
  --profile=<name>    Use specified profile (instead of default)
  -h --help           Show help

Advanced commands:
  set (global|<name>) <key> <value>   Set key/value pair for given profile, or globally
  unset (global|<name>) <key>         Unset key/value pair for given profile, or globally
  keys                                List all available keys and their default values

For additional features, install the Pennsieve CLI Agent:
https://developer.pennsieve.io/agent
"""

from __future__ import absolute_import, print_function
from builtins import input

import io
import os

from docopt import docopt

import pennsieve
from pennsieve import DEFAULT_SETTINGS, Pennsieve, Settings


def main():
    args = docopt(__doc__, version=pennsieve.__version__)

    # Test for these two commands first as they
    # do not require a Pennsieve client or reading the settings file
    if args["help"]:
        print(__doc__.strip("\n"))
        return
    elif args["version"]:
        print(pennsieve.__version__)
        return

    settings = Settings(args["--profile"])
    if not os.path.exists(settings.config_file):
        setup_assistant(settings)
    elif args["create"]:
        create_profile(settings, args["<name>"])
    elif args["show"]:
        show_profile(settings, args["<name>"])
    elif args["delete"]:
        delete_profile(settings, args["<name>"], args["--force"])
    elif args["list"]:
        list_profiles(settings, args["--contents"])
    elif args["set-default"]:
        set_default(settings, args["<name>"])
    elif args["unset-default"]:
        unset_default(settings, args["--force"])
    elif args["set"]:
        set_key(
            settings, args["<key>"], args["<value>"], args["--profile"], args["--force"]
        )
    elif args["unset"]:
        unset_key(settings, args["<key>"], args["--profile"], args["--force"])
    elif args["keys"]:
        list_keys(settings, args["--profile"])
    elif args["status"]:
        ps = Pennsieve(args["--profile"])
        show_status(ps)
    else:
        invalid_usage()

    with io.open(settings.config_file, "w") as configfile:
        settings.config.write(configfile)


def setup_assistant(settings):
    settings.config.clear()

    print("Pennsieve profile setup assistant")

    settings.config["global"] = {"default_profile": "none"}

    print("Create a profile:")
    create_profile(settings)

    print(
        "Setup complete. Run 'pennsieve-profile help' for available commands and actions"
    )


# User commands
# =======================================================


def create_profile(settings, name=None):
    if not name:
        name = input("  Profile name [default]: ") or "default"

    if name in settings.config:
        if name in ("global", "agent", "none"):
            print(
                "Profile name '{}' reserved for system. Please try a different name".format(
                    name
                )
            )
        else:
            print("Profile '{}' already exists".format(name))
        abort()

    print("Creating profile '{}'".format(name))
    settings.config[name] = {}

    settings.config[name]["api_token"] = input("  API token: ")
    settings.config[name]["api_secret"] = input("  API secret: ")

    if settings.config["global"]["default_profile"] == "none":
        settings.config["global"]["default_profile"] = name
        print("Default profile: {}".format(name))

    else:
        if yesno_prompt("Would you like to set '{}' as default (Y/n)? ".format(name)):
            set_default(settings, [name])


def delete_profile(settings, name, force):
    if not name:
        name = input("  Profile to delete: ")

    if not valid_name(settings, name):
        abort()

    if not force:
        if not yesno_prompt("Delete profile '{}' (Y/n)? ".format(name)):
            print("abort")
            abort()

    print("Deleting profile '{}'".format(name))
    settings.config.remove_section(name)

    if settings.config["global"]["default_profile"] == name:
        settings.config["global"]["default_profile"] = "none"
        print(
            "\033[31m* Warning: default profile unset. Use 'ps profile set-default <name>' to set a new default\033[0m"
        )


def list_profiles(settings, contents):
    """
    Lists all profiles
    """
    print("Profiles:")
    for section in settings.config.sections():
        if section not in ["global"]:
            if section == settings.config["global"]["default_profile"]:
                print("* \033[32m{}\033[0m".format(section))
            else:
                print("  {}".format(section))
            if contents:
                print_profile(settings, section, 4)
    if contents:
        if len(settings.config["global"]) > 1:
            print("Global Settings:")
            print_profile(settings, "global", 2)


def set_default(settings, name):
    if not name:
        name = input("  Profile name to set as default: ")

    if not valid_name(settings, name):
        abort()

    print("Default profile: {}".format(name))
    settings.config["global"]["default_profile"] = name


def unset_default(settings, force):
    original = settings.config["global"]["default_profile"]

    if not force:
        if not yesno_prompt("Unset default profile '{}' (Y/n)? ".format(original)):
            print("abort")
            abort()

    print(
        "Default profile '{}' unset. Using global settings and environment variables".format(
            original
        )
    )
    settings.config["global"]["default_profile"] = "none"


def show_profile(settings, profile):
    if profile:
        name = profile
    else:
        name = settings.default_profile

    if name == "global" or valid_name(settings, name):
        print("{} contents:".format(name))
        print_profile(settings, name, 2, True)


# Advanced commands
# =======================================================


def set_key(settings, key, value, profile, force):
    if not key in DEFAULT_SETTINGS:
        print(
            "Invalid key: '{}'\n see 'ps profile keys' for available keys".format(key)
        )
        return

    if profile:
        name = profile
    else:
        name = settings.default_profile

    if not name in settings.config:
        print("Profile '{}' does not exist".format(name))
        return

    if not force and key in settings.config[name]:
        if not yesno_prompt("{}: {} already set. Overwrite (Y/n)? ".format(name, key)):
            abort()

    print("{}: {}={}".format(name, key, value))
    settings.config[name][key] = value


def unset_key(settings, key, profile, force):
    if profile:
        name = profile
    else:
        name = settings.default_profile

    if not name in settings.config:
        print("Profile '{}' does not exist".format(name))
        abort()

    if not key in settings.config[name]:
        print("{}: {} not set".format(name, key))
        abort()

    if not force and key in settings.config[name]:
        if not yesno_prompt("{}: Unset {} (Y/n)? ".format(name, key)):
            abort()

    print("{}: {} unset".format(name, key))
    settings.config[name].pop(key)


def list_keys(settings, profile):
    if profile:
        name = profile
    else:
        name = settings.default_profile

    print("Keys and default values for '{}'".format(name))
    for key, value in sorted(settings.profiles[name].items()):
        print("  {} : {}".format(key, value))


def show_status(ps):
    print("Active profile:\n  \033[32m{}\033[0m\n".format(ps.settings.active_profile))

    if ps.settings.env:
        key_len = 0
        value_len = 0
        for key, value in ps.settings.env.items():
            valstr = "{}".format(value)
            key_len = max(key_len, len(key))
            value_len = max(value_len, len(valstr))

        print("Environment variables:")
        print(
            "  \033[4m{:{key_len}}    {:{value_len}}    {}".format(
                "Key",
                "Value",
                "Environment Variable\033[0m",
                key_len=key_len,
                value_len=value_len,
            )
        )
        for value, evar in sorted(ps.settings.env.items()):
            valstr = "{}".format(value)
            # get internal variable
            ivar = ""
            for k, v in ps.settings.__dict__.items():
                if str(v) == str(evar):
                    ivar = k
            print(
                "  {:{key_len}}    {:{value_len}}    {}".format(
                    ivar, evar, valstr, key_len=key_len, value_len=value_len
                )
            )
        print()

    print("Pennsieve environment:")
    print("  User               : {}".format(ps.profile.email))
    print("  Organization       : {} (id: {})".format(ps.context.name, ps.context.id))
    print("  API Location       : {}".format(ps.settings.api_host))


# Helper functions
# =======================================================


def abort():
    exit(1)


def yesno_prompt(msg):
    return input(msg).lower() in ("y", "yes")


def invalid_usage():
    print("Invalid usage. See `pennsieve-profile help` for available commands")
    abort()


def valid_name(settings, name):
    if name not in settings.config or name == "global":
        print("Profile '{}' does not exist".format(name))
        return False
    return True


def print_profile(settings, name, indent=0, show_all=False):
    if show_all:
        key_len = 0
        for key in settings.profiles[name].keys():
            key_len = max(key_len, len(key))

        for key, value in sorted(settings.profiles[name].items()):
            if key != "default_profile":
                if key in settings.config[name] and name != "global":
                    print(
                        " " * indent
                        + "{:{key_len}} : \033[32m{}\033[0m ({})".format(
                            key, value, name, key_len=key_len
                        )
                    )
                elif key in settings.config["global"]:
                    print(
                        " " * indent
                        + "{:{key_len}} : \033[34m{}\033[0m (global)".format(
                            key, value, key_len=key_len
                        )
                    )
                else:
                    print(
                        " " * indent
                        + "{:{key_len}} : \033[0m{}\033[0m".format(
                            key, value, key_len=key_len
                        )
                    )

    else:
        key_len = 0
        for key in settings.config[name].keys():
            key_len = max(key_len, len(key))

        for key, value in sorted(settings.config[name].items()):
            if key != "default_profile":
                print(
                    " " * indent
                    + "{:{key_len}} : {}".format(key, value, key_len=key_len)
                )
