# -*- coding: UTF-8 -*-
# Copyright (c) Huawei Technologies Co., Ltd. 2019-2022. All rights reserved.

import re
import codecs
import shutil
import os

from cbb.frame.cli.cliUtil import get_system_version_with_ret
from cbb.frame.context import contextUtil
from cbb.business.collect import log_collector
from cbb.frame.base.product import isKunpeng
from cbb.frame.cli import cliUtil
from cbb.frame.util.tar_util import decompress_tar_all_file

from common.baseFactory import log

from com.huawei.ism.tool.obase.utils import ApplicationContext

# 当前日志
DEFAULT_TRANS_FILE_PATH = {
    'rasdaemon.log': '/OSM/log/cur_debug/rasdaemon.log',
}

# 历史日志
COLLECTED_FILES = {
    "rasdaemon_his": {
        "path": '/OSM/coffer_log/log/his_debug/rasdaemon/',
        "file_pattern": "rasdaemon"
    },
    "power_on_his": {
        "path": "/OSM/coffer_log/log/his_debug/log_debug/",
        "file_pattern": "his_tar"
    },
}


def execute(context):
    """
    控制器ras数量检查
    :param context: 上下文
    :return:
    """
    log_collect_context = get_context(context)
    lang = context.get("lang")
    ret_list = []
    try:
        flag, ret = is_risk_version(context)
        ret_list.append(ret)
        if not flag:
            return True, '', ret

        collected_failed_engines, export_files = log_collector.execute(log_collect_context)
        log.info(context, "failed_eng:{}, export_files:{}".format(
            collected_failed_engines, export_files))

        upper_limit_error_list, error_list = check_all_ctrl_ras_num(
            export_files, context, ret_list
        )
        all_ret = "\n".join(ret_list)
        err_msg = get_error_msg(
            lang, upper_limit_error_list, error_list)
        if err_msg:
            return False, err_msg, all_ret
        return True, '', all_ret
    except Exception as e:
        log.error(context, "check_ras_cnt_before_upgrade except:{}".format(
            str(e)))
        msg = cliUtil.getMsg(lang, "query.result.abnormal")
        return False, msg, '\n'.join(ret_list)
    finally:
        clean_up(context, log_collect_context)


def clean_up(context, log_collect_context):
    """
    回退到cli模式，清理残留文件
    :param context:
    :param log_collect_context:
    :return:
    """
    clear_temp_log_file(log_collect_context)
    cli = log_collect_context.get("cli")
    if cli:
        cliUtil.enterCliModeFromSomeModel(cli, context.get("lang"))


def clear_temp_log_file(log_collect_context):
    tmp_path = log_collect_context.get("log_path", '')
    shutil.rmtree(tmp_path, ignore_errors=True)


def get_error_msg(lang, upper_limit_error_list, error_list):
    total_err_list = upper_limit_error_list + error_list
    if upper_limit_error_list:
        return cliUtil.getMsg(
            lang, "ras.cnt.before.upgrade.upper.limit",
            (", ".join(total_err_list), ", ".join(upper_limit_error_list)))

    if error_list:
        return cliUtil.getMsg(
            lang, "ras.cnt.before.upgrade.not.pass",
            ", ".join(error_list))

    return ''


def is_risk_version(context):
    """
    是否风险版本
    :param context:
    :return: True 是风险版本，False 非风险版本
    """

    flag, product_version, patch_version, ret = get_system_version_with_ret(
        contextUtil.getCli(context), context.get("lang"))
    dev = context.get('dev')
    mem_version = str(dev.getProductVersion())
    if not isKunpeng(mem_version):
        return False, ret

    if product_version != 'V500R007C60SPC100':
        return False, ret

    if patch_version == '--':
        return True, ret

    regx = re.compile("SPH(\d+)")
    res = regx.findall(patch_version)
    if not res:
        return True, ret

    if int(res[0]) >= 106:
        return False, ret

    return True, ret


def is_target_version_valid(context):
    """
    目标补丁版本是否大于106
    :param context:
    :return:
    """
    target_patch_version = context.get("pkgVersion")
    regx = re.compile("SPH(\d+)")
    res_target = regx.findall(target_patch_version)
    if not res_target:
        return False

    if int(res_target[0]) >= 106:
        return True

    return False


def get_work_path():
    return ApplicationContext.getInstance().getWorkPath()


def get_tmp_log_path(context):
    return get_work_path() + "\\log_collect\\" + get_sn(context)


def get_sn(context):
    return context.get('dev').getDeviceSerialNumber()


def get_context(origin_context):
    """
    获取context
    :param origin_context:
    :return:
    """
    import com.huawei.ism.tool.obase.entity.EntityUtils as EntityUtils
    from com.huawei.ism.tool.bizpack.wizardparse.util import DevNodeUtil
    new_context = dict()
    new_context["logger"] = origin_context.get("logger")
    new_context["lang"] = origin_context.get("lang")
    dev_node = origin_context.get("dev")
    new_context['origin_context'] = origin_context
    new_dev_node = EntityUtils.toNewDev(dev_node)
    new_context['dev'] = DevNodeUtil.getDevMapDict(new_dev_node)
    new_context['devInfo'] = new_dev_node
    new_context['protocalContext'] = origin_context.get("protocalContext")
    new_context['cli'] = contextUtil.getCli(origin_context)
    new_context["log_path"] = get_tmp_log_path(origin_context)
    new_context["collected_files"] = COLLECTED_FILES
    new_context["default_trans_file_path"] = DEFAULT_TRANS_FILE_PATH
    return new_context


def check_all_ctrl_ras_num(
        export_files, context, ret_list):
    """
    检查所有控制器的ras数量
    :param ctrl_power_on_time_file_dict: 控制器启动时间列表
    :param export_files: 已导出的文件路径
    :param context: 上下文
    :param ret_list: 回文列表
    :return:
    """
    upper_limit_error_list = []
    error_list = []
    regex = re.compile("(\d{8}_\d{6})_poweron.tgz")
    for ctrl_name, res_list in export_files.items():
        latest_power_on_time = get_latest_power_on_time(res_list, regex)
        log.info(context,
                 "{}: latest_power_on_time:{}".format(ctrl_name, latest_power_on_time))
        if not latest_power_on_time:
            continue

        if not res_list:
            continue

        error_ras_count = 0
        for ctrl_file_path in res_list:
            error_ras_count += check_ras_event_num(
                ctrl_file_path, latest_power_on_time
            )
        ret_list.append("{}:{}".format(ctrl_name, error_ras_count))
        log.info(context, "{}: error ras count:{}".format(ctrl_name, error_ras_count))
        if error_ras_count == 0:
            continue

        if error_ras_count > 2000:
            upper_limit_error_list.append(ctrl_name)
            continue

        # ras num 小于等于2000，且目标版本是106,则检查通过，否则不通过。
        if is_target_version_valid(context):
            continue

        error_list.append(ctrl_name)

    return upper_limit_error_list, error_list


def get_latest_power_on_time(res_list, regex):
    """
    获取最新的开机时间
    :param res_list: 获取最新的时间
    :param regex:
    :return:
    """
    tmp_str = "".join(res_list)
    res = regex.findall(tmp_str)
    if not res:
        return None
    res.sort()
    return res[-1].replace("_", '')


def check_ras_event_num(file_path, filter_time):
    """
    检查文件中的ras 错误数量
    :param file_path:
    :param filter_time:
    :return:
    """
    if "rasdaemon" not in file_path:
        return 0
    if str(file_path).endswith(".tgz"):
        return check_ras_event_num_in_zip_file(file_path, filter_time)

    return check_ras_event_num_in_text_file(file_path, filter_time)


def check_ras_event_num_in_text_file(file_path, filter_time):
    """
    处理单个文件，解析mc_event:             2021-06-20 19:26:48
    :param file_path: 文件路径
    :param filter_time: 时间
    :return:
    """
    res = []
    with codecs.open(file_path, 'r', encoding='utf-8') as f:
        res = f.readlines()
    res_str = "".join(res)
    regex = re.compile("event:\s*(\d{4}-\d{2}-\d{2}\s*\d{2}:\d{2}:\d{2})")
    all_event = regex.findall(res_str)
    if not all_event:
        return 0
    return filter_event_time(all_event, filter_time)


def check_ras_event_num_in_zip_file(file_path, filter_time):
    """
    处理压缩文件
    :param file_path: 文件路径
    :param filter_time:
    :return:
    """
    target_dir = file_path.replace(".tgz", "")
    if not decompress_tar_all_file(file_path, target_dir):
        target_dir = ''
    error_ras_count = 0
    for f_path in os.listdir(target_dir):
        error_ras_count += check_ras_event_num_in_text_file(
            os.path.join(target_dir, f_path), filter_time
        )
    shutil.rmtree(target_dir, ignore_errors=True)
    return error_ras_count


def filter_event_time(all_event, filter_time):
    """
    过滤出满足时间的event
    :param all_event: 全部event数据
    :param filter_time:
    :return:
    """
    count = 0
    for time_str in all_event:
        time_str = time_str.replace("-", '').replace(" ", '').replace(":", '')
        if time_str >= filter_time:
            count += 1

    return count
