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

"""
@file: multi_patch_packages.py
@function: 安装多补丁包
"""
import os
import shutil
import tarfile
import time

from common.baseFactory import log, threadUp
from common import resourceParse
from cbb.frame.util.tar_util import UnZipLimit, get_safe_entry_name


TWO_PATCH_INTERVAL_TIME = 60
PATCH_CONFIG_FILE_NAME = "patch.conf"
FORBID_UPDATE_PROGRESS_KEY = "forbidProgress"
PACKAGE_END_NAME_KEYs = ["-1.tgz", "-2.tgz"]
MULTI_PATCH_DORADO_VERSIONS = ["V300R001C01SPC100"]
MULTI_PATCH_MIX_VERSIONS = ["V300R003C00SPC100", "V300R003C10SPC100",
                            "V300R002C10SPC100", "V300R002C10SPC200"]


class PatchMultiPackagesManager(object):

    def __init__(self, context, file_path):
        self._file_path = file_path
        self._context = context
        self._child_directory = file_path[:file_path.index('.tgz')]
        self._child_patch_package_paths = list()
        self._had_patch_config_file = False
        if self._is_multi_patch_version():
            self._decompression_package(file_path)

    def _decompression_package(self, file_path):
        limit = UnZipLimit()
        with tarfile.open(file_path, "r:gz") as tar_obj:
            for child_file in tar_obj:
                if child_file.name == PATCH_CONFIG_FILE_NAME:
                    self._had_patch_config_file = True
                child_path = self._child_directory + os.sep + child_file.name
                limit.increase_one_file_num()
                find_num = len(self._child_patch_package_paths)
                if find_num >= len(PACKAGE_END_NAME_KEYs):
                    break
                if child_file.name.endswith(PACKAGE_END_NAME_KEYs[find_num]):
                    self._child_patch_package_paths.append(child_path)
                    log.info(self._context, "find file:{}".format(child_path))
                    limit.increase_file_size(child_file.size)
                    tar_obj.extract(get_safe_entry_name(child_file.name),
                                    self._child_directory)
                if limit.is_file_stat_over_limit():
                    break

    def _is_multi_patch_version(self):
        dev_node = self._context.get('dev')
        dev_type = dev_node.getDeviceType().toString()
        dev_version = dev_node.getProductVersion()
        if "dorado" in dev_type.lower():
            return dev_version in MULTI_PATCH_DORADO_VERSIONS
        return dev_version in MULTI_PATCH_MIX_VERSIONS

    def is_multi_patch_package(self):
        return self._is_multi_patch_version() and not \
            self._had_patch_config_file and len(PACKAGE_END_NAME_KEYs) == len(
            self._child_patch_package_paths)

    def is_valid_multi_package(self):
        return len(self._child_patch_package_paths) > 1

    def get_last_patch_package(self):
        if len(self._child_patch_package_paths) == 0:
            raise Exception("not have child packages")
        return self._child_patch_package_paths[-1]

    def get_first_patch_package(self):
        if len(self._child_patch_package_paths) == 0:
            raise Exception("not have child packages")
        return self._child_patch_package_paths[0]

    @property
    def child_patch_package_paths(self):
        return self._child_patch_package_paths

    def del_child_directory(self):
        shutil.rmtree(self._child_directory, ignore_errors=True)

    def is_forbid_progress(self):
        return FORBID_UPDATE_PROGRESS_KEY in self._context

    def forbid_progress(self):
        self._context[FORBID_UPDATE_PROGRESS_KEY] = True

    def _get_resource_msg(self, msg_key):
        resource_key = "resource"
        if resource_key not in self._context:
            lang = self._context.get('lang', "en")
            resource = resourceParse.execute(lang)
            self._context[resource_key] = resource
        return self._context.get(resource_key).get(msg_key)

    def install_multi_packages(self, upload, upgrade, check_patch, upgrade_total_time):
        """
        安装多补丁包
        :param upload: 上传补丁包函数
        :param upgrade: 安装补丁包函数
        :param check_patch: 补丁检查的execute函数
        :param upgrade_total_time: 安装补丁包界面显示预估时间
        :return: 安装结果：True/False, 错误信息
        """
        if not self.is_valid_multi_package():
            log.error(self._context, 'Patch multi package invalid')
            return False, self._get_resource_msg('upload.pkgInvalid')
        threadUp(self._context, "upgrade", upgrade_total_time *
                 len(self.child_patch_package_paths))
        self.forbid_progress()

        log.info(self._context, '1 patch package upgrade')
        flag, err_msg = upgrade(self._context)
        if not flag:
            return False, u"Patch {}:\n{}".format(1, err_msg)

        patch_funcs = (upload, check_patch, upgrade)
        for index in range(1, len(self.child_patch_package_paths)):
            log.info(self._context, 'Patch multi package sleep')
            time.sleep(TWO_PATCH_INTERVAL_TIME)
            self._context['packagePath'] = self.child_patch_package_paths[
                index]
            for patch_func in patch_funcs:
                log.info(self._context, "[The {} patch package]: {}".format(
                    index + 1, str(patch_func)))
                result = patch_func(self._context)
                if not result[0]:
                    return False, u"Patch {}:\n{}".format(index + 1, result[1])
        return True, ""
