# -*- encoding=utf-8 -*-

"""

# **********************************************************************************

# Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.

# [openeuler-jenkins] is licensed under the Mulan PSL v2.

# You can use this software according to the terms and conditions of the Mulan PSL v2.

# You may obtain a copy of Mulan PSL v2 at:

#          http://license.coscl.org.cn/MulanPSL2

# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,

# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,

# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.

# See the Mulan PSL v2 for more details.

# Author:

# Create: 2023-07-18

# Description: Generate hot patch metadata files

# **********************************************************************************

"""

import argparse

import os

import re

import sys

import traceback

from xml.etree.ElementTree import Element

import xml.etree.ElementTree as ET

from xml.dom import minidom

from src.logger import logger

from src.proxy.gitee_proxy import GiteeProxy





def init_metadata_xml(filename):

    try:

        doc = minidom.Document()

        root = doc.createElement('hotpatchdoc')

        root.setAttribute('xmlns', meta_url)

        root.setAttribute('xmlns:cvrf', meta_url)

        doc.appendChild(root)

        with open(filename, 'w', encoding='utf-8') as xml_file:

            doc.writexml(xml_file, encoding="utf-8")

    except IOError:

        logger.error('init metadata.xml error: %s', traceback.format_exc())

        sys.exit(1)





def update_hotpatch(xml_root, hotpatch_issue):

    version, release = get_version(xml_root)

    hotpatch = Element('hotpatch')

    if mode == "ACC":

        hotpatch.attrib['version'] = version

        hotpatch.attrib['release'] = str(int(release) + 1)

    elif mode == "SGL":

        name = xml_root.find('.//hotpatch[@name="SGL-%s"]' % cve_issue_id)

        logger.warning("name=SGL-%s result:%s", cve_issue_id, name)

        if name:

            err_info = "修复issue:%s 的SGL补丁pr已经创建,请勿重复提交" % cve_issue_id

            comment_source_pr(err_info)

        hotpatch.attrib['name'] = "SGL-%s" % cve_issue_id

        hotpatch.attrib['version'] = "1"

        hotpatch.attrib['release'] = "1"



    hotpatch.attrib['type'] = patch_type

    hotpatch.attrib['inherit'] = "0"

    hotpatch.attrib['status'] = "unconfirmed"



    src_link = Element('SRC_RPM')

    src_link.text = src_url

    hotpatch.append(src_link)



    x86_debug_link = Element('Debug_RPM_X86_64')

    x86_debug_link.text = x86_url

    hotpatch.append(x86_debug_link)



    aarch64_debug_link = Element('Debug_RPM_Aarch64')

    aarch64_debug_link.text = arm_url

    hotpatch.append(aarch64_debug_link)



    for patch_file in args.patch.split(","):

        patch = Element('patch')

        patch.text = patch_file

        hotpatch.append(patch)



    issue_id = Element('issue')

    issue_id.attrib['id'] = cve_issue_id

    issue_id.attrib['issue_href'] = cve_issue_url

    hotpatch.append(issue_id)



    hotpatch_issue_link = Element('hotpatch_issue_link')

    hotpatch_issue_link.text = hotpatch_issue

    hotpatch.append(hotpatch_issue_link)



    p = xml_root.find('.//Package[@name="%s-%s"]' % (args.repo, repo_version))

    p.append(hotpatch)



    return xml_root





def new_metadata(xml_root, hotpatch_issue):

    logger.info("src_url=%s", src_url)

    logger.info("x86_url=%s", x86_url)

    logger.info("arm_url=%s", arm_url)



    DocumentTitle = Element('DocumentTitle')

    DocumentTitle.attrib['xml:lang'] = "en"

    DocumentTitle.attrib['type'] = mode



    DocumentTitle.text = "Managing Hot Patch Metadata"

    xml_root.append(DocumentTitle)



    HotPatchList = Element('HotPatchList')

    xml_root.append(HotPatchList)



    Package = Element('Package')

    Package.attrib['name'] = "%s-%s" % (args.repo, repo_version)

    HotPatchList.append(Package)



    hotpatch = Element('hotpatch')

    if mode == "SGL":

        hotpatch.attrib['name'] = "SGL-%s" % cve_issue_id



    hotpatch.attrib['version'] = "1"

    hotpatch.attrib['release'] = "1"

    hotpatch.attrib['type'] = patch_type

    hotpatch.attrib['inherit'] = "0"

    hotpatch.attrib['status'] = "unconfirmed"

    logger.info("patch_type=%s", patch_type)



    Package.append(hotpatch)



    src_link = Element('SRC_RPM')

    src_link.text = src_url

    hotpatch.append(src_link)



    x86_debug_link = Element('Debug_RPM_X86_64')

    x86_debug_link.text = x86_url

    hotpatch.append(x86_debug_link)



    aarch64_debug_link = Element('Debug_RPM_Aarch64')

    aarch64_debug_link.text = arm_url

    hotpatch.append(aarch64_debug_link)



    logger.info("patch=%s", args.patch)

    for patch_file in args.patch.split(","):

        patch = Element('patch')

        patch.text = patch_file

        logger.info("patch_file=%s", patch_file)

        hotpatch.append(patch)



    issue_id = Element('issue')

    issue_id.attrib['id'] = cve_issue_id

    issue_id.attrib['issue_href'] = cve_issue_url

    hotpatch.append(issue_id)

    logger.info("cve_issue_id=%s", cve_issue_id)

    logger.info("cve_issue_url=%s", cve_issue_url)



    hotpatch_issue_link = Element('hotpatch_issue_link')

    hotpatch_issue_link.text = hotpatch_issue

    hotpatch.append(hotpatch_issue_link)

    logger.info("hotpatch_issue_url=%s", hotpatch_issue)



    return xml_root





def get_version(xml_root):

    hotpatch = xml_root.iter('hotpatch')

    version = 0

    release = 0

    for child in hotpatch:

        version = child.attrib["version"]

        release = child.attrib["release"]



    logger.info("version: %s, release: %s", version, release)

    return version, release





def del_hotpatch(xml_root):

    hotpatch_issue_number = ''

    del_version, del_release = get_version(xml_root)

    Package = xml_root.find('.//Package[@name="%s-%s"]' % (args.repo, repo_version))

    if mode == "SGL":

        p = xml_root.find('.//hotpatch[@name="SGL-%s"][@version="%s"][@release="%s"]' % (

            cve_issue_id, del_version, del_release))

    else:

        p = xml_root.find('.//hotpatch[@version="%s"][@release="%s"]' % (del_version, del_release))



    issue_url = p.find('hotpatch_issue_link').text

    logger.info("hotpatch_issue_url exist: %s" % issue_url)

    Package.remove(p)

    if issue_url:

        hotpatch_issue_number = issue_url.split("/")[-1]

    return hotpatch_issue_number





def add_hotmetadata_xml():

    if os.path.exists(metadata_file):

        tree = ET.parse(metadata_file)

        root = tree.getroot()

        root = update_hotpatch(root, hotpatch_issue_url)

    else:

        init_metadata_xml(metadata_file)

        tree = ET.ElementTree(file=metadata_file)

        root = tree.getroot()

        root = new_metadata(root, hotpatch_issue_url)



    write_to_hotmetadata_xml(root)





def write_to_hotmetadata_xml(xml_root):

    """

    Convert the ElementTree into a formated string, and write it to the output path.

    """

    logger.info("---------------------")

    raw_text = ET.tostring(xml_root)

    dom = minidom.parseString(raw_text)

    pretty_text = dom.toprettyxml(indent='\t')

    new_pretty_text = re.sub('\n[\s|]*\n', '\n', pretty_text)

    with open(metadata_file, 'w') as meta_f:

        meta_f.write(new_pretty_text)





def get_issue_info(issue):

    enterprises = "open_euler"

    if patch_type not in ["cve", "bugfix", "feature"]:

        comment_source_pr("修复的问题类型%s不是支持的类型,可选类型[cve/bugfix/feature],请重新指定\n" % patch_type)



    resp = gp.get_issue(issue, enterprises)

    html_url = resp.get("html_url")

    if not resp:

        err_info = "获取issue: %s 报错,请检查该issue是否存在" % issue

        comment_source_pr(err_info)



    if patch_type == "cve":

        issue_id = resp.get("title")

        if not issue_id.lower().startswith("cve-"):

            command_format = r"""makerhotpatch命令格式如下:

```text

/makehotpatch <version> <mode> <patch_name> <patch_type> <issue_id> <os_branch>

version:    源码包版本号,必填

mode:       热补丁包演进方式<ACC/SGL>, 必填

patch_name: 冷补丁包名,支持多patch包按顺序传入,以逗号隔开,src-openeuler下的仓库必填;kernel仓库不用填,门禁会自动打包patch文件

patch_type: 修复的问题类型<cve/bugfix/feature>,必填

issue_id:   修复问题的issue id,必填

os_branch:  本次热补丁基于哪个分支做,必填

```"""

            comment_source_pr("issue %s 不是cve类型,请重新指定\n %s" % (issue, command_format))

    else:

        issue_id = issue

    return issue_id, html_url





def comment_source_pr(err_info):

    body_str = "In response to this:\n > %s  \n\n命令执行结果:\n %s" % (args.comment, err_info)

    gp.comment_pr(args.prid, body_str)

    logger.error(err_info)

    sys.exit(1)





def create_update_issue(number=""):

    owner = "openeuler"

    title = "[hotpatch][{}-{}]fix {}".format(args.repo, repo_version, cve_issue)

    if mode == "SGL":

        hotpatch_metadata = "{}/blob/master/{}/{}/{}/hotmetadata_SGL.xml".format(

            meta_url, args.branch, args.repo, repo_version)

    else:

        hotpatch_metadata = "{}/blob/master/{}/{}/{}/hotmetadata_ACC.xml".format(

            meta_url, args.branch, args.repo, repo_version)



    body = "问题类别:{}\n热补丁元数据:{}\n".format(patch_type, hotpatch_metadata)

    if args.func == "add":

        data = {"access_token": args.token, "repo": "hotpatch_meta", "title": title, "issue_type": "hotpatch",

                "body": body}

        resp = gp.create_issue(owner, data)

        if not resp:

            err_info = "创建热补丁issue失败,请重试"

            comment_source_pr(err_info)

    elif args.func == "update":

        data = {"access_token": args.token, "repo": "hotpatch_meta", "title": title, "body": body}

        resp = gp.update_issue(owner, data, number)

        if not resp:

            err_info = "更新热补丁issue:%s 失败,请重试" % number

            comment_source_pr(err_info)



    resp_issue_number = resp.get("html_url")

    logger.info("issue_number=%s", resp_issue_number)



    return resp_issue_number





def get_patch_list(metadata):

    patch_list = []

    patch = []

    with open(metadata, "r") as xml_file:

        try:

            lines = xml_file.readlines()

            package = lines[4].strip()

            patch_list.append(package)

            for line in lines[5:-3]:

                line = line.strip()

                if line.startswith("<hotpatch "):

                    patch = []

                    patch.append(line)

                elif line.startswith("</hotpatch>"):

                    patch.append(line.strip())

                    patch_list.append("\n".join(patch))

                else:

                    patch.append(line)

        except IndexError:

            logger.error('metadata.xml format error: %s', traceback.format_exc())

            sys.exit(1)



    logger.info("patch_list: %s", patch_list)

    return patch_list





def init_args():

    """

    init args

    :return:

    """

    parser = argparse.ArgumentParser(description='generate hotmetadata.xml')

    parser.add_argument("-c", type=str, dest="community", default="src-openeuler",

                        help="src-openeuler or openeuler")

    parser.add_argument("-t", type=str, dest="token", help="gitee api token")

    parser.add_argument("-r", type=str, dest="repo", help="repo name")

    parser.add_argument("-o", type=str, dest="output", help="output file to save result")

    parser.add_argument("-m", type=str, dest="comment", help="trigger comment")

    parser.add_argument("-b", type=str, dest="branch", help="src branch")

    parser.add_argument("-pr", type=str, dest="prid", help="src prid")

    parser.add_argument("-p", type=str, dest="patch", help="patch_list")

    parser.add_argument("-f", type=str, dest="func", help="add or update")

    parser.add_argument("-i", type=str, dest="hotpatch_file", help="hotpatch issue file")

    parser.add_argument("-l", type=str, dest="download_link", help="src and debuginfo download link")

    return parser.parse_args()





if __name__ == '__main__':

    args = init_args()

    # makehotpatch 命令解析

    if len(args.comment.split()) == 6:

        _, repo_version, mode, patch_type, cve_issue, os_branch = args.comment.split()

    elif len(args.comment.split()) == 7:

        _, repo_version, mode, patch_name, patch_type, cve_issue, os_branch = args.comment.split()

    else:

        logger.error(

            "commond error, please keep it consistent: /makehotpatch <version> <mode> <patch_name> "

            "<patch_type> <cve_issue_id> <os_branch>")

        sys.exit(1)



    issue_number = ""

    metadata_path = args.output

    src_url, x86_url, arm_url = args.download_link.split(",")

    meta_url = "https://gitee.com/openeuler/hotpatch_meta"

    patch_path = os.path.join(metadata_path, args.branch, args.repo, repo_version, "patch")

    if not os.path.exists(patch_path):

        os.makedirs(patch_path)



    version_path = os.path.join(metadata_path, args.branch, args.repo, repo_version)

    if mode == "SGL":

        metadata_file = os.path.join(version_path, "hotmetadata_SGL.xml")

    else:

        metadata_file = os.path.join(version_path, "hotmetadata_ACC.xml")



    logger.info("new_metadata_patch: %s", metadata_file)



    gp = GiteeProxy(args.community, args.repo, args.token)



    # 获取需要修复的issue信息

    cve_issue_id, cve_issue_url = get_issue_info(cve_issue)



    # 删除最新version,在下面重新生成

    if args.func == "update" and os.path.exists(metadata_file):

        del_tree = ET.parse(metadata_file)

        del_root = del_tree.getroot()

        issue_number = del_hotpatch(del_root)

        write_to_hotmetadata_xml(del_root)



    # 创建热补丁issue,存在的话更新热补丁issue

    hotpatch_issue_url = create_update_issue(issue_number)

    with open(args.hotpatch_file, "w") as f:

        f.write(hotpatch_issue_url)



    # 生成元数据文件

    add_hotmetadata_xml()



    logger.info("success: hotmetadata.xml has been generated.")

    sys.exit(0)