# -*- coding: UTF-8 -*-

import time
import traceback

from java.lang import Exception as JException
from com.huawei.ism.tool.protocol.tlv.exception import TLVException

from common import resourceParse
from common import constant
from common.patch_service import RunHelper, RestService
from common.baseFactory import log, finishProcess, setProgress, set_zone_progress
from cbb.frame.base import baseUtil
from cbb.frame.context import contextUtil
from cbb.frame.rest.restUtil import Tlv2Rest
from cbb.frame.rest import restData
from cbb.business.checkitems.nginx_service_status_check import sph7_nginx_service_check
from cbb.business.checkitems.UpgradeHotPatchSet import UpgradeHotPatchSet

UPGRDE_PROGRESS_ITEM = "upgrade"

CHECK_RESULT = []


def execute(dataDict):
    """
    功能说明：
    """
    try:
        lang = dataDict['lang']
        """解析resource文件并保存"""
        resource = resourceParse.execute(lang)
        dataDict["resource"] = resource
        log.info(dataDict, "execute upgrade")

        set_result = UpgradeHotPatchSet(dataDict).execute()
        if not set_result[0]:
            return False, set_result[1]

        # 升级前检查状态是否允许升级
        check_list = check_upgrade_state(dataDict)
        if not check_list[0]:
            return False, check_list[1]

        # 执行升级
        process_ret = upgrade(dataDict)
    finally:
        finishProcess(dataDict, UPGRDE_PROGRESS_ITEM)
    return process_ret


def roll_back(data_dict):
    try:
        lang = data_dict['lang']
        """解析resource文件并保存"""
        resource = resourceParse.execute(lang)
        data_dict["resource"] = resource
        log.info(data_dict, "execute upgrade")

        set_result = UpgradeHotPatchSet(data_dict).execute()
        if not set_result[0]:
            return False, set_result[1]

        data_dict["flowchoice"] = 3
        data_dict["model_type"] = 7
        # 执行升级
        return upgrade(data_dict)
    except Exception:
        log.error(data_dict, "execute upgrade failed." + traceback.format_exc())
        return False, resource.get("upgrade.faild")
    finally:
        finishProcess(data_dict, UPGRDE_PROGRESS_ITEM)


def upgrade(data_dict):
    """
    功能说明：执行升级并轮询升级结果
    输入：工具框架上下文
    输出：bool执行结果False/True，str错误信息
    """
    resource = data_dict["resource"]
    try:
        # 执行补丁安装命令。
        RestService(data_dict).execute_upgrade_patch()
        time.sleep(10)
        dev_type = str(data_dict.get("dev").getDeviceType())
        if dev_type == "OceanStor A800":
            flag, err_msg = query_a800_check_result(data_dict)
        else:
            flag, err_msg = query_check_result(data_dict)
        if not flag:
            return False, err_msg
    except:
        log.error(data_dict, "upgrade error!" + str(traceback.format_exc()))
        return False, resource.get("upgrade.faild")

    # 补丁安装成功的场景检查nginx服务
    flag, err_msg = sph7_nginx_service_check(data_dict, True)
    if not flag:
        log.error(data_dict, "check nginx service error..")
        return False, err_msg
    wait_for_swm_normal(data_dict)
    return True, ''


def query_a800_check_result(data_dict):
    """
    轮询A800查询补丁安装的进度和详情。
    :param data_dict: 上下文
    :return: 安装结果，错误信息。
    """
    resource = data_dict["resource"]
    poll_count = 0  # 轮询计数器
    status = None
    # 产品BUG会导致补丁安装状态从10跳变到0，工具每10s查询一次，查到0了也要继续
    upgrade_status_time = 0
    global CHECK_RESULT
    err_msg = ""
    while constant.OM_MSG_OP_UPD_LST_SYS_PROGRESS.UPD_UPD_SUCCESS != status:
        try:
            poll_count = poll_count + 1
            if poll_count > constant.UPGRADE_PACKAGE_POLL_TIMES:
                return False, resource.get("upgrade.faild")
            # 查询升级进度
            recs = RestService(data_dict).query_upgrade_patch_process()
            if data_dict.get("model_type", 5) == 7:
                recs = filter_install_node(data_dict, recs)
            result = []
            query_a800_upgrade_result(data_dict, result, recs, upgrade_status_time)
            # 所有zone结束安装，结束轮询。
            log.info(data_dict, "a800 upgrade result status is {}".format(result))
            if len(result) == len(recs):
                return is_all_success(result), ""
            baseUtil.safeSleep(constant.UPGRADE_PACKAGE_POLL_INTERVAL)
        except (JException, Exception) as e:
            log.error(data_dict, "device upgrade query progress error" + str(e))
            # 第一个原子执行时间较长，会抛出该错误码，继续执行查询
            if isinstance(e, TLVException) and constant.OM_OPERATE_FAIL_CODE == e.getErrorId():
                continue
            flag, err_msg = RunHelper(data_dict).check_dev_reboot()
            if flag:
                continue
            if not err_msg:
                err_msg = resource.get("upgrade.rebooterr")
            return False, err_msg
        finally:
            finish_zone_process(data_dict, err_msg)
    return True, ""


def filter_install_node(data_dict, recs):
    result = []
    for zone_info in recs:
        log.info(data_dict, "upgrade patch zone process:" + str(zone_info))
        sys_run_mode = Tlv2Rest.getRecordValue(zone_info, restData.Upgrade.UpdLstSysProgress.CMO_UPD_SYS_MODE)
        # 补丁回滚场景，过滤为操作为补丁安装的zone。
        if sys_run_mode == 5:
            continue
        result.append(zone_info)
    return result


def is_all_success(result):
    return all(zone_status == constant.OM_MSG_OP_UPD_LST_SYS_PROGRESS.UPD_UPD_SUCCESS for zone_status in result)


def query_check_result(data_dict):
    """
    轮询查询补丁安装的进度和详情。
    :param data_dict: 上下文
    :return: 安装结果，错误信息。
    """
    resource = data_dict["resource"]
    poll_count = 0  # 轮询计数器
    status = None
    # 产品BUG会导致补丁安装状态从10跳变到0，工具每10s查询一次，查到0了也要继续
    upgrade_status_time = 0
    while constant.OM_MSG_OP_UPD_LST_SYS_PROGRESS.UPD_UPD_SUCCESS != status:
        try:
            poll_count = poll_count + 1
            if poll_count > constant.UPGRADE_PACKAGE_POLL_TIMES:
                return False, resource.get("upgrade.faild")
            # 查询升级进度
            recs = RestService(data_dict).query_upgrade_patch_process()
            if recs[0]:
                status = set_process(data_dict, recs[0])
            # 升级状态检查
            if constant.OM_MSG_OP_UPD_LST_SYS_PROGRESS.UPD_READY == status:
                upgrade_status_time += 1
                log.info(data_dict, "upgrade_status_time:" + str(upgrade_status_time))
            if not RunHelper.check_upgrade_status_is_upgrade(status, upgrade_status_time):
                return False, query_upgrade_detail(data_dict, resource, status)
            baseUtil.safeSleep(constant.UPGRADE_PACKAGE_POLL_INTERVAL)
        except (JException, Exception) as e:
            log.error(data_dict, "device upgrade query progress error" + str(e))
            # 第一个原子执行时间较长，会抛出该错误码，继续执行查询
            if isinstance(e, TLVException) and constant.OM_OPERATE_FAIL_CODE == e.getErrorId():
                continue
            flag, err_msg = RunHelper(data_dict).check_dev_reboot()
            if flag:
                continue
            if not err_msg:
                err_msg = resource.get("upgrade.rebooterr")
            return False, err_msg
    return True, ""


def finish_zone_process(data_dict, err_msg):
    if not CHECK_RESULT:
        log.info(data_dict, "query zone failed CHECK_RESULT:" + str(CHECK_RESULT))
        return
    if not err_msg:
        return
    for process in CHECK_RESULT:
        process["status"] = constant.OM_MSG_OP_UPD_LST_SYS_PROGRESS.UPD_UPD_FAIL
        process["progress"] = 99
        process["err_msg"] = err_msg
    set_zone_progress(data_dict, CHECK_RESULT, UPGRDE_PROGRESS_ITEM)


def query_a800_upgrade_result(data_dict, result, recs, upgrade_status_time):
    """
    查询A800设备补丁安装的进度及详细请
    """
    resource = data_dict["resource"]
    for zone_info in recs:
        log.info(data_dict, "upgrade patch zone process:" + str(zone_info))
        sys_run_mode = Tlv2Rest.getRecordValue(zone_info, restData.Upgrade.UpdLstSysProgress.CMO_UPD_SYS_MODE)
        # 补丁回滚场景，过滤为操作为补丁安装的zone。
        if data_dict.get("model_type", 5) == 7 and sys_run_mode == 5:
            continue
        status = Tlv2Rest.getRecordValue(zone_info, restData.Upgrade.UpdLstSysProgress.CMO_UPD_SYS_STEP_TASK_STATUS)
        remain_time = Tlv2Rest.getRecordValue(zone_info, restData.Upgrade.UpdLstSysProgress.CMO_UPD_SYS_STEP_REMAINTIME)
        progress = Tlv2Rest.getRecordValue(zone_info, restData.Upgrade.UpdLstSysProgress.CMO_UPD_SYS_STEP_STEP_PERCENT)
        zone_id = Tlv2Rest.getRecordValue(zone_info, restData.Upgrade.UpdLstSysProgress.CMO_UPD_SYS_ZONE_ID)
        log.info(data_dict, "set zone process start.zone_id:{},progress:{}".format(zone_id, progress))
        progress_dict = {"zone_id": zone_id, "status": status, "progress": progress, "remain_time": remain_time}
        if not RunHelper.check_upgrade_status_is_upgrade(status, upgrade_status_time):
            log.info(data_dict, "upgrade patch zone status:" + str(status))
            result.append(status)
            err_msg = query_upgrade_detail(data_dict, resource, status)
            progress_dict["err_msg"] = err_msg
        if status == constant.OM_MSG_OP_UPD_LST_SYS_PROGRESS.UPD_UPD_SUCCESS:
            result.append(status)
        update_zone_process(progress_dict)
    # 刷新进度信息
    check_res = CHECK_RESULT
    set_zone_progress(data_dict, check_res, UPGRDE_PROGRESS_ITEM)


def update_zone_process(progress_dict):
    for i, process in enumerate(CHECK_RESULT):
        if progress_dict.get("zone_id") == process.get("zone_id"):
            CHECK_RESULT[i] = progress_dict
            return
    CHECK_RESULT.append(progress_dict)


def query_upgrade_detail(data_dict, resource, status):
    """
       查询升级详情
    """
    dev_type = contextUtil.getDevObj(data_dict).get("type", "")
    upgrade_res = baseUtil.UpgradeRes(dev_type)
    log.info(data_dict, "system status is not in upgrading, status is:" + str(status))
    if not RunHelper.check_upgrade_status_is_normal(status):
        log.info(data_dict, "system status is not normal.")
    # 查询升级详细信息
    recs = RestService(data_dict).query_upgrade_detail()
    err_msg = ""
    for rec in recs:
        node_id = Tlv2Rest.getRecordValue(rec, restData.Upgrade.UpdListDetaiInfo.NODE_ID)
        name_key = Tlv2Rest.getRecordValue(rec, restData.Upgrade.UpdListDetaiInfo.NAME_KEY)
        item_state = Tlv2Rest.getRecordValue(rec, restData.Upgrade.UpdListDetaiInfo.ITEM_STATE)
        error_key = Tlv2Rest.getRecordValue(rec, restData.Upgrade.UpdListDetaiInfo.ERROY_KEY)
        if item_state != '3':
            continue

        if not error_key:
            err_msg += "%s--%s--%s\n" % (node_id, upgrade_res.get_res(name_key), upgrade_res.get_res('failed'))
            continue

        error_key_list = error_key.split(":")
        error_key_list_len = len(error_key_list)
        error_key = error_key_list[0]
        try:
            if error_key_list_len >= 2:
                first_comma_loc = error_key.find(":")  # 第一个冒号前为key值，后面为错误详情参数，此方法避免参数中存在冒号
                msg_key = error_key[:first_comma_loc]
                err_param = error_key[first_comma_loc + 1:]  # 冒号之后为错误参数信息
                parse_error_info_ret = RunHelper.parse_atom_info(err_param)
                log.info(data_dict, "msgKey: %s parseErrorInfoRet:%s" % (msg_key, parse_error_info_ret))
                err_msg += upgrade_res.get_res(msg_key) % parse_error_info_ret + '\n'
            else:
                err_msg += upgrade_res.get_res(error_key) + '\n'
        except:
            log.error(data_dict, "parse atom info failed:%s" % err_msg)
            err_msg = resource.get("upgrade.faild")
    return err_msg


def set_process(data_dict, rec):
    """
    设置补丁安装的进度
    """
    log.info(data_dict, "upgrade process:" + str(rec))
    status = Tlv2Rest.getRecordValue(rec, restData.Upgrade.UpdLstSysProgress.CMO_UPD_SYS_STEP_TASK_STATUS)
    remain_time = Tlv2Rest.getRecordValue(rec, restData.Upgrade.UpdLstSysProgress.CMO_UPD_SYS_STEP_REMAINTIME)
    progress = Tlv2Rest.getRecordValue(rec, restData.Upgrade.UpdLstSysProgress.CMO_UPD_SYS_STEP_STEP_PERCENT)
    setProgress(data_dict, progress, remain_time, UPGRDE_PROGRESS_ITEM)
    return status


def check_upgrade_state(data_dict):
    """
        功能说明：升级前检查状态是否允许升级
        输入：工具框架上下文
        输出：bool检查结果False/True，str错误信息
    """
    rest = contextUtil.getRest(data_dict)
    lang = data_dict.get('lang')
    """解析resource文件并保存"""
    resource = resourceParse.execute(lang)
    data_dict["resource"] = resource
    params = []
    dev_type = str(data_dict.get("dev").getDeviceType())
    if dev_type == "OceanStor A800":
        msg_param1 = (restData.Upgrade.UpdLstSysProgress.CMO_UPD_FLOW_ID, restData.Enum.UpgQueryFlowEnum.PATCH_INSTALL)
        params.append(msg_param1)
    # 查询状态
    recs = Tlv2Rest.execCmdJlist(rest, restData.TlvCmd.OM_MSG_OP_UPD_LST_SYS_PROGRESS, params,
                                 constant.CMD_DEFAULT_TIMEOUT)
    rec = recs[0]
    log.info(data_dict,
             'TLV cmd [%s] send[%s] receive[%s]' % (restData.TlvCmd.OM_MSG_OP_UPD_LST_SYS_PROGRESS, str([]), str(rec)))
    status = Tlv2Rest.getRecordValue(rec, restData.Upgrade.UpdLstSysProgress.CMO_UPD_SYS_STEP_TASK_STATUS)
    # 得到状态值

    log.info(data_dict, "current device status is:" + str(status))

    # 不能执行升级的状态有（升级中，回滚中，同步中）
    if status == constant.OM_MSG_OP_UPD_LST_SYS_PROGRESS.UPD_UPDING:  # 升级中判断
        return False, resource.get("upgrade.upding")
    elif status == constant.OM_MSG_OP_UPD_LST_SYS_PROGRESS.UPD_ROLLBACKING:  # 回滚中判断
        return False, resource.get("upgrade.rollbacking")

    return True, ""


def wait_for_swm_normal(data_dict):
    """
    V5R7C60SPC100基线版本没有swm补丁自升级机制，会在升级后1分钟重启升级进程，可能会导致检查项异常。
    :param data_dict: 上下文信息
    :return:None
    """
    dev_version = data_dict.get('dev').getProductVersion()
    log.info(data_dict, "[handle_4_special_ver] dev version:%s" % dev_version)
    if dev_version == constant.NEED_SLEEP_VERSION:
        baseUtil.safeSleep(constant.NEED_SLEEP_TIME)
