"""
Copyright 2020-2021 VMware, Inc.  All rights reserved. -- VMware Confidential
"""

import logging
import sys
from cis.cisreglib import (
    _get_syscfg_info, LookupServiceClient, SsoClient, AuthzClient,
    CisregOptionParser)
from vstats import utils
from cis.vecs import SsoGroup

sys.path.append('/usr/lib/vmware-vmafd/lib64')
import vmafdpy3

logger = logging.getLogger(__name__)
PATCH_VERSION=4

# vStatsGroup for SSO authentication.
VSTATS_GROUP = 'vStatsGroup'

# vStatsGroup description.
VSTATS_GROUP_DESCRIPTION = 'SSO group to access ESX stats apis'

# vStatsGroup role.
VSTATS_GROUP_ROLE = 'vStatsAdmin'

class SsoPatch:
    def __init__(self):
        vmafdClient = vmafdpy3.client('localhost')
        username = vmafdClient.GetMachineName()
        password = vmafdClient.GetMachinePassword()
        self.sso_group = SsoGroup(username, password)

    def ensure_groups_exist(self, group, description):
        """
        Tries to create the group specified if they do not exist.
        :param group:
        :param description:
        """
        logger.debug('Attempting to create group "%s"', group)
        if self.sso_group.create(group, description):
            logger.info('Added "%s" group', group)
        else:
            # SsoGroup.create returns False when group already exists
            logger.debug('Group "%s" already exists', group)


class AuthzPatch:
    def __init__(self):
        """
        Initializes AuthzClient needed for patching roles and permissions.
        """
        ls_url, self.domain_name = _get_syscfg_info()
        logger.debug("Connecting to Lookup Service, url=%s", ls_url)
        ls_obj = LookupServiceClient(ls_url, retry_count=1)
        sts_url, sts_cert_data = ls_obj.get_sts_endpoint_data()

        logger.debug("Logging into SSO AdminClient as machine solution user, "
                     "url=%s", sts_url)
        MACHINE_NAME = 'machine'
        self.sso_client = SsoClient.init_with_vecs(sts_url, sts_cert_data,
                                                   MACHINE_NAME, MACHINE_NAME)
        ls_obj.set_sso_client(self.sso_client)
        authz_url, _ = ls_obj.get_authz_endpoint_data()
        logger.debug("Connecting to Authz, url=%s", authz_url)
        self.authz_client = AuthzClient(authz_url, self.sso_client)
        logger.debug("Successfully initialized AuthzClient")


    def ensure_roles_exist(self, role_def_file):
        """
        Creates all roles defined in role_def_file.

        :param role_def_file: path to XML file containing role definitions.
        :type role_def_file: str
        """
        cisreg_options = {
            'permission.newRole': role_def_file
        }
        cisreg_optparser = CisregOptionParser(cisreg_options)
        roles = cisreg_optparser.get_roles()
        logger.debug("Ensuring roles exist: %s", roles)
        if len(roles) > 0:
            added = self.authz_client.load_roles(roles)
            logger.info("Successfully loaded authz roles, added %d new roles",
            added)

    def ensure_privileges_exist(self, privileges_def_file):
        """
        Creates all privileges defined in privileges_def_file.

        :param privileges_def_file: path to XML file containing role definitions.
        :type privileges_def_file: str
        """
        cisreg_options = {
            'permission.newPrivilege': privileges_def_file
        }
        cisreg_optparser = CisregOptionParser(cisreg_options)
        privsList = cisreg_optparser.get_privs()
        logger.debug("Ensuring privileges exist: %s", privsList)
        if len(privsList) > 0:
            self.authz_client.load_privs(privsList)
            logger.info("Successfully updated vStats privileges: %s" % privsList)

    def assign_groups_to_roles(self, group, role):
        """
        Assign given role to the group specified.
        :param group: Name of group
        :param role: Role
        :return: None
        """
        logger.debug("Creating global permission for group %s and role %s",
            group, role)
        # Last boolean argument in set_permission is "isGroup".
        self.authz_client.set_permission(
            self.domain_name, role, group, True)

def patch_sso() :
    # Verify that all the needed SSO groups exist.
    sso_patch = SsoPatch()
    sso_patch.ensure_groups_exist(VSTATS_GROUP, VSTATS_GROUP_DESCRIPTION)


def patch_authz():
    # Verify that all desired roles exist.
    authz_patch = AuthzPatch()
    authz_patch.ensure_privileges_exist('/usr/lib/vmware-vstats/config/privileges.xml')
    authz_patch.ensure_roles_exist('/usr/lib/vmware-vstats/config/roles.xml')

    # Verify that groups are mapped to roles.
    authz_patch.assign_groups_to_roles(VSTATS_GROUP, VSTATS_GROUP_ROLE)

def doPatching(ctx):
    logger.info("Applying patch_03 on vStats service")
    patch_sso()
    patch_authz()


def is_patch_needed(current_version):
    return utils.is_patch_needed(current_version, PATCH_VERSION)

def doExpand(ctx):
    logger.info("No expand required for version 3 on vstats service")

def doContract(ctx):
    logger.info("No contract required for version 3 on vstats service")
