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


class RspBodyConstraintChecker:
    def __init__(self, target_path, uri, schema_dir, rsc_name, prop_name, intf_type):
        self.target_path = target_path
        self.uri = uri
        self.schema_dir = schema_dir
        self.rsc_filename = rsc_name + '.json'
        self.rsc_name = su.get_rsc_name(self.rsc_filename) or 'None'
        self.version = su.get_version(self.rsc_filename) or 'None'
        self.prop_name = prop_name
        self.intf_type = intf_type.lower()
        self.schema_json = None
        self.is_success = True

    def check_get_required_cons(self, target, required, schema_path, prop_path, errors):
        for r in required:
            if r not in target:
                self.is_success = False
                errors.append(['2-6',
                               self.target_path,
                               schema_path,
                               r,
                               self.uri,
                               self.intf_type,
                               prop_path,
                               self.rsc_name,
                               self.version])
    
    def check_prop_def(self, prop_name, schema, schema_path, prop_path, errors):
        if 'properties' not in schema or prop_name not in schema['properties']:
            self.is_success = False
            errors.append(['2-3',
                           self.target_path,
                           schema_path,
                           prop_name,
                           self.uri,
                           self.intf_type,
                           prop_path,
                           self.rsc_name,
                           self.version])
            return False
        return True
    
    def check_rsp_body_prop(self, target, schema, schema_path, prop_path, errors):
        # flag用于判断当前属性开始的检查是否有误
        flag = True
        for prop in target:
            is_def = self.check_prop_def(prop, schema, schema_path, '/'.join([prop_path, prop]), errors)
            flag = is_def
            if is_def and isinstance(target[prop], dict):
                flag = self.check_rsp_body(target[prop], schema['properties'][prop], schema_path,
                                           '/'.join([prop_path, prop]), errors) and flag
            elif is_def and isinstance(target[prop], list):
                for p in target[prop]:
                    if isinstance(p, dict):
                        flag = self.check_rsp_body(p, schema['properties'][prop], schema_path,
                                                   '/'.join([prop_path, prop]), errors) and flag
        return flag

    def check_rsp_body(self, target, schema, schema_path, prop_path, errors):
        if not (isinstance(target, dict) or (isinstance(target, list))):
            return True
        if self.intf_type == 'get' and 'required' in schema:
            self.check_get_required_cons(target, schema['required'], schema_path, prop_path, errors)
        while '$ref' in schema and su.is_local_ref(schema['$ref']):
            # 根据当前检查属性对应的schema动态加载
            schema, p = su.get_prop_from_local_ref(su.load_json(schema_path), schema['$ref'], self.schema_dir)
            if p is not None:
                schema_path = p
        # schema中下一级定义用远程路径，跳过后续检查
        if '$ref' in schema and schema['$ref'].startswith('http'):
            return True
        # 兼容anyOf场景，anyOf的模式满足其一即可
        # 有任一模式满足，则不会报错；没有模式满足，则所有模式的报错都会记录
        flag = False
        es = []
        if 'anyOf' in schema:
            for ss in schema['anyOf']:
                e = []
                if self.check_rsp_body(target, ss, schema_path, prop_path, e) is True:
                    flag = True
                    break
                else:
                    es = es + e
            if flag is False:
                errors.extend(es)
        else:
            flag = self.check_rsp_body_prop(target, schema, schema_path, prop_path, es)
            errors.extend(es)
        return flag

            
    def check(self, rsp_body):
        schema_path = os.path.join(self.schema_dir, self.rsc_filename)
        self.schema_json = su.load_json(schema_path)
        if 'definitions' not in self.schema_json or self.prop_name not in self.schema_json['definitions']:
            su.ep.print_errorlog('2-3',
                                 self.target_path,
                                 schema_path,
                                 self.prop_name,
                                 self.uri,
                                 self.intf_type,
                                 '未在schema文件中定义资源名',
                                 self.rsc_name,
                                 self.version)
            return False
        errors = []
        self.check_rsp_body(rsp_body, self.schema_json['definitions'][self.prop_name], schema_path, 'RspBody', errors)
        su.ep.get_error_list(self.is_success, errors)
        return self.is_success
        