#
# Copyright 2019 VMware, Inc.  All rights reserved. -- VMware Confidential
#
import os
import tempfile
import logging
import shutil

from pyVim import sso  #pylint: disable=E0401

logger = logging.getLogger(__name__)

# Location of the CA bundle inside the VCSA
VC_CA_BUNDLE = "/etc/ssl/certs/"
SOLUTION_USER = "vpxd-extension"
DIR_PATH = "/tmp/hardware_component/"

class TokenGenerationError(Exception):
    pass

class TokenGenerator:
    def __init__(self, sts_url, cert_file, key_file):
        """
        :param sts_url: STS endpoint URL
        :type sts_url: str

        :param cert_file: Path to the certificate file
        :type cert_file: str

        :param key_file: Path to the certificate key file
        :type key_file: str
        """
        try:
            self.sso_stub = sso.SsoAuthenticator(sts_url)
        except Exception as e:  # pylint: disable=W0703
            logger.error(str(e))
            raise TokenGenerationError("Failed to "
                                       "create SsoAuthenticator stub")

        self.cert_file = cert_file
        self.key_file = key_file

    def get_token(self, delegatable=True):
        """
        Returns SAML token

        :param delegatable: Indicates if the token should be delegatable or not
        :type delegatable: bool

        :return: SAML token
        :rtype: string
        """
        try:
            token = self.sso_stub.get_hok_saml_assertion(
                                                    self.cert_file,
                                                    self.key_file,
                                                    delegatable=delegatable
            )
            return token
        except Exception as e:  # pylint: disable=W0703
            logger.error(str(e))
            raise TokenGenerationError("Failed to generate SAML token")

class KeyStoreException(Exception):
    pass

class CertificateStore:
    """
    Handles the storing of the the certificate and key files from VECS.
    """
    def __init__(self, storename=None):
        """
        Create an instance of a given CertificateStore

        :param storename: A name of the certificate store
        :type storename: str
        """
        self.storename = storename if storename else SOLUTION_USER
        self._key_file, self._cert_file = self._load_certs_from_vecs()

    def _load_certs_from_vecs(self):
        """
        Connects to the key store and loads the certificate and key.

        :return: File locations for the key and certificate file
        :rtype: tuple (str, str)
        """
        try:
            logger.info("Loading certificate and key for user '%s' from VECS",
                        self.storename)
            cert_file, key_file = None, None

            from identity.vmkeystore import VmKeyStore  # pylint: disable=E0401

            with VmKeyStore("VKS") as ks:
                ks.load(self.storename)
                cert = ks.get_certificate(self.storename)
                key = ks.get_key(self.storename)

            shutil.rmtree(path=DIR_PATH, ignore_errors=True)
            os.mkdir(path=DIR_PATH, mode=777)
            with tempfile.NamedTemporaryFile(delete=False, dir=DIR_PATH) as \
                    tempfp:
                tempfp.write(cert.encode('utf-8'))
                cert_file = tempfp.name

            with tempfile.NamedTemporaryFile(delete=False, dir=DIR_PATH) as\
                    tempfp:
                tempfp.write(key.encode('utf-8'))
                key_file = tempfp.name

            #logger.info("Key saved in file: %s", key_file)
            #logger.info("Certificate saved in file: %s", cert_file)

            return key_file, cert_file
        except Exception as e:
            logger.exception(str(e))
            raise KeyStoreException()

    def get_key_file(self):
        """
        Returns the file location of the key file

        :return: Key file location
        :rtype: str
        """
        return self._key_file

    def get_cert_file(self):
        """
        Returns the file location of the certificate file

        :return: Certificate file location
        :rtype: str
        """
        return self._cert_file


    def cleanup(self):
        """
        Deletes the stored key and certificate files
        """
        logger.info("Deleting key file: %s", self._key_file)
        os.remove(self._key_file)
        logger.info("Deleting cert file: %s", self._cert_file)
        os.remove(self._cert_file)
