"""
Skills工具层 - Skills加载和execute
tools/skills_tools.py
"""

from __future__ import annotations

import asyncio
import importlib.util
import inspect
import os
import re
import sys
from pathlib import Path
from typing import Dict, Any, Optional, List
from dataclasses import dataclass, field

if __package__ in (None, ""):
    sys.path.insert(0, str(Path(__file__).parent.parent.parent))
    from ..utils.tool_registry import ToolRegistry, ToolSchema, ToolParameter
else:
    from ..utils.tool_registry import ToolRegistry, ToolSchema, ToolParameter


@dataclass
class SkillInfo:
    """Skill信息"""

    name: str
    description: str
    module_path: str
    function_name: str
    parameters: Dict[str, Any]
    examples: List[Dict[str, Any]]
    skill_dir: str
    # 新增：元数据字段， with 于渐进式披露
    display_name: str = ""
    trigger_keywords: List[str] = field(default_factory=list)
    # 新增：skill使 with 指南
    usage_guide: str = ""
    # 新增：加载状态标记
    metadata_loaded: bool = False
    full_instruction_loaded: bool = False
    # 新增：支持多函数（函数名 -> 参数信息）
    functions: Dict[str, Dict[str, Any]] = field(default_factory=dict)
    # 新增：完整的SKILL.md内容
    full_md_content: str = ""


class SkillsManager:
    """Skills管理器"""

    def __init__(
        self, project_path: Path, skills_config: Optional[Dict[str, Any]] = None
    ):
        self.project_path = project_path
        # skills目录相对于项目根目录，而不是当前工作目录
        # 首先尝试从配置Get ，默认为项目根目录下的skills
        skills_dir_name = "skills"
        if skills_config and "skills_dir" in skills_config:
            skills_dir_name = skills_config["skills_dir"]

        # 如果skills_dir是相对路径，假设相对于项目根目录
        skills_dir_path = Path(skills_dir_name)
        if not skills_dir_path.is_absolute():
            # Get 项目根目录（aacode_local目录）
            project_root = Path(__file__).parent.parent.absolute()
            self.skills_dir = project_root / skills_dir_name
        else:
            self.skills_dir = skills_dir_path

        self.loaded_skills: Dict[str, SkillInfo] = {}
        self.enabled_skills: List[str] = []
        # 新增：从配置加载的元数据
        self.skills_metadata: Dict[str, Dict[str, Any]] = {}
        if skills_config:
            self.skills_metadata = skills_config.get("skills_metadata", {})
            # 从配置加载启 with 的skills
            self.enabled_skills = skills_config.get("enabled_skills", [])

    def discover_skills(
        self, load_full_instructions: bool = False
    ) -> Dict[str, SkillInfo]:
        """发现并加载所有skills（从 SKILL.md 提取元数据，不依赖 yaml）"""
        if not self.skills_dir.exists():
            return {}

        for skill_dir in self.skills_dir.iterdir():
            if not skill_dir.is_dir():
                continue
            skill_name = skill_dir.name
            if skill_name.startswith(".") or skill_name.startswith("_"):
                continue

            # 所有 skill 统一从 SKILL.md 提取元数据
            skill_info = self._load_skill(skill_dir)
            if skill_info is None:
                continue

            skill_info.metadata_loaded = True
            if not load_full_instructions:
                # 渐进式：先不加载完整指令
                skill_info.full_instruction_loaded = False
            self.loaded_skills[skill_name] = skill_info

        return self.loaded_skills

    def _load_full_instruction(self, skill_name: str) -> bool:
        """加载技能的完整指令（按需加载）"""
        if skill_name not in self.loaded_skills:
            return False

        skill_info = self.loaded_skills[skill_name]
        if skill_info.full_instruction_loaded:
            return True

        skill_dir = Path(skill_info.skill_dir)
        if not skill_dir.exists():
            return False

        # 读取完整的SKILL.md内容
        skill_md = skill_dir / "SKILL.md"
        if skill_md.exists():
            skill_info.full_md_content = self._read_full_skill_md(skill_md)

        # 加载完整技能信息
        full_info = self._load_skill(skill_dir)
        if full_info:
            # 合并信息，保留元数据
            skill_info.description = full_info.description
            skill_info.module_path = full_info.module_path
            skill_info.function_name = full_info.function_name
            skill_info.parameters = full_info.parameters
            skill_info.examples = full_info.examples
            skill_info.functions = full_info.functions  # 合并多函数信息
            skill_info.full_instruction_loaded = True
            return True

        # 即使没有py文件，只要读取了MD内容也算加载成功
        if skill_info.full_md_content:
            skill_info.full_instruction_loaded = True
            return True

        return False

    def _load_skill(self, skill_dir: Path) -> Optional[SkillInfo]:
        """加载单个skill（从 SKILL.md + .py 提取所有信息）"""
        skill_name = skill_dir.name

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

        md_info = self._parse_skill_md(skill_md)

        impl_files = [
            f
            for f in skill_dir.iterdir()
            if f.is_file() and f.suffix == ".py" and f.name != "__pycache__"
        ]

        if not impl_files:
            return None

        main_impl = impl_files[0]

        func_names = self._find_all_functions(main_impl)
        if not func_names:
            return None

        main_func_name = func_names[0]

        all_functions = {}
        for func_name in func_names:
            params, examples = self._extract_function_info(main_impl, func_name)
            all_functions[func_name] = {"parameters": params, "examples": examples}

        #  with  SKILL.md 的 Parameters 段落补全函数参数描述
        params_desc = md_info.get("parameters_desc", {})
        if params_desc:
            for func_name in func_names:
                func_params = all_functions[func_name]["parameters"]
                for pname, pdesc in params_desc.items():
                    if pname in func_params:
                        func_params[pname]["description"] = pdesc

        #  with  SKILL.md 的 Example 段落补全示例
        examples_text = md_info.get("examples_text", "")
        if examples_text:
            for func_name in func_names:
                if examples_text not in all_functions[func_name]["examples"]:
                    all_functions[func_name]["examples"].append(examples_text)

        return SkillInfo(
            name=skill_name,
            description=md_info.get("description", ""),
            module_path=str(main_impl),
            function_name=main_func_name,
            parameters=all_functions[main_func_name]["parameters"],
            examples=all_functions[main_func_name]["examples"],
            skill_dir=str(skill_dir),
            functions=all_functions,
            display_name=skill_name.replace("_", " ").title(),
            trigger_keywords=[],
            usage_guide=md_info.get("full_content", ""),
            full_md_content=md_info.get("full_content", ""),
        )

    def _find_all_functions(self, impl_file: Path) -> List[str]:
        """查找所有公开函数（过滤私有函数，优先返回async函数）"""
        try:
            source = impl_file.read_text(encoding="utf-8")
            import re

            # 改进的正则表达式：只匹配顶级函数（ lines首没有空格）
            function_pattern = r"^(async\s+)?def\s+(\w+)\s*\("
            lines = source.split("\n")

            async_functions = []
            sync_functions = []
            for line in lines:
                # 检查是否是顶级函数（ lines首没有空格或制表符）
                if line and line[0] in (" ", "\t"):
                    continue  # 跳过有缩进的 lines（嵌套函数）

                match = re.match(function_pattern, line.strip())
                if match:
                    is_async = bool(match.group(1))
                    func_name = match.group(2)
                    # 排除私有函数和main
                    if not func_name.startswith("_") and func_name != "main":
                        if is_async:
                            async_functions.append(func_name)
                        else:
                            sync_functions.append(func_name)

            # 优先返回async函数，如果没有async函数才返回sync函数
            return (
                async_functions
                if async_functions
                else (sync_functions if sync_functions else [])
            )

        except:
            return []

    def _parse_skill_md(self, skill_md: Path) -> Dict:
        """从 SKILL.md 解析结构化字段（fastclaw 格式）"""
        try:
            content = skill_md.read_text(encoding="utf-8")
        except Exception:
            return {}
        lines = content.strip().split("\n")
        result = {
            "full_content": content,
            "description": "",
            "parameters_desc": {},
            "examples_text": "",
        }

        current_section = None
        section_lines: list[str] = []

        for line in lines:
            stripped = line.strip()
            if stripped.startswith("## "):
                if current_section and section_lines:
                    self._store_section(result, current_section, section_lines)
                current_section = stripped[3:].strip().lower()
                section_lines = []
            elif current_section:
                section_lines.append(line)

        if current_section and section_lines:
            self._store_section(result, current_section, section_lines)

        if not result["description"]:
            result["description"] = lines[0] if lines else ""

        return result

    def _store_section(self, result: dict, section: str, lines: list):
        text = "\n".join(lines).strip()
        if "description" in section:
            result["description"] = text
        elif "parameter" in section:
            for line in lines:
                match = re.match(r"-\s*(\w+)\s*:\s*(.+)", line.strip())
                if match:
                    result["parameters_desc"][match.group(1)] = match.group(2).strip()
        elif "example" in section:
            result["examples_text"] = text

    def _read_full_skill_md(self, skill_md: Path) -> str:
        """读取完整的SKILL.md内容"""
        try:
            return skill_md.read_text(encoding="utf-8")
        except:
            return ""

    def _extract_function_info(self, impl_file: Path, func_name: str) -> tuple:
        """提取函数参数和示例"""
        try:
            spec = importlib.util.spec_from_file_location("skill", impl_file)
            if spec is None or spec.loader is None:
                return {}, []

            module = importlib.util.module_from_spec(spec)
            spec.loader.exec_module(module)

            func = getattr(module, func_name, None)
            if not func:
                return {}, []

            sig = inspect.signature(func)
            params = {}
            for name, param in sig.parameters.items():
                if name in ("self", "kwargs"):
                    continue

                # 改进类型提取，处理复杂类型
                param_type = self._extract_param_type(param.annotation)

                param_info = {
                    "type": param_type,
                    "required": param.default == inspect.Parameter.empty,
                    "description": "",
                }
                if param.default != inspect.Parameter.empty:
                    param_info["default"] = param.default
                params[name] = param_info

            examples: list[str] = []
            docstring = func.__doc__ or ""
            return params, examples

        except:
            return {}, []

    def _extract_param_type(self, annotation) -> str:
        """提取参数类型，处理复杂类型"""
        import typing

        if annotation is inspect.Parameter.empty:
            return "Any"

        # 检查是否是 typing 模块的类型
        if hasattr(annotation, "__origin__"):
            # 处理泛型类型如 Optional, List, Dict
            origin = annotation.__origin__
            args = annotation.__args__ if hasattr(annotation, "__args__") else ()

            # Optional[X] 实际上是 Union[X, None]
            if origin is typing.Union:
                # 检查是否有 None 类型
                non_none_args = [arg for arg in args if arg is not type(None)]
                if len(non_none_args) == 1 and type(None) in args:
                    # 这是一个 Optional 类型
                    inner_type = non_none_args[0]
                    inner_str = self._extract_param_type(inner_type)
                    return f"Optional[{inner_str}]"
                return str(annotation)

            # List[X]
            if origin is list:
                if args:
                    inner_str = self._extract_param_type(args[0])
                    return f"List[{inner_str}]"
                return "List"

            # Dict[K, V]
            if origin is dict:
                if args and len(args) >= 2:
                    key_str = self._extract_param_type(args[0])
                    val_str = self._extract_param_type(args[1])
                    return f"Dict[{key_str}, {val_str}]"
                return "Dict"

        # 处理简单类型
        type_str = str(annotation)

        # 处理 <class 'str'> 格式
        if type_str.startswith("<class '") and type_str.endswith("'>"):
            return type_str[8:-2]

        # 处理 typing 模块的简单类型
        if type_str.startswith("typing."):
            simple_types = {
                "typing.Str": "str",
                "typing.Int": "int",
                "typing.Bool": "bool",
                "typing.Float": "float",
                "typing.List": "List",
                "typing.Dict": "Dict",
                "typing.Any": "Any",
            }
            if type_str in simple_types:
                return simple_types[type_str]
            # 返回简化形式
            return type_str[7:]  # 去掉 "typing."

        return type_str if type_str else "Any"

    async def execute_skill(
        self, skill_name: str, func_name: str | None = None, **kwargs
    ) -> Dict[str, Any]:
        """executeskill（按需加载完整指令）
        Args:
            skill_name: skill名称
            func_name: 要execute的函数名，如果为None则使 with 主函数
        """
        if skill_name not in self.loaded_skills:
            return {"success": False, "error": f"Skill not found: {skill_name}"}

        if skill_name not in self.enabled_skills:
            return {"success": False, "error": f"Skill not enabled: {skill_name}"}

        skill_info = self.loaded_skills[skill_name]

        # 按需加载完整指令（渐进式披露核心）
        if not skill_info.full_instruction_loaded:
            self._load_full_instruction(skill_name)
            skill_info = self.loaded_skills[skill_name]

        # 确定要execute的函数
        target_func_name = func_name if func_name else skill_info.function_name

        # 验证函数是否存在
        if skill_info.functions and target_func_name not in skill_info.functions:
            available_funcs = list(skill_info.functions.keys())
            return {
                "success": False,
                "error": f"Function '{target_func_name}' not found, available functions: {available_funcs}",
            }

        try:
            spec = importlib.util.spec_from_file_location(
                "skill", skill_info.module_path
            )
            if spec is None or spec.loader is None:
                return {
                    "success": False,
                    "error": f"Unable to load skill module: {skill_info.module_path}",
                }

            module = importlib.util.module_from_spec(spec)
            spec.loader.exec_module(module)

            func = getattr(module, target_func_name)
            if asyncio.iscoroutinefunction(func):
                result = await func(**kwargs)
            else:
                result = func(**kwargs)

            if isinstance(result, dict) and "success" not in result:
                result = {"success": True, "result": result}

            return result

        except Exception as e:
            return {"success": False, "error": str(e)}

    def enable_skills(self, skill_names: List[str]):
        """启 with skills"""
        for name in skill_names:
            if name in self.loaded_skills:
                self.enabled_skills.append(name)

    def list_enabled_skills(self) -> List[str]:
        """列出启 with 的skills"""
        return self.enabled_skills

    def get_skills_list_for_prompt(self) -> str:
        """生成用于 system prompt 的技能列表字符串（仅名称+描述）"""
        if not self.enabled_skills:
            return "(暂无可用 Skills)"
        lines = []
        for name in self.enabled_skills:
            info = self.loaded_skills.get(name)
            desc = info.description if info else ""
            lines.append(f"- {name}: {desc}")
        return "\n".join(lines)


