from __future__ import print_function


#
# TODO THIS IS CODE FROM THE PREVIOUS CLOUDMESH, WE NEEED TO CLEAN AND POSSIBLY SIMPLIFY
#

# noinspection PyPep8Naming
class Key(ListResource):

    @classmethod
    def info(cls, **kwargs):
        raise NotImplementedError()

    def get_from_dir(cls, directory=None, store=True):
        """
        reads the key from ~/.ssh or a directory
        :param directory:
        :param store:
        :return:
        """
        directory = directory or Config.path_expand("~/.ssh")
        files = [file for file in os.listdir(expanduser(Config.path_expand(directory)))
                 if file.lower().endswith(".pub")]
        d = []
        for file in files:
            location = Config.path_expand("{:}/{:}".format(directory, file))

            sshkey = SSHkey(location).get()

            i = sshkey["comment"]
            if i is not None:
                i = i.replace("@", "_")
                i = i.replace("-", "_")
                i = i.replace(" ", "_")
                i = i.replace(".", "_")
            else:
                # use base name
                i = file.replace(".pub", "")
            sshkey["kind"] = "key"
            sshkey["source"] = 'file'

            if store:
                cls._add_from_sshkey(
                    dict(sshkey),
                    keyname=sshkey["name"],
                    source=sshkey["source"],
                    uri=sshkey["uri"])
            else:
                d.append(dict(sshkey))
        if not store:
            return d

    @classmethod
    def get_from_git(cls, username, store=True):
        """

        :param store:
        :param username: the github username
        :return: an array of public keys
        :rtype: list
        """
        uri = 'https://github.com/{:}.keys'.format(username)
        content = requests.get(uri).text.strip("\n").split("\n")

        d = []

        for key in range(0, len(content)):
            value = content[key]
            thekey = {}

            name = "{}_git_{}".format(username, key)

            thekey = {
                'uri': uri,
                'string': value,
                'fingerprint': SSHkey._fingerprint(value),
                'name': name,
                'comment': name,
                'cm_id': name,
                'source': 'git',
                'kind': 'key'
            }

            thekey["type"], thekey["key"], thekey["comment"] = SSHkey._parse(value)

            if thekey["comment"] is None:
                thekey["comment"] = name
            d.append(thekey)
            if store:
                try:

                    cls.cm.add(thekey)
                except:
                    Console.error("Key already in db", traceflag=False)
        if not store:
            return d
            # noinspection PyProtectedMember,PyUnreachableCode,PyUnusedLocal

    @classmethod
    def get_from_yaml(cls, filename=None, load_order=None, store=True):
        """
        :param store:
        :param load_order:
        :param filename: name of the yaml file
        :return: a SSHKeyManager (dict of keys)
        """
        config = None
        if filename is None:
            # default = Config.path_expand(os.path.join("~", ".cloudmesh", "cloudmesh.yaml"))
            # config = ConfigDict("cloudmesh.yaml")
            filename = "cloudmesh.yaml"
            config = ConfigDict(filename)
        elif load_order:
            config = ConfigDict(filename, load_order)
        else:
            Console.error("Wrong arguments")
            return
        config_keys = config["cloudmesh"]["keys"]
        default = config_keys["default"]
        keylist = config_keys["keylist"]

        uri = Config.path_expand(os.path.join("~", ".cloudmesh", filename))

        d = []
        for key in list(keylist.keys()):
            keyname = key
            value = keylist[key]
            if os.path.isfile(Config.path_expand(value)):
                path = Config.path_expand(value)
                if store:
                    Key.add_from_path(path, keyname)
                else:
                    d.append(Key.add_from_path(path, keyname, store=False))
            else:

                keytype, string, comment = SSHkey._parse(value)
                thekey = {
                    'uri': 'yaml://{}'.format(uri),
                    'string': value,
                    'fingerprint': SSHkey._fingerprint(value),
                    'name': keyname,
                    'comment': comment,
                    'source': 'git',
                    'kind': 'key'
                }

                thekey["type"], thekey["key"], thekey["comment"] = SSHkey._parse(value)

                if thekey["comment"] is None:
                    thekey["comment"] = keyname
                if store:
                    try:
                        cls.cm.add(thekey)
                    except:
                        Console.error("Key already in db", traceflag=False)
                else:
                    d.append(thekey)
        if not store:
            return d

        """
        take a look into original cloudmesh code, its possible to either specify a key or a filename
        the original one is able to figure this out and do the rightthing. We may want to add this
        logic to the SSHkey class, so we can initialize either via filename or key string.
        It would than figure out the right thing

        cloudmesh:
          keys:
            idrsa: ~/.ssh/id_rsa.pub

        cloudmesh:
        ...
          keys:
            default: name of the key
            keylist:
              keyname: ~/.ssh/id_rsa.pub
              keyname: ssh rsa hajfhjldahlfjhdlsak ..... comment
              github-x: github
        """

    @classmethod
    def get_from_cloud(cls, cloud, live=False, format="table"):
        """
        This method lists all keys of the cloud
        :param format:
        :param live:
        :param cloud: the cloud name
        :return: a SSHKeyManager (dict of keys)
        """
        try:
            return CloudProvider(cloud).provider.list_key(cloud)

        except Exception as ex:
            Console.error(ex.message)

    @classmethod
    def _add_from_sshkey(cls,
                         sshkey,
                         keyname=None,
                         user=None,
                         source=None,
                         uri=None):

        user = user or cls.cm.user

        if keyname is None:
            try:
                keyname = sshkey['name']
            except:
                pass
        if keyname is None:
            print("ERROR: keyname is None")

        thekey = {
            "kind": "key",
            "name": keyname,
            "uri": sshkey['uri'],
            "source": sshkey['source'],
            "fingerprint": sshkey['fingerprint'],
            "comment": sshkey['comment'],
            "value": sshkey['string'],
            "category": "general",
            "user": user}

        cls.cm.add(thekey)

    @classmethod
    def add_key_to_cloud(cls, user, keyname, cloud):
        """

        :param user:
        :param keyname:
        :param cloud:
        """

        key = cls.cm.find_image(kind="key", name=keyname, scope="first")
        if key is None:
            Console.error("Key with the name {:} not found in database.".format(keyname))
            return

        try:
            if cloud is not None:
                print("Adding key {:} to cloud {:}".format(keyname, cloud))
                cloud_provider = CloudProvider(cloud).provider
                cloud_provider.add_key_to_cloud(keyname, key["value"])
        except Exception as e:
            Console.error("problem uploading key {} to cloud {}: {}".format(keyname, cloud, e.message), traceflag=False)

    @classmethod
    def list(cls, category=None, live=False, output="table"):
        """this does not work only returns all ceys in the db"""
        (order, header) = CloudProvider(category).get_attributes("key")
        d = cls.cm.find_image(kind="key", scope="all", output=output)
        return Printer.write(d,
                             order=order,
                             header=header,
                             output=output)

    @classmethod
    def list_on_cloud(cls, cloud, live=False, format="table"):
        """
        This method lists all keys of the cloud
        :param format:
        :param live:
        :param cloud: the cloud name
        """
        try:
            keys = CloudProvider(cloud).provider.list_key(cloud)
            for key in keys:
                keys[key]["category"] = cloud
            if keys is None or keys is []:
                return None

            (order, header) = CloudProvider(cloud).get_attributes("key")

            return Printer.write(keys,
                                 order=order,
                                 header=header,
                                 output=format)
        except Exception as ex:
            Console.error(ex.message)

    @classmethod
    def run_command(cls, cmd):
        """ Runs a command in a shell, returns the result"""
        p = subprocess.Popen(cmd, shell=True,
                             stdout=subprocess.PIPE)
        return p.stdout.read()

    @classmethod
    def add_azure_key_to_db(cls, key_name, key_path, certificate_path, pfx_path):
        """
            Adds the public key to the existing database model and adds the certificate, key and
            fingerprint into the azure key database model.
        :param key_name: Key name to be added
        :param key_path: Public key path
        :param certificate_path: Certificate file path(PEM file)
        :param pfx_path: PKCS encoded certificate path
        :return:
        """
        pprint("add_azure_key_to_db")
        # Add to the current DB
        cls.add_from_path(key_path,
                          key_name,
                          source="ssh",
                          uri="file://" + key_path)

        # Add certificate to the new DB
        fingerprint_cmd = "openssl x509 -in " + certificate_path + " -sha1 -noout -fingerprint | sed s/://g"
        # print("fingerprint_cmd:", fingerprint_cmd)
        fingerprint = cls.run_command(fingerprint_cmd)
        fingerprint = fingerprint.split('=')[1]
        fingerprint = fingerprint.rstrip('\n')
        # pprint("Certificate Fingerprint="+fingerprint)
        key_azure_obj = {
            "kind": "key_azure",
            "name": key_name,
            "fingerprint": fingerprint,
            "certificate": certificate_path,
            "key_path": key_path,
            "pfx_path": pfx_path}
        cls.cm.add(key_azure_obj)
        Console.info("Azure key added.ok.")

    @classmethod
    def clear(cls, **kwargs):
        raise NotImplementedError()

    @classmethod
    def refresh(cls, **kwargs):
        raise NotImplementedError()

    @classmethod
    def delete(cls, name=None, cloud=None):

        if cloud is not None and name is not None:
            result = CloudProvider(cloud).provider.delete_key_from_cloud(name)
        elif cloud is not None and name is None:
            #
            # get a list of keys from cloud
            # loop over all keys and use the provider delete from cloud to delete that key

            Console.error("delete all keys from cloud not yet implemented")

        if name is None:
            cls.cm.delete(kind="key", provider="general")
        else:
            cls.cm.delete(name=name, kind="key", provider="general")

    @classmethod
    def all(cls, output="dict"):
        return cls.cm.find_image(kind="key", scope="all", output=output)

    @classmethod
    def find(cls, name=None, output="dict"):
        return cls.get(name=name, output=output)

    @classmethod
    def get(cls, name=None, output="dict"):
        """
        Finds the key on the database by name

        :param output:
        :param name: name of the key to be found
        :return: Query object of the search
        """
        if name is None:
            return cls.cm.find_image(kind="key", output=output)
        else:
            return cls.cm.find_image(kind="key", name=name, output=output, scope="first")

    @classmethod
    def set_default(cls, name):
        Default.set_key(name)

    # deprecated use Default.key
    @classmethod
    def get_default(cls):
        return Default.key

    @classmethod
    def delete_from_cloud(cls, name, cloud=None):
        pass

    @classmethod
    def _delete_from_db(cls, name=None):
        if name is None:
            cls.cm.delete(kind='key')
        else:
            cls.cm.delete(kind='key', name=name)

    @classmethod
    def select(cls):
        options = []
        d = cls.get(output='dict')
        for i in d:
            line = '{}: {}'.format(d[i]['name'], d[i]['fingerprint'])
            options.append(line)
        num = menu_return_num('KEYS', options)
        if num != 'q':
            return options[num]
        return num

    #
    # ADD
    #
    @classmethod
    def add_from_path(cls,
                      path,
                      keyname=None,
                      user=None,
                      source=None,
                      uri=None,
                      store=True):
        """
        Adds the key to the database based on the path

        :param path:
        :param keyname: name of the key or path to the key
        :param user:
        :param source:
        :param uri:
        :param store:
        :return:
        """

        user = user or cls.cm.user

        sshkey = SSHkey(Config.path_expand(path))

        if store:
            cls._add_from_sshkey(sshkey.__key__,
                                 keyname,
                                 user,
                                 source=source,
                                 uri=uri)
        else:
            return sshkey.__key__
