#!/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 re
import os
import schema_utils as su


class UriConstraintChecker:
    INTF_TYPES = ['deletable', 'insertable', 'updatable']
    TYPE_MAP = {
        'delete': 'deletable',
        'post': 'insertable',
        'patch': 'updatable'
    }

    def __init__(self, target_path, schema_dir):
        self.target_path = target_path
        self.schema_dir = schema_dir
        self.base_path = None
        self.odata_type = None
        self.base_rscname = None
        self.type_table = {}
        self.intf_type_list = []
        self.is_success = True

    @staticmethod
    def is_extern_intf(uri):
        return uri.startswith('/redfish/v1')
    
    @staticmethod
    def get_uri_without_slots(uri):
        # uri中的分隔符为'/' '_' '.'
        rets = re.split('\/|_|\.', uri)
        for i in range(len(rets)):
            # 跳过形如{{var}}的通配符内容
            if rets[i] .startswith('{{') and rets[i].endswith('}}'):
                continue
            # 替换动态槽位号内容
            elif (rets[i].startswith(':')) or (rets[i].startswith('{') and rets[i].endswith('}')):
                rets[i] = '*'
        return '/'.join(rets)
    
    def get_odata_type(self):
        return self.odata_type
    
    def load_uri_info(self, uri):
        self.base_path = os.path.join(self.schema_dir, self.base_rscname + '.json')
        if not os.path.isfile(self.base_path):
            su.ep.print_errorlog('2-1',
                                 self.target_path,
                                 'None',
                                 'None',
                                 uri,
                                 'None',
                                 self.base_rscname+'未定义schema',
                                 'None',
                                 'None')
            return False
        base_json = su.load_json(self.base_path)
        if '$ref' in base_json:
            main_prop = su.get_prop_from_ref(base_json, base_json['$ref'])
        else:
            print(self.base_rscname + '.json', '缺少主属性定义')
            return False
        if 'uris' in main_prop:
            self.type_table['uris'] = []
            for uri in main_prop['uris']:
                self.type_table['uris'].append(self.get_uri_without_slots(uri))
        else:
            self.type_table['uris'] = None
        for intf_type in self.INTF_TYPES:
            if intf_type in main_prop:
                self.type_table[intf_type] = main_prop[intf_type]
            else:
                self.type_table[intf_type] = None
        return True

    def get_base_rscname(self, get_intf):
        # Actions类型接口，此处不检查
        if 'RspBody' not in get_intf:
            return None
        info_complete = True
        for o_info in su.ODATA_INFO:
            if o_info not in get_intf['RspBody']:
                info_complete = False
        if info_complete is False:
            return None
        self.odata_type = get_intf['RspBody']['@odata.type']
        ret = re.search('#[a-zA-Z0-9_]+\.', self.odata_type)
        if ret is None:
            print('外部接口映射器配置@odata.type:', self.odata_type, '错误')
            return None
        index = ret.span()
        return self.odata_type[1:index[1]-1].lower()

    # 校验Uri定义完整性
    def check_uris_existence(self, uri):
        no_slots_uri = self.get_uri_without_slots(uri)
        if self.type_table['uris'] is None or no_slots_uri not in self.type_table['uris']:
            self.is_success = False
            su.ep.print_errorlog('2-1',
                                 self.target_path,
                                 self.base_path,
                                 uri,
                                 uri,
                                 'None',
                                 'Uri',
                                 self.base_rscname,
                                 'None')
        return

    # 校验资源方法有效性
    def check_uri_intf_type(self, uri):
        for intf_type in self.intf_type_list:
            schema_type = self.TYPE_MAP[intf_type]
            if self.type_table[schema_type] is None:
                self.is_success = False
                su.ep.print_errorlog('2-2',
                                     self.target_path,
                                     self.base_path,
                                     intf_type,
                                     uri,
                                     schema_type,
                                     'Interfaces/Type',
                                     self.base_rscname,
                                     'None')
            elif self.type_table[schema_type] is False:
                self.is_success = False
                su.ep.print_errorlog('2-2',
                                     self.target_path,
                                     self.base_path,
                                     intf_type,
                                     uri,
                                     schema_type,
                                     'Interfaces/Type',
                                     self.base_rscname,
                                     'None')
        return
    
    def check(self, uri, intfs):
        for i in range(len(intfs)):
            intf_type = intfs[i]['Type'].lower()
            if intf_type == 'get':
                self.base_rscname = self.get_base_rscname(intfs[i])
            else:
                self.intf_type_list.append(intf_type)
        if self.base_rscname is None:
            print(uri, '未定义GET类型接口')
            return
        if self.load_uri_info(uri) is True:
            self.check_uris_existence(uri)
            self.check_uri_intf_type(uri)
        # 加载uri信息失败，则资源没有找到对应schema，odata_type可能非法，不会往下检查
        else:
            self.odata_type = None
        return