# Copyright (c) 2025 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 json
import re
if __name__ == "__main__":
    import PrivilegeMapUriObtain
else:
    from . import PrivilegeMapUriObtain


class UriData:
    def __init__(self, data, entity=None):
        """
        资源配置解析器，从odata.type中获取资源类型
        """
        self.method = []
        self._entity = entity
        self._parse(data)

    @classmethod
    def _get_resource_entity(cls, rsp_body):
        if isinstance(rsp_body, dict):
            odata_type = rsp_body["@odata.type"]
            return odata_type.split('.')[-1]
        return "NA"

    def get_entity(self):
        return self._entity

    def _parse(self, data):
        for interface in data:
            method = interface["Type"].upper()
            self.method.append(method)
            if method == 'GET':
                self._entity = self._entity or self._get_resource_entity(interface["RspBody"])


class PrivilegeMapChecker:
    def __init__(self):
        self.tool_path = os.path.dirname(os.path.abspath(__file__))
        self.tool_config = os.path.join(self.tool_path, "privilege_map_config.json")
        self._init_config()
        self.error = []
        self._privilege_map = PrivilegeMapUriObtain.create_privilege_mapper(self.error)

        self._uri2entity = {}

    def check_invalid_data(self, resource_file):
        """
        资源必须有GET，Action方法必须为POST，其他均为接口配置错误
        以及其他一些因格式错误无法解析的接口
        """
        with open(resource_file, 'r', encoding='utf-8') as f:
            data = json.load(f)
        if len(data) > 0:
            self.error.append(f"接口配置中有异常数据，数据详情见文件{resource_file}")

    def check_get_uri(self, resource_file):
        """
        所有资源均有GET方法，通过检测所有GET接口对应的uri检测所有资源
        """
        with open(resource_file, 'r', encoding='utf-8') as f:
            data = json.load(f)
        for uri, interfaces in data.items():
            flag = None
            entity = None
            if uri in self.config["Special"]:
                flag = self.config["Special"][uri]["Flag"]
                if flag == "Add":
                    entity = self.config["Special"][uri]["Entity"]

            if flag == "Pass":
                continue
            uri_data = UriData(interfaces, entity=entity)
            entity = uri_data.get_entity()
            self._uri2entity[uri] = entity
            if flag == "Except":
                continue
                
            if entity not in self._privilege_map:
                self.error.append(f"GET接口：{uri}，类型：{entity} 未配置PrivilegeMap")
                return
            mapper = self._privilege_map[entity]
            for method in uri_data.method:
                privilege_count = len(mapper.method[method])
                if uri in mapper.resource_uri_overrides:
                    privilege_count += len(mapper.resource_uri_overrides[uri].method[method])
                if privilege_count == 0:
                    self.error.append(f"{method}接口：{uri}，类型：{entity} 未配置PrivilegeMap")

    def check_post_uri(self, resource_file):
        """
        检测Action类型的POST接口，这类接口配置在父资源的ResourceURIOverrides中
        """
        with open(resource_file, 'r', encoding='utf-8') as f:
            data = json.load(f)
        for uri in data:
            match = re.search(r"^(.*)/Oem/[^/]+/Actions", uri) or re.search(r"^(.*)/Actions", uri)
            if not match:
                self.error.append(f"接口：{uri}只有POST，没有GET")
                continue
            if uri in self.config["Special"]:
                # 特殊的资源跳过检测
                continue
            parent = match.group(1)
            if parent not in self._uri2entity:
                self.error.append(f"POST接口：{uri}无法获取根资源")
                continue
            entity = self._uri2entity[parent]
            if entity not in self._privilege_map:
                self.error.append(f"POST接口：{uri}根资源类型{entity} 未配置PrivilegeMap")
                continue
            if uri not in self._privilege_map[entity].resource_uri_overrides:
                self.error.append(f"POST接口：{uri}未在根资源{entity}配置PrivilegeMap")

    def run(self):
        post_list = []
        for steps in self.config["Steps"]:
            temp_paths = PrivilegeMapUriObtain.create_uri_mapper(steps["Resource"], steps["UriStorage"])
            if not temp_paths:
                return False
            self.check_invalid_data(temp_paths["other_data"])
            self.check_get_uri(temp_paths["resource_uri"])
            post_list.append(temp_paths["post_only"])
        list(map(self.check_post_uri, post_list))

        if len(self.error) == 0:
            return True
        self._err_print()
        return False

    def _init_config(self):
        """
        解析手动配置接口文件
        """
        with open(self.tool_config, 'r', encoding='utf-8') as f:
            self.config = json.load(f)

    def _err_print(self):
        print("\n" + "=" * 80)
        print("PrivilegeMap检查不通过，错误详情如下：")
        print("=" * 80)
        for err in self.error:
            print(err)
        print("=" * 80)
        print("若要在不配置PrivilegeMap的情况下个人出包，"
              "请用在MapperChecker中注释check_privilege_map的方法进行屏蔽\n"
              "特殊资源请在privilege_map_config.json文件中手动声明资源类型")
        print("=" * 80 + "\n")


if __name__ == "__main__":
    checker = PrivilegeMapChecker()
    checker.run()
