#!/usr/bin/env python
# coding=utf-8
# Copyright (c) 2026 Huawei Technologies Co., Ltd.
# openUBMC is licensed under 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.

"""
文件名：task_package_metadata.py
功能：将收集到的MDS/MDB文件打包为conan制品
版权信息：华为技术有限公司，版本所有(C) 2026
"""

import os
import stat
import shutil
import yaml

from bmcgo.tasks.task import Task
from bmcgo.utils.config import Config
from bmcgo import misc


class TaskClass(Task):
    """MDS/MDB文件打包为conan制品的任务类"""

    def __init__(self, config: Config, work_name=""):
        super().__init__(config, work_name)
        self.upload_to_remote = False
        self.package_dir = os.path.join(self.config.build_path, "metadata")
        if os.path.isdir(self.package_dir):
            shutil.rmtree(self.package_dir)
        os.makedirs(self.package_dir, exist_ok=True)

    def set_upload_to_remote(self, upload):
        """设置是否上传到远程仓库"""
        self.upload_to_remote = upload

    def run(self):
        """执行元信息打包任务"""
        if not self._check_need_package():
            return

        self.info(">>>>>>>>>>>>>>>>> 开始打包MDS/MDB为conan制品 <<<<<<<<<<<<<<<<")

        try:
            # 收集MDS/MDB文件并生成metadata.yml
            self._collect_and_prepare_files()

            self._generate_conanfile()

            self._create_conan_package()

            self.success(">>>>>>>>>>>>>>>>> MDS/MDB打包成功 <<<<<<<<<<<<<<<<")
        except Exception as e:
            self.error(f"MDS/MDB打包失败: {e}")
            raise

    def _check_need_package(self):
        """检查是否不支持打包元信息"""
        # 不支持conan1
        if misc.conan_v1():
            self.warning(">>>>>>>>>>>>>>>>> 当前环境为conan1，跳过打包元信息制品 <<<<<<<<<<<<<<<<")
            return False

        # 检查是否是sc包，当zip_code参数存在时，该变量不会被设置
        if self.config.tosupporte_code is None:
            self.warning(">>>>>>>>>>>>>>>>> 当前仅支持tosupporte类型单板归档制品，跳过打包 <<<<<<<<<<<<<<<<")
            return False

        # 检查MDS/MDB目录是否存在
        if not self._check_mds_mdb_exists():
            self.warning(f">>>>>>>>>>>>>>>>> MDS/MDB目录 {self.config.mdb_output} 不存在或为空，跳过打包 <<<<<<<<<<<<<<<<")
            return False

        return True

    def _check_mds_mdb_exists(self):
        """检查MDS/MDB目录是否存在且包含文件"""
        if not os.path.isdir(self.config.mdb_output):
            return False

        # 检查目录是否包含文件
        has_files = False
        for _, _, files in os.walk(self.config.mdb_output):
            if files:
                has_files = True
                break

        return has_files

    def _collect_and_prepare_files(self):
        """收集MDS/MDB文件并生成metadata.yml"""
        # 创建 mdb 子目录，用于匹配 exports_sources = "mdb/*"
        mdb_dest = os.path.join(self.package_dir, "mdb")
        os.makedirs(mdb_dest, exist_ok=True)

        # 将 mdb_output 的所有内容复制到 package_dir/mdb
        for item in os.listdir(self.config.mdb_output):
            src = os.path.join(self.config.mdb_output, item)
            dst = os.path.join(mdb_dest, item)
            if os.path.isdir(src):
                shutil.copytree(src, dst, dirs_exist_ok=True)
            else:
                shutil.copy2(src, dst)

        # 创建 schemas 子目录，用于匹配 exports_sources = "schemas/*"
        schema_dest = os.path.join(self.package_dir, "schemas")
        os.makedirs(schema_dest, exist_ok=True)

        # 将 schema文件复制到 package_dir/schemas
        schema_list = [
            "/usr/share/bingo/schema/base.json",
            "/usr/share/bingo/schema/csr.schema.json"
        ]
        for schema_path in schema_list:
            if os.path.exists(schema_path):
                schema_name = os.path.basename(schema_path)
                dest_path = os.path.join(schema_dest, schema_name)
                self.run_command(f"cp -f {schema_path} {dest_path}", sudo=True)
                self.info(f"已复制 schema 文件: {schema_path} -> {dest_path}")
            else:
                self.warning(f"schema 文件不存在，跳过: {schema_path}")

        # 生成metadata.yml清单文件（放在package_dir根目录）
        self._generate_metadata()

        self.info(f"已收集MDS/MDB文件到: {self.package_dir}")

    def _generate_metadata(self):
        """生成metadata.yml清单文件"""
        metadata = {
            "metadata": {
                "board_name": self.config.board_name,
                "version": self.config.version,
                "build_type": self.config.build_type,
                "stage": self.config.stage,
            }
        }

        # 写入metadata.yml
        metadata_file = os.path.join(self.package_dir, "metadata.yml")
        with os.fdopen(os.open(metadata_file, os.O_WRONLY | os.O_CREAT | os.O_TRUNC,
                               stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH), 'w') as f:
            yaml.safe_dump(metadata, f, sort_keys=False)

        self.info(f"已生成metadata.yml: {metadata_file}")

    def _get_package_name(self):
        """获取包名，格式: metadata-{board_name}-{code}
        优先使用manufacture_code，其次tosupporte_code
        """
        code = self.config.manufacture_code or self.config.tosupporte_code or "default"
        package_name = f"metadata-{self.config.board_name}-{code}"
        return package_name.lower()

    def _generate_conanfile(self):
        """使用mako模板动态生成conanfile.py"""
        from mako.lookup import TemplateLookup

        version = self.config.version
        if version is not None:
            version = version.lower()
        package_name = self._get_package_name()

        # 获取模板目录
        template_dir = os.path.join(os.path.dirname(__file__), "conan", "template")
        lookup = TemplateLookup(directories=template_dir, input_encoding='utf-8')
        template = lookup.get_template("conanfile.py.mako")

        exports_sources = ["mdb/*", "schemas/*", "metadata.yml"]
        # 渲染模板
        conanfile_content = template.render(
            package_name=package_name,
            version=version,
            exports_sources=exports_sources
        )

        # 写入conanfile.py
        conanfile_path = os.path.join(self.package_dir, "conanfile.py")
        with os.fdopen(os.open(conanfile_path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC,
                               stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH), 'w', encoding='utf-8') as f:
            f.write(conanfile_content)

        self.info(f"已生成conanfile.py: {conanfile_path}")

    def _create_conan_package(self):
        """执行conan export-pkg命令"""
        self.chdir(self.package_dir)

        version = self.config.version
        if version is not None:
            version = version.lower()
        package_name = self._get_package_name()

        # 使用conan export-pkg直接从预编译文件创建包
        cmd = f"conan export-pkg . --name={package_name} --version={version} --user openubmc --channel stable"
        self.info(f"执行conan export-pkg命令: {cmd}")
        self.run_command(cmd)

        # 如果需要上传到远程仓库
        remote = misc.upload_metadata_remote()
        if remote is not None:
            # 上传刚创建的recipe和package
            try:
                upload_cmd = f"conan upload {package_name}/{version}@openubmc/stable --only-recipe -r {remote} -c"
                self.info(f"上传到远程仓库: {upload_cmd}")
                self.run_command(upload_cmd)
            except Exception as e:
                self.warning(f"尝试上传元数据归档到conan远端{remote}失败: {e}")
        else:
            self.info("仅本地创建conan制品，不上传到远程仓库")

        self.success(f"成功创建conan制品: {package_name}/{version}")
