# Copyright (c) 2019-2020 VMware, Inc.  All rights reserved.
# -- VMware Confidential
# coding: utf-8

"""
Contains precheck functions to be carried out in
WCP AutoUpgrade scenario
"""
import os
import sys
import logging
import json

logger = logging.getLogger(__name__)

try:
    from . import utils
except Exception:
    import utils

rootDir = os.path.abspath(os.path.dirname(__file__))
sys.path.append(os.path.join(rootDir, '../../../', 'libs'))
sys.path.extend(os.environ['VMWARE_VAPI_PYTHONPATH'].split(':'))
from fss_utils import getTargetFSS
from cis.cisreglib import VmafdClient
from vmware.vapi.data.value import (BooleanValue)

sys.path.append(
    os.path.join(rootDir, '../../../', 'libs', 'resource-model-bindings'))
from com.vmware.cis.data import provider_client

from . import provider

_PropertyPredicate = provider_client.ResourceModel.PropertyPredicate
_Filter = provider_client.ResourceModel.Filter
_ComparisonOperator = _PropertyPredicate.ComparisonOperator
_SortCriterion = provider_client.ResourceModel.SortCriterion
_SortDirection = provider_client.ResourceModel.SortCriterion.SortDirection

UPDATE_CONF_FILE = "/etc/applmgmt/appliance/update.conf"


class QueryFilterData(object):
    """
    Holds data for a Provider filter query.
    """
    def __init__(self, query_spec, desc, validate_func=None):
        self.query_spec = query_spec
        self.desc = desc
        self.validate_func = validate_func


def get_clustermodel_query_spec(dp_obj):
    """
      Get DPQueryFilterTestData with a query spec to get all clusters
      filtered by license_expired.
    """
    license_predicate = dp_obj.create_predicate(
        _ComparisonOperator.EQUAL, 'license_expired', BooleanValue(False))
    filter_spec = dp_obj.create_filter(_Filter.LogicalOperator.AND,
                                       [license_predicate])
    query_spec = dp_obj.create_query_spec(
        ['com.vmware.wcp.ClusterModel'],
        ['@modelKey', '@type', "license_expired"], filter_spec)
    return QueryFilterData(
        query_spec,
        'Get clusters with active licenses.')


def get_valid_license_clusters():
    """
    gets a list of all the clusters in the current VC with a
    valid license.
    """
    logger.info("getting valid license clusters")
    valid_license_clusters = []
    try:
        vmafd_client = VmafdClient()
        hostname = vmafd_client.get_pnid()
        dp_obj = provider.DataProvider(hostname)
        result = get_clustermodel_query_spec(dp_obj)
        logger.info('Execute query to %s' % result.desc)
        res = dp_obj.query_data(result.query_spec)

        for items in res.items:
            prop_val = items.property_values[0]
            if prop_val.value not in valid_license_clusters:
                valid_license_clusters.append(prop_val.value)
        return valid_license_clusters

    except Exception as e:
        msg = "Failed to get list of clusters \
               with active licenses. Err {}".format(str(e))
        logger.info(msg)
        raise Exception(msg)


class precheckResult:
    """
    A data structure that holds state of WCP supervisor cluster after
    prechecks are perfored
    """
    def __init__(self):
        self.cluster = ''
        self.upgrade_state = ''
        self.cluster_version = ''
        self.compatibility = ''
        self.license_status = ''

    def __repr__(self):
        return self.__str__()

    def __str__(self):
        return str(self.__dict__)


def is_license_feature_available():
    """
    One of the WCP autoupgrade prechecks includes checking for the license
    status of WCP enabled clusters on VC setup.
    This license feature is available from version 7.0.1.00000 onwards.

    This function checks if the current setup is of version 70u1 or more. If
    it is of a lower version, license feature is not available and the function
    would  return False.
    If the current setup  version is higher than the threshold, function would
    return True as the license check can be carried out.
    """
    thresholdVersion = '7.0.1.00000'
    flag = True
    try:
        with open(UPDATE_CONF_FILE) as fp:
            setup_info = json.load(fp)
            installedVersion = setup_info['version']

        installedVersionList = installedVersion.split(".")
        thresholdVersionList = thresholdVersion.split(".")
        # Versioning format  : X.Y.Z.UPPHH
        # X.Y - Major release number reserved.
        # Z - Increments with Update release and resets with every Major
        # release.
        for i in range(len(installedVersionList)):
            if int(installedVersionList[i]) < int(thresholdVersionList[i]):
                flag = False
                return flag
        return flag
    except Exception as e:
        msg = "Failed to compare the setup version and target version." \
              "Err {}".format(str(e))
        logger.info(msg)
        raise e


def doAutoUpgradePrecheck(at_risk_clusters):
    """
    at_risk_clusters: all the clusters whose current version are not present
    in the incoming patch.
    So we need to check, if the version is present in wcp_versions.yaml
    as a value in "supported_upgrade_from" key. In which case these clusters
    can be autoupgraded to the next compatible version
    Prechecks include:
    1. Check for the WCP license state of the cluster
    2. Check if the cluster version is present in supported list of target
    """
    logger.info("in Autoupgrade Precheck function")
    list_of_objects = []
    valid_license_clusters = []

    # check for the source machine version
    # license status of wcp cluster will only be checked for setups 7.0u1
    # onwards, else all the clusters would be considered valid and further
    # prechecks would be carried on
    try:
        if is_license_feature_available():
            # Get all clusters in the setup with valid license
            valid_license_clusters = get_valid_license_clusters()
            logger.info("Clusters with valid license returned: %s",
                        valid_license_clusters)
        else:
            logger.info("the setup is of a lower version, license check would "
                        "not be valid in this case")
            # consider all WCP clusters as valid
            for clusters in at_risk_clusters:
                valid_license_clusters.append(clusters.cluster)
            logger.info("Clusters with valid license returned: %s",
                        valid_license_clusters)
    except Exception as e:
        msg = "Failed to get the list of clusters with valid licenses" \
              "Err {}".format(str(e))
        logger.info(msg)
        raise Exception(msg)

    # Get all auto upgrade compatible versions
    auto_upgr_compatible_vers = utils.upgrade_version_compatibility_check()
    logger.info("n-3 compatible versions returned: %s",
                auto_upgr_compatible_vers)
    for cluster in at_risk_clusters:
        obj = precheckResult()
        obj.cluster = cluster.cluster
        obj.upgrade_state = cluster.upgrade_state
        obj.cluster_version = cluster.cluster_version

        # check if the cluster has a valid license or not
        if cluster.cluster in valid_license_clusters:
            obj.license_status = True
        else:
            obj.license_status = False
        # check if cluster is included in the list of clusters
        # that are auto-upgrade compatible
        if utils.extractVersion(
                cluster.cluster_version) in auto_upgr_compatible_vers:
            # this is an N-3 compatible scenario, hence setting value as True
            obj.compatibility = True
        else:
            # this is an N-4 or more incompatible scenario, hence setting value
            # as False
            obj.compatibility = False

        list_of_objects.append(obj)

    logger.info("returning this", list_of_objects)
    return list_of_objects
