# -*- coding: UTF-8 -*-
from com.huawei.ism.tlv.lang import UnsignedInt32
from com.huawei.ism.tool.devicepatch.utils import DevicePatchUtil
import os
import re
import shutil
from common import resourceParse
from common.constant import *
from common.baseFactory import log
from cbb.frame.context import contextUtil
from cbb.frame.rest.restUtil import Tlv2Rest
from cbb.frame.rest import restData
from common.baseFactory import deleteTempPatch
from cbb.frame.cli import cliUtil
from cbb.frame.base import config
from common.baseFactory import safeSleep
from cbb.frame.util.tar_util import decompress_tar_special_file


patchConfigKeyDict ={
    "2600 V3 for Video":"NoNAS",
    "2100 V3":"NoNAS",
    "2600 V3":"NAS",
    "2200 V3 8.000GB":"NoNAS",
    "2200 V3 16.000GB":"NAS",                        
    "2600 V3 Enhanced":"NAS",
    "2200 V3 Enhanced 8.000GB":"NoNAS",
    "2200 V3 Enhanced 16.000GB":"NAS",
}

def isSpecialPkgVersion(devType, productVersion):
    typeEnum = ['2200 V3', "2100 V3", '2600 V3', '2600 V3 for Video', '2200 V3 Enhanced', '2600 V3 Enhanced']
    need_unzip_least_version = 'V300R006C00SPC100'
    if devType in typeEnum and "V300" in productVersion and productVersion >= need_unzip_least_version:
        return True
    return False

def getPatchPKGSubPatchPath(dataDict, filePath):
    """
    功能说明：解析补丁包，判断是否为多补丁压缩包（是否存在 version.conf文件），如果则获取阵列数字版本号，若数字版本号与version.conf匹配则返回子补丁文件路径
   输入：上下文，多补丁压缩包路径。
    @return: (falg, Val)
        flag:
            True: 包括不需要处理的版本  +  空字符串， 或正确解析出阵列匹配的子补丁路径
            False: 补丁文件格式错误  +  错误消息资源ID
    """
    subPatchPath = ''
    packageValid = False
    versionConf = 'version.conf'
    context = dataDict.get("context")
    

    #限定特定版本和设备类型
    dev = dataDict.get('dev')
    deviceSN = str(dev.getDeviceSerialNumber())
    devType = str(dev.getDeviceType())
    productVersion = dev.getProductVersion()
    log.info(dataDict, 'devType=%s,productVersion=%s'%(devType, productVersion))
    if not isSpecialPkgVersion(devType, productVersion):
        return (True, "")

    #如果路径存在则删除，用于补丁路径修改路径
    patchPathKey = 'patch_dir_%s'%deviceSN
    deleteTempPatch(dataDict)
            
    extractPath = filePath[0: filePath.index('.tgz')]
    extractPath = "%s_%s" %(extractPath,deviceSN)
    context[patchPathKey] = extractPath
    log.info(dataDict, 'extractPath=%s' % extractPath)
    if os.path.isdir(extractPath):
        try:
            shutil.rmtree(extractPath, ignore_errors=True)
        except Exception as e:
            log.error(dataDict,'failed=%s' % unicode(e))
        

    try:
        #查看热补丁包是否有效
        # 普通热补丁包：patch.conf文件在第一层压缩目录下
        packageValid, _ = decompress_tar_special_file(filePath, extractPath, versionConf)
        safeSleep(2)
        #补丁包无效，直接返回为空
        if not packageValid:
            log.warn(dataDict, 'not find version.conf, its not a mult hotpatch')
            return (True, "")
        packageValid = False
        log.info(dataDict, 'find %s' % versionConf)
        
        retGetCacheCapacity = (True,"")
        patchConfigKey = ""
        if devType in patchConfigKeyDict.keys():
            patchConfigKey = patchConfigKeyDict.get(devType)
        else:
            retGetCacheCapacity = getCacheCapacity(dataDict)
            if not retGetCacheCapacity[0]:
                return (False, "HotPatchCheck.notpass.multpkgGetCacheCapacitySupportedErr")
            patchConfigKey = patchConfigKeyDict.get("%s %s"%(devType,retGetCacheCapacity[1]))
        
        if not patchConfigKey:
            log.info(dataDict, 'No matching to patch information; devType=[%s],CacheCapacity=[%s].'%(devType,retGetCacheCapacity[1]))
            return (False, "HotPatchCheck.notpass.multpkgGetCacheCapacitySupportedErr")

        #解析补丁配置文件
        confFile = os.path.join(extractPath,versionConf)
        if len(confFile) > config.PATCH_PATH_SAFE_LEN - config.PATCH_PKG_NAME_LEN:
            return (False, "HotPatchCheck.notpass.multpkgetPathTooLongErr")
        conFileFd = open(confFile, "r")
        confInfo = conFileFd.read()
        #关闭文件句柄
        conFileFd.close()
        lineList = confInfo.splitlines()

        #从配置文件中查找版本号是否与产品版本匹配
        patchFileTmp = ""
        for line in lineList:

            field = line.split()
            if len(field) < 2:
                continue
            #获取版本配置信息
            if field[0] == "PatchConfig":
                pconfigArr = field[1].split(':')
                if len(pconfigArr) < 2:
                    continue
                isNAS = pconfigArr[0]
                patchPkgName = pconfigArr[1]
                patchConfigKeyTmp = str(isNAS).strip()
                if patchConfigKeyTmp == patchConfigKey:
                    patchFileTmp = str(patchPkgName).strip()
                    break

        if patchFileTmp == "":
            return (False, "HotPatchCheck.notpass.multpkSysVerNotMatchErr")
        log.info(dataDict, 'find patchFileTmp=%s' % patchFileTmp)

        #解压补丁文件
        # 普通热补丁包：patch.conf文件在第一层压缩目录下
        packageValid, _ = decompress_tar_special_file(filePath, extractPath, patchFileTmp)
        if packageValid:
            subPatchPath = os.path.join(extractPath, patchFileTmp)
            if len(subPatchPath) > config.PATCH_PATH_SAFE_LEN:
                return False, "HotPatchCheck.notpass.multpkgetPathTooLongErr"
        if subPatchPath == "":
            return (False, "HotPatchCheck.notpass.multpkSysVerNotMatchErr")
    except Exception, e:
        packageValid = False
        log.error(dataDict,'failed=%s' % unicode(e))
        return (False, "HotPatchCheck.notpass.multpkErr")
    finally:
        if not packageValid:
            try:
                shutil.rmtree(extractPath, ignore_errors=True)
            except Exception, e:
                log.error(dataDict,'failed=%s' % unicode(e))
        try:
            if conFileFd:
                conFileFd.close()
        except Exception, e:
            log.error(dataDict,'failed=%s' % unicode(e))
        
    log.info(dataDict, 'find subPatchPath=%s' % subPatchPath)
    return (True,subPatchPath)

# *************************************************************#
# 函数名称: getCacheCapacity
# 功能说明: 获取设备内存规格
# 其 他   :  无
# *************************************************************#  
def getCacheCapacity(dataDict):
    cli = dataDict["ssh"]
    cmd = 'show controller general |filterColumn include columnList=Cache\sCapacity'
    excuteRet = cliUtil.excuteCmdInCliMode(cli,cmd,True)
    log.info(dataDict, 'excuteRetMsg=%s' % excuteRet[2])
    cliRet = excuteRet[1]
    cliRetLinesList = cliRet.splitlines()
    cacheCapacityList = []
    
    for line in cliRetLinesList:
        fields = line.split(":")
        if len(fields) < 2:
            continue
        
        fieldName = fields[0].strip()
        fieldValue = fields[1].strip()
        
        if fieldName == "Cache Capacity":
            cacheCapacity = fieldValue
            cacheCapacityList.append(cacheCapacity)
            
    if len(cacheCapacityList) == 0:
        log.info(dataDict, "cannot get cacheCapacity info.")
        return (False, cliRet)
    
    if len(set(cacheCapacityList)) != 1:
        return (False, cliRet)
    
    return (True, cacheCapacityList[0])
    


# *************************************************************#
# 函数名称: getHotPatchPKGVersion
# 功能说明: 解压热补丁包的配置文件，获取热补丁包版本号配套关系
# 其 他   :  无
# *************************************************************#  
def getPatchPKGVersion(data_dict, filePath):
    """
    功能说明：解析补丁包，获取补丁包类型、热补丁版本、热补丁B版本、异构补丁名称和异构补丁版本
   输入：补丁包路径。
  输出：bool解析成功结果False/True，dict获取的补丁包信息(patchType、hotPatchPkgVersion、SysVersionBSupported、AslName、AslVersion)
    """
    packageVersion = {}
    packageValid = False

    extractPath = filePath[0: filePath.index('.tgz')]
    
    try:
        # 查看热补丁包是否有效
        # 补丁包无效，直接返回为空
        packageValid, _ = decompress_tar_special_file(filePath, extractPath, 'patch.yml')
        if not packageValid:
            log.warn(data_dict, 'not find patch.yml, its not a mult hotpatch')
            return False, "", "", ""
    
        #解析补丁配置文件
        confFile = extractPath + os.sep + 'patch.yml'
        conFileFd = open(confFile, "r")
        confInfo = conFileFd.read()
        #关闭文件句柄
        conFileFd.close()
        lineList = confInfo.splitlines()
        
        #获取配置文件中的热补丁版本号
        for line in lineList:
    
            field = line.strip().split(":")
            if len(field) < 2:
                continue
            #获取版本配置信息
            if field[0] == "patch_version":
                packageVersion['hotPatchPkgVersion'] = field[1].strip()
            elif field[0] == "PATCHTYPE":
                packageVersion['patchType'] = field[1]
            elif "spc" in field[0].lower():  # 获取spc版本，补丁匹配的设备版本号
                packageVersion['SysVersionBSupported'] = field[1].strip()
            elif field[0] == 'Asl_Version':
                packageVersion['AslVersion'] = field[1]
            elif field[0] == 'Asl_Name':
                packageVersion['AslName'] = field[1]
            else:
                continue
    except Exception as e:
        log.error(data_dict, 'parse patch.yml config happened exception, failed={}'.format(str(e)))
        packageValid = False
    finally:
        shutil.rmtree(filePath[0:-4], ignore_errors=True)
    
    return (packageValid,packageVersion)

def getHotPatchCurVersion(dataDict):
    """
    功能说明：查询当前阵列已安装的热补丁版本号
    输入：工具框架上下文
    返回：bool查询成功结果False/True，str热补丁版本号
    """
    rest = contextUtil.getRest(dataDict)
    params = [] 
    param0 = (restData.Upgrade.LstVer.CMO_VER_PACKAGE_TYPE, UnsignedInt32(TLV_PACKAGE_TYPE.HOT_PATCH_PKG))
    params.append(param0)
    recs = Tlv2Rest.execCmd(rest, restData.TlvCmd.OM_MSG_OP_LST_VER, params)
    rec = recs[0]
    log.info(dataDict,'TLV cmd [%d] send[%s] receive[%s]'%(restData.TlvCmd.OM_MSG_OP_LST_VER.get('cmd'),str(params),str(rec)))
    curHotPatchVersion = Tlv2Rest.getRecordValue(rec, restData.Upgrade.LstVer.CMO_VER_CUR_VERSION)
    if not curHotPatchVersion:
        log.error(dataDict,'Get curHotPatchVersion failed')
        return (True,'--')
    return (True,curHotPatchVersion)
    
def getAslPatchCurVersion(dataDict):
    """
    功能说明：查询当前阵列已安装的异构补丁版本号
    输入：工具框架上下文
    返回：bool查询成功结果False/True，str异构补丁版本号
    """
    curAslPatchVersions = ''
    rest = contextUtil.getRest(dataDict)
    
    params = [] 
    param0 = (restData.Upgrade.LstVer.CMO_VER_PACKAGE_TYPE, UnsignedInt32(TLV_PACKAGE_TYPE.ASL_PATCH_PKG))
    params.append(param0)
    recs = Tlv2Rest.execCmd(rest, restData.TlvCmd.OM_MSG_OP_LST_VER, params)
    log.info(dataDict,'TLV cmd [%d] send[%s] receive[%s]'%(TLV_CMD.OM_MSG_OP_LST_VER,str(params),str(recs)))
    itemNum = recs.size()
    for index in range(0,itemNum):
        curAslPatchVersioninfo = recs.get(index)
        if index == 0:
            result = Tlv2Rest.getRecordValue(curAslPatchVersioninfo, restData.Upgrade.LstVer.CMO_VER_PACKAGE_TYPE)
            log.info(dataDict,'getAslPatchCurVersion result:'+str(result))
            if result != 0:
                log.error(dataDict,'Get AslPatchCurVersion failed   result:'+ str(result))
                return (True,curAslPatchVersions)
        else:
            AslPatchName = Tlv2Rest.getRecordValue(curAslPatchVersioninfo, restData.Upgrade.LstVer.CMO_VER_CUR_VERSION)
            log.info(dataDict,'AslPatchName:'+AslPatchName)
            AslPatchVersion = Tlv2Rest.getRecordValue(curAslPatchVersioninfo, restData.Upgrade.LstVer.CMO_VER_SAVED_VERSION)
            log.info(dataDict,'AslPatchVersion:'+AslPatchVersion)
            curAslPatchVersions += AslPatchName + ' ' + AslPatchVersion + '; '
    return (True,curAslPatchVersions)

# *************************************************************#
# 函数名称: execute
# 功能说明: 解析热补丁包所在路径，返回字典：热补丁版本号+对应的文件名(入口函数)
# 其 他   :  无
# *************************************************************#
def execute(dataDict):
    """
          函数名称: execute
         功能说明: 解析热补丁包所在路径，返回字典：补丁包版本号+当前阵列补丁版本号(入口函数)
        其 他   :  无
    """
    lang = dataDict.get("lang")
    hot_patch_tgz_pkg_name = dataDict['packagePath']
    device_sn = str(dataDict.get('dev').getDeviceSerialNumber())
    check_dict = {}
    # 解析resource文件并保存
    resource = resourceParse.execute(lang)
    dataDict["resource"] = resource
    if not os.path.isfile(hot_patch_tgz_pkg_name):
        log.error(dataDict, 'patchPath is follow')
        log.error(dataDict, hot_patch_tgz_pkg_name)
        return False, resource.get('upload.pkgPathAbnormality'), check_dict

    # 检查是否为多补丁压缩包，判断是否存在 version.conf 文件
    package_path_key = "{}_packagePath".format(device_sn)
    context = dataDict.get("context")
    context[package_path_key] = ""  # 初始化为空
    get_sub_patch_ret = getPatchPKGSubPatchPath(dataDict, hot_patch_tgz_pkg_name)
    if get_sub_patch_ret[0]:
        if len(get_sub_patch_ret[1]) > 1:
            hot_patch_tgz_pkg_name = get_sub_patch_ret[1]
            context[package_path_key] = hot_patch_tgz_pkg_name
    else:
        errMsgID = get_sub_patch_ret[1]
        log.info(dataDict, "not a valid mul hotPatchPackge: %s" % errMsgID)
        return False, (resource.get(errMsgID)), check_dict
    log.info(dataDict, "hotPatchTgzPkgName = %s" % hot_patch_tgz_pkg_name)

    # 获取补丁包信息
    get_patch_version_ret = getPatchPKGVersion(dataDict, hot_patch_tgz_pkg_name)
    package_valid = get_patch_version_ret[0]
    if not package_valid:
        log.error(dataDict, 'Patch package invalid')
        return False, resource.get('upload.pkgInvalid'), check_dict

    package_version = get_patch_version_ret[1]
    pkg_version = package_version.get('hotPatchPkgVersion')
    patch_type = package_version.get('patchType')
    pkg_system_spc_ver_comp = package_version.get('SysVersionBSupported')
    log.info(dataDict, "package_version:{} .".format(package_version))
    # 判断补丁类型（热补丁、异构补丁）如果为空则为热补丁
    if not patch_type:
        patch_type = PATCH_TYPE.HOT_PATCH
        flag, result_info = _get_pkg_system_spc_version(dataDict, package_version, resource)
        if not flag:
            return False, result_info, check_dict
        pkg_system_spc_ver_comp = result_info
        # 校验patch_version
        if not pkg_version:
            return False, resource.get('upload.pkgAbnormality'), check_dict

    elif patch_type != PATCH_TYPE.ASL_PATCH:
        log.error(dataDict, u'获取补丁包类型异常   PATCH_TYPE:{}'.format(patch_type))
        errMsg = resource.get('upload.pkgTypeAbnormality')
        return False, errMsg, check_dict

    # 查询当前阵列安装的补丁版本号
    cur_patch_version = ''
    hot_patch_pkg_version = ''
    asl_version = package_version.get('AslVersion')
    asl_name = package_version.get('AslName')
    if patch_type == PATCH_TYPE.HOT_PATCH:
        ret = getHotPatchCurVersion(dataDict)
        if not ret[0]:
            log.error(dataDict, 'gethotpatchversion failed')
        else:
            cur_patch_version = ret[1]
        log.info(dataDict, "pkg_system_spc_ver_comp:{} pkg_version: {}.".format(pkg_system_spc_ver_comp, pkg_version))
        hot_patch_pkg_version = _get_hot_patch_pkg_version(pkg_system_spc_ver_comp, pkg_version)
    else:
        if not asl_name or not asl_version:
            errMsg = resource.get('upload.pkgAbnormality')
            return False, errMsg, check_dict
        ret = getAslPatchCurVersion(dataDict)
        if not ret[0]:
            log.error(dataDict, 'getaslpatchversion failed')
        else:
            cur_patch_version = ret[1]
        hot_patch_pkg_version = asl_name + ' ' + asl_version

    # 将补丁类型放入框架公共字典
    context["patchType_{}".format(device_sn)] = patch_type
    # 填充返回字典
    check_dict = {"hotPatchVersion": hot_patch_pkg_version, 'curPatchVersion': cur_patch_version}
    # 检查结果返回
    return True, '', check_dict


def _get_pkg_system_spc_version(data_dict, package_version, resource):
    """
        获取补丁配置文件中的spc_versison
        1、如果spc_version不存在，提示用户手工确认补丁是否匹配，
        yes:继续流程，No：返回终止，提示用户更换补丁包重新载入。
        2、spc_version存在，获取对应的VRC_version.VxxxRxxxCxx
        如果返回True，及 VRC_version, False ,报错信息。
    """
    pkg_system_spc_ver_comp = package_version.get('SysVersionBSupported')
    # 检查是否存在spc_version参数。
    if pkg_system_spc_ver_comp is None:
        # 当前补丁中确实缺少对应的设备版本,当前补丁是否配对将在补丁包导入流程校验，是否继续打补丁？
        choose = DevicePatchUtil.showYesNoDialog(data_dict["loadPatchPathScene"],
                                                 resource.get('upload.check.pkgversino.match'),
                                                 resource.get("upload.check.pkgversino.yes"),
                                                 resource.get("upload.check.pkgversino.no"))
        log.info(data_dict, "[check patch package] User choose: {}.".format(choose))
        if not choose:
            return False, resource.get('upload.check.pkgversino.not.match.suggestion')
        pkg_system_spc_ver_comp = ""
    sys_version_regex = re.compile(r'^V\d{0,3}R\d{0,3}C\d{0,2}|\d+.\d+.\w+')
    temp_result = sys_version_regex.search(pkg_system_spc_ver_comp)
    if temp_result is None:
        pkg_system_spc_ver_comp = ""
    else:
        pkg_system_spc_ver_comp = temp_result.group()
    return True, pkg_system_spc_ver_comp


def _get_hot_patch_pkg_version(pkg_system_spc_ver_comp, pkg_version):
    """
    DoradoV6的数字版本拼结果为6.0.1.SPH25
    非数字版本为V500R007C70SPH203
    设备版本为“”的结果为 SPH9
    """
    digital_version_regex = re.compile(r"^\d+.\d+.\w+")
    if digital_version_regex.search(pkg_system_spc_ver_comp):
        return ".".join([pkg_system_spc_ver_comp, pkg_version])
    return pkg_system_spc_ver_comp + pkg_version
