def _init_stacks(self):
     """Instantiate a StackdIO object"""
     self.stacks = StackdIO(
         base_url=self.config["url"],
         auth=(
             self.config["username"],
             keyring.get_password(self.KEYRING_SERVICE, self.config.get("username") or "")
         ),
         verify=self.config.get('verify', True)
     )
class StackdioShell(
        Cmd,
        mixins.bootstrap.BootstrapMixin,
        mixins.stacks.StackMixin,
        mixins.formulas.FormulaMixin,
        mixins.blueprints.BlueprintMixin):

    CFG_DIR = os.path.expanduser("~/.stackdio-cli/")
    CFG_FILE = os.path.join(CFG_DIR, "config.json")
    BOOTSTRAP_FILE = os.path.join(CFG_DIR, "bootstrap.yaml")
    KEYRING_SERVICE = "stackdio_cli"
    PROMPT = "\n{username} @ {url}\n> "
    HELP_CMDS = [
        "account_summary",
        "stacks", "blueprints", "formulas",
        "initial_setup", "bootstrap",
        "help", "exit", "quit",
    ]

    Cmd.intro = """
######################################################################
                      s  t  a  c  k  d  .  i  o
######################################################################
"""

    def __init__(self):
        Cmd.__init__(self)
        mixins.bootstrap.BootstrapMixin.__init__(self)
        self._load_config()
        if 'url' in self.config and self.config['url']:
            self._init_stacks()
        self._validate_auth()

    def preloop(self):
        self._setprompt()

    def precmd(self, line):
        self._setprompt()
        return line

    def postloop(self):
        print("\nGoodbye!")

    def get_names(self):
        if self.validated:
            return ["do_%s" % c for c in self.HELP_CMDS]
        else:
            return ["do_initial_setup", "do_help"]

    def _init_stacks(self):
        """Instantiate a StackdIO object"""
        self.stacks = StackdIO(
            base_url=self.config["url"],
            auth=(
                self.config["username"],
                keyring.get_password(self.KEYRING_SERVICE, self.config.get("username") or "")
            ),
            verify=self.config.get('verify', True)
        )

    def _load_config(self):
        """Attempt to load config file, otherwise fallback to DEFAULT_CONFIG"""

        try:
            self.config = json.loads(open(self.CFG_FILE).read())
            self.config['blueprint_dir'] = os.path.expanduser(self.config.get('blueprint_dir', ''))

        except ValueError:
            print(self.colorize(
                "What happened?! The config file is not valid JSON. A "
                "re-install is likely the easiest fix.", "red"))
            raise
        except IOError:
            self.config = {
                'url': None,
                'username': None,
            }
            print(self.colorize(
                "It seems like this is your first time using the CLI.  Please run "
                "'initial_setup' to configure.", "green"))
            # print(self.colorize(
            #     "What happened?! Unable to find a config file. A re-install "
            #     "is likely the easiest fix.", "red"))
            # raise

        self.has_public_key = None

    def _validate_auth(self):
        """Verify that we can connect successfully to the api"""

        # If there is no config, just force the user to go through initial setup
        if self.config['url'] is None:
            return

        try:
            self.stacks.get_root()
            status_code = 200
            self.validated = (200 <= status_code <= 299)
        except ConnectionError:
            print(self.colorize(
                "Unable to connect to {0}".format(self.config["url"]),
                "red"))
            raise

        if self.validated:
            print(self.colorize(
                "Config loaded and validated", "blue"))
            self.has_public_key = self.stacks.get_public_key()
        else:
            print(self.colorize(
                "ERROR: Unable to validate config", "red"))
            self.has_public_key = None

    def _setprompt(self):

        Cmd.prompt = self.colorize(
            self.PROMPT.format(**self.config),
            "blue")

        if not self.validated and self.config['url'] is not None:
            print(self.colorize("""
##
## Unable to validate connection - one of several possibilities exist:
## If this is the first time you've fired this up, you need to run
## 'initial_setup' to configure your account details.  If you've already
## done that, there could be a network connection issue anywhere between
## your computer and your stackd.io instance,
## or your password may be incorrect, or ... etc.
##
                """,
                                "green"))

        if self.validated and not self.has_public_key:
            print(self.colorize(
                "## Your account is missing the public key, run 'bootstrap' to fix",
                "red"))

    def _print_summary(self, title, components):
        num_components = len(components)
        print("## {0} {1}{2}".format(
            num_components,
            title,
            "s" if num_components == 0 or num_components > 1 else ""))

        for item in components:
            print("- Title: {0}\n  Description: {1}".format(
                item.get("title"), item.get("description")))

            if "status_detail" in item:
                print("  Status Detail: {0}\n".format(
                    item.get("status_detail")))
            else:
                print("")

    def do_account_summary(self, args=None):
        """Get a summary of your account."""
        sys.stdout.write("Polling {0} ... ".format(self.config["url"]))
        sys.stdout.flush()

        public_key = self.stacks.get_public_key()
        formulas = self.stacks.list_formulas()
        blueprints = self.stacks.list_blueprints()
        stacks = self.stacks.list_stacks()

        sys.stdout.write("done\n")

        print("## Username: {0}".format(self.config["username"]))
        print("## Public Key:\n{0}".format(public_key))

        self._print_summary("Formula", formulas)
        self._print_summary("Blueprint", blueprints)
        self._print_summary("Stack", stacks)