"""SKILL.md 解析器 - 从 Markdown 文件解析技能定义

支持渐进式分层加载：
- Level 0: 概要（name + description）
- Level 1: 完整内容（所有 sections）
- Level 2: 参考信息（含外部链接等）
"""

import logging
import re
from typing import Any

import yaml

from agentkit.core.exceptions import ConfigValidationError
from agentkit.skills.base import SkillConfig

logger = logging.getLogger(__name__)


class SkillMdParser:
    """解析 SKILL.md 文件为 SkillConfig

    SKILL.md 格式：
    1. YAML frontmatter（--- 包裹）：包含元数据
    2. Markdown body：包含 # Trigger / # Steps / # Pitfalls / # Verification 等 section
    """

    @staticmethod
    def parse(file_path: str) -> tuple[dict[str, Any], dict[str, str], str]:
        """解析 SKILL.md 文件

        Note: Only H1 headings (# ) are treated as section delimiters.
        H2+ headings (## , ### , etc.) are treated as regular content
        and merged into their parent H1 section. This is by design —
        SKILL.md uses a flat section model where sub-structure within
        a section is preserved as-is in the section body text.

        Args:
            file_path: SKILL.md 文件路径

        Returns:
            - frontmatter: YAML 元数据字典
            - sections: section 标题 → 内容的映射
            - raw_markdown: 去掉 frontmatter 后的完整 Markdown 内容
        """
        with open(file_path, "r", encoding="utf-8") as f:
            content = f.read()

        # 提取 YAML frontmatter（--- 标记之间）
        frontmatter: dict[str, Any] = {}
        body = content
        if content.startswith("---"):
            parts = content.split("---", 2)
            if len(parts) >= 3:
                frontmatter = yaml.safe_load(parts[1]) or {}
                body = parts[2].strip()

        # 按 # 标题解析 sections
        sections: dict[str, str] = {}
        current_section: str | None = None
        current_lines: list[str] = []

        for line in body.split("\n"):
            # 匹配 H1 标题（# 开头但不是 ##）
            h1_match = re.match(r"^# (.+)$", line)
            if h1_match:
                # 保存前一个 section
                if current_section is not None:
                    sections[current_section] = "\n".join(current_lines).strip()
                current_section = h1_match.group(1).strip().lower()
                current_lines = []
            else:
                current_lines.append(line)

        # 保存最后一个 section
        if current_section is not None:
            sections[current_section] = "\n".join(current_lines).strip()

        return frontmatter, sections, body

    @staticmethod
    def to_skill_config(
        frontmatter: dict[str, Any],
        sections: dict[str, str],
        file_path: str,
        disclosure_level: int = 1,
    ) -> SkillConfig:
        """将解析后的 SKILL.md 数据转换为 SkillConfig

        Args:
            frontmatter: YAML 元数据
            sections: Markdown sections
            file_path: 原始文件路径
            disclosure_level: 渐进式加载层级（0=概要, 1=完整, 2=参考）

        Returns:
            SkillConfig 实例
        """
        # 构建 IntentConfig
        intent_data = frontmatter.get("intent") or {}
        intent_config_data: dict[str, Any] = {
            "keywords": intent_data.get("keywords", []),
            "description": intent_data.get("description", ""),
            "examples": intent_data.get("examples", []),
        }

        # 构建 QualityGateConfig
        qg_data = frontmatter.get("quality_gate") or {}
        quality_gate_config_data: dict[str, Any] = {
            "required_fields": qg_data.get("required_fields", []),
            "min_word_count": qg_data.get("min_word_count", 0),
            "max_retries": qg_data.get("max_retries", 0),
            "custom_validator": qg_data.get("custom_validator"),
        }

        # 从 sections 构建 prompt
        prompt: dict[str, str] = {}
        if sections.get("steps"):
            prompt["instructions"] = sections["steps"]
        if sections.get("pitfalls"):
            prompt["constraints"] = sections["pitfalls"]
        if sections.get("verification"):
            prompt["output_format"] = sections["verification"]
        if sections.get("trigger"):
            prompt["context"] = sections["trigger"]

        # Level 0: 仅保留 name + description，prompt 仅含 identity
        if disclosure_level == 0:
            prompt = {"identity": frontmatter.get("description", frontmatter.get("name", ""))}

        # 确保 prompt 非空（llm_generate 模式要求 prompt 配置）
        if not prompt:
            prompt = {"identity": frontmatter.get("description", frontmatter.get("name", ""))}

        # 校验必要字段
        name = frontmatter.get("name", "")
        if not name:
            raise ConfigValidationError(
                agent_name="unknown",
                key="name",
                reason="SKILL.md frontmatter must contain a non-empty 'name' field",
            )

        return SkillConfig(
            name=name,
            agent_type=frontmatter.get("agent_type", frontmatter.get("name", "")),
            description=frontmatter.get("description", ""),
            task_mode="llm_generate",
            prompt=prompt,
            execution_mode=frontmatter.get("execution_mode", "react"),
            intent=intent_config_data,
            quality_gate=quality_gate_config_data,
            skill_md_path=file_path,
            disclosure_level=disclosure_level,
        )
