#!/usr/bin/env python3
# -*- coding:utf-8 -*-
try:
    import sys
    import os

    sys.path.append(sys.path[0] + "/../../../../")
    from gspylib.common.ErrorCode import ErrorCode
    from gspylib.common.Common import DefaultValue, ClusterInstanceConfig, ConfigGUC
    from gspylib.component.Kernel.Kernel import Kernel
    from gspylib.common.DbClusterInfo import dbClusterInfo
    from gspylib.os.gsfile import g_file
    from gspylib.os.gsOSlib import g_OSlib
except ImportError as ie:
    sys.exit("[GAUSS-52200] : Unable to import module: %s." % str(ie))

METHOD_TRUST = "trust"
METHOD_SHA = "sha256"
MAX_PARA_NUMBER = 1000
INSTANCE_TYPE_UNDEFINED = -1
MASTER_INSTANCE = 0
STANDBY_INSTANCE = 1
DUMMY_STANDBY_INSTANCE = 2


class DN_OLAP(Kernel):
    '''
    The class is used to define base component.
    '''

    def getDnGUCDict(self):
        """
        function : get init DN install guc parameter
        input : String,String,String,int
        output : String
        """
        tmpDict = {"ssl": "on", "ssl_cert_file": "'server.crt'", "ssl_key_file": "'server.key'",
                   "ssl_ca_file": "'cacert.pem'"}
        return tmpDict

    def copyAndModCertFiles(self):
        """
        function : copy and chage permission cert files
        input : NA
        output : NA
        """
        toolspath = DefaultValue.getEnv(DefaultValue.TOOL_PATH_ENV)
        # cp cert files
        g_file.cpFile("%s/lib/sslcert/server.crt" % toolspath, "%s/" % self.instInfo.datadir)
        g_file.cpFile("%s/lib/sslcert/server.key" % toolspath, "%s/" % self.instInfo.datadir)
        g_file.cpFile("%s/lib/sslcert/cacert.pem" % toolspath, "%s/" % self.instInfo.datadir)
        g_file.cpFile("%s/lib/sslcert/server.key.cipher" % toolspath, "%s/" % self.instInfo.datadir)
        g_file.cpFile("%s/lib/sslcert/server.key.rand" % toolspath, "%s/" % self.instInfo.datadir)
        # change mode
        g_file.changeMode(DefaultValue.KEY_FILE_MODE, "%s/server.crt" % self.instInfo.datadir)
        g_file.changeMode(DefaultValue.KEY_FILE_MODE, "%s/server.key" % self.instInfo.datadir)
        g_file.changeMode(DefaultValue.KEY_FILE_MODE, "%s/cacert.pem" % self.instInfo.datadir)
        g_file.changeMode(DefaultValue.KEY_FILE_MODE, "%s/server.key.cipher" % self.instInfo.datadir)
        g_file.changeMode(DefaultValue.KEY_FILE_MODE, "%s/server.key.rand" % self.instInfo.datadir)

    def initInstance(self):
        """
        function:
            init DN instance
        input:string:NA
        output:
        """
        if not os.path.exists(self.instInfo.datadir):
            raise Exception(ErrorCode.GAUSS_502["GAUSS_50201"] % ("data directory [%s]" % self.instInfo.datadir))

        nodename = self.getInstanceNodeName()
        image_path = DefaultValue.DWS_IMAGE_PATH
        packageName = "%s/datanode.tar.gz" % image_path
        if self.dwsMode and os.path.exists(packageName):
            g_file.decompressFiles(packageName, self.instInfo.datadir)
            # set GUC parameter
            tmpDict = {"pgxc_node_name": "'%s'" % nodename}
            self.setGucConfig(tmpDict)
        else:
            cmd = "%s/gs_initdb --locale=C -D %s --nodename=%s %s -C %s" % \
                  (self.binPath, self.instInfo.datadir, nodename,
                   " ".join(self.initParas), self.binPath)
            self.logger.debug("Command for initializing DN instance: %s" % cmd)
            (status, output) = DefaultValue.retryGetstatusoutput(cmd)
            if (status != 0):
                raise Exception(ErrorCode.GAUSS_516["GAUSS_51615"] + " Command:%s. Error:\n%s" % (cmd, output))
        # Only use for single-inst, set ssl to dn nodes.
        if self.clusterType == DefaultValue.CLUSTER_TYPE_SINGLE_INST:
            dnGucParas = self.getDnGUCDict()
            self.setGucConfig(dnGucParas)
            self.copyAndModCertFiles()

    def getInstanceNodeName(self):
        """
        function: Get Instance Node Name
        input : NA
        output: instance node name
        """
        user = g_OSlib.getUserInfo()["name"]
        clusterInfo = dbClusterInfo()
        clusterInfo.initFromStaticConfig(user)
        peerInsts = clusterInfo.getPeerInstance(self.instInfo)
        if clusterInfo.isMasterStandbyCluster() or clusterInfo.isMasterStandbyMultiAZCluster():
            nodename = ClusterInstanceConfig.setReplConninfo(self.instInfo, peerInsts)[3]
        elif (clusterInfo.isSinglePrimaryMultiStandbyCluster() or
              (clusterInfo.isSingleInstCluster() and not clusterInfo.isHtapCluster())):
            nodename = \
                ClusterInstanceConfig.setReplConninfoForSinglePrimaryMultiStandbyCluster(
                    self.instInfo, peerInsts)[1]
        else:
            nodename = "dn_%d" % self.instInfo.instanceId

        return nodename

    def getDNDictForMultiStandby(self, peerInsts, azNames):
        """
        function: Get DN configuration for SINGLE_PRIMARY_MULTI_STANDBY or
                  SINGLE_INST
        input : peerInsts, azNames
        output: tmpDNDict
        """
        tmpDNDict = {"enable_data_replicate": "off", "replication_type": "1",
                     "max_replication_slots": "%d" % (len(peerInsts) + 4), "max_wal_senders": "8",
                     "application_name": "'dn_%s'" % self.instInfo.instanceId}

        if len(azNames) == 1 and len(peerInsts) <= 2:
            tmpDNDict["synchronous_standby_names"] = "'ANY 1(AZ1)'"
        elif len(azNames) == 1 and len(peerInsts) == 3:
            tmpDNDict["synchronous_standby_names"] = "'ANY 2(AZ1)'"
        elif len(azNames) == 2 and len(peerInsts) == 3:
            tmpDNDict["synchronous_standby_names"] = "'ANY 2(AZ1,AZ2)'"
        elif len(azNames) == 3 and len(peerInsts) == 3:
            tmpDNDict["synchronous_standby_names"] = "'ANY 2(AZ1,AZ2)'"
        elif len(azNames) == 3 and len(peerInsts) == 4:
            tmpDNDict["synchronous_standby_names"] = "'FIRST 2(AZ1,AZ2)'"
        elif len(azNames) == 3 and len(peerInsts) <= 7:
            tmpDNDict["synchronous_standby_names"] = "'FIRST 3(AZ1,AZ2)'"

        if self.clusterType == DefaultValue.CLUSTER_TYPE_SINGLE_INST:
            if len(peerInsts) == 1:
                tmpDNDict["most_available_sync"] = "on"
        return tmpDNDict

    def getDNDict(self, user, configItemType=None, peerInsts=None, azNames=None):
        """
        function: Get DN configuration
        input : NA
        output: NA
        """
        if peerInsts is None:
            peerInsts = []
        if azNames is None:
            azNames = []

        clusterInfo = dbClusterInfo()
        clusterInfo.initFromStaticConfig(user)

        tmpDNDict = {}
        if (clusterInfo.isSinglePrimaryMultiStandbyCluster() or
                (clusterInfo.isSingleInstCluster() and not clusterInfo.isHtapCluster())):
            tmpDNDict = self.getDNDictForMultiStandby(peerInsts, azNames)
        if clusterInfo.isHtapCluster():
            tmpDNDict["listen_addresses"] = "'localhost,%s'" % ",".join(self.instInfo.listenIps)
        else:
            tmpDNDict["listen_addresses"] = "'%s'" % ",".join(self.instInfo.listenIps)
        tmpDNDict["local_bind_address"] = "'%s'" % self.instInfo.listenIps[0]
        tmpDNDict["port"] = self.instInfo.port
        tmpDNDict["comm_control_port"] = str(self.instInfo.controlPort)
        tmpDNDict["comm_sctp_port"] = str(self.instInfo.sctpPort)

        if self.clusterType == DefaultValue.CLUSTER_TYPE_SINGLE or clusterInfo.isHtapCluster():
            tmpDNDict["replication_type"] = "2"

        if configItemType != "ChangeIPUtility":
            tmpDNDict["log_directory"] = "'%s/pg_log/dn_%d'" % \
                                         (DefaultValue.getUserLogDirWithUser(user),
                                          self.instInfo.instanceId)
            tmpDNDict["audit_directory"] = "'%s/pg_audit/dn_%d'" % \
                                           (DefaultValue.getUserLogDirWithUser(user),
                                            self.instInfo.instanceId)

        if len(self.instInfo.ssdDir) != 0 and configItemType != "ChangeIPUtility":
            tmpDNDict["ssd_cache_dir"] = "'%s'" % (self.instInfo.ssdDir)
            tmpDNDict["enable_adio_function"] = "on"
            tmpDNDict["enable_cstore_ssd_cache"] = "on"
        return tmpDNDict

    def getDummpyStandbyConfigItem(self):
        """
        function: get the parameter at dummyStandby instance. It only be used by DN instance.
        input : Inst, configFile
        output: NA
        """
        # only modify config item for dummpy standby instance
        tmpDNDict = {}
        if self.instInfo.instanceType == DefaultValue.DUMMY_STANDBY_INSTANCE:
            tmpDNDict = DefaultValue.getPrivateGucParamList()
        return tmpDNDict

    def getPrimaryStandyConnInfo(self, peerInsts):
        """
        function: get replconninfo for datanode
        input : peerInsts
        output: NA
        """
        connInfo2 = None
        dummyStandbyInst = None
        user = g_OSlib.getUserInfo()["name"]
        clusterInfo = dbClusterInfo()
        clusterInfo.initFromStaticConfig(user)
        tmpDict1 = {}
        tmpDict2 = {}
        if (clusterInfo.isSinglePrimaryMultiStandbyCluster() or
                clusterInfo.isSingleInstCluster()):
            (connInfo1, _) = \
                ClusterInstanceConfig.setReplConninfoForSinglePrimaryMultiStandbyCluster(
                    self.instInfo, peerInsts)
            for i in range(len(connInfo1)):
                connInfo = "replconninfo%d" % (i + 1)
                tmpDict1[connInfo] = "'%s'" % connInfo1[i]
        else:
            (connInfo1, connInfo2, dummyStandbyInst, _) = \
                ClusterInstanceConfig.setReplConninfo(self.instInfo, peerInsts)
            connInfo = "replconninfo1"
            tmpDict1[connInfo] = "'%s'" % connInfo1
        if dummyStandbyInst is not None:
            tmpDict2["replconninfo2"] = "'%s'" % connInfo2
        return {**tmpDict1, **tmpDict2}

    def config_dynamic_guc(self, clusterInfo, min_value, set_mode):
        """
        """
        dynamic_dict = ConfigGUC.dynamic_guc("dn", self.clusterType, clusterInfo, min_value, self.instInfo)
        if not dynamic_dict:
            raise Exception(ErrorCode.GAUSS_502["GAUSS_50219"] % dynamic_dict)
        self.logger.debug("set dynamic guc parameters for dn instances.")
        self.setGucConfig(dynamic_dict, set_mode)

    def configInstance(self, user, dataConfig, gtmList, peerInsts, configItemType=None,
                       alarm_component=None, azNames=None, clusterInfo=None):
        """
        peerInsts : peerInsts is empty means that it is a single cluster.
        """
        common_dict = self.setCommonItems()
        gtm_dict = {}
        if clusterInfo is None or (clusterInfo is not None and not clusterInfo.isSingleInstCluster()):
            gtm_dict = ClusterInstanceConfig.setGtmInfo(gtmList)
        tmp_dn_dict = self.getDNDict(user, configItemType, peerInsts, [] if azNames is None else azNames)
        tmp_dn_dict.update(dataConfig)
        tmp_dn_dict["alarm_component"] = "'%s'" % alarm_component

        primary_standy_dict = {}
        if len(peerInsts):
            primary_standy_dict = self.getPrimaryStandyConnInfo(peerInsts)
        else:
            primary_standy_dict["synchronous_commit"] = "off"
            if clusterInfo is not None and clusterInfo.isHtapCluster():
                primary_standy_dict["use_workload_manager"] = "off"
            else:
                primary_standy_dict["enable_stream_replication"] = "off"
        dummpy_standby_dict = self.getDummpyStandbyConfigItem()

        # merge all dict common_dict + gtm_dict + tmp_dn_dict + primary_standy_dict + dummpy_standby_dict
        # the merge function only spported by at least python 3.5
        real_dict = {**common_dict, **gtm_dict, **tmp_dn_dict, **primary_standy_dict, **dummpy_standby_dict}
        self.setGucConfig(real_dict)

    def setPghbaConfig(self, clusterAllIpList):
        """
        """
        principal = None
        if DefaultValue.checkKerberos(DefaultValue.getMpprcFile()):

            (status, output) = g_OSlib.getGrepValue("-Er", "^default_realm",
                                                    os.path.join(os.path.dirname(DefaultValue.getMpprcFile()),
                                                                 DefaultValue.FI_KRB_CONF))
            if status != 0:
                raise Exception(ErrorCode.GAUSS_502["GAUSS_50222"] % "krb5.conf" + "Error:\n%s" % output)
            principal = output.split("=")[1].strip()

        # build ip string list
        # Every 1000 records merged into one
        i = 0
        guc_paras_str = ""
        guc_paras_str_list = []
        for ipAddress in clusterAllIpList:
            i += 1
            # Set the initial user and initial database access permissions
            if principal is None:
                format_ip = ClusterInstanceConfig.getHbaItemForIP(ipAddress, METHOD_TRUST)
                guc_paras_str = "%s-h \"%s\" " % (guc_paras_str, format_ip)
            else:
                hba_item_ip = ClusterInstanceConfig.getHbaItemForIP(ipAddress,
                                                                    "gss    include_realm=1    krb_realm=%s" %
                                                                    principal)
                guc_paras_str = "%s-h \"%s\" " % (guc_paras_str, hba_item_ip)

            if i % MAX_PARA_NUMBER == 0:
                guc_paras_str_list.append(guc_paras_str)
                i = 0
                guc_paras_str = ""

        if guc_paras_str != "":
            guc_paras_str_list.append(guc_paras_str)

        for paras_str in guc_paras_str_list:
            self.doGUCConfig("set", paras_str, True)

    def fixPermission(self):
        pass

    def upgrade(self):
        pass

    def createPath(self):
        pass

    def postCheck(self):
        pass
