"""
Skills 发现和加载器
"""

import re
import json
from pathlib import Path
from typing import Optional, List, Dict, Any
from dataclasses import dataclass

import yaml


# 默认 Skills 搜索路径（项目级优先，用户级兜底）
DEFAULT_SKILL_PATHS = [
    Path.cwd() / ".claude" / "skills",
    Path.home() / ".claude" / "skills",
]


@dataclass
class SkillMetadata:
    """Skill 元数据（Level 1）"""
    name: str
    description: str
    skill_path: Path
    requires: list[str]  # 依赖的其他 skill 名称列表
    id: Optional[str] = None  # Skill ID（用于API选择）
    category: Optional[str] = None  # 分类
    tags: List[str] = None  # 标签
    capabilities: Optional[str] = None  # 能力描述（用于选择）

    def __post_init__(self):
        if self.tags is None:
            self.tags = []

    def to_prompt_line(self) -> str:
        requires_suffix = f" (requires: {', '.join(self.requires)})" if self.requires else ""
        return f"- **{self.name}**: {self.description}{requires_suffix}"


@dataclass
class SkillReference:
    """Skill 参考文档"""
    name: str
    path: Path
    description: str = ""  # 可选：从文件第一行提取描述

    def to_prompt_line(self) -> str:
        desc = f" - {self.description}" if self.description else ""
        return f"- `{self.name}`{desc}"


@dataclass
class SkillContent:
    """Skill 完整内容（Level 2）"""
    metadata: SkillMetadata
    instructions: str
    references: list[SkillReference]  # 可用的参考文档列表
    loaded_skills: dict[str, 'SkillContent']  # 已加载的依赖 skills {skill_name: SkillContent}

    def get_reference(self, name: str) -> Optional[SkillReference]:
        """根据名称获取参考文档"""
        for ref in self.references:
            if ref.name == name:
                return ref
        return None


class SkillLoader:
    """Skills 加载器"""

    def __init__(self, skill_paths: list[Path] | None = None):
        self.skill_paths = skill_paths or DEFAULT_SKILL_PATHS
        self._metadata_cache: dict[str, SkillMetadata] = {}

    def scan_skills(self) -> list[SkillMetadata]:
        """Level 1: 扫描所有 Skills 元数据"""
        skills = []
        seen_names = set()

        for base_path in self.skill_paths:
            if not base_path.exists():
                continue

            for skill_dir in base_path.iterdir():
                if not skill_dir.is_dir():
                    continue

                skill_md = skill_dir / "SKILL.md"
                if not skill_md.exists():
                    continue

                metadata = self._parse_skill_metadata(skill_md)
                if metadata and metadata.name not in seen_names:
                    skills.append(metadata)
                    seen_names.add(metadata.name)
                    self._metadata_cache[metadata.name] = metadata

        return skills

    def _parse_skill_metadata(self, skill_md_path: Path) -> Optional[SkillMetadata]:
        try:
            content = skill_md_path.read_text(encoding="utf-8")
        except Exception:
            return None

        frontmatter_match = re.match(
            r'^---\s*\n(.*?)\n---\s*\n',
            content,
            re.DOTALL
        )

        if not frontmatter_match:
            return None

        try:
            frontmatter = yaml.safe_load(frontmatter_match.group(1))
            name = frontmatter.get("name", "")
            description = frontmatter.get("description", "")
            requires = frontmatter.get("requires", [])

            # 确保 requires 是列表
            if isinstance(requires, str):
                requires = [requires]
            elif not isinstance(requires, list):
                requires = []

            if not name:
                return None

            return SkillMetadata(
                name=name,
                description=description,
                skill_path=skill_md_path.parent,
                requires=requires,
            )
        except yaml.YAMLError:
            return None

    def load_skill(self, skill_name: str, loading: Optional[set[str]] = None) -> Optional[SkillContent]:
        """
        Level 2: 加载 Skill 完整内容（包含 references 列表和依赖的 skills）

        Args:
            skill_name: 要加载的 skill 名称
            loading: 正在加载的 skill 集合（用于检测循环依赖）
        """
        if loading is None:
            loading = set()

        # 检测循环依赖
        if skill_name in loading:
            return None

        loading.add(skill_name)

        metadata = self._metadata_cache.get(skill_name)
        if not metadata:
            self.scan_skills()
            metadata = self._metadata_cache.get(skill_name)

        if not metadata:
            loading.discard(skill_name)
            return None

        skill_md = metadata.skill_path / "SKILL.md"
        try:
            content = skill_md.read_text(encoding="utf-8")
        except Exception:
            loading.discard(skill_name)
            return None

        body_match = re.match(
            r'^---\s*\n.*?\n---\s*\n(.*)$',
            content,
            re.DOTALL
        )
        instructions = body_match.group(1).strip() if body_match else content

        # 扫描 references/ 目录
        references = self._scan_references(metadata.skill_path)

        # 递归加载依赖的 skills
        loaded_skills: dict[str, SkillContent] = {}
        for required_skill in metadata.requires:
            if required_skill not in loading:  # 避免循环
                required_content = self.load_skill(required_skill, loading)
                if required_content:
                    loaded_skills[required_skill] = required_content

        loading.discard(skill_name)

        return SkillContent(
            metadata=metadata,
            instructions=instructions,
            references=references,
            loaded_skills=loaded_skills,
        )

    def _scan_references(self, skill_path: Path) -> list[SkillReference]:
        """扫描 skill 的 references/ 目录"""
        references_dir = skill_path / "references"
        if not references_dir.exists() or not references_dir.is_dir():
            return []

        references = []
        for ref_file in sorted(references_dir.iterdir()):
            if ref_file.is_file() and ref_file.suffix in ['.md', '.txt', '.json']:
                # 尝试从文件第一行提取描述
                description = ""
                try:
                    first_line = ref_file.read_text(encoding="utf-8").split('\n')[0]
                    # 移除 Markdown 标题符号
                    first_line = re.sub(r'^#+\s*', '', first_line).strip()
                    if first_line and not first_line.startswith('#'):
                        description = first_line[:100]  # 限制描述长度
                except Exception:
                    pass

                references.append(SkillReference(
                    name=ref_file.name,
                    path=ref_file,
                    description=description
                ))

        return references

    def load_reference(self, skill_name: str, reference_name: str) -> Optional[str]:
        """加载指定 skill 的 reference 文件内容"""
        metadata = self._metadata_cache.get(skill_name)
        if not metadata:
            self.scan_skills()
            metadata = self._metadata_cache.get(skill_name)

        if not metadata:
            return None

        ref_path = metadata.skill_path / "references" / reference_name
        if not ref_path.exists():
            return None

        try:
            return ref_path.read_text(encoding="utf-8")
        except Exception:
            return None

    def build_system_prompt(self, base_prompt: str = "") -> str:
        """构建包含 Skills 列表的 system prompt"""
        skills = self.scan_skills()

        if skills:
            skills_section = "## Available Skills\n\n"
            skills_section += "You have access to the following specialized skills:\n\n"
            for skill in skills:
                skills_section += skill.to_prompt_line() + "\n"
            skills_section += "\n"
            skills_section += "### How to Use Skills\n\n"
            skills_section += "1. **Discover**: Review the skills list above\n"
            skills_section += "2. **Load**: When a user request matches a skill's description, "
            skills_section += "use `load_skill(skill_name)` to get detailed instructions\n"
            skills_section += "3. **References**: If the loaded skill lists available references, "
            skills_section += "use `load_reference(skill_name, reference_name)` to load specific reference material when needed\n"
            skills_section += "4. **Execute**: Follow the skill's instructions, which may include "
            skills_section += "running scripts via `bash`\n\n"
            skills_section += "**Important**:\n"
            skills_section += "- Only load a skill when it's relevant to the user's request\n"
            skills_section += "- Reference files are NOT automatically loaded - only load them when the skill instructions indicate they are needed\n"
            skills_section += "- Script code never enters the context - only their output does\n"
        else:
            skills_section = "## Skills\n\nNo skills currently available.\n"

        if base_prompt:
            return f"{base_prompt}\n\n{skills_section}"
        else:
            return f"You are a helpful coding assistant.\n\n{skills_section}"

    # ============================================
    # 智能Skill选择方法
    # ============================================

    async def select_skills_for_task(
        self,
        user_intent: str,
        available_skills: Optional[List[Dict[str, Any]]] = None,
        context: Optional[Dict[str, Any]] = None
    ) -> List[str]:
        """
        根据用户意图智能选择最相关的Skills

        Args:
            user_intent: 用户意图描述
            available_skills: 可用的技能列表（从API获取）
            context: 额外的上下文信息

        Returns:
            选择的Skill ID列表
        """
        try:
            # 如果有可用技能列表（从API），进行智能选择
            if available_skills:
                selected = []

                # 基于关键词的简单匹配
                intent_lower = user_intent.lower()
                for skill in available_skills:
                    skill_id = skill.get('id', '')
                    name = skill.get('name', '').lower()
                    description = skill.get('description', '').lower()
                    capabilities = skill.get('capabilities', '').lower()
                    tags = skill.get('tags', [])

                    # 检查描述和能力是否匹配
                    match_score = 0
                    if name in intent_lower:
                        match_score += 10
                    for tag in tags:
                        if tag.lower() in intent_lower or any(k in intent_lower for k in tag.lower().split('_')):
                            match_score += 5

                    # 检查描述匹配
                    keywords = ['大纲', '内容', '对话', '角色', '世界观', '审核', '创作', '写作']
                    for keyword in keywords:
                        if keyword in intent_lower and keyword in description:
                            match_score += 3

                    if match_score > 0:
                        selected.append((skill_id, match_score))

                # 按匹配分数排序
                selected.sort(key=lambda x: x[1], reverse=True)
                return [skill_id for skill_id, _ in selected[:5]]

            # 如果没有可用技能列表，使用本地扫描的技能
            skills = self.scan_skills()
            selected = []

            intent_lower = user_intent.lower()
            for skill in skills:
                match_score = 0
                if skill.name.lower() in intent_lower:
                    match_score += 10
                for tag in skill.tags:
                    if tag.lower() in intent_lower:
                        match_score += 5

                if match_score > 0:
                    selected.append((skill.name, match_score))

            selected.sort(key=lambda x: x[1], reverse=True)
            return [skill_name for skill_name, _ in selected[:5]]

        except Exception as e:
            from app.utils.logger import get_logger
            logger = get_logger(__name__)
            logger.warning(f"智能Skill选择失败: {e}")
            return []

    def get_user_skills_metadata(self, user_skills: List[Dict[str, Any]]) -> str:
        """
        获取用户配置Skills的元数据格式

        Args:
            user_skills: 用户Skills列表

        Returns:
            格式化后的Skills信息
        """
        if not user_skills:
            return ""

        lines = ["### 用户配置的Skills\n\n"]
        for skill in user_skills:
            skill_name = skill.get('name', 'Unknown')
            skill_id = skill.get('id', '')
            behavior = skill.get('behavior', skill.get('description', ''))[:200]

            lines.append(f"- **{skill_name}** (ID: `{skill_id}`)")
            lines.append(f"  {behavior}...")
            lines.append("")

        return "\n".join(lines)
