"""
Skill 管理器

核心功能：
1. 加载和解析 Skill 文件
2. 提取行为指导内容
3. 缓存管理
4. Skill 验证
5. Script 执行（Builtin Skills）
6. Tool 注册和路由（Agent-Skill 生命周期）
"""
from pathlib import Path
from typing import Optional, List, Dict, Any, Callable
import re
import subprocess
import json
import sys
from datetime import datetime
import yaml
import asyncio

from app.models.skill import Skill, SkillCreate, SkillUpdate
from app.config import settings
from app.utils.logger import get_logger

logger = get_logger(__name__)


# ============================================================================
# Tool Registry - Agent-Skill 生命周期：注册阶段
# ============================================================================

class ToolRegistry:
    """
    工具注册表

    实现 Agent-Skill 生命周期的第一阶段：注册
    - 将 Skill 的 callable tools 注册到系统中
    - 为每个 tool 生成 JSON Schema 供 LLM 路由使用
    """

    def __init__(self):
        self._tools: Dict[str, Dict[str, Any]] = {}
        self._skill_tool_map: Dict[str, List[str]] = {}  # skill_id -> [tool_names]

    def register_tool(
        self,
        tool_id: str,
        skill_id: str,
        name: str,
        description: str,
        parameters: Dict[str, Any],
        handler: Callable
    ):
        """
        注册一个工具

        Args:
            tool_id: 工具唯一标识
            skill_id: 所属 Skill ID
            name: 工具名称
            description: 工具描述（用于 LLM 路由决策）
            parameters: JSON Schema 格式的参数定义
            handler: 执行函数
        """
        self._tools[tool_id] = {
            "id": tool_id,
            "skill_id": skill_id,
            "name": name,
            "description": description,
            "parameters": parameters,
            "handler": handler
        }

        if skill_id not in self._skill_tool_map:
            self._skill_tool_map[skill_id] = []
        self._skill_tool_map[skill_id].append(tool_id)

        logger.info(f"注册工具: {tool_id} (from {skill_id})")

    def get_tool(self, tool_id: str) -> Optional[Dict[str, Any]]:
        """获取工具定义"""
        return self._tools.get(tool_id)

    def get_tools_by_skill(self, skill_id: str) -> List[Dict[str, Any]]:
        """获取指定 Skill 的所有工具"""
        tool_ids = self._skill_tool_map.get(skill_id, [])
        return [self._tools[tid] for tid in tool_ids if tid in self._tools]

    def list_all_tools(self) -> List[Dict[str, Any]]:
        """列出所有已注册工具（用于 LLM 路由决策）"""
        return [
            {
                "id": t["id"],
                "skill_id": t["skill_id"],
                "name": t["name"],
                "description": t["description"],
                "parameters": t["parameters"]
            }
            for t in self._tools.values()
        ]

    def get_tool_schemas_for_llm(self) -> List[Dict[str, Any]]:
        """
        获取所有工具的 JSON Schema（注入到 System Prompt 或 API tools 字段）

        这是 Agent-Skill 生命周期第一阶段的关键输出
        """
        return [
            {
                "type": "function",
                "function": {
                    "name": t["name"],
                    "description": t["description"],
                    "parameters": t["parameters"]
                }
            }
            for t in self._tools.values()
        ]


# 全局工具注册表
_tool_registry: Optional[ToolRegistry] = None


def get_tool_registry() -> ToolRegistry:
    """获取工具注册表单例"""
    global _tool_registry
    if _tool_registry is None:
        _tool_registry = ToolRegistry()
    return _tool_registry


class SkillManager:
    """
    Skill 管理器

    负责加载、解析、验证和管理所有 Skills
    实现 Agent-Skill 生命周期：注册 → 路由 → 执行 → 反馈
    """

    def __init__(self, skills_dir: Optional[str] = None):
        """
        初始化 Skill 管理器

        Args:
            skills_dir: Skills 存储目录
        """
        self.skills_dir = Path(skills_dir or settings.skills_dir)
        self.builtin_dir = self.skills_dir / "builtin_skills"
        self.user_dir = self.skills_dir / "user_skills"
        self._cache: Dict[str, Skill] = {}
        self.tool_registry = get_tool_registry()

        # 确保目录存在
        self.builtin_dir.mkdir(parents=True, exist_ok=True)
        self.user_dir.mkdir(parents=True, exist_ok=True)

        logger.info(f"Skill 管理器初始化完成，目录: {self.skills_dir}")

        # 技能列表缓存（用于快速返回列表，不需要每次都扫描目录）
        self._skills_list_cache: Optional[List[Skill]] = None
        self._cache_dirty = True  # 标记缓存需要更新

    async def warmup_cache(self):
        """预热缓存 - 在应用启动时调用，预加载所有 skills"""
        logger.info("开始预热 Skill 缓存...")
        skills = await self.list_skills()
        self._skills_list_cache = skills
        self._cache_dirty = False
        logger.info(f"Skill 缓存预热完成，共加载 {len(skills)} 个 skills")

    async def load_skill(self, skill_id: str) -> Optional[Skill]:
        """
        加载 Skill

        Args:
            skill_id: Skill ID

        Returns:
            Skill 对象或 None
        """
        # 先检查缓存
        if skill_id in self._cache:
            logger.debug(f"从缓存加载 Skill: {skill_id}")
            return self._cache[skill_id]

        # 确定路径
        skill_path = self._find_skill_path(skill_id)
        if not skill_path:
            logger.warning(f"Skill 未找到: {skill_id}")
            return None

        try:
            # 解析 SKILL.md（异步读取文件）
            skill = await self._parse_skill_file(skill_path, skill_id)

            # 缓存
            self._cache[skill_id] = skill
            logger.info(f"成功加载 Skill: {skill_id} - {skill.name}")
            return skill

        except Exception as e:
            logger.error(f"加载 Skill 失败 {skill_id}: {str(e)}")
            return None

    def _find_skill_path(self, skill_id: str) -> Optional[Path]:
        """
        查找 Skill 路径

        优先级：用户 Skills > 内置 Skills
        """
        # 先查用户 Skills
        user_path = self.user_dir / skill_id / "SKILL.md"
        if user_path.exists():
            return user_path

        # 再查内置 Skills
        builtin_path = self.builtin_dir / skill_id / "SKILL.md"
        if builtin_path.exists():
            return builtin_path

        return None

    async def _parse_skill_file(self, skill_path: Path, skill_id: str) -> Skill:
        """
        解析 SKILL.md 文件

        Args:
            skill_path: Skill 文件路径
            skill_id: Skill ID

        Returns:
            Skill 对象
        """
        # 使用 asyncio.to_thread 在单独的线程中读取文件，避免阻塞事件循环
        content = await asyncio.to_thread(skill_path.read_text, encoding='utf-8')

        # 确定类型
        skill_type = "builtin" if "builtin_skills" in str(skill_path) else "user"

        # 提取元数据
        metadata = self._extract_metadata(content)

        # 提取行为指导（核心部分）
        behavior_guide = self._extract_behavior_guide(content)

        # 检查是否有 Scripts（仅内置 Skill）
        has_scripts = (skill_path.parent / "scripts").exists()
        tools = []
        if has_scripts:
            tools = await asyncio.to_thread(self._load_tools, skill_path.parent / "scripts")

        return Skill(
            id=skill_id,
            name=metadata.get("name", skill_id),
            version=metadata.get("version", "1.0.0"),
            author=metadata.get("author", "未知"),
            type=skill_type,
            behavior_guide=behavior_guide,
            config=self._extract_config(content),
            template=metadata.get("template"),
            tools=tools if has_scripts else None,
            category=metadata.get("category", "通用"),
            tags=metadata.get("tags", []),
            visibility="private",
            file_path=str(skill_path),
            created_at=datetime.now(),
            updated_at=datetime.now()
        )

    def _extract_metadata(self, content: str) -> Dict[str, Any]:
        """
        提取 Skill 元数据

        从 Markdown 的 YAML frontmatter 或 ## 基础信息 部分提取
        """
        metadata = {}

        # 尝试提取 YAML frontmatter
        yaml_match = re.match(r'^---\n(.*?)\n---', content, re.DOTALL)
        if yaml_match:
            try:
                metadata = yaml.safe_load(yaml_match.group(1)) or {}
            except:
                pass

        # 从 ## 基础信息 提取
        if not metadata:
            lines = content.split('\n')
            in_basic_info = False
            for i, line in enumerate(lines):
                if "## 基础信息" in line or "## Basic Information" in line:
                    in_basic_info = True
                    continue
                if in_basic_info:
                    if line.strip().startswith("##"):
                        break
                    # 解析 - **key**: value 格式
                    match = re.match(r'-\s*\*\*([^*]+)\*\*:\s*(.+)', line)
                    if match:
                        key = match.group(1).strip()
                        value = match.group(2).strip()
                        metadata[key] = value

        return metadata

    def _extract_behavior_guide(self, content: str) -> str:
        """
        提取行为指导部分（核心功能）

        查找 ## 行为指导 或 ## Behavior Guide 部分

        如果找到专门的章节，返回该章节内容；
        如果没有找到，返回完整内容（整个SKILL.md就是行为指导）
        """
        lines = content.split('\n')
        start_idx = -1

        for i, line in enumerate(lines):
            if "行为指导" in line or "Behavior Guide" in line or "行为规则" in line:
                start_idx = i
                break

        if start_idx == -1:
            # 如果没有专门的章节，返回完整内容
            # 整个SKILL.md就是行为指导，不应该截断
            return content

        # 提取到下一个 ## 之前的内容
        guide_lines = []
        for i in range(start_idx + 1, len(lines)):
            if lines[i].strip().startswith("##"):
                break
            guide_lines.append(lines[i])

        return "\n".join(guide_lines).strip()

    def _extract_config(self, content: str) -> Dict[str, Any]:
        """提取配置信息"""
        # 默认配置
        return {
            "preset": None,
            "parameters": {},
            "weights": {}
        }

    def _load_tools(self, scripts_dir: Path) -> List[Dict[str, Any]]:
        """加载 Scripts 目录下的工具"""
        tools = []
        if not scripts_dir.exists():
            return tools

        for script_file in scripts_dir.glob("*.py"):
            tools.append({
                "name": script_file.stem,
                "description": f"Tool from {script_file.name}",
                "script": script_file.name
            })

        return tools

    async def reload_skill(self, skill_id: str) -> Optional[Skill]:
        """
        重新加载 Skill（清除缓存）

        Args:
            skill_id: Skill ID

        Returns:
            重新加载的 Skill 或 None
        """
        if skill_id in self._cache:
            del self._cache[skill_id]
        return await self.load_skill(skill_id)

    async def list_skills(
        self,
        skill_type: Optional[str] = None,
        category: Optional[str] = None
    ) -> List[Skill]:
        """
        列出所有 Skills

        Args:
            skill_type: Skill 类型 (builtin/user)
            category: 分类筛选

        Returns:
            Skill 列表
        """
        # 如果不需要筛选且有缓存，直接返回缓存
        if not skill_type and not category and self._skills_list_cache is not None:
            return self._skills_list_cache

        skills = []

        # 扫描内置 Skills
        if skill_type is None or skill_type == "builtin":
            for skill_dir in self.builtin_dir.iterdir():
                if skill_dir.is_dir() and (skill_dir / "SKILL.md").exists():
                    skill = await self.load_skill(skill_dir.name)
                    if skill and (category is None or skill.category == category):
                        skills.append(skill)

        # 扫描用户 Skills
        if skill_type is None or skill_type == "user":
            for skill_dir in self.user_dir.iterdir():
                if skill_dir.is_dir() and (skill_dir / "SKILL.md").exists():
                    skill = await self.load_skill(skill_dir.name)
                    if skill and (category is None or skill.category == category):
                        skills.append(skill)

        # 如果没有筛选条件，更新缓存
        if not skill_type and not category:
            self._skills_list_cache = skills

        return skills

    async def create_user_skill(self, skill_data: SkillCreate) -> Skill:
        """
        创建新的用户 Skill

        Args:
            skill_data: Skill 创建数据

        Returns:
            创建的 Skill 对象
        """
        # 创建目录
        skill_dir = self.user_dir / skill_data.id
        skill_dir.mkdir(exist_ok=True)

        # 写入 SKILL.md
        skill_file = skill_dir / "SKILL.md"
        skill_file.write_text(skill_data.content, encoding='utf-8')

        # 清除列表缓存
        self._skills_list_cache = None

        logger.info(f"创建用户 Skill: {skill_data.id}")

        # 加载并返回
        return await self.load_skill(skill_data.id)

    async def update_user_skill(
        self,
        skill_id: str,
        skill_data: SkillUpdate
    ) -> Optional[Skill]:
        """
        更新用户 Skill

        Args:
            skill_id: Skill ID
            skill_data: 更新数据

        Returns:
            更新后的 Skill 或 None
        """
        skill_path = self._find_skill_path(skill_id)
        if not skill_path:
            return None

        # 只能更新用户 Skills
        if "builtin_skills" in str(skill_path):
            logger.warning(f"不能更新内置 Skill: {skill_id}")
            return None

        # 读取现有内容
        content = skill_path.read_text(encoding='utf-8')

        # 更新内容
        if skill_data.content:
            content = skill_data.content
        else:
            # TODO: 更新特定字段
            pass

        # 写入更新后的内容
        skill_path.write_text(content, encoding='utf-8')

        # 清除缓存
        self._skills_list_cache = None

        # 重新加载
        return await self.reload_skill(skill_id)

    async def delete_user_skill(self, skill_id: str) -> bool:
        """
        删除用户 Skill

        Args:
            skill_id: Skill ID

        Returns:
            是否删除成功
        """
        skill_path = self._find_skill_path(skill_id)
        if not skill_path:
            return False

        # 只能删除用户 Skills
        if "builtin_skills" in str(skill_path):
            logger.warning(f"不能删除内置 Skill: {skill_id}")
            return False

        # 删除目录
        import shutil
        shutil.rmtree(skill_path.parent)

        # 清除缓存
        if skill_id in self._cache:
            del self._cache[skill_id]
        self._skills_list_cache = None

        logger.info(f"删除用户 Skill: {skill_id}")
        return True

    # ========================================================================
    # Script 执行 - Builtin Skills 的可执行脚本
    # ========================================================================

    async def execute_script(
        self,
        skill_id: str,
        script_name: str,
        args: List[str] = None
    ) -> Dict[str, Any]:
        """
        执行 Builtin Skill 的脚本

        这是 Agent-Skill 生命周期第三阶段：执行
        - 调用 Skill 的 scripts/ 目录下的可执行脚本
        - 捕获输出并返回结果

        Args:
            skill_id: Skill ID（必须是 builtin 类型）
            script_name: 脚本文件名（如 "init_skill.py"）
            args: 脚本参数列表

        Returns:
            执行结果字典 {"success": bool, "output": str, "error": str}
        """
        skill_path = self._find_skill_path(skill_id)
        if not skill_path:
            return {"success": False, "error": f"Skill 未找到: {skill_id}"}

        # 只允许执行 builtin skills 的脚本
        if "builtin_skills" not in str(skill_path):
            return {"success": False, "error": f"只允许执行 builtin Skills 的脚本"}

        script_path = skill_path.parent / "scripts" / script_name
        if not script_path.exists():
            return {"success": False, "error": f"脚本不存在: {script_name}"}

        try:
            # 构建命令
            cmd = [sys.executable, str(script_path)]
            if args:
                cmd.extend(args)

            logger.info(f"执行脚本: {' '.join(cmd)}")

            # 执行脚本
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                timeout=60,
                cwd=str(script_path.parent)
            )

            if result.returncode == 0:
                logger.info(f"脚本执行成功: {script_name}")
                return {
                    "success": True,
                    "output": result.stdout,
                    "error": result.stderr
                }
            else:
                logger.error(f"脚本执行失败: {script_name}, {result.stderr}")
                return {
                    "success": False,
                    "output": result.stdout,
                    "error": result.stderr
                }

        except subprocess.TimeoutExpired:
            return {"success": False, "error": "脚本执行超时"}
        except Exception as e:
            logger.error(f"执行脚本异常: {str(e)}")
            return {"success": False, "error": str(e)}

    # ========================================================================
    # skill-creator 专用方法
    # ========================================================================

    async def init_skill_template(
        self,
        skill_name: str,
        output_path: str = None
    ) -> Dict[str, Any]:
        """
        使用 skill-creator 的 init_skill.py 初始化 Skill 模板

        Args:
            skill_name: Skill 名称（hyphen-case）
            output_path: 输出路径（默认为 user_skills 目录）

        Returns:
            执行结果
        """
        if output_path is None:
            output_path = str(self.user_dir)

        return await self.execute_script(
            skill_id="skill-creator",
            script_name="init_skill.py",
            args=[skill_name, "--path", output_path]
        )

    async def validate_skill_structure(
        self,
        skill_id: str
    ) -> Dict[str, Any]:
        """
        使用 skill-creator 的 quick_validate.py 验证 Skill 结构

        Args:
            skill_id: 要验证的 Skill ID

        Returns:
            验证结果
        """
        skill_path = self._find_skill_path(skill_id)
        if not skill_path:
            return {"success": False, "error": f"Skill 未找到: {skill_id}"}

        return await self.execute_script(
            skill_id="skill-creator",
            script_name="quick_validate.py",
            args=[str(skill_path.parent)]
        )

    async def package_skill(
        self,
        skill_id: str,
        output_dir: str = None
    ) -> Dict[str, Any]:
        """
        使用 skill-creator 的 package_skill.py 打包 Skill

        Args:
            skill_id: 要打包的 Skill ID
            output_dir: 输出目录

        Returns:
            打包结果
        """
        skill_path = self._find_skill_path(skill_id)
        if not skill_path:
            return {"success": False, "error": f"Skill 未找到: {skill_id}"}

        args = [str(skill_path.parent)]
        if output_dir:
            args.append(output_dir)

        return await self.execute_script(
            skill_id="skill-creator",
            script_name="package_skill.py",
            args=args
        )

    # ========================================================================
    # Tool 注册和执行 - Agent-Skill 生命周期核心
    # ========================================================================

    async def register_builtin_tools(self):
        """
        注册所有 Builtin Skills 的工具

        在应用启动时调用，实现 Agent-Skill 生命周期第一阶段：注册
        """
        # 注册 skill-creator 的工具
        self._register_skill_creator_tools()

        # 可以继续注册其他 builtin skills 的工具
        # self._register_other_skill_tools()

        logger.info("Builtin Skills 工具注册完成")

    def _register_skill_creator_tools(self):
        """注册 skill-creator 的工具"""
        skill_id = "skill-creator"

        # Tool: init_skill
        self.tool_registry.register_tool(
            tool_id=f"{skill_id}_init_skill",
            skill_id=skill_id,
            name="init_skill_template",
            description="初始化一个新的 Skill 模板，创建包含 SKILL.md、scripts/、references/、assets/ 的标准目录结构",
            parameters={
                "type": "object",
                "properties": {
                    "skill_name": {
                        "type": "string",
                        "description": "Skill 名称（hyphen-case，如 'my-awesome-skill'）"
                    },
                    "output_path": {
                        "type": "string",
                        "description": "输出路径（可选，默认为 user_skills 目录）"
                    }
                },
                "required": ["skill_name"]
            },
            handler=lambda **kwargs: self._execute_init_skill(**kwargs)
        )

        # Tool: validate_skill
        self.tool_registry.register_tool(
            tool_id=f"{skill_id}_validate_skill",
            skill_id=skill_id,
            name="validate_skill_structure",
            description="验证 Skill 的结构是否符合规范，检查 YAML frontmatter、命名规范等",
            parameters={
                "type": "object",
                "properties": {
                    "skill_id": {
                        "type": "string",
                        "description": "要验证的 Skill ID"
                    }
                },
                "required": ["skill_id"]
            },
            handler=lambda **kwargs: self._execute_validate_skill(**kwargs)
        )

        # Tool: package_skill
        self.tool_registry.register_tool(
            tool_id=f"{skill_id}_package_skill",
            skill_id=skill_id,
            name="package_skill",
            description="将 Skill 打包成 .skill 文件用于分发",
            parameters={
                "type": "object",
                "properties": {
                    "skill_id": {
                        "type": "string",
                        "description": "要打包的 Skill ID"
                    },
                    "output_dir": {
                        "type": "string",
                        "description": "输出目录（可选）"
                    }
                },
                "required": ["skill_id"]
            },
            handler=lambda **kwargs: self._execute_package_skill(**kwargs)
        )

    # Handler 方法（需要是 async 的，所以用 wrapper）
    async def _execute_init_skill(self, skill_name: str, output_path: str = None):
        return await self.init_skill_template(skill_name, output_path)

    async def _execute_validate_skill(self, skill_id: str):
        return await self.validate_skill_structure(skill_id)

    async def _execute_package_skill(self, skill_id: str, output_dir: str = None):
        return await self.package_skill(skill_id, output_dir)

    # ========================================================================
    # 路由和执行 - Agent-Skill 生命周期第二、三阶段
    # ========================================================================

    def get_available_tools_for_llm(self) -> List[Dict[str, Any]]:
        """
        获取所有可用工具的 Schema（用于注入 LLM）

        这是 Agent-Skill 生命周期第一阶段的输出
        - 将所有已注册工具的 JSON Schema 返回
        - 这些 Schema 将被注入到 System Prompt 或 API 的 tools 字段
        """
        return self.tool_registry.get_tool_schemas_for_llm()

    async def route_and_execute_tool(
        self,
        tool_name: str,
        parameters: Dict[str, Any]
    ) -> Dict[str, Any]:
        """
        路由并执行工具

        实现 Agent-Skill 生命周期：
        - 第二阶段：路由（根据 tool_name 找到对应的 handler）
        - 第三阶段：执行（调用 handler 执行实际操作）

        Args:
            tool_name: LLM 决定调用的工具名称
            parameters: LLM 生成的参数

        Returns:
            执行结果（将反馈给 LLM，实现第四阶段：反馈）
        """
        # 查找工具
        tool = None
        for t in self.tool_registry._tools.values():
            if t["name"] == tool_name:
                tool = t
                break

        if not tool:
            return {
                "success": False,
                "error": f"工具未找到: {tool_name}"
            }

        logger.info(f"路由到工具: {tool_name} (from {tool['skill_id']})")

        try:
            # 执行工具
            result = await tool["handler"](**parameters)

            # 返回结果（将反馈给 LLM）
            return result

        except Exception as e:
            logger.error(f"执行工具失败: {str(e)}")
            return {
                "success": False,
                "error": str(e)
            }

    # ========================================================================
    # 参考文件加载 - 融入 LLM 上下文
    # ========================================================================

    async def load_skill_references(
        self,
        skill_id: str,
        max_file_size: int = 100000  # 100KB
    ) -> Dict[str, str]:
        """
        加载 Skill 的参考文件内容

        参考文件会被融入到 LLM 的 system prompt 中
        这样 LLM 在执行任务时可以参考这些资料

        Args:
            skill_id: Skill ID
            max_file_size: 单个文件最大大小（字节），超过则跳过

        Returns:
            参考文件内容字典 {filename: content}
        """
        skill_path = self._find_skill_path(skill_id)
        if not skill_path:
            logger.warning(f"Skill 未找到，无法加载参考文件: {skill_id}")
            return {}

        references_dir = skill_path.parent / "references"
        if not references_dir.exists():
            logger.debug(f"Skill 没有参考文件目录: {skill_id}")
            return {}

        references = {}
        allowed_extensions = {'.md', '.txt', '.json', '.yaml', '.yml'}

        for file_path in references_dir.iterdir():
            if not file_path.is_file():
                continue

            # 检查文件扩展名
            if file_path.suffix.lower() not in allowed_extensions:
                logger.debug(f"跳过不支持的文件类型: {file_path.name}")
                continue

            # 检查文件大小
            file_size = file_path.stat().st_size
            if file_size > max_file_size:
                logger.warning(
                    f"参考文件过大，跳过: {file_path.name} ({file_size / 1024:.1f}KB > {max_file_size / 1024}KB)"
                )
                continue

            try:
                # 读取文件内容
                content = file_path.read_text(encoding='utf-8')
                references[file_path.name] = content
                logger.info(f"加载参考文件: {skill_id}/references/{file_path.name} ({file_size} bytes)")
            except UnicodeDecodeError:
                logger.warning(f"参考文件编码错误，跳过: {file_path.name}")
                continue
            except Exception as e:
                logger.error(f"读取参考文件失败: {file_path.name}, {str(e)}")
                continue

        if references:
            logger.info(f"成功加载 {len(references)} 个参考文件: {skill_id}")

        return references

    async def get_skill_with_references(
        self,
        skill_id: str,
        include_references: bool = True
    ) -> Optional[Dict[str, Any]]:
        """
        获取 Skill 及其参考文件

        这是一个便捷方法，返回 Skill 对象和参考文件内容

        Args:
            skill_id: Skill ID
            include_references: 是否包含参考文件

        Returns:
            包含 skill 和 references 的字典
        """
        skill = await self.load_skill(skill_id)
        if not skill:
            return None

        result = {
            "skill": skill,
            "references": {}
        }

        if include_references:
            result["references"] = await self.load_skill_references(skill_id)

        return result


# 全局单例
_skill_manager: Optional[SkillManager] = None


def get_skill_manager() -> SkillManager:
    """获取 Skill 管理器单例"""
    global _skill_manager
    if _skill_manager is None:
        _skill_manager = SkillManager()
    return _skill_manager


# 便捷导出
skill_manager = get_skill_manager()
