"""本体提取服务层

本模块提供本体提取的业务逻辑封装,协调OntologyExtractor和OWLValidator。
包括本体提取、OWL文件导出等功能。

Classes:
    OntologyService: 本体提取服务类,封装业务逻辑
"""

import logging
import time
from typing import Any, Dict, List, Optional

from sqlalchemy.orm import Session

from app.core.memory.llm_tools.openai_client import OpenAIClient
from app.core.memory.models.ontology_scenario_models import (
    OntologyClass,
    OntologyExtractionResponse,
)
from app.core.memory.storage_services.extraction_engine.knowledge_extraction.ontology_extraction import (
    OntologyExtractor,
)
from app.core.memory.utils.validation.owl_validator import OWLValidator


logger = logging.getLogger(__name__)


class OntologyService:
    """本体提取服务层
    
    封装本体提取的业务逻辑,协调各个组件:
    - OntologyExtractor: 执行LLM驱动的本体提取
    - OWLValidator: OWL语义验证
    
    Attributes:
        extractor: 本体提取器实例
        owl_validator: OWL验证器实例
        db: 数据库会话
    """
    
    # 默认配置参数
    DEFAULT_MAX_CLASSES = 15
    DEFAULT_MIN_CLASSES = 5
    DEFAULT_MAX_DESCRIPTION_LENGTH = 500
    DEFAULT_LLM_TEMPERATURE = 0.3
    DEFAULT_LLM_MAX_TOKENS = 2000
    DEFAULT_LLM_TIMEOUT = 30.0
    DEFAULT_ENABLE_OWL_VALIDATION = True
    
    # 从环境变量获取默认语言
    from app.core.config import settings
    DEFAULT_LANGUAGE = settings.DEFAULT_LANGUAGE
    
    def __init__(
        self,
        llm_client: OpenAIClient,
        db: Session
    ):
        """初始化本体提取服务
        
        Args:
            llm_client: OpenAI客户端实例
            db: SQLAlchemy数据库会话
        """
        self.extractor = OntologyExtractor(llm_client)
        self.owl_validator = OWLValidator()
        self.db = db
        
        # 初始化Repository
        from app.repositories.ontology_scene_repository import OntologySceneRepository
        from app.repositories.ontology_class_repository import OntologyClassRepository
        
        self.scene_repo = OntologySceneRepository(db)
        self.class_repo = OntologyClassRepository(db)
        
        logger.info("OntologyService initialized")
    
    async def extract_ontology(
        self,
        scenario: str,
        domain: Optional[str] = None,
        scene_id: Optional[Any] = None,
        workspace_id: Optional[Any] = None,
        language: str = "zh"
    ) -> OntologyExtractionResponse:
        """执行本体提取
        
        使用默认配置参数调用OntologyExtractor执行提取。
        提取结果仅返回给前端，不会自动保存到数据库。
        前端需要调用 /class 接口来保存选中的类型。
        
        Args:
            scenario: 场景描述文本
            domain: 可选的领域提示
            scene_id: 可选的场景ID,用于权限验证（不再用于自动保存）
            workspace_id: 可选的工作空间ID,用于权限验证
            language: 输出语言 ("zh" 中文, "en" 英文)
            
        Returns:
            OntologyExtractionResponse: 提取结果
            
        Raises:
            ValueError: 场景描述为空、场景不存在或无权限
            RuntimeError: 提取过程失败
            
        Examples:
            >>> service = OntologyService(llm_client, db)
            >>> response = await service.extract_ontology(
            ...     scenario="医院管理患者记录...",
            ...     domain="Healthcare",
            ...     scene_id=scene_uuid,
            ...     workspace_id=workspace_uuid
            ... )
            >>> len(response.classes)
            7
        """
        # 开始计时
        start_time = time.time()
        
        # 验证输入
        if not scenario or not scenario.strip():
            logger.error("Scenario description is empty")
            raise ValueError("Scenario description cannot be empty")
        
        # 如果提供了scene_id,验证场景是否存在且有权限
        if scene_id and workspace_id:
            logger.info(f"Validating scene access - scene_id={scene_id}, workspace_id={workspace_id}")
            scene = self.scene_repo.get_by_id(scene_id)
            if not scene:
                logger.warning(f"Scene not found: {scene_id}")
                raise ValueError("场景不存在")
            
            if not self.scene_repo.check_ownership(scene_id, workspace_id):
                logger.warning(
                    f"Permission denied - scene_id={scene_id}, "
                    f"workspace_id={workspace_id}"
                )
                raise ValueError("无权限在该场景下创建类型")
        
        logger.info(
            f"Starting ontology extraction service - "
            f"scenario_length={len(scenario)}, "
            f"domain={domain}, "
            f"scene_id={scene_id}"
        )
        
        try:
            # 调用提取器执行提取(使用默认配置)
            logger.info("Calling OntologyExtractor with default config")
            extraction_start_time = time.time()
            
            response = await self.extractor.extract_ontology_classes(
                scenario=scenario,
                domain=domain,
                max_classes=self.DEFAULT_MAX_CLASSES,
                min_classes=self.DEFAULT_MIN_CLASSES,
                enable_owl_validation=self.DEFAULT_ENABLE_OWL_VALIDATION,
                llm_temperature=self.DEFAULT_LLM_TEMPERATURE,
                llm_max_tokens=self.DEFAULT_LLM_MAX_TOKENS,
                max_description_length=self.DEFAULT_MAX_DESCRIPTION_LENGTH,
                timeout=self.DEFAULT_LLM_TIMEOUT,
                language=language,
            )
            
            extraction_duration = time.time() - extraction_start_time
            
            # 检查是否成功提取到类
            if not response.classes:
                logger.error("Ontology extraction failed: No classes extracted (structured output may have failed)")
                raise RuntimeError("本体提取失败：结构化输出失败，未能提取到任何本体类")
            
            # 注释：提取结果仅返回给前端，不保存到数据库
            # 前端将从返回结果中选择需要的类型，然后调用 /class 接口创建
            logger.info(
                f"Extraction completed. Classes will be saved to ontology_class "
                f"via /class endpoint based on user selection"
            )
            
            total_duration = time.time() - start_time
            
            # 记录提取统计
            logger.info(
                f"Ontology extraction service completed - "
                f"extracted_classes={len(response.classes)}, "
                f"domain={response.domain}, "
                f"extraction_duration={extraction_duration:.2f}s, "
                f"total_duration={total_duration:.2f}s"
            )
            
            return response
            
        except ValueError:
            # 重新抛出验证错误
            total_duration = time.time() - start_time
            logger.error(
                f"Validation error after {total_duration:.2f}s",
                exc_info=True
            )
            raise
        except Exception as e:
            total_duration = time.time() - start_time
            error_msg = f"Ontology extraction failed after {total_duration:.2f}s: {str(e)}"
            logger.error(error_msg, exc_info=True)
            raise RuntimeError(error_msg) from e
    
    async def export_owl_file(
        self,
        classes: List[OntologyClass],
        output_path: str,
        format: str = "rdfxml",
    ) -> str:
        """导出OWL文件
        
        将提取的本体类导出为OWL文件,支持多种格式。
        
        Args:
            classes: 本体类列表
            output_path: 输出文件路径
            format: 导出格式,可选值: "rdfxml", "turtle", "ntriples" (默认: "rdfxml")
            
        Returns:
            str: 导出的OWL文件内容
            
        Raises:
            ValueError: 类列表为空或格式不支持
            RuntimeError: 导出失败
            
        Examples:
            >>> service = OntologyService(llm_client, db)
            >>> owl_content = await service.export_owl_file(
            ...     classes=response.classes,
            ...     output_path="ontology.owl",
            ...     format="rdfxml"
            ... )
        """
        # 验证输入
        if not classes:
            logger.error("Classes list is empty")
            raise ValueError("Classes list cannot be empty")
        
        valid_formats = ["rdfxml", "turtle", "ntriples"]
        if format not in valid_formats:
            error_msg = f"Unsupported format '{format}'. Must be one of: {', '.join(valid_formats)}"
            logger.error(error_msg)
            raise ValueError(error_msg)
        
        logger.info(
            f"Starting OWL export - "
            f"classes_count={len(classes)}, "
            f"output_path={output_path}, "
            f"format={format}"
        )
        
        try:
            # 步骤1: 验证本体类
            logger.debug("Validating ontology classes")
            is_valid, errors, world = self.owl_validator.validate_ontology_classes(
                classes=classes,
            )
            
            if not is_valid:
                logger.warning(
                    f"OWL validation found {len(errors)} issues during export: {errors}"
                )
                # 继续导出,但记录警告
            
            if not world:
                error_msg = "Failed to create OWL world for export"
                logger.error(error_msg)
                raise RuntimeError(error_msg)
            
            # 步骤2: 导出OWL文件
            logger.info(f"Exporting to {format} format")
            owl_content = self.owl_validator.export_to_owl(
                world=world,
                output_path=output_path,
                format=format
            )
            
            logger.info(
                f"OWL export completed - "
                f"output_path={output_path}, "
                f"content_length={len(owl_content)}"
            )
            
            return owl_content
            
        except Exception as e:
            error_msg = f"OWL export failed: {str(e)}"
            logger.error(error_msg, exc_info=True)
            raise RuntimeError(error_msg) from e

    
    # ==================== 本体场景管理方法 ====================
    
    def create_scene(
        self,
        scene_name: str,
        scene_description: Optional[str],
        workspace_id: Any
    ):
        """创建本体场景
        
        Args:
            scene_name: 场景名称
            scene_description: 场景描述
            workspace_id: 所属工作空间ID
            
        Returns:
            OntologyScene: 创建的场景对象
            
        Raises:
            ValueError: 场景名称为空
            RuntimeError: 创建失败
            
        Examples:
            >>> service = OntologyService(llm_client, db)
            >>> scene = service.create_scene(
            ...     "医疗场景",
            ...     "用于医疗领域的本体建模",
            ...     workspace_id
            ... )
        """
        # 验证输入
        if not scene_name or not scene_name.strip():
            logger.error("Scene name is empty")
            raise ValueError("场景名称不能为空")
        
        logger.info(
            f"Creating scene - "
            f"name={scene_name}, workspace_id={workspace_id}"
        )
        
        try:
            scene_data = {
                "scene_name": scene_name.strip(),
                "scene_description": scene_description
            }
            
            scene = self.scene_repo.create(scene_data, workspace_id)
            self.db.commit()
            
            logger.info(f"Scene created successfully: {scene.scene_id}")
            
            return scene
            
        except ValueError:
            raise
        except Exception as e:
            self.db.rollback()
            error_msg = f"Failed to create scene: {str(e)}"
            logger.error(error_msg, exc_info=True)
            raise RuntimeError(error_msg) from e
    
    def update_scene(
        self,
        scene_id: Any,
        scene_name: Optional[str],
        scene_description: Optional[str],
        workspace_id: Any
    ):
        """更新本体场景
        
        Args:
            scene_id: 场景ID
            scene_name: 场景名称（可选）
            scene_description: 场景描述（可选）
            workspace_id: 工作空间ID（用于权限验证）
            
        Returns:
            OntologyScene: 更新后的场景对象
            
        Raises:
            ValueError: 场景不存在或无权限
            RuntimeError: 更新失败
            
        Examples:
            >>> service = OntologyService(llm_client, db)
            >>> scene = service.update_scene(
            ...     scene_id,
            ...     "新名称",
            ...     "新描述",
            ...     workspace_id
            ... )
        """
        logger.info(f"Updating scene: {scene_id}")
        
        try:
            # 检查场景是否存在
            scene = self.scene_repo.get_by_id(scene_id)
            if not scene:
                logger.warning(f"Scene not found: {scene_id}")
                raise ValueError("场景不存在")
            
            # 检查权限
            if not self.scene_repo.check_ownership(scene_id, workspace_id):
                logger.warning(
                    f"Permission denied - scene_id={scene_id}, "
                    f"workspace_id={workspace_id}"
                )
                raise ValueError("无权限操作该场景")
            
            # 准备更新数据
            update_data = {}
            if scene_name is not None:
                if not scene_name.strip():
                    raise ValueError("场景名称不能为空")
                update_data["scene_name"] = scene_name.strip()
            
            if scene_description is not None:
                update_data["scene_description"] = scene_description
            
            # 如果没有更新数据，直接返回
            if not update_data:
                logger.info("No update data provided, returning existing scene")
                return scene
            
            # 执行更新
            updated_scene = self.scene_repo.update(scene_id, update_data)
            self.db.commit()
            
            logger.info(f"Scene updated successfully: {scene_id}")
            
            return updated_scene
            
        except ValueError:
            raise
        except Exception as e:
            self.db.rollback()
            error_msg = f"Failed to update scene: {str(e)}"
            logger.error(error_msg, exc_info=True)
            raise RuntimeError(error_msg) from e
    
    def delete_scene(
        self,
        scene_id: Any,
        workspace_id: Any
    ) -> bool:
        """删除本体场景
        
        Args:
            scene_id: 场景ID
            workspace_id: 工作空间ID（用于权限验证）
            
        Returns:
            bool: 删除成功返回True
            
        Raises:
            ValueError: 场景不存在或无权限
            RuntimeError: 删除失败
            
        Examples:
            >>> service = OntologyService(llm_client, db)
            >>> success = service.delete_scene(scene_id, workspace_id)
        """
        logger.info(f"Deleting scene: {scene_id}")
        
        try:
            # 检查场景是否存在
            scene = self.scene_repo.get_by_id(scene_id)
            if not scene:
                logger.warning(f"Scene not found: {scene_id}")
                raise ValueError("场景不存在")
            
            # 检查权限
            if not self.scene_repo.check_ownership(scene_id, workspace_id):
                logger.warning(
                    f"Permission denied - scene_id={scene_id}, "
                    f"workspace_id={workspace_id}"
                )
                raise ValueError("无权限操作该场景")
            
            # 执行删除
            success = self.scene_repo.delete(scene_id)
            self.db.commit()
            
            logger.info(f"Scene deleted successfully: {scene_id}")
            
            return success
            
        except ValueError:
            raise
        except Exception as e:
            self.db.rollback()
            error_msg = f"Failed to delete scene: {str(e)}"
            logger.error(error_msg, exc_info=True)
            raise RuntimeError(error_msg) from e
    
    def get_scene_by_id(
        self,
        scene_id: Any,
        workspace_id: Any
    ):
        """获取单个场景
        
        Args:
            scene_id: 场景ID
            workspace_id: 工作空间ID（用于权限验证）
            
        Returns:
            Optional[OntologyScene]: 场景对象
            
        Raises:
            ValueError: 场景不存在或无权限
            
        Examples:
            >>> service = OntologyService(llm_client, db)
            >>> scene = service.get_scene_by_id(scene_id, workspace_id)
        """
        logger.debug(f"Getting scene by ID: {scene_id}")
        
        try:
            # 获取场景
            scene = self.scene_repo.get_by_id(scene_id)
            if not scene:
                logger.warning(f"Scene not found: {scene_id}")
                raise ValueError("场景不存在")
            
            # 检查权限
            if not self.scene_repo.check_ownership(scene_id, workspace_id):
                logger.warning(
                    f"Permission denied - scene_id={scene_id}, "
                    f"workspace_id={workspace_id}"
                )
                raise ValueError("无权限访问该场景")
            
            return scene
            
        except ValueError:
            raise
        except Exception as e:
            error_msg = f"Failed to get scene: {str(e)}"
            logger.error(error_msg, exc_info=True)
            raise RuntimeError(error_msg) from e
    
    def get_scene_by_name(
        self,
        scene_name: str,
        workspace_id: Any
    ):
        """根据场景名称获取场景（精确匹配）
        
        Args:
            scene_name: 场景名称
            workspace_id: 工作空间ID
            
        Returns:
            Optional[OntologyScene]: 场景对象
            
        Raises:
            ValueError: 场景不存在
            
        Examples:
            >>> service = OntologyService(llm_client, db)
            >>> scene = service.get_scene_by_name("医疗场景", workspace_id)
        """
        logger.debug(f"Getting scene by name: {scene_name}, workspace_id: {workspace_id}")
        
        try:
            # 获取场景
            scene = self.scene_repo.get_by_name(scene_name, workspace_id)
            if not scene:
                logger.warning(f"Scene not found: {scene_name} in workspace {workspace_id}")
                raise ValueError("场景不存在")
            
            return scene
            
        except ValueError:
            raise
        except Exception as e:
            error_msg = f"Failed to get scene by name: {str(e)}"
            logger.error(error_msg, exc_info=True)
            raise RuntimeError(error_msg) from e
    
    def search_scenes_by_name(
        self,
        keyword: str,
        workspace_id: Any
    ) -> List:
        """根据关键词模糊搜索场景
        
        Args:
            keyword: 搜索关键词
            workspace_id: 工作空间ID
            
        Returns:
            List[OntologyScene]: 匹配的场景列表
            
        Raises:
            RuntimeError: 搜索失败
            
        Examples:
            >>> service = OntologyService(llm_client, db)
            >>> scenes = service.search_scenes_by_name("医疗", workspace_id)
        """
        logger.debug(f"Searching scenes by keyword: {keyword}, workspace_id: {workspace_id}")
        
        try:
            scenes = self.scene_repo.search_by_name(keyword, workspace_id)
            
            logger.info(
                f"Found {len(scenes)} scenes matching keyword '{keyword}' "
                f"in workspace {workspace_id}"
            )
            
            return scenes
            
        except Exception as e:
            error_msg = f"Failed to search scenes by keyword: {str(e)}"
            logger.error(error_msg, exc_info=True)
            raise RuntimeError(error_msg) from e
    
    def list_scenes(
        self,
        workspace_id: Any,
        page: Optional[int] = None,
        page_size: Optional[int] = None
    ) -> tuple:
        """获取工作空间下的所有场景（支持分页）
        
        Args:
            workspace_id: 工作空间ID
            page: 页码（可选，从1开始）
            page_size: 每页数量（可选）
            
        Returns:
            tuple: (场景列表, 总数量)
            
        Raises:
            RuntimeError: 查询失败
            
        Examples:
            >>> service = OntologyService(llm_client, db)
            >>> scenes, total = service.list_scenes(workspace_id)
            >>> scenes, total = service.list_scenes(workspace_id, page=1, page_size=10)
        """
        logger.debug(f"Listing scenes for workspace: {workspace_id}, page={page}, page_size={page_size}")
        
        try:
            scenes, total = self.scene_repo.get_by_workspace(workspace_id, page, page_size)
            
            logger.info(f"Found {len(scenes)} scenes (total: {total}) in workspace {workspace_id}")
            
            return scenes, total
            
        except Exception as e:
            error_msg = f"Failed to list scenes: {str(e)}"
            logger.error(error_msg, exc_info=True)
            raise RuntimeError(error_msg) from e
    
    # ==================== 本体类型管理方法 ====================
    
    def create_class(
        self,
        scene_id: Any,
        class_name: str,
        class_description: Optional[str],
        workspace_id: Any
    ):
        """创建本体类型
        
        Args:
            scene_id: 所属场景ID
            class_name: 类型名称
            class_description: 类型描述
            workspace_id: 工作空间ID（用于权限验证）
            
        Returns:
            OntologyClass: 创建的类型对象
            
        Raises:
            ValueError: 类型名称为空、场景不存在或无权限
            RuntimeError: 创建失败
            
        Examples:
            >>> service = OntologyService(llm_client, db)
            >>> ontology_class = service.create_class(
            ...     scene_id,
            ...     "患者",
            ...     "医院患者信息",
            ...     workspace_id
            ... )
        """
        # 验证输入
        if not class_name or not class_name.strip():
            logger.error("Class name is empty")
            raise ValueError("类型名称不能为空")
        
        logger.info(
            f"Creating class - "
            f"name={class_name}, scene_id={scene_id}"
        )
        
        try:
            # 检查场景是否存在且属于当前工作空间
            scene = self.scene_repo.get_by_id(scene_id)
            if not scene:
                logger.warning(f"Scene not found: {scene_id}")
                raise ValueError("所属场景不存在")
            
            if not self.scene_repo.check_ownership(scene_id, workspace_id):
                logger.warning(
                    f"Permission denied - scene_id={scene_id}, "
                    f"workspace_id={workspace_id}"
                )
                raise ValueError("无权限在该场景下创建类型")
            
            # 创建类型
            class_data = {
                "class_name": class_name.strip(),
                "class_description": class_description
            }
            
            ontology_class = self.class_repo.create(class_data, scene_id)
            self.db.commit()
            
            logger.info(f"Class created successfully: {ontology_class.class_id}")
            
            return ontology_class
            
        except ValueError:
            raise
        except Exception as e:
            self.db.rollback()
            error_msg = f"Failed to create class: {str(e)}"
            logger.error(error_msg, exc_info=True)
            raise RuntimeError(error_msg) from e
    
    def create_classes_batch(
        self,
        scene_id: Any,
        classes: List[Dict[str, Optional[str]]],
        workspace_id: Any
    ):
        """批量创建本体类型
        
        Args:
            scene_id: 所属场景ID
            classes: 类型列表，每个元素包含 class_name 和 class_description
            workspace_id: 工作空间ID（用于权限验证）
            
        Returns:
            Tuple[List, List[str]]: (成功创建的类型列表, 错误信息列表)
            
        Raises:
            ValueError: 场景不存在或无权限
            
        Examples:
            >>> service = OntologyService(llm_client, db)
            >>> classes_data = [
            ...     {"class_name": "患者", "class_description": "医院患者信息"},
            ...     {"class_name": "医生", "class_description": "医院医生信息"}
            ... ]
            >>> created_classes, errors = service.create_classes_batch(
            ...     scene_id,
            ...     classes_data,
            ...     workspace_id
            ... )
        """
        logger.info(
            f"Batch creating classes - "
            f"count={len(classes)}, scene_id={scene_id}"
        )
        
        # 检查场景是否存在且属于当前工作空间（只检查一次）
        scene = self.scene_repo.get_by_id(scene_id)
        if not scene:
            logger.warning(f"Scene not found: {scene_id}")
            raise ValueError("所属场景不存在")
        
        if not self.scene_repo.check_ownership(scene_id, workspace_id):
            logger.warning(
                f"Permission denied - scene_id={scene_id}, "
                f"workspace_id={workspace_id}"
            )
            raise ValueError("无权限在该场景下创建类型")
        
        created_classes = []
        errors = []
        
        for idx, class_data in enumerate(classes):
            class_name = class_data.get("class_name", "").strip()
            class_description = class_data.get("class_description")
            
            if not class_name:
                error_msg = f"第 {idx + 1} 个类型名称为空，已跳过"
                logger.warning(error_msg)
                errors.append(error_msg)
                continue
            
            try:
                # 创建类型（不需要再次检查权限）
                create_data = {
                    "class_name": class_name,
                    "class_description": class_description
                }
                
                ontology_class = self.class_repo.create(create_data, scene_id)
                created_classes.append(ontology_class)
                logger.info(f"Class created successfully: {class_name}")
                
            except Exception as e:
                error_msg = f"创建类型 '{class_name}' 失败: {str(e)}"
                logger.error(error_msg)
                errors.append(error_msg)
        
        # 统一提交所有成功的创建
        try:
            self.db.commit()
            logger.info(
                f"Batch creation completed - "
                f"success={len(created_classes)}, failed={len(errors)}"
            )
        except Exception as e:
            self.db.rollback()
            error_msg = f"批量创建提交失败: {str(e)}"
            logger.error(error_msg, exc_info=True)
            raise RuntimeError(error_msg) from e
        
        return created_classes, errors
    
    def update_class(
        self,
        class_id: Any,
        class_name: Optional[str],
        class_description: Optional[str],
        workspace_id: Any
    ):
        """更新本体类型
        
        Args:
            class_id: 类型ID
            class_name: 类型名称（可选）
            class_description: 类型描述（可选）
            workspace_id: 工作空间ID（用于权限验证）
            
        Returns:
            OntologyClass: 更新后的类型对象
            
        Raises:
            ValueError: 类型不存在或无权限
            RuntimeError: 更新失败
            
        Examples:
            >>> service = OntologyService(llm_client, db)
            >>> ontology_class = service.update_class(
            ...     class_id,
            ...     "新名称",
            ...     "新描述",
            ...     workspace_id
            ... )
        """
        logger.info(f"Updating class: {class_id}")
        
        try:
            # 检查类型是否存在
            ontology_class = self.class_repo.get_by_id(class_id)
            if not ontology_class:
                logger.warning(f"Class not found: {class_id}")
                raise ValueError("类型不存在")
            
            # 检查权限（通过场景关联）
            if not self.class_repo.check_ownership(class_id, workspace_id):
                logger.warning(
                    f"Permission denied - class_id={class_id}, "
                    f"workspace_id={workspace_id}"
                )
                raise ValueError("无权限操作该类型")
            
            # 准备更新数据
            update_data = {}
            if class_name is not None:
                if not class_name.strip():
                    raise ValueError("类型名称不能为空")
                update_data["class_name"] = class_name.strip()
            
            if class_description is not None:
                update_data["class_description"] = class_description
            
            # 如果没有更新数据，直接返回
            if not update_data:
                logger.info("No update data provided, returning existing class")
                return ontology_class
            
            # 执行更新
            updated_class = self.class_repo.update(class_id, update_data)
            self.db.commit()
            
            logger.info(f"Class updated successfully: {class_id}")
            
            return updated_class
            
        except ValueError:
            raise
        except Exception as e:
            self.db.rollback()
            error_msg = f"Failed to update class: {str(e)}"
            logger.error(error_msg, exc_info=True)
            raise RuntimeError(error_msg) from e
    
    def delete_class(
        self,
        class_id: Any,
        workspace_id: Any
    ) -> bool:
        """删除本体类型
        
        Args:
            class_id: 类型ID
            workspace_id: 工作空间ID（用于权限验证）
            
        Returns:
            bool: 删除成功返回True
            
        Raises:
            ValueError: 类型不存在或无权限
            RuntimeError: 删除失败
            
        Examples:
            >>> service = OntologyService(llm_client, db)
            >>> success = service.delete_class(class_id, workspace_id)
        """
        logger.info(f"Deleting class: {class_id}")
        
        try:
            # 检查类型是否存在
            ontology_class = self.class_repo.get_by_id(class_id)
            if not ontology_class:
                logger.warning(f"Class not found: {class_id}")
                raise ValueError("类型不存在")
            
            # 检查权限（通过场景关联）
            if not self.class_repo.check_ownership(class_id, workspace_id):
                logger.warning(
                    f"Permission denied - class_id={class_id}, "
                    f"workspace_id={workspace_id}"
                )
                raise ValueError("无权限操作该类型")
            
            # 执行删除
            success = self.class_repo.delete(class_id)
            self.db.commit()
            
            logger.info(f"Class deleted successfully: {class_id}")
            
            return success
            
        except ValueError:
            raise
        except Exception as e:
            self.db.rollback()
            error_msg = f"Failed to delete class: {str(e)}"
            logger.error(error_msg, exc_info=True)
            raise RuntimeError(error_msg) from e
    
    def get_class_by_id(
        self,
        class_id: Any,
        workspace_id: Any
    ):
        """获取单个类型
        
        Args:
            class_id: 类型ID
            workspace_id: 工作空间ID（用于权限验证）
            
        Returns:
            Optional[OntologyClass]: 类型对象
            
        Raises:
            ValueError: 类型不存在或无权限
            
        Examples:
            >>> service = OntologyService(llm_client, db)
            >>> ontology_class = service.get_class_by_id(class_id, workspace_id)
        """
        logger.debug(f"Getting class by ID: {class_id}")
        
        try:
            # 获取类型
            ontology_class = self.class_repo.get_by_id(class_id)
            if not ontology_class:
                logger.warning(f"Class not found: {class_id}")
                raise ValueError("类型不存在")
            
            # 检查权限（通过场景关联）
            if not self.class_repo.check_ownership(class_id, workspace_id):
                logger.warning(
                    f"Permission denied - class_id={class_id}, "
                    f"workspace_id={workspace_id}"
                )
                raise ValueError("无权限访问该类型")
            
            return ontology_class
            
        except ValueError:
            raise
        except Exception as e:
            error_msg = f"Failed to get class: {str(e)}"
            logger.error(error_msg, exc_info=True)
            raise RuntimeError(error_msg) from e
    
    def get_class_by_name(
        self,
        class_name: str,
        scene_id: Any,
        workspace_id: Any
    ):
        """根据类型名称获取类型（精确匹配）
        
        Args:
            class_name: 类型名称
            scene_id: 场景ID
            workspace_id: 工作空间ID（用于权限验证）
            
        Returns:
            Optional[OntologyClass]: 类型对象
            
        Raises:
            ValueError: 类型不存在或无权限
            
        Examples:
            >>> service = OntologyService(llm_client, db)
            >>> ontology_class = service.get_class_by_name("患者", scene_id, workspace_id)
        """
        logger.debug(f"Getting class by name: {class_name}, scene_id: {scene_id}")
        
        try:
            # 检查场景是否存在且属于当前工作空间
            scene = self.scene_repo.get_by_id(scene_id)
            if not scene:
                logger.warning(f"Scene not found: {scene_id}")
                raise ValueError("场景不存在")
            
            if not self.scene_repo.check_ownership(scene_id, workspace_id):
                logger.warning(
                    f"Permission denied - scene_id={scene_id}, "
                    f"workspace_id={workspace_id}"
                )
                raise ValueError("无权限访问该场景")
            
            # 获取类型
            ontology_class = self.class_repo.get_by_name(class_name, scene_id)
            if not ontology_class:
                logger.warning(f"Class not found: {class_name} in scene {scene_id}")
                raise ValueError("类型不存在")
            
            return ontology_class
            
        except ValueError:
            raise
        except Exception as e:
            error_msg = f"Failed to get class by name: {str(e)}"
            logger.error(error_msg, exc_info=True)
            raise RuntimeError(error_msg) from e
    
    def search_classes_by_name(
        self,
        keyword: str,
        scene_id: Any,
        workspace_id: Any
    ) -> List:
        """根据关键词模糊搜索类型
        
        Args:
            keyword: 搜索关键词
            scene_id: 场景ID
            workspace_id: 工作空间ID（用于权限验证）
            
        Returns:
            List[OntologyClass]: 匹配的类型列表
            
        Raises:
            ValueError: 场景不存在或无权限
            RuntimeError: 搜索失败
            
        Examples:
            >>> service = OntologyService(llm_client, db)
            >>> classes = service.search_classes_by_name("患者", scene_id, workspace_id)
        """
        logger.debug(
            f"Searching classes by keyword: {keyword}, "
            f"scene_id: {scene_id}, workspace_id: {workspace_id}"
        )
        
        try:
            # 检查场景是否存在且属于当前工作空间
            scene = self.scene_repo.get_by_id(scene_id)
            if not scene:
                logger.warning(f"Scene not found: {scene_id}")
                raise ValueError("场景不存在")
            
            if not self.scene_repo.check_ownership(scene_id, workspace_id):
                logger.warning(
                    f"Permission denied - scene_id={scene_id}, "
                    f"workspace_id={workspace_id}"
                )
                raise ValueError("无权限访问该场景")
            
            # 搜索类型
            classes = self.class_repo.search_by_name(keyword, scene_id)
            
            logger.info(
                f"Found {len(classes)} classes matching keyword '{keyword}' "
                f"in scene {scene_id}"
            )
            
            return classes
            
        except ValueError:
            raise
        except Exception as e:
            error_msg = f"Failed to search classes by keyword: {str(e)}"
            logger.error(error_msg, exc_info=True)
            raise RuntimeError(error_msg) from e
    
    def list_classes_by_scene(
        self,
        scene_id: Any,
        workspace_id: Any
    ) -> List:
        """获取场景下的所有类型
        
        Args:
            scene_id: 场景ID
            workspace_id: 工作空间ID（用于权限验证）
            
        Returns:
            List[OntologyClass]: 类型列表
            
        Raises:
            ValueError: 场景不存在或无权限
            RuntimeError: 查询失败
            
        Examples:
            >>> service = OntologyService(llm_client, db)
            >>> classes = service.list_classes_by_scene(scene_id, workspace_id)
        """
        logger.debug(f"Listing classes for scene: {scene_id}")
        
        try:
            # 检查场景是否存在且属于当前工作空间
            scene = self.scene_repo.get_by_id(scene_id)
            if not scene:
                logger.warning(f"Scene not found: {scene_id}")
                raise ValueError("场景不存在")
            
            if not self.scene_repo.check_ownership(scene_id, workspace_id):
                logger.warning(
                    f"Permission denied - scene_id={scene_id}, "
                    f"workspace_id={workspace_id}"
                )
                raise ValueError("无权限访问该场景的类型")
            
            # 获取类型列表
            classes = self.class_repo.get_classes_by_scene(scene_id)
            
            logger.info(f"Found {len(classes)} classes in scene {scene_id}")
            
            return classes
            
        except ValueError:
            raise
        except Exception as e:
            error_msg = f"Failed to list classes: {str(e)}"
            logger.error(error_msg, exc_info=True)
            raise RuntimeError(error_msg) from e
