#!/usr/bin/env python
# coding: utf-8
# Copyright (c) 2024 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.

import os
import sys
import shlex
import logging
import argparse
import tempfile
import shutil
import glob
import subprocess
from multiprocessing import Process

HTTPS_PROXY_STR = "https_proxy"


class Task(Process):
    @staticmethod
    def log_init():
        logger = logging.getLogger()
        logger.setLevel(logging.INFO)
        loglevel = os.environ.get("LOG")
        if loglevel is not None:
            formatter = logging.Formatter(fmt='[{asctime} {levelname} {filename}:{lineno} {funcName:4}] {message}',
                                          style="{")
            if loglevel == "debug":
                logger.setLevel(logging.DEBUG)
            elif loglevel == "warn":
                logger.setLevel(logging.WARNING)
            elif loglevel == "error":
                logger.setLevel(logging.ERROR)
            else:
                logger.setLevel(logging.INFO)
        else:
            formatter = logging.Formatter(fmt='[{levelname}] {message}',
                                          style='{')
            handler = logging.StreamHandler(sys.stdout)
            handler.setFormatter(formatter)
            logger.handlers = []
            logger.addHandler(handler)

    @staticmethod
    def format_command(command, sudo=False):
        cmd_args: list[str] = []
        if isinstance(command, list):
            cmd_args = command
        elif isinstance(command, str):
            cmd_args = shlex.split(command)
            cmd_args[0] = shutil.which(cmd_args[0])
            if cmd_args[0] is None:
                raise Exception(f"{cmd_args}不存在，请检查命令或环境配置")
        else:
            raise Exception("Command with invalid instance")
        sudo_cmd = shutil.which("sudo")
        if sudo and sudo_cmd and cmd_args[0] != sudo_cmd:
            cmd_args.insert(0, sudo_cmd)
        return cmd_args
    
    def chdir(self, path):
        logging.info(f"chdir: {path}")
        os.chdir(path)

    def run_command(self, command, check=True, sudo=False, capture_output=False):
        logging.info(f">>>> {command}")
        command = self.format_command(command, sudo)
        return subprocess.run(command, check=check, timeout=600, capture_output=capture_output)


class WorkPrepare(Task):
    def __init__(self):
        super().__init__()
        parser = argparse.ArgumentParser(
            prog="openUBMC Development environment initialization",
            formatter_class=argparse.RawTextHelpFormatter
        )
        parser.add_argument("-path", "--bmc_sdk_path", help="conan远程仓的密码，即为openUBMC社区账户的密码", required=True)
        parsed_args, _ = parser.parse_known_args(sys.argv)

        self.home = os.environ["HOME"]
        self.cwd = os.getcwd()
        self.bmc_sdk_path = parsed_args.bmc_sdk_path
        
    def proxy_check(self):
        https_proxy = os.environ.get(HTTPS_PROXY_STR)
        if https_proxy:
            logging.warning("检测到环境设置了https_proxy代理，可能导致初始化失败，请确认是否关闭代理运行？(Y/N)")
            if input().upper() == "Y":
                os.environ[HTTPS_PROXY_STR] = ""
                os.environ[HTTPS_PROXY_STR.UPPER()] = ""
    
    def environ_check(self):
        sudouser = os.environ.get("SUDO_USER")
        if sudouser is not None:
            raise Exception("请直接执行，不支持以sudo环境执行脚本")
        with open("/etc/issue", "r") as fq:
            issue = fq.readline().strip()
            if not issue.startswith("Ubuntu 24.04"):
                raise Exception(f"仅支持Ubuntu 24.04操作系统，当前系统为 {issue}")
    
    def ubuntu_init(self):
        self.run_command("ln -snf /bin/bash /bin/sh", sudo=True)
        logging.info("Initial ubuntu env")
        self.run_command(
            f"sed -i 's/archive.ubuntu/mirrors.huaweicloud/g' /etc/apt/sources.list.d/ubuntu.sources", 
            sudo=True, check=False
        )
        self.run_command(
            f"sed -i 's/security.ubuntu/mirrors.huaweicloud/g' /etc/apt/sources.list.d/ubuntu.sources", 
            sudo=True, check=False
        )
        self.run_command("apt-get update", sudo=True)
       
    def install_dependencies(self):
        self.run_command(
            f"apt-get install -y python3 python3-pip python3-dev git wget curl ca-certificates \
            pkgconf libdbus-1-dev libglib2.0-dev gcc-9 g++-9 unzip npm", 
            sudo=True
        )
        self.run_command(
            f"update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 20 \
            --slave /usr/bin/g++ g++ /usr/bin/g++-9 --slave /usr/bin/gcov gcov /usr/bin/gcov-9", 
            sudo=True
        )
        self.run_command(f"apt remove -y python3-urllib3 python3-distro", sudo=True)
        self.run_command(f"apt autoremove -y", sudo=True)

    def python_config(self):
        self.run_command(
            f"pip config set global.index-url https://mirrors.huaweicloud.com/repository/pypi", 
            sudo=True
        )
        self.run_command(
            f"pip config set global.extra-index-url https://mirrors.huaweicloud.com/repository/pypi/simple", 
            sudo=True
        )
        self.run_command(
            f"pip config set global.trusted-host 'mirrors.huaweicloud.com'", 
            sudo=True
        )

    def install_old_openssl(self):
        tmpdir = tempfile.TemporaryDirectory()
        self.chdir(tmpdir.name)
        self.run_command(
            f"wget https://mirrors.huaweicloud.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2_amd64.deb", 
            sudo=True
        )
        self.run_command(f"dpkg -i libssl1.1_1.1.1f-1ubuntu2_amd64.deb", sudo=True)
        os.chdir(self.cwd)

    def install_dev_tools(self):
        real_path = os.path.join(self.cwd, self.bmc_sdk_path)
        tmpdir = tempfile.TemporaryDirectory()
        self.chdir(tmpdir.name)
        self.run_command(f"unzip {real_path} ", sudo=True)
        self.run_command(f"cp -rf rtos_compiler {self.home}", sudo=True)
        self.run_command(f"cp -rf sdk {self.home}", sudo=True)
        self.run_command(f"cp -rf sdk/hi1711sdk.tar.gz {self.home}/rtos_compiler", sudo=True)
        self.run_command(f"cp -rf lua-format /usr/bin", sudo=True)
        self.run_command(f"chmod +x /usr/bin/lua-format", sudo=True)
        self.run_command(f"chmod -R +x hpm_tools", sudo=True)
        self.run_command(f"cp -rf hpm_tools/. /usr/local/bin", sudo=True)
        os.chdir(self.cwd)

    def install_node(self):
        self.run_command(f"npm install -g n", sudo=True)
        self.run_command(f"n 20.18.0")
        self.run_command(f"npm install -g npm@10.8.2", sudo=True)
 
    def install_bingo(self):
        tmpdir = tempfile.TemporaryDirectory()
        self.chdir(tmpdir.name)
        self.run_command(f"wget https://repo.openubmc.cn/25.03/tools/openUBMC-bingo_25.03.zip -O bingo.zip", sudo=True)
        self.run_command(f"unzip ./bingo.zip", sudo=True)
        whl_file = glob.glob(os.path.join(os.getcwd(), "*.whl"))[0]
        deb_file = glob.glob(os.path.join(os.getcwd(), "*.deb"))[0]
        if not whl_file or not deb_file:
            raise Exception(f"未找到bingo工具的安装包，无法进行安装")
        self.run_command(f"pip3 install {whl_file}  --break-system-packages", sudo=True)
        try:
            self.run_command(f"dpkg -i {deb_file}", sudo=True)
        except subprocess.CalledProcessError as e:
            self.run_command("apt install -y -f", sudo=True)
            self.run_command(f"dpkg -i {deb_file}", sudo=True)
        os.chdir(self.cwd)

    def conan_config(self):
        conan_cache = os.path.join(self.home, ".conan")
        self.run_command(f"rm -rf {conan_cache}", sudo=True, check=False)
        self.run_command("conan config init", sudo=True)
        self.run_command("conan config set general.revisions_enabled=1", sudo=True)
        self.run_command("conan remote add openubmc_dev 'https://conan.openubmc.cn/conan_1/' false -f", sudo=True)

    def install_sdk(self):
        os.chdir(self.cwd)
        self.run_command("bingo build -t install_sdk -b openUBMC -ucc", sudo=True)

    def install_qemu_compile_dependencies(self):
        self.run_command(
            f"apt-get install -y make bzip2 pkg-config libslirp-dev libpixman-1-dev ninja-build \
            libcap-ng-dev libfdt-dev libattr1-dev", sudo=True)
        
    def install_qemu_runtime_dependencies(self):
        self.run_command("cp /etc/apt/sources.list.d/ubuntu.sources \
                         /etc/apt/sources.list.d/ubuntu.sources.bak", sudo=True)
        self.run_command("sed -i 's/Suites:/Suites: plucky/g' /etc/apt/sources.list.d/ubuntu.sources", sudo=True)
        self.run_command("apt-get update", sudo=True)
        self.run_command("apt-get install -y libcapstone5", sudo=True)
        self.run_command("rm /etc/apt/sources.list.d/ubuntu.sources", sudo=True)
        self.run_command("mv /etc/apt/sources.list.d/ubuntu.sources.bak \
                         /etc/apt/sources.list.d/ubuntu.sources", sudo=True)
        self.run_command("apt-get update", sudo=True)
        
    def run(self):
        try:
            self.proxy_check()
            self.environ_check()
            self.ubuntu_init()
            self.install_dependencies()
            self.install_old_openssl()
            self.python_config()
            self.install_bingo()
            self.conan_config()
            self.install_dev_tools()
            self.install_node()
            self.install_sdk()
            # 初始化qemu编译和运行时依赖
            self.install_qemu_compile_dependencies()
            self.install_qemu_runtime_dependencies()
            logging.info("qemu依赖环境初始化成功")
            logging.info("openUBMC开发者环境初始化完成！")
        except Exception as exc:
            if os.environ.get("LOG"):
                import traceback
                logging.error(traceback.format_exc())
            msg = str(exc)
            logging.error(msg)
            return -1
        return 0

if __name__ == "__main__":
    wk = WorkPrepare()
    sys.exit(wk.run())