"""
Tool Guide Registry for Mid Platform.
Provides tool-based usage guidance with caching support.

Tool guides are usage manuals for tools, loaded on-demand with metadata scanning.
This separates tool definitions (Function Calling) from usage guides (Tool Guides).
"""

import logging
import re
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any

logger = logging.getLogger(__name__)

TOOLS_DIR = Path(__file__).parent.parent.parent.parent / "tools"


@dataclass
class ToolGuideMetadata:
    """Lightweight tool guide metadata for quick scanning (~100 tokens)."""
    name: str
    description: str
    triggers: list[str] = field(default_factory=list)
    anti_triggers: list[str] = field(default_factory=list)
    tools: list[str] = field(default_factory=list)


@dataclass
class ToolGuideDefinition:
    """Full tool guide definition with complete content."""
    name: str
    description: str
    triggers: list[str]
    anti_triggers: list[str]
    tools: list[str]
    content: str
    raw_markdown: str


class ToolGuideRegistry:
    """
    Tool guide registry with caching support.
    
    Features:
    - Load tool guides from .md files
    - Cache tool guides in memory for high-frequency access
    - Provide lightweight metadata for quick scanning
    - Provide full content on demand
    """
    
    _instance = None
    _initialized = False
    
    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance
    
    def __init__(self, tools_dir: Path | None = None):
        if ToolGuideRegistry._initialized:
            return
        
        self._tools_dir = tools_dir or TOOLS_DIR
        self._tool_guides: dict[str, ToolGuideDefinition] = {}
        self._metadata_cache: dict[str, ToolGuideMetadata] = {}
        self._tool_to_guide: dict[str, str] = {}
        self._loaded = False
        
        ToolGuideRegistry._initialized = True
        logger.info(f"[ToolGuideRegistry] Initialized with tools_dir={self._tools_dir}")
    
    def load_tools(self, force_reload: bool = False) -> None:
        """
        Load all tool guides from tools directory into cache.
        
        Args:
            force_reload: Force reload even if already loaded
        """
        if self._loaded and not force_reload:
            logger.debug("[ToolGuideRegistry] Tool guides already loaded, skipping")
            return
        
        if not self._tools_dir.exists():
            logger.warning(f"[ToolGuideRegistry] Tools directory not found: {self._tools_dir}")
            return
        
        self._tool_guides.clear()
        self._metadata_cache.clear()
        self._tool_to_guide.clear()
        
        for tool_file in self._tools_dir.glob("*.md"):
            try:
                tool_guide = self._parse_tool_file(tool_file)
                if tool_guide:
                    self._tool_guides[tool_guide.name] = tool_guide
                    self._metadata_cache[tool_guide.name] = ToolGuideMetadata(
                        name=tool_guide.name,
                        description=tool_guide.description,
                        triggers=tool_guide.triggers,
                        anti_triggers=tool_guide.anti_triggers,
                        tools=tool_guide.tools,
                    )
                    for tool_name in tool_guide.tools:
                        self._tool_to_guide[tool_name] = tool_guide.name
                    logger.info(f"[ToolGuideRegistry] Loaded tool guide: {tool_guide.name} (tools: {tool_guide.tools})")
            except Exception as e:
                logger.error(f"[ToolGuideRegistry] Failed to load tool guide from {tool_file}: {e}")
        
        self._loaded = True
        logger.info(f"[ToolGuideRegistry] Loaded {len(self._tool_guides)} tool guides")
    
    def _parse_tool_file(self, file_path: Path) -> ToolGuideDefinition | None:
        """
        Parse a tool guide markdown file.
        
        Expected format:
        ---
        name: tool_name
        description: Tool description
        triggers:
          - trigger 1
          - trigger 2
        anti_triggers:
          - anti trigger 1
        tools:
          - tool_name
        ---
        
        ## Usage Guide
        ...
        """
        content = file_path.read_text(encoding="utf-8")
        
        frontmatter_match = re.match(r"^---\s*\n(.*?)\n---\s*\n(.*)$", content, re.DOTALL)
        if not frontmatter_match:
            logger.warning(f"[ToolGuideRegistry] No frontmatter found in {file_path}")
            return None
        
        frontmatter_text = frontmatter_match.group(1)
        body = frontmatter_match.group(2)
        
        metadata: dict[str, Any] = {}
        current_key = None
        current_list: list[str] | None = None
        
        for line in frontmatter_text.split("\n"):
            if not line.strip():
                continue
            
            key_match = re.match(r"^(\w+):\s*(.*)$", line)
            if key_match:
                current_key = key_match.group(1)
                value = key_match.group(2).strip()
                
                if value:
                    metadata[current_key] = value
                    current_list = None
                else:
                    current_list = []
                    metadata[current_key] = current_list
            elif line.startswith("  - ") and current_list is not None:
                current_list.append(line[4:].strip())
        
        name = metadata.get("name", file_path.stem)
        description = metadata.get("description", "")
        triggers = metadata.get("triggers", [])
        anti_triggers = metadata.get("anti_triggers", [])
        tools = metadata.get("tools", [])
        
        if isinstance(triggers, str):
            triggers = [triggers]
        if isinstance(anti_triggers, str):
            anti_triggers = [anti_triggers]
        if isinstance(tools, str):
            tools = [tools]
        
        return ToolGuideDefinition(
            name=name,
            description=description,
            triggers=triggers,
            anti_triggers=anti_triggers,
            tools=tools,
            content=body.strip(),
            raw_markdown=content,
        )
    
    def get_tool_guide(self, name: str) -> ToolGuideDefinition | None:
        """Get full tool guide definition by name."""
        if not self._loaded:
            self.load_tools()
        return self._tool_guides.get(name)
    
    def get_tool_metadata(self, name: str) -> ToolGuideMetadata | None:
        """Get lightweight tool guide metadata by name."""
        if not self._loaded:
            self.load_tools()
        return self._metadata_cache.get(name)
    
    def get_guide_for_tool(self, tool_name: str) -> ToolGuideDefinition | None:
        """Get tool guide associated with a tool."""
        if not self._loaded:
            self.load_tools()
        guide_name = self._tool_to_guide.get(tool_name)
        if guide_name:
            return self._tool_guides.get(guide_name)
        return None
    
    def list_tools(self) -> list[str]:
        """List all tool guide names."""
        if not self._loaded:
            self.load_tools()
        return list(self._tool_guides.keys())
    
    def list_tool_metadata(self) -> list[ToolGuideMetadata]:
        """List all tool guide metadata (lightweight)."""
        if not self._loaded:
            self.load_tools()
        return list(self._metadata_cache.values())
    
    def build_tools_prompt_section(self, tool_names: list[str] | None = None) -> str:
        """
        Build tools section for ReAct prompt.
        
        Args:
            tool_names: If provided, only include tools for these names.
                       If None, include all tools.
        
        Returns:
            Formatted tools section string
        """
        if not self._loaded:
            self.load_tools()
        
        if not self._tool_guides:
            return ""
        
        tools_to_include: list[ToolGuideDefinition] = []
        
        if tool_names:
            for tool_name in tool_names:
                tool_guide = self.get_guide_for_tool(tool_name)
                if tool_guide and tool_guide not in tools_to_include:
                    tools_to_include.append(tool_guide)
        else:
            tools_to_include = list(self._tool_guides.values())
        
        if not tools_to_include:
            return ""
        
        lines = ["## 工具使用指南", ""]
        lines.append("以下是每个工具的详细使用说明：")
        lines.append("")
        
        for tool_guide in tools_to_include:
            lines.append(f"### {tool_guide.name}")
            lines.append(f"描述: {tool_guide.description}")
            
            if tool_guide.triggers:
                lines.append("触发条件:")
                for trigger in tool_guide.triggers:
                    lines.append(f"  - {trigger}")
            
            if tool_guide.anti_triggers:
                lines.append("不应触发:")
                for anti in tool_guide.anti_triggers:
                    lines.append(f"  - {anti}")
            
            lines.append("")
            lines.append(tool_guide.content)
            lines.append("")
        
        return "\n".join(lines)
    
    def build_compact_tools_section(self) -> str:
        """
        Build compact tools section with only name and description.
        This is used for the initial tool list, with full guidance loaded separately.
        """
        if not self._loaded:
            self.load_tools()
        
        if not self._metadata_cache:
            return "当前没有可用的工具使用指南。"
        
        lines = ["## 可用工具列表", "", "以下是你可以使用的工具：", ""]
        
        for meta in self._metadata_cache.values():
            lines.append(f"- **{meta.name}**: {meta.description}")
            if meta.tools:
                lines.append(f"  关联工具: {', '.join(meta.tools)}")
        
        return "\n".join(lines)


_tool_guide_registry: ToolGuideRegistry | None = None


def get_tool_guide_registry() -> ToolGuideRegistry:
    """Get global tool guide registry instance."""
    global _tool_guide_registry
    if _tool_guide_registry is None:
        _tool_guide_registry = ToolGuideRegistry()
    return _tool_guide_registry


def init_tool_guide_registry(tools_dir: Path | None = None) -> ToolGuideRegistry:
    """Initialize and return tool guide registry."""
    global _tool_guide_registry
    _tool_guide_registry = ToolGuideRegistry(tools_dir=tools_dir)
    _tool_guide_registry.load_tools()
    return _tool_guide_registry
