"""SkillLoader - 从 YAML/SKILL.md 目录/Python 包批量加载 Skill"""

from __future__ import annotations

import glob
import logging
import os
import sys

from agentkit.skills.base import Skill, SkillConfig
from agentkit.skills.registry import SkillRegistry
from agentkit.tools.registry import ToolRegistry

logger = logging.getLogger(__name__)

# entry_points group 名称，用于自动发现 Skill 插件
SKILL_ENTRY_POINT_GROUP = "agentkit.skills"


class SkillLoader:
    """从 YAML/SKILL.md 目录/Python 包批量加载 Skill 并注册到 SkillRegistry

    v2 增强：
    - 支持从 Python 包通过 entry_points 自动发现并加载 Skill
    """

    def __init__(
        self,
        skill_registry: SkillRegistry,
        tool_registry: ToolRegistry | None = None,
    ):
        self._skill_registry = skill_registry
        self._tool_registry = tool_registry

    def load_from_directory(self, directory: str) -> list[Skill]:
        """加载目录下所有 YAML 和 SKILL.md 文件为 Skill，并注册到 SkillRegistry

        无效的文件会被跳过并记录警告。
        """
        skills: list[Skill] = []

        # 加载 YAML 文件
        yaml_pattern = os.path.join(directory, "*.yaml")
        yaml_files = sorted(glob.glob(yaml_pattern))
        for yaml_path in yaml_files:
            try:
                skill = self._load_skill_from_file(yaml_path)
                skills.append(skill)
            except Exception as e:
                logger.warning(f"Skipping invalid YAML file '{yaml_path}': {e}")

        # 加载 SKILL.md 文件
        md_pattern = os.path.join(directory, "*.md")
        md_files = sorted(glob.glob(md_pattern))
        for md_path in md_files:
            try:
                skill = self.load_from_skill_md(md_path)
                skills.append(skill)
            except Exception as e:
                logger.warning(f"Skipping invalid SKILL.md file '{md_path}': {e}")

        return skills

    def load_from_file(self, path: str) -> Skill:
        """加载单个 YAML 文件为 Skill，并注册到 SkillRegistry"""
        skill = self._load_skill_from_file(path)
        return skill

    def _load_skill_from_file(self, path: str) -> Skill:
        """从 YAML 文件加载 SkillConfig，创建 Skill，绑定工具，注册"""
        config = SkillConfig.from_yaml(path)
        tools = self._bind_tools(config)
        skill = Skill(config, tools=tools)
        self._skill_registry.register(skill)
        logger.info(f"Loaded skill '{skill.name}' from '{path}'")
        return skill

    def load_from_skill_md(self, path: str, disclosure_level: int = 1) -> Skill:
        """加载 SKILL.md 文件为 Skill，并注册到 SkillRegistry

        Args:
            path: SKILL.md 文件路径
            disclosure_level: 渐进式加载层级（0=概要, 1=完整, 2=参考）

        Returns:
            加载的 Skill 实例
        """
        from agentkit.skills.skill_md import SkillMdParser

        frontmatter, sections, body = SkillMdParser.parse(path)
        config = SkillMdParser.to_skill_config(
            frontmatter, sections, path, disclosure_level=disclosure_level,
        )
        tools = self._bind_tools(config)
        skill = Skill(config, tools=tools)
        self._skill_registry.register(skill)
        logger.info(f"Loaded skill '{skill.name}' from SKILL.md '{path}' (level={disclosure_level})")
        return skill

    def load_from_entry_points(self, group: str | None = None) -> list[Skill]:
        """从 Python 包的 entry_points 自动发现并加载 Skill

        第三方包可通过在 pyproject.toml 或 setup.py 中声明 entry_points
        来注册 Skill 插件::

            [project.entry-points."agentkit.skills"]
            my_rag_skill = "my_package.skills:rag_skill"

        其中 `rag_skill` 应为 Skill 实例或返回 Skill 的可调用对象。

        Args:
            group: entry_points 组名，默认为 "agentkit.skills"

        Returns:
            加载的 Skill 列表
        """
        group_name = group or SKILL_ENTRY_POINT_GROUP
        skills: list[Skill] = []

        try:
            # Python 3.12+ 使用 importlib.metadata
            if sys.version_info >= (3, 12):
                from importlib.metadata import entry_points as _entry_points
                eps = _entry_points(group=group_name)
            else:
                from importlib.metadata import entry_points as _entry_points
                eps = _entry_points().get(group_name, [])
        except Exception as e:
            logger.warning(f"Failed to discover entry_points for group '{group_name}': {e}")
            return skills

        for ep in eps:
            try:
                loaded = ep.load()
                # 支持两种形式：直接是 Skill 实例，或者是返回 Skill 的可调用对象
                if isinstance(loaded, Skill):
                    skill = loaded
                elif callable(loaded):
                    result = loaded()
                    if isinstance(result, Skill):
                        skill = result
                    else:
                        logger.warning(
                            f"Entry point '{ep.name}' did not return a Skill instance, "
                            f"got {type(result)}"
                        )
                        continue
                else:
                    logger.warning(
                        f"Entry point '{ep.name}' is neither a Skill nor callable, "
                        f"got {type(loaded)}"
                    )
                    continue

                self._skill_registry.register(skill)
                skills.append(skill)
                logger.info(
                    f"Loaded skill '{skill.name}' v{skill.version} "
                    f"from entry_point '{ep.name}'"
                )
            except Exception as e:
                logger.warning(
                    f"Failed to load skill from entry_point '{ep.name}': {e}"
                )

        return skills

    def _bind_tools(self, config: SkillConfig) -> list:
        """根据配置中的 tools 列表绑定工具"""
        if not self._tool_registry or not config.tools:
            return []

        tools = []
        for tool_name in config.tools:
            try:
                tool = self._tool_registry.get(tool_name)
                tools.append(tool)
                logger.info(f"Bound tool '{tool_name}' to skill '{config.name}'")
            except Exception as e:
                logger.warning(
                    f"Failed to bind tool '{tool_name}' to skill '{config.name}': {e}"
                )
        return tools
