#!/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 re
import json
import shutil
from collections import defaultdict
from enum import Enum
from datetime import datetime


ODATA_INFO = ['@odata.context', '@odata.id', '@odata.type']
NUM2LET_DIA = 64


def get_abspath_under_rackmount(path):
    return os.path.join(os.getcwd(), path)


def mk_new_dir(path):
    if os.path.exists(path):
        shutil.rmtree(path)
    os.makedirs(path)


def load_json(path):
    if os.path.exists(path):
        try:
            with open(path, 'r', encoding='utf-8') as fp:
                fp_json = json.load(fp)
                return fp_json
        except json.JSONDecodeError as e:
                print(f"Error decoding JSON: {e}")
        except Exception as e:
                print(f"An error occurred when load {path}: {e}")
    return {}
    

def dump_json(path, json_dict):
    with open(path, 'w', encoding='utf-8') as fp:
        fp.write(json.dumps(json_dict, indent=4, ensure_ascii=False))
        return True
    return False


def del_json(path):
    if os.path.exists(path):
        os.remove(path)


def is_local_ref(ref):
    pattern = r'^(#\/)|(oem\/\w+\.json#\/)'
    ret = re.search(pattern, ref)
    if ret:
        return True
    return False


def get_rscname(filename):
    pattern = re.compile(r'\.v[0-9]+_[0-9]+_[0-9]+\.json')
    ret = re.match(pattern, filename) or re.match(r'\.json', filename) or None
    if ret:
        return filename[:ret.span()[0]]
    else:
        return None

def get_version(filename):
    pattern = r'\.v(\d+)_(\d+)_(\d+)\.json'
    match = re.search(pattern, filename)
    if match:
        # 如果匹配成功，使用group方法获取匹配的子串
        return 'v' + '_'.join(match.groups())
    else:
        # 如果没有找到匹配，返回None，根据具体需求可更改
        return None


def get_prop_list_from_ref(ref):
    prop_list = []
    # 依次匹配/和/之间的字符串并提取到prop_list
    if not ref.endswith('/'):
        ref = ref + '/'
    ret = re.search(r'/(.+?)/', ref)
    while ret is not None:
        index = ret.span()
        prop_list.append(ref[(index[0]+1):(index[1]-1)])
        ref = ref[(index[1]-1):]
        ret = re.search(r'/(.+?)/', ref)
    return prop_list


def get_prop_from_ref(input_json, ref):
    prop_list = get_prop_list_from_ref(ref)
    prop = input_json
    for p in prop_list:
        if p in prop:
            prop = prop[p]
        else:
            return {}
    return prop


def get_rsc_name(filename):
    pattern = r'(\.v(\d+\_){2}\d)?\.json'
    ret = re.search(pattern, filename)
    if ret is not None:
        index = ret.span()
        return filename[:index[0]]
    return ''


def get_schema_from_odata_type(odata_type):
    pattern = r'^#(\w+)(?:\.v\d+_\d+_\d+)?\.(\w+)$'
    ret = re.search(pattern, odata_type)
    # 映射器会检查，一般不会失配
    if ret:
        p = re.split(r'#|\.', odata_type)
        rsc_name = '.'.join(p[1:-1])
        prop_name = p[-1]
        return rsc_name.lower(), prop_name
    else:
        return None
    

def get_prop_from_local_ref(input_json, ref, schema_dir):
    if ref.startswith('#'):
        return get_prop_from_ref(input_json, ref), None
    path_list = re.split('/', ref)
    file_loc = 0
    for file_loc in range(len(path_list)):
        if path_list[file_loc].endswith('#'):
            break
        schema_dir = '/'.join([schema_dir, path_list[file_loc]])
    schema_dir = '/'.join([schema_dir, path_list[file_loc][:-1]])
    input_json = load_json(schema_dir)
    return get_prop_from_ref(input_json, '/'.join(path_list[file_loc:])), schema_dir


class ErrorlogPrinter:
    def __init__(self):
        self.error_ruleinfo_table = defaultdict(self.default_error)
        self.error_ruleinfo_table['1-2'] = '自定义属性语法校验失败'
        self.error_ruleinfo_table['2-1'] = '资源定义完整性检查失败'
        self.error_ruleinfo_table['2-2'] = '资源方法有效性检查失败'
        self.error_ruleinfo_table['2-3'] = '属性定义完整性检查失败'
        self.error_ruleinfo_table['2-4'] = '属性类型定义一致性检查失败'
        self.error_ruleinfo_table['2-5'] = 'POST请求的requiredOnCreate约束检查失败'
        self.error_ruleinfo_table['2-6'] = 'GET请求的required约束检查失败'
        self.error_ruleinfo_table['2-7'] = 'PATCH请求的readonly约束检查失败'
        self.error_ruleinfo_table['3-1'] = 'schema关联的JSONSchemas节点检查失败'
        self.row_num = 0
        self.check_time = datetime.now()
        self.check_time = self.check_time.strftime("%Y-%m-%d %H:%M:%S")

    def default_error(self):
        return '未定义的Rule序号'
    
    def get_error_list(self, is_success, error_list):
        if is_success is True:
            return
        for e in error_list:
            self.print_errorlog(e[0], *e[1:])
    
    def print_errorlog(self, rule_id, *error_info):
        self.print_errorlog2tmn(rule_id, *error_info)

    def print_errorlog2tmn(self, rule_id, *error_info):
        error_ruleinfo = self.error_ruleinfo_table[rule_id]
        print('=============constraint errors found in file:')
        print('映射器文件: {}'.format(error_info[0]))
        print('Schema文件: {}'.format(error_info[1]))
        print('[Rule {}]: {}, 映射器错误值: {}, 错误位置: URI: {}, Type: {}, 位置: {}'.format(
            rule_id, 
            error_ruleinfo,
            error_info[2], 
            error_info[3], 
            error_info[4],
            error_info[5]))
        
ep = ErrorlogPrinter()