# -*- coding: utf-8 -*-
"""
Skill 加载器模块
扫描指定目录下的 SKILL.md 文件，解析 YAML frontmatter + 正文，
支持按关键词匹配 Skill 并生成注入 System Prompt 的文本。

SKILL.md 格式遵循 agentskills.io 规范：
- name（必填，kebab-case）：唯一标识
- description（必填）：Agent 据此判断何时加载此技能
- version/author/tags（可选）

加载策略：按需延迟加载——根据用户输入关键词匹配 description。
"""
import re
from dataclasses import dataclass, field
from pathlib import Path
from typing import Dict, List, Optional


@dataclass
class Skill:
    """单个 Agent Skill：元数据 + 正文内容。"""
    name: str
    description: str
    content: str
    path: Path = None
    version: str = ""
    author: str = ""
    tags: List[str] = field(default_factory=list)


class SkillLoader:
    """扫描并加载 Agent Skills，支持按关键词匹配。"""

    def __init__(self, skill_dirs: List[Path]):
        self.skill_dirs = skill_dirs
        self._skills: Dict[str, Skill] = {}

    def discover(self) -> List[Skill]:
        """递归扫描所有 SKILL.md 文件并解析。返回已加载的 Skill 列表"""
        self._skills.clear()
        for skill_dir in self.skill_dirs:
            if not skill_dir.exists() or not skill_dir.is_dir():
                continue
            for md_file in skill_dir.rglob("SKILL.md"):
                skill = self._parse_skill(md_file)
                if skill is not None:
                    self._skills[skill.name] = skill
        return list(self._skills.values())

    def _parse_skill(self, path: Path) -> Optional[Skill]:
        """解析单个 SKILL.md：拆分 frontmatter 和正文，校验必填字段"""
        try:
            raw = path.read_text(encoding="utf-8")
        except Exception:
            return None

        frontmatter, body = self._split_frontmatter(raw)
        if frontmatter is None:
            return None

        name = frontmatter.get("name", "").strip()
        description = frontmatter.get("description", "").strip()
        if not name or not description:
            return None  # spec 要求 name 和 description 必填

        return Skill(
            name=name,
            description=description,
            content=body.strip(),
            path=path.parent,
            version=str(frontmatter.get("version", "")),
            author=str(frontmatter.get("author", "")),
            tags=frontmatter.get("tags", []) or [],
        )

    @staticmethod
    def _split_frontmatter(raw: str):
        """拆分 YAML frontmatter 和正文。返回 (dict, str)，无 frontmatter 返回 (None, raw)"""
        if not raw.startswith("---"):
            return None, raw
        # 找第二个 --- 作为 frontmatter 结束
        end_idx = raw.find("\n---", 3)
        if end_idx == -1:
            return None, raw
        fm_text = raw[3:end_idx].strip()
        body = raw[end_idx + 4:].lstrip("\n")
        # 简单 YAML 解析（仅支持 key: value 和列表）
        fm = {}
        for line in fm_text.splitlines():
            line = line.rstrip()
            if not line or line.startswith("#"):
                continue
            if line.startswith("  - "):
                # 列表项，归到最近一个 key
                if fm:
                    last_key = list(fm.keys())[-1]
                    if not isinstance(fm[last_key], list):
                        fm[last_key] = []
                    fm[last_key].append(line[4:].strip())
                continue
            if ":" in line:
                key, _, value = line.partition(":")
                key = key.strip()
                value = value.strip()
                if value:
                    fm[key] = value
                else:
                    fm[key] = []  # 可能是列表开头
        return fm, body

    def get(self, name: str) -> Optional[Skill]:
        """按名获取已加载的 Skill"""
        return self._skills.get(name)

    def list_names(self) -> List[str]:
        """返回所有已加载 Skill 名"""
        return list(self._skills.keys())

    def match_by_keyword(self, user_input: str, limit: int = 3) -> List[Skill]:
        """根据用户输入关键词匹配 Skill 的 description。
        简单实现：description 中关键词出现在用户输入中即命中。
        更复杂的语义匹配可后续用 embedding 替换。
        """
        matched = []
        user_lower = user_input.lower()
        for skill in self._skills.values():
            desc_lower = skill.description.lower()
            # 提取 description 中的关键词（中文按字符，英文按单词）
            keywords = re.findall(r"[a-zA-Z]+|[\u4e00-\u9fa5]+", desc_lower)
            hit_count = sum(1 for kw in keywords if len(kw) >= 2 and kw in user_lower)
            if hit_count > 0:
                matched.append((hit_count, skill))
        # 按命中数降序
        matched.sort(key=lambda x: x[0], reverse=True)
        return [s for _, s in matched[:limit]]

    def build_system_prompt_extension(self, active_skills: List[Skill]) -> str:
        """生成要注入 System Prompt 的 Skill 内容，用 <skill> 标签包裹"""
        if not active_skills:
            return ""
        parts = []
        for skill in active_skills:
            parts.append(f"<skill name='{skill.name}'>\n{skill.content}\n</skill>")
        return "\n".join(parts)


def create_default_loader() -> SkillLoader:
    """创建指向 ~/.abyss/skills/ 的默认 SkillLoader"""
    return SkillLoader([Path.home() / ".abyss" / "skills"])
