#!/usr/bin/env python3
# Copyright (c) 2026 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.

"""
JSON Schema 到 XML CSDL 转换脚本
用于将 Redfish OEM JSON Schema 转换为 XML CSDL 格式
"""

import json
import re
import sys
import xml.etree.ElementTree as ET
from xml.dom import minidom
from typing import Any, Dict, List, Optional, Set, Tuple
from pathlib import Path

# 注册 XML 命名空间前缀（防止 ElementTree 自动生成 ns0, ns1 等前缀）
# 只注册 edmx 命名空间，Schema 元素使用 xmlns 属性声明命名空间
ET.register_namespace('edmx', 'http://docs.oasis-open.org/odata/ns/edmx')


class JsonToCsdlConverter:
    """JSON Schema 到 XML CSDL 转换器"""

    # XML 命名空间
    NAMESPACES = {
        'edmx': 'http://docs.oasis-open.org/odata/ns/edmx',
        'edm': 'http://docs.oasis-open.org/odata/ns/edm'
    }

    # 类型映射
    TYPE_MAPPING = {
        'string': 'Edm.String',
        'integer': 'Edm.Int32',
        'number': 'Edm.Double',
        'boolean': 'Edm.Boolean',
        'array': None,  # 需要特殊处理
        'object': None   # 需要特殊处理
    }

    def __init__(self, json_file_path: str, output_dir: Optional[str] = None):
        """
        初始化转换器

        Args:
            json_file_path: JSON schema 文件路径
            output_dir: 输出目录（可选）
        """
        self.json_file_path = Path(json_file_path)
        self.output_dir = Path(output_dir) if output_dir else self.json_file_path.parent

        # 加载 JSON schema
        with open(self.json_file_path, 'r', encoding='utf-8') as f:
            self.json_schema = json.load(f)

        # 提取元数据
        self._extract_metadata()

    @staticmethod
    def _create_enum_type(enum_name: str, enum_def: Dict[str, Any]) -> ET.Element:
        """
        创建枚举类型

        Args:
            enum_name: 枚举类型名称
            enum_def: 枚举定义

        Returns:
            枚举类型元素
        """
        enum_type = ET.Element('EnumType', {'Name': enum_name})

        enum_values = enum_def.get('enum', [])
        enum_descriptions = enum_def.get('enumDescriptions', {})
        enum_long_descriptions = enum_def.get('enumLongDescriptions', {})

        for value in enum_values:
            member = ET.SubElement(enum_type, 'Member', {'Name': value})

            # 添加描述注解
            if value in enum_descriptions:
                ET.SubElement(member, 'Annotation', {'Term': 'OData.Description', 'String': enum_descriptions[value]})

            # 添加长描述注解
            if value in enum_long_descriptions:
                ET.SubElement(member, 'Annotation', {'Term': 'OData.LongDescription', 'String': enum_long_descriptions[value]})

        return enum_type

    @staticmethod
    def _parse_external_ref(ref: str) -> str:
        """
        解析外部引用

        Args:
            ref: 外部引用字符串

        Returns:
            完整的类型名称
        """
        # 例如: http://redfish.dmtf.org/schemas/v1/Resource.json#/definitions/Oem
        # 或: /redfish/v1/Schemas/openUBMCBackupBatteryUnitCollection.json#/definitions/openUBMCBackupBatteryUnitCollection
        if '.json' in ref:
            match = re.search(r'/([^/]+)\.json', ref)
            if match:
                schema_name = match.group(1)
                # 提取定义名称
                if '#/definitions/' in ref:
                    def_name = ref.split('#/definitions/')[-1]
                    return f"{schema_name}.{def_name}"
                # 如果只有版本号（如 v1_0_0），直接返回 schema_name
                if '.v1_' in schema_name:
                    # 对于 openUBMCXXX.v1_0_0.json 这样的文件，schema_name 就是完整名称
                    pass
                return schema_name
        return ref

    @staticmethod
    def _infer_enum_name(prop_def: Dict[str, Any], parent_name: str = '') -> str:
        """
        推断枚举类型名称

        Args:
            prop_def: 属性定义
            parent_name: 父级名称

        Returns:
            枚举类型名称
        """
        # 如果定义中有明确的类型名称
        if '_type_name' in prop_def:
            return prop_def['_type_name']

        # 从父级名称推断
        if parent_name:
            return f"{parent_name}Type"

        return "EnumType"

    @staticmethod
    def _is_nullable(prop_def: Dict[str, Any]) -> bool:
        """
        检查属性是否可为空

        Args:
            prop_def: 属性定义

        Returns:
            是否可为空
        """
        prop_type = prop_def.get('type')
        if isinstance(prop_type, list):
            return 'null' in prop_type
        return False

    @staticmethod
    def _is_readonly(prop_def: Dict[str, Any]) -> bool:
        """
        检查属性是否只读

        Args:
            prop_def: 属性定义

        Returns:
            是否只读
        """
        return prop_def.get('readonly', False)

    @staticmethod
    def _create_annotations(prop_def: Dict[str, Any]) -> List[ET.Element]:
        """
        创建属性注解

        Args:
            prop_def: 属性定义

        Returns:
            注解元素列表
        """
        annotations = []

        # Description
        desc = prop_def.get('description')
        if desc:
            ann = ET.Element('Annotation', {'Term': 'OData.Description', 'String': str(desc)})
            annotations.append(ann)

        # LongDescription
        long_desc = prop_def.get('longDescription')
        if long_desc:
            ann = ET.Element('Annotation', {'Term': 'OData.LongDescription', 'String': str(long_desc)})
            annotations.append(ann)

        # 总是添加Permissions注解
        ann = ET.Element('Annotation', {'Term': 'OData.Permissions'})
        if JsonToCsdlConverter._is_readonly(prop_def):
            ann.set('EnumMember', 'OData.Permission/Read')
        else:
            ann.set('EnumMember', 'OData.Permission/ReadWrite')
        annotations.append(ann)

        # Minimum
        minimum = prop_def.get('minimum')
        if minimum is not None:
            ann = ET.Element('Annotation', {'Term': 'Validation.Minimum'})
            ann.set('Int', str(minimum))
            annotations.append(ann)

        # Maximum
        maximum = prop_def.get('maximum')
        if maximum is not None:
            ann = ET.Element('Annotation', {'Term': 'Validation.Maximum'})
            ann.set('Int', str(maximum))
            annotations.append(ann)

        # Required
        required = prop_def.get('required', False)
        if required:
            ann = ET.Element('Annotation', {'Term': 'Redfish.Required'})
            annotations.append(ann)

        return annotations

    def _extract_metadata(self):
        """从 JSON schema 中提取元数据"""
        # $id 包含命名空间信息
        self.schema_id = self.json_schema.get('$id', '')
        # 只有当JSON中存在这些字段时才提取，否则设为None
        self.owning_entity = self.json_schema.get('owningEntity')
        self.language = self.json_schema.get('language')
        self.copyright = self.json_schema.get('copyright', '')
        self.release = self.json_schema.get('release', '')

        # 从 title 中提取名称和版本
        # OEM JSON schema 的 title 格式: #OemAccountService.v1_0_0.OemAccountService
        title = self.json_schema.get('title', '')
        if title.startswith('#'):
            title = title[1:]

        # 解析名称和版本 (例如: OemAccountService.v1_0_0.OemAccountService)
        if '.' in title:
            parts = title.split('.')
            self.schema_name = parts[0]
            if len(parts) > 1 and parts[1].startswith('v'):
                self.version = parts[1]
            else:
                self.version = 'v1_0_0'
        else:
            self.schema_name = title
            self.version = 'v1_0_0'

        # 命名空间
        self.namespace = f"{self.schema_name}.{self.version}"

        # 主定义名称（如果有 $ref）
        ref = self.json_schema.get('$ref', '')
        if ref.startswith('#/definitions/'):
            self.main_definition = ref.replace('#/definitions/', '')
        else:
            # 如果没有 $ref，使用 schema_name
            self.main_definition = self.schema_name

        # 跟踪所有需要生成的类型（包括内联枚举）
        self._enum_types_to_generate = {}
        self._complex_types_to_generate = {}

    def _get_edm_type(self, prop_def: Dict[str, Any], parent_name: str = '') -> str:
        """
        获取属性的 EDM 类型

        Args:
            prop_def: 属性定义
            parent_name: 父级属性名称（用于推断枚举名称）

        Returns:
            EDM 类型字符串
        """
        # 处理 anyOf 类型（优先处理 $ref 引用）
        if 'anyOf' in prop_def:
            anyof_list = prop_def['anyOf']
            # 查找非null的引用类型
            for option in anyof_list:
                if '$ref' in option:
                    ref = option['$ref']
                    if ref.startswith('#/definitions/'):
                        ref_name = ref.replace('#/definitions/', '')
                        return f"{self.namespace}.{ref_name}"
                    else:
                        return self._parse_external_ref(ref)
            # 如果没有找到引用，使用第一个非null类型
            for option in anyof_list:
                if isinstance(option, dict) and 'type' in option and option['type'] != 'null':
                    prop_def = {'type': option['type'], 'items': option.get('items')}
                    break

        prop_type = prop_def.get('type')

        # 处理联合类型（包含 null）
        if isinstance(prop_type, list):
            # 过滤掉 'null'，获取实际类型
            non_null_types = [t for t in prop_type if t != 'null']
            if non_null_types:
                prop_type = non_null_types[0]
            else:
                prop_type = 'string'

        # 处理数组类型
        if prop_type == 'array':
            items_def = prop_def.get('items', {})
            if '$ref' in items_def:
                # 引用类型
                ref = items_def['$ref']
                if ref.startswith('#/definitions/'):
                    ref_name = ref.replace('#/definitions/', '')
                    item_type = f"{self.namespace}.{ref_name}"
                else:
                    item_type = self._parse_external_ref(ref)
            else:
                item_type = self._get_edm_type(items_def, parent_name)
                if item_type is None:
                    item_type = 'Edm.String'
            return f"Collection({item_type})"

        # 处理引用类型
        if '$ref' in prop_def:
            ref = prop_def['$ref']
            return self._parse_ref_type(ref)

        # 处理枚举类型
        if 'enum' in prop_def:
            # 如果有 parent_name，说明是内部定义的枚举
            if parent_name:
                enum_name = f"{parent_name}Type"
                # 注册这个枚举类型以便后续生成
                self._enum_types_to_generate[enum_name] = prop_def
                return f"{self.namespace}.{enum_name}"
            else:
                # 顶层定义的枚举
                enum_name = self._infer_enum_name(prop_def, parent_name)
                return f"{self.namespace}.{enum_name}"

        # 处理 object 类型 - 检查是否有 properties
        if prop_type == 'object' and 'properties' in prop_def:
            # 这是一个内联对象定义，应该作为复杂类型处理
            # 返回 None 以便调用者决定如何处理
            return None

        # 基本类型映射
        if prop_type in self.TYPE_MAPPING:
            return self.TYPE_MAPPING[prop_type]

        # 默认为字符串
        return 'Edm.String'

    def _parse_ref_type(self, ref: str) -> str:
        """
        解析引用类型

        Args:
            ref: 引用字符串

        Returns:
            完整的类型名称
        """
        if ref.startswith('#/definitions/'):
            # 内部引用
            ref_name = ref.replace('#/definitions/', '')
            return f"{self.namespace}.{ref_name}"
        else:
            # 外部引用
            return self._parse_external_ref(ref)

    def _create_property(self, prop_name: str, prop_def: Dict[str, Any], parent_name: str = '') -> ET.Element:
        """
        创建属性元素

        Args:
            prop_name: 属性名称
            prop_def: 属性定义
            parent_name: 父级属性名称（用于推断枚举名称）

        Returns:
            属性元素
        """
        edm_type = self._get_edm_type(prop_def, parent_name)
        nullable = JsonToCsdlConverter._is_nullable(prop_def)

        # 确保类型不为 None
        if edm_type is None:
            edm_type = 'Edm.String'

        attrs = {
            'Name': prop_name,
            'Type': edm_type
        }

        if nullable:
            attrs['Nullable'] = 'true'

        prop = ET.Element('Property', attrs)

        return prop

    def _create_navigation_property(self, prop_name: str, prop_def: Dict[str, Any], parent_name: str = '') -> ET.Element:
        """
        创建导航属性元素

        Args:
            prop_name: 属性名称
            prop_def: 属性定义
            parent_name: 父级属性名称

        Returns:
            导航属性元素
        """
        edm_type = self._get_edm_type(prop_def, parent_name)

        # 确保类型不为 None
        if edm_type is None:
            edm_type = 'Edm.String'

        # 检查是否是集合类型
        is_collection = prop_def.get('type') == 'array'

        attrs = {
            'Name': prop_name,
            'Type': edm_type
        }

        if not is_collection:
            attrs['ContainsTarget'] = 'true'

        nav_prop = ET.Element('NavigationProperty', attrs)

        return nav_prop

    def _create_complex_type(self, type_name: str, type_def: Dict[str, Any]) -> ET.Element:
        """
        创建复杂类型

        Args:
            type_name: 类型名称
            type_def: 类型定义

        Returns:
            复杂类型元素
        """
        attrs = {'Name': type_name}

        # 检查是否有 BaseType
        if 'BaseType' in type_def:
            attrs['BaseType'] = type_def['BaseType']
        else:
            # 默认 BaseType 为 Resource.OemObject（用于OpenBMC OEM扩展）
            attrs['BaseType'] = 'Resource.OemObject'

        # 检查是否是抽象类型
        if type_def.get('Abstract', False):
            attrs['Abstract'] = 'true'

        complex_type = ET.Element('ComplexType', attrs)

        # 添加描述注解
        if 'description' in type_def:
            ET.SubElement(complex_type, 'Annotation', {'Term': 'OData.Description', 'String': type_def['description']})

        if 'longDescription' in type_def:
            ET.SubElement(complex_type, 'Annotation', {'Term': 'OData.LongDescription', 'String': type_def['longDescription']})

        # 添加 additionalProperties 注解
        if 'additionalProperties' in type_def:
            add_props_value = type_def['additionalProperties']
            # 转换为字符串 "true" 或 "false"
            if isinstance(add_props_value, bool):
                bool_str = 'true' if add_props_value else 'false'
                ET.SubElement(complex_type, 'Annotation', {
                    'Term': 'OData.AdditionalProperties',
                    'Bool': bool_str
                })

        # 添加属性
        properties = type_def.get('properties', {})
        for prop_name, prop_def in properties.items():
            # 跳过 OData 系统属性
            if prop_name.startswith('@odata.'):
                continue

            # 判断属性类型和是否包含引用
            prop_type = prop_def.get('type')
            has_direct_ref = '$ref' in prop_def
            has_items_ref = (prop_type == 'array' and
                           '$ref' in prop_def.get('items', {}))

            # 如果是直接引用（非数组），创建导航属性
            if has_direct_ref and prop_type != 'array':
                # 创建导航属性
                nav_prop = self._create_navigation_property(prop_name, prop_def, type_name)
                complex_type.append(nav_prop)

                # 添加注解
                for ann in self._create_annotations(prop_def):
                    nav_prop.append(ann)
            else:
                # 创建普通属性（包括数组+items引用的情况）
                prop = self._create_property(prop_name, prop_def, type_name)
                complex_type.append(prop)

                # 添加注解
                for ann in self._create_annotations(prop_def):
                    prop.append(ann)

        return complex_type

    def _create_entity_type(self, type_name: str, type_def: Dict[str, Any]) -> ET.Element:
        """
        创建实体类型

        Args:
            type_name: 类型名称
            type_def: 类型定义

        Returns:
            实体类型元素
        """
        # 检查是否有 anyOf 结构
        if 'anyOf' in type_def:
            return self._create_entity_type_from_anyof(type_name, type_def)

        attrs = {
            'Name': type_name,
            'Abstract': 'true'
        }

        # 检查基类型
        base_type = type_def.get('BaseType')
        if base_type:
            attrs['BaseType'] = base_type
        else:
            # 默认基类型
            attrs['BaseType'] = 'Resource.v1_0_0.Resource'

        entity_type = ET.Element('EntityType', attrs)

        # 添加描述注解
        if 'description' in type_def:
            ET.SubElement(entity_type, 'Annotation', {'Term': 'OData.Description', 'String': type_def['description']})

        if 'longDescription' in type_def:
            ET.SubElement(entity_type, 'Annotation', {'Term': 'OData.LongDescription', 'String': type_def['longDescription']})

        # 添加属性
        properties = type_def.get('properties', {})
        for prop_name, prop_def in properties.items():
            # 跳过 OData 系统属性
            if prop_name.startswith('@odata.'):
                continue

            # 特殊处理 Actions 和 Links
            if prop_name in ['Actions', 'Links']:
                continue

            # 检查是否是引用类型
            has_ref = '$ref' in prop_def
            has_items_ref = (prop_def.get('type') == 'array' and
                           '$ref' in prop_def.get('items', {}))

            if has_ref or has_items_ref:
                # 创建导航属性
                nav_prop = self._create_navigation_property(prop_name, prop_def, type_name)
                entity_type.append(nav_prop)

                # 添加注解
                for ann in self._create_annotations(prop_def):
                    nav_prop.append(ann)
            elif prop_name == '@odata.id' and prop_def.get('type') == 'string':
                # 特殊处理 @odata.id
                nav_prop = ET.SubElement(entity_type, 'NavigationProperty', {
                    'Name': prop_name.replace('@', ''),
                    'Type': 'Edm.String',
                    'ContainsTarget': 'true'
                })
            else:
                # 创建普通属性
                prop = self._create_property(prop_name, prop_def, type_name)
                entity_type.append(prop)

                # 添加注解
                for ann in self._create_annotations(prop_def):
                    prop.append(ann)

        return entity_type

    def _create_entity_type_from_anyof(self, type_name: str, type_def: Dict[str, Any]) -> ET.Element:
        """
        从 anyOf 定义创建实体类型

        Args:
            type_name: 类型名称
            type_def: 包含 anyOf 的类型定义

        Returns:
            实体类型元素
        """
        anyof_list = type_def.get('anyOf', [])

        # 查找实际的属性定义（anyOf 中的 object 类型）
        actual_properties = {}
        actual_type_def = {}
        has_idref = False

        for option in anyof_list:
            # 检查是否是 idRef 引用
            if '$ref' in option:
                ref = option['$ref']
                if 'idRef' in ref or 'idref' in ref.lower():
                    has_idref = True
                    continue

            # 检查是否有实际的 object 定义
            if option.get('type') == 'object' and 'properties' in option:
                actual_type_def = option
                actual_properties = option.get('properties', {})
                break

        attrs = {
            'Name': type_name,
            'Abstract': 'true'
        }

        # 检查基类型
        base_type = type_def.get('BaseType')
        if base_type:
            attrs['BaseType'] = base_type
        else:
            # 默认基类型
            attrs['BaseType'] = 'Resource.v1_0_0.Resource'

        entity_type = ET.Element('EntityType', attrs)

        # 添加描述注解
        description = type_def.get('description') or actual_type_def.get('description')
        if description:
            ET.SubElement(entity_type, 'Annotation', {'Term': 'OData.Description', 'String': description})

        long_description = type_def.get('longDescription') or actual_type_def.get('longDescription')
        if long_description:
            ET.SubElement(entity_type, 'Annotation', {'Term': 'OData.LongDescription', 'String': long_description})

        # 添加 uris、deprecated 等属性注解（如果有）
        if 'uris' in type_def:
            for uri in type_def.get('uris', []):
                uri_elem = ET.SubElement(entity_type, 'Annotation', {'Term': 'Redfish.Uris'})
                collection = ET.SubElement(uri_elem, 'Collection')
                ET.SubElement(collection, 'String').text = uri

        if 'urisDeprecated' in type_def:
            for uri in type_def.get('urisDeprecated', []):
                uri_elem = ET.SubElement(entity_type, 'Annotation', {'Term': 'Redfish.DeprecatedUris'})
                collection = ET.SubElement(uri_elem, 'Collection')
                ET.SubElement(collection, 'String').text = uri

        # 添加 Capabilities 注解（insertable, updatable, deletable）
        if 'insertable' in type_def or 'updatable' in type_def or 'deletable' in type_def:
            capabilities_elem = ET.SubElement(entity_type, 'Annotation', {'Term': 'Capabilities.Insertable'})

            if 'insertable' in type_def:
                for record in capabilities_elem.iter('Record'):
                    capabilities_elem.remove(record)

                record = ET.SubElement(capabilities_elem, 'Record')
                ET.SubElement(record, 'PropertyValue', {
                    'Property': 'Insertable',
                    'Bool': 'true' if type_def.get('insertable', True) else 'false'
                })

            if 'updatable' in type_def:
                for record in capabilities_elem.iter('Record'):
                    capabilities_elem.remove(record)

                record = ET.SubElement(capabilities_elem, 'Record')
                ET.SubElement(record, 'PropertyValue', {
                    'Property': 'Updatable',
                    'Bool': 'true' if type_def.get('updatable', True) else 'false'
                })

            if 'deletable' in type_def:
                for record in capabilities_elem.iter('Record'):
                    capabilities_elem.remove(record)

                record = ET.SubElement(capabilities_elem, 'Record')
                ET.SubElement(record, 'PropertyValue', {
                    'Property': 'Deletable',
                    'Bool': 'true' if type_def.get('deletable', True) else 'false'
                })

        # 添加属性
        for prop_name, prop_def in actual_properties.items():
            # 跳过 OData 系统属性
            if prop_name.startswith('@odata.'):
                continue

            # 特殊处理 Actions 和 Links
            if prop_name in ['Actions', 'Links']:
                continue

            # 检查是否是引用类型
            has_ref = '$ref' in prop_def
            has_items_ref = (prop_def.get('type') == 'array' and
                           '$ref' in prop_def.get('items', {}))

            if has_ref or has_items_ref:
                # 创建导航属性
                nav_prop = self._create_navigation_property(prop_name, prop_def, type_name)
                entity_type.append(nav_prop)

                # 添加注解
                for ann in self._create_annotations(prop_def):
                    nav_prop.append(ann)
            elif prop_name == '@odata.id' and prop_def.get('type') == 'string':
                # 特殊处理 @odata.id
                nav_prop = ET.SubElement(entity_type, 'NavigationProperty', {
                    'Name': prop_name.replace('@', ''),
                    'Type': 'Edm.String',
                    'ContainsTarget': 'true'
                })
            else:
                # 创建普通属性
                prop = self._create_property(prop_name, prop_def, type_name)
                entity_type.append(prop)

                # 添加注解
                for ann in self._create_annotations(prop_def):
                    prop.append(ann)

        return entity_type

    @staticmethod
    def _create_action(action_name: str, action_def: Dict[str, Any]) -> ET.Element:
        """
        创建 Action

        Args:
            action_name: Action 名称
            action_def: Action 定义

        Returns:
            Action 元素
        """
        # 清理 action 名称（移除 # 前缀）
        clean_name = action_name.lstrip('#')

        action = ET.Element('Action', {
            'Name': clean_name.split('.')[-1],  # 获取最后一部分作为名称
            'IsBound': 'true'
        })

        # 添加描述注解
        if 'description' in action_def:
            ET.SubElement(action, 'Annotation', {'Term': 'OData.Description', 'String': action_def['description']})

        if 'longDescription' in action_def:
            ET.SubElement(action, 'Annotation', {'Term': 'OData.LongDescription', 'String': action_def['longDescription']})

        return action

    def _convert(self) -> str:
        """
        执行转换

        Returns:
            格式化后的 XML 字符串
        """
        # 创建根元素
        edmx = ET.Element('edmx:Edmx', {
            'xmlns:edmx': self.NAMESPACES['edmx'],
            'Version': '4.0'
        })

        # 添加引用
        refs = JsonToCsdlConverter._create_references()
        for ref in refs:
            edmx.append(ref)

        # 添加数据服务
        data_services = ET.SubElement(edmx, 'edmx:DataServices')

        # 处理定义
        definitions = self.json_schema.get('definitions', {})

        # 首先收集并处理顶层枚举类型
        enum_types = {}
        complex_types = {}
        entity_type = None
        actions = {}

        for def_name, def_value in definitions.items():
            # 检查是否是 Action 定义（有 title 和 target 属性）
            if ('properties' in def_value and
                'title' in def_value.get('properties', {}) and
                'target' in def_value.get('properties', {})):
                actions[def_name] = def_value
                continue

            # 检查是否是枚举类型
            if 'enum' in def_value and def_value.get('type') == 'string':
                enum_types[def_name] = def_value
                continue

            # 检查是否是主定义（实体类型）
            if def_name == self.main_definition:
                entity_type = (def_name, def_value)
                continue

            # 其他对象类型作为复杂类型
            # 注意：有些定义可能省略 type 字段，只要有 properties 就视为对象类型
            if (def_value.get('type') == 'object' or 'properties' in def_value) and 'enum' not in def_value:
                # 排除已经处理过的枚举类型
                complex_types[def_name] = def_value

        # 1. 创建不带版本号的 Schema（用于生成基文件）
        unversioned_schema = ET.SubElement(data_services, 'Schema', {
            'xmlns': self.NAMESPACES['edm'],
            'Namespace': self.schema_name
        })

        # 添加不带版本号 Schema 的注解（只在JSON中有这些字段时才添加）
        if self.owning_entity is not None:
            ET.SubElement(unversioned_schema, 'Annotation', {'Term': 'Redfish.OwningEntity', 'String': self.owning_entity})
        if self.language is not None:
            ET.SubElement(unversioned_schema, 'Annotation', {'Term': 'Redfish.Language', 'String': self.language})

        # 在不带版本号的 schema 中添加抽象实体类型（带有 anyOf 引用）
        if entity_type:
            type_name, type_def = entity_type
            # 创建不带版本号的抽象 EntityType
            attrs = {
                'Name': type_name,
                'Abstract': 'true'
            }
            # 检查基类型
            base_type = type_def.get('BaseType')
            if base_type:
                attrs['BaseType'] = base_type
            else:
                # 默认基类型（使用不带版本号的 Resource）
                attrs['BaseType'] = 'Resource.v1_0_0.Resource'

            entity = ET.Element('EntityType', attrs)
            unversioned_schema.append(entity)

        # 2. 创建带版本号的 Schema（包含完整的类型定义）
        versioned_schema = ET.SubElement(data_services, 'Schema', {
            'xmlns': self.NAMESPACES['edm'],
            'Namespace': self.namespace
        })

        # 添加带版本号 Schema 的注解（只在JSON中有这些字段时才添加）
        if self.owning_entity is not None:
            ET.SubElement(versioned_schema, 'Annotation', {'Term': 'Redfish.OwningEntity', 'String': self.owning_entity})
        if self.language is not None:
            ET.SubElement(versioned_schema, 'Annotation', {'Term': 'Redfish.Language', 'String': self.language})

        # 首先添加顶层枚举类型
        for enum_name, enum_def in enum_types.items():
            enum_type = self._create_enum_type(enum_name, enum_def)
            versioned_schema.append(enum_type)

        # 添加复杂类型（这会收集内部枚举）
        for type_name, type_def in complex_types.items():
            complex_type = self._create_complex_type(type_name, type_def)
            versioned_schema.append(complex_type)

        # 添加内部枚举类型
        for enum_name, enum_def in self._enum_types_to_generate.items():
            enum_type = self._create_enum_type(enum_name, enum_def)
            versioned_schema.append(enum_type)

        # 添加实体类型
        if entity_type:
            type_name, type_def = entity_type
            entity = self._create_entity_type(type_name, type_def)
            versioned_schema.append(entity)

        # 如果带版本号的Schema中没有任何实际内容（只有空的EntityType），则移除它
        # 检查是否有实际的子元素
        has_content = False
        for child in versioned_schema:
            # Annotation注解不算作实际内容
            if child.tag == 'Annotation' or child.tag.endswith('Annotation'):
                continue
            # 排除只有Name和Abstract属性的空EntityType
            elif child.tag == 'EntityType' or child.tag.endswith('EntityType'):
                # 检查是否有属性或子元素
                has_properties = False
                for prop in child:
                    # 排除Annotation注解
                    if not (prop.tag == 'Annotation' or prop.tag.endswith('Annotation')):
                        has_properties = True
                        break
                if has_properties:
                    has_content = True
                    break
            else:
                # EnumType或ComplexType算作有内容
                has_content = True
                break

        # 如果带版本号的Schema没有实际内容（只有空的EntityType），则移除它
        # 不带版本号的Schema应该保留，因为它定义了类型的存在（即使是空的）
        if not has_content:
            data_services.remove(versioned_schema)

        # 格式化输出
        return JsonToCsdlConverter._format_xml(edmx)

    @staticmethod
    def _create_references() -> List[ET.Element]:
        """
        创建引用元素列表

        Returns:
            引用元素列表
        """
        refs = []

        # 标准引用（只包含必要的引用）
        # 注意：Resource和Resource.v1_0_0应该在同一个Reference中
        standard_refs = [
            ('http://docs.oasis-open.org/odata/odata/v4.0/errata03/csd01/complete/vocabularies/Org.OData.Core.V1.xml',
             [('Org.OData.Core.V1', 'OData')]),
            ('http://docs.oasis-open.org/odata/odata/v4.0/errata03/csd01/complete/vocabularies/Org.OData.Measures.V1.xml',
             [('Org.OData.Measures.V1', 'Measures')]),
            ('http://redfish.dmtf.org/schemas/v1/RedfishExtensions_v1.xml',
             [('RedfishExtensions.v1_0_0', 'Redfish')]),
            ('http://redfish.dmtf.org/schemas/v1/Resource_v1.xml',
             [('Resource', None), ('Resource.v1_0_0', None)]),
        ]

        for uri, includes in standard_refs:
            ref_elem = ET.Element('edmx:Reference', {'Uri': uri})
            for namespace, alias in includes:
                include_elem = ET.SubElement(ref_elem, 'edmx:Include', {'Namespace': namespace})
                if alias:
                    include_elem.set('Alias', alias)
            refs.append(ref_elem)

        return refs

    @staticmethod
    def _format_xml(elem: ET.Element) -> str:
        """
        格式化 XML 输出

        Args:
            elem: XML 元素

        Returns:
            格式化后的 XML 字符串
        """
        # 只添加 XML 声明，不添加注释
        header = '<?xml version="1.0" encoding="UTF-8"?>\n'

        # 使用 minidom 格式化
        rough_string = ET.tostring(elem, encoding='unicode')
        reparsed = minidom.parseString(rough_string)
        pretty_xml = reparsed.toprettyxml(indent='  ', encoding='UTF-8').decode('utf-8')

        # 移除 minidom 添加的额外声明
        lines = pretty_xml.split('\n')
        if lines[0].startswith('<?xml'):
            lines = lines[1:]

        # 过滤掉空行和只包含空格的行
        lines = [line for line in lines if line.strip()]

        # 修复 Schema 命名空间：将 <ns1:Schema 转换为 <Schema xmlns="..."
        # 将 </ns1:Schema> 转换为 </Schema>
        # 将 <ns1: 转换为 <，将 </ns1: 转换为 </
        fixed_lines = []
        for line in lines:
            # 移除多余的 xmlns:ns1 声明
            line = line.replace(' xmlns:ns1="http://docs.oasis-open.org/odata/ns/edm"', '')

            # 修复开头的 Schema 标签
            if '<ns1:Schema ' in line or '<ns1:Schema>' in line:
                line = line.replace('<ns1:Schema ', '<Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" ')
                line = line.replace('<ns1:Schema>', '<Schema xmlns="http://docs.oasis-open.org/odata/ns/edm">')
            # 修复结尾的 Schema 标签
            line = line.replace('</ns1:Schema>', '</Schema>')
            # 修复其他子元素的开头标签
            line = line.replace('<ns1:', '<')
            # 修复其他子元素的结尾标签
            line = line.replace('</ns1:', '</')
            fixed_lines.append(line)

        return header + '\n'.join(fixed_lines)

    @staticmethod
    def _merge_schema_definitions(existing_schema: ET.Element, new_schema: ET.Element, namespaces: dict):
        """
        合并两个 Schema 的定义

        Args:
            existing_schema: 现有的 Schema 元素
            new_schema: 新的 Schema 元素
            namespaces: XML 命名空间
        """
        # 收集现有 Schema 中的所有类型
        existing_types = {}

        # 收集现有 EntityType
        for entity in existing_schema:
            if entity.tag == 'EntityType' or entity.tag.endswith('EntityType'):
                existing_types[entity.get('Name')] = entity

        # 收集现有 ComplexType
        for complex_type in existing_schema:
            if complex_type.tag == 'ComplexType' or complex_type.tag.endswith('ComplexType'):
                existing_types[complex_type.get('Name')] = complex_type

        # 收集现有 EnumType
        for enum_type in existing_schema:
            if enum_type.tag == 'EnumType' or enum_type.tag.endswith('EnumType'):
                existing_types[enum_type.get('Name')] = enum_type

        # 遍历新 Schema 的所有元素
        for child in new_schema:
            child_name = child.get('Name')
            child_tag = child.tag

            if child_name is None:
                # 没有名称的元素（如 Annotation）
                # 检查是否是 OwningEntity 或 Language 注解，避免重复添加
                if child_tag == 'Annotation' or child_tag.endswith('Annotation'):
                    term = child.get('Term')
                    # 检查是否已经存在相同Term的注解
                    already_exists = False
                    for existing_child in existing_schema:
                        if (existing_child.tag == 'Annotation' or existing_child.tag.endswith('Annotation')):
                            if existing_child.get('Term') == term:
                                already_exists = True
                                break
                    if not already_exists:
                        existing_schema.append(child)
                else:
                    # 其他没有名称的元素直接添加
                    existing_schema.append(child)
                continue

            if child_name not in existing_types:
                # 新类型，直接添加
                existing_schema.append(child)
                existing_types[child_name] = child
            else:
                # 类型已存在，根据类型决定如何合并
                if child.tag == 'EntityType' or child.tag.endswith('EntityType'):
                    # EntityType 通常不合并，跳过
                    continue
                elif child.tag == 'ComplexType' or child.tag.endswith('ComplexType'):
                    # ComplexType 不合并，跳过
                    continue
                elif child.tag == 'EnumType' or child.tag.endswith('EnumType'):
                    # EnumType 不合并，跳过
                    continue
                else:
                    # 其他类型直接添加
                    existing_schema.append(child)

    def _merge_to_existing(self, existing_file: Path) -> str:
        """
        合并新的 Schema 定义到现有 XML 文件

        Args:
            existing_file: 已存在的 XML 文件路径

        Returns:
            合并后的 XML 字符串
        """
        # 解析现有 XML
        tree = ET.parse(existing_file)
        root = tree.getroot()

        # 获取命名空间
        namespaces = {
            'edmx': 'http://docs.oasis-open.org/odata/ns/edmx',
            'edm': 'http://docs.oasis-open.org/odata/ns/edm'
        }

        # 找到 DataServices
        data_services = None
        for child in root.iter():
            if child.tag == 'edmx:DataServices' or child.tag.endswith('DataServices'):
                data_services = child
                break
        if data_services is None:
            # 如果没有 DataServices，直接使用新内容
            return self._convert()

        # 获取新的 Schema 元素（用于比较）
        new_edmx = self._create_edmx_element()
        new_data_services = None
        for child in new_edmx.iter():
            if child.tag == 'edmx:DataServices' or child.tag.endswith('DataServices'):
                new_data_services = child
                break

        if new_data_services is None:
            # 没有找到 DataServices，直接返回新内容
            return self._convert()

        new_schema_element = None
        for child in new_data_services:
            if child.tag == 'Schema' or child.tag.endswith('Schema'):
                ns_attr = child.get('Namespace')
                if ns_attr == self.namespace:
                    new_schema_element = child
                    break

        if new_schema_element is None:
            # 没有新的 Schema，直接返回现有内容
            return JsonToCsdlConverter._format_xml(root)

        # 查找现有 Schema
        existing_schema = None
        for child in data_services:
            if child.tag == 'Schema' or child.tag.endswith('Schema'):
                ns_attr = child.get('Namespace')
                if ns_attr == self.namespace:
                    existing_schema = child
                    break

        if existing_schema is None:
            # 如果不存在相同命名空间的 Schema，直接追加
            data_services.append(new_schema_element)
        else:
            # 命名空间已存在，合并子元素
            JsonToCsdlConverter._merge_schema_definitions(existing_schema, new_schema_element, namespaces)

        # 格式化输出
        return JsonToCsdlConverter._format_xml(root)

    def _create_edmx_element(self) -> ET.Element:
        """
        创建 EDMX 根元素（用于内部处理）

        Returns:
            EDMX 元素
        """
        # 创建根元素
        edmx = ET.Element('edmx:Edmx', {
            'xmlns:edmx': self.NAMESPACES['edmx'],
            'Version': '4.0'
        })

        # 添加引用
        refs = JsonToCsdlConverter._create_references()
        for ref in refs:
            edmx.append(ref)

        # 添加数据服务
        data_services = ET.SubElement(edmx, 'edmx:DataServices')
        schema = ET.SubElement(data_services, 'Schema', {
            'xmlns': self.NAMESPACES['edm'],
            'Namespace': self.namespace
        })

        # 添加 Schema 注解（只在JSON中有这些字段时才添加）
        if self.owning_entity is not None:
            ET.SubElement(schema, 'Annotation', {'Term': 'Redfish.OwningEntity', 'String': self.owning_entity})
        if self.language is not None:
            ET.SubElement(schema, 'Annotation', {'Term': 'Redfish.Language', 'String': self.language})

        # 处理定义
        definitions = self.json_schema.get('definitions', {})

        # 首先处理枚举类型
        for def_name, def_value in definitions.items():
            if 'enum' in def_value and def_value.get('type') == 'string':
                enum_type = self._create_enum_type(def_name, def_value)
                schema.append(enum_type)

        # 然后处理复杂类型和实体类型
        for def_name, def_value in definitions.items():
            # 跳过枚举类型和 Action
            if 'enum' in def_value:
                continue
            if def_name.endswith(('RootCertificate', 'ImportRootCertificate', 'DeleteRootCertificate',
                                   'ImportCrl', 'DeleteCrl')):
                # 这些看起来像 Action
                continue

            # 判断是复杂类型还是实体类型
            if def_name == self.main_definition:
                # 主定义作为实体类型
                entity_type = self._create_entity_type(def_name, def_value)
                schema.append(entity_type)
            elif (def_value.get('type') == 'object' or 'properties' in def_value) and 'enum' not in def_value:
                # 其他对象类型作为复杂类型（包括省略 type 的定义）
                complex_type = self._create_complex_type(def_name, def_value)
                schema.append(complex_type)

        # 处理 Actions
        for def_name, def_value in definitions.items():
            if def_value.get('type') == 'object' and 'title' in def_value and 'target' in def_value:
                # 这是一个 Action 定义
                action = self._create_action(def_name, def_value)
                schema.append(action)

        return edmx

    def save(self, output_path: Optional[str] = None):
        """
        保存转换结果

        Args:
            output_path: 输出文件路径（可选）
        """
        xml_content = self._convert()

        if output_path is None:
            # 自动生成输出文件名
            # oemaccountservice.v1_0_0.json -> oemaccountservice_v1.xml
            # OpenBMCAccountService.v1_0_0.json -> OpenBMCAccountService_v1.xml
            base_name = self.json_file_path.stem
            # 分割文件名
            name_parts = base_name.split('.')
            if len(name_parts) > 1:
                schema_name = name_parts[0]
                # 保持原始大小写，不要转换为小写
                output_name = f"{schema_name}_v1.xml"
            else:
                output_name = f"{base_name}_v1.xml"
            output_path = self.output_dir / output_name
        else:
            output_path = Path(output_path)
        
        # 检查文件是否已存在
        if output_path.exists():
            # 合并到现有文件
            xml_content = self._merge_to_existing(output_path)
        else:
            # 创建新文件
            xml_content = self._convert()

        with open(output_path, 'w', encoding='utf-8') as f:
            f.write(xml_content)

        print(f"转换完成: {output_path}")
        return output_path


def convert_file(json_file: str, output_dir: Optional[str] = None) -> Path:
    """
    转换单个 JSON schema 文件

    Args:
        json_file: JSON 文件路径
        output_dir: 输出目录

    Returns:
        输出文件路径
    """
    converter = JsonToCsdlConverter(json_file, output_dir)
    return converter.save()


def convert_directory(input_dir: str, output_dir: Optional[str] = None):
    """
    转换目录中的所有 JSON schema 文件

    Args:
        input_dir: 输入目录
        output_dir: 输出目录
    """
    input_path = Path(input_dir)
    if not input_path.is_dir():
        print(f"错误: {input_dir} 不是目录")
        return

    output_path = Path(output_dir) if output_dir else input_path

    # 查找所有 JSON 文件
    json_files = list(input_path.glob('*.json'))

    if not json_files:
        print(f"在 {input_dir} 中未找到 JSON 文件")
        return

    print(f"找到 {len(json_files)} 个 JSON 文件")

    # 清空输出目录中的所有文件
    if output_path.exists() and output_path.is_dir():
        import shutil
        for file in output_path.glob('*'):
            if file.is_file():
                file.unlink()
        print(f"已清空输出目录: {output_path}")

    # 转换每个文件
    success_count = 0
    for json_file in json_files:
        try:
            converter = JsonToCsdlConverter(str(json_file), str(output_path))
            converter.save()
            success_count += 1
        except Exception as e:
            print(f"转换失败 {json_file}: {e}")

    print(f"\n转换完成: {success_count}/{len(json_files)} 个文件成功")


def main():
    """主函数"""
    if len(sys.argv) < 2:
        print("用法:")
        print("  python3 json_to_csdl.py --input <input_dir> --output <output_dir>")
        sys.exit(1)

    # 解析命令行参数
    input_dir = None
    output_dir = None

    i = 1
    while i < len(sys.argv):
        if sys.argv[i] == '--input':
            if i + 1 >= len(sys.argv):
                print("错误: --input 参数需要指定目录")
                sys.exit(1)
            input_dir = sys.argv[i + 1]
            i += 2
        elif sys.argv[i] == '--output':
            if i + 1 >= len(sys.argv):
                print("错误: --output 参数需要指定目录")
                sys.exit(1)
            output_dir = sys.argv[i + 1]
            i += 2
        else:
            print(f"错误: 未知参数 {sys.argv[i]}")
            print("用法: python3 json_to_csdl.py --input <input_dir> --output <output_dir>")
            sys.exit(1)

    if not input_dir:
        print("错误: 需要指定 --input 参数")
        sys.exit(1)

    # 转换目录
    convert_directory(input_dir, output_dir)


if __name__ == '__main__':
    main()

