#! /usr/bin/env python3
#
#  edge_cluster_arg_resolver.py
#
# Copyright 2022 VMware, Inc.  All rights reserved. -- VMware Confidential
# Description:  Provides class EdgeClusterArgResolver.
# Disabled: True
#

import logging
import requests

from vcf_utils.edge_cluster_constants import EdgeClusterConstants
from vcf_utils.sddc_mgr_rest import SddcMgrRest
from vcf_utils.vc_connection import VcConnection
from vcf_utils.vcf_creds import VcfCreds


logger = logging.getLogger(__name__)


class EdgeClusterArgResolver:
    """
    Provides common post-parse arg validation and initialization support
    for Edge cluster related scripts.
    """

    def __init__(self, ssoUser, ssoPassword, sddcMgrAddr,
                 edgeClusterName, workloadName=None,
                 edgeClusterArgName="--edge-cluster"):

        self.ssoUser = ssoUser
        self.ssoPassword = ssoPassword
        self.sddcMgrAddr = sddcMgrAddr
        self.sddcMgrRest = SddcMgrRest(sddcMgrAddr=sddcMgrAddr)
        self.edgeClusterName = edgeClusterName
        self.workloadName = workloadName
        self.edgeClusterArgName = edgeClusterArgName

        # Placeholder values. These are properly derived by calling the cited
        # methods in this class.

        # checkEdgeClusterArgs()
        self.vcfAllEdgeClustersMap = None
        self.edgeClustVcClusterIds = None
        self.workloadId = None

        # getServerCredentials()
        self.creds = None
        self.vcConn = None
        self.nsxSession = None

        return

    def getOptQuotedName(self, name):
        """Return input name, adding surrounding quotes iff the name
        includes any spaces.
        """
        if ' ' in name:
            return f"'{name}'"

        return name

    def _getAllEdgeClustersInventory(self):
        """Get VCF inventory of all known edge clusters. This includes
        edge clusters from all workload domains, and also those which
        have no WLD affiliation noted in inventory.
        """
        queryUrl = EdgeClusterConstants.VCF_EDGE_CLUST_INV_LIST_URL
        return self.sddcMgrRest.runSddcMgrRestGet(queryUrl)

    def getEdgeClusterNames(self):
        """Return a list of known edge cluster names. The list is all in a
        single line (no line breaks), but any names including spaces are
        also quoted.
        """
        quotedNames = []
        for name in self.vcfAllEdgeClustersMap.keys():
            quotedNames.append(self.getOptQuotedName(name))

        return ", ".join(quotedNames)

    def _getAllWldsInventory(self):
        """Get all workload domain records from VCF inventory.
        """
        queryUrl = "http://localhost/inventory/domains"
        return self.sddcMgrRest.runSddcMgrRestGet(queryUrl)

    def _getWorkloadVcClusterIds(self, workloadId):
        """Obtain the given workload's detailed inventory record, and
        return the VCF IDs of all the VC clusters used by this workload.
        """
        queryUrl = ("http://localhost/inventory/domains/%s/inventory" %
                    workloadId)
        wlInfo = self.sddcMgrRest.runSddcMgrRestGet(queryUrl)
        wlVcClIds = [cl["id"] for cl in wlInfo["clusters"]]
        return wlVcClIds

    def checkEdgeClusterArgs(self):
        """Verify that user-supplied Edge cluster args as given to our
        constructor are acceptable.
        """
        vcfAllEdgeClusters = self._getAllEdgeClustersInventory()

        self.vcfAllEdgeClustersMap = (
            {cl["name"]: cl for cl in vcfAllEdgeClusters})

        if not self.edgeClusterName:
            logger.error("Please specify an Edge cluster with {}".format(
                self.edgeClusterArgName))
            logger.error("{} may select one of the following Edge clusters:"
                         .format(self.edgeClusterArgName))
            logger.error("  %s" % self.getEdgeClusterNames())
            return False

        vcfAllWorkloadDomains = self._getAllWldsInventory()
        vcfAllWldsMap = {wld["name"]: wld for wld in vcfAllWorkloadDomains}

        # if user specified a workload, make sure it exists:
        if self.workloadName and self.workloadName not in vcfAllWldsMap:
            logger.error("Unknown workload name '%s'" %
                         self.workloadName)
            logger.error("Workloads are: %s" %
                         ", ".join(vcfAllWldsMap.keys()))
            return False

        # User specified a particular edge cluster name. Verify that it is in
        # VCF inventory.
        if self.edgeClusterName not in self.vcfAllEdgeClustersMap:
            logger.error("Unknown edge cluster name. Please choose one of:")
            logger.error("  %s" % self.getEdgeClusterNames())
            return False

        # Edge cluster is in our inventory. If it doesn't have VC cluster
        # info, then ensure that both --workload and --force-cluster are
        # also specified.
        edgeClustVcClusterIds = (
            self.vcfAllEdgeClustersMap[self.edgeClusterName]["clusterIds"])

        # (also save a copy in instance data for later use)
        self.edgeClustVcClusterIds = edgeClustVcClusterIds

        if not edgeClustVcClusterIds:
            # no VC cluster, and thus no WLD affiliation. Require the
            # --workload option.
            # (This is for now-ancient 4.0 bug 2543467.)
            if not self.workloadName:
                logger.error("Inventory data for cluster %s" %
                             self.edgeClusterName)
                logger.error("does not include its workload domain info.")
                logger.error("Please provide the correct workload name using "
                             "--workload.")
                logger.error("Workload name may be one of these values:")
                logger.error("  %s" % ", ".join(vcfAllWldsMap.keys()))
                return False

            self.workloadId = vcfAllWldsMap[self.workloadName]["id"]

        else:
            # edge cluster has VC cluster(s) listed.

            # If --workload is also
            # given, verify that they agree.
            if self.workloadName:
                wldVcClusterIds = self._getWorkloadVcClusterIds(
                    vcfAllWldsMap[self.workloadName]["id"])
                # (checking a single VC cluster is sufficient, since workloads
                #  never share clusters)
                if edgeClustVcClusterIds[0] not in wldVcClusterIds:
                    logger.error(
                        "Edge cluster %s is not part of workload %s" %
                        (self.edgeClusterName, self.workloadName))
                    return False

            else:
                # Infer workload name from edge cluster's own VC cluster(s).
                # (checking a single VC cluster is sufficient, since workloads
                #  never share clusters)
                inferredWldName = None
                for wld in vcfAllWorkloadDomains:
                    wldVcClusterIds = self._getWorkloadVcClusterIds(wld["id"])
                    if edgeClustVcClusterIds[0] in wldVcClusterIds:
                        inferredWldName = wld["name"]
                        break

                if not inferredWldName:
                    logger.error("Edge cluster %s's VC cluster %s" %
                                 (self.edgeClusterName,
                                  edgeClustVcClusterIds[0]))
                    logger.error("Not found in any workload.")
                    logger.error("Please contact your support representative.")
                    return False

                self.workloadName = inferredWldName

            self.workloadId = vcfAllWldsMap[self.workloadName]["id"]

        return True

    def getServerCredentials(self, ssoPassword):
        """
        Get basic credentials for SDDC Manager, NSX Manager and vCenter,
        and establish connections to NSX Manager and vCenter.
        """

        if (not self.workloadName) or (not self.workloadId):
            logger.error("Internal error: workload params not set")
            return False

        logger.info("Getting credentials from SDDC Manager...")
        self.creds = VcfCreds(workload=self.workloadName,
                              username=self.ssoUser,
                              password=ssoPassword,
                              sddcMgrAddr=self.sddcMgrAddr)
        logger.info("Credential retrieval completed.")

        if ((self.creds.wldsSupportedByOurNsxt > 1) and
                not self.edgeClusterName):
            logger.error("Workload's NSX-T Manager is shared with %s other "
                         "workload(s)." %
                         (self.creds.wldsSupportedByOurNsxt - 1))

            edgeClusterNames = self._getEdgeClusterNamesInWorkload()

            logger.error("Found %s clusters: %s" % (len(edgeClusterNames),
                                                    edgeClusterNames))

            logger.error(
                "To avoid ambiguity, please use the --edge-cluster option.")
            return False

        self.vcConn = VcConnection(self.creds.vcCred.ipAddr,
                                   self.creds.vcCred.username,
                                   self.creds.vcCred.password)
        logger.info("Connection established to vCenter at %s" %
                    self.creds.vcCred.ipAddr)

        self.nsxSession = requests.Session()
        self.nsxSession.auth = (self.creds.nsxtCred.username,
                                self.creds.nsxtCred.password)
        self.nsxSession.verify = False
        self.nsxMgrIp = self.creds.nsxtCred.ipAddr
        self.nsxUrlPrefix = f"https://{self.nsxMgrIp}"

        return True
