# -*- coding: utf-8 -*-
"""
工具注册中心
替代硬编码 TOOLS dict，支持动态注册/注销，为 MCP 工具接入预留扩展点。
"""
import json
from typing import Any, Dict, List, Optional

from .base import Tool
from .file_read import FileReadTool
from .file_write import FileWriteTool
from .file_edit import FileEditTool
from .shell_exec import ShellExecTool
from .web_search import WebSearchTool
from .web_fetch import WebFetchTool
from .todo_write import TodoWriteTool
from ..security import get_default_filter
from ..context.snip import snip


def get_tc_field(tc, name: str, default=None):
    """兼容 dict 和 SDK 对象两种 tool_call 格式"""
    if isinstance(tc, dict):
        return tc.get(name, default)
    return getattr(tc, name, default)


def merge_tool_calls(chunks) -> List[Dict[str, Any]]:
    """合并流式 tool_calls delta，按 index 聚合 arguments。返回完整 tool_calls 列表"""
    merged: Dict[int, Dict[str, Any]] = {}
    for tc in chunks:
        idx = get_tc_field(tc, "index", 0)
        if idx not in merged:
            merged[idx] = {"id": None, "type": "function", "function": {"name": "", "arguments": ""}}
        m = merged[idx]
        tc_id = get_tc_field(tc, "id", None)
        if tc_id:
            m["id"] = tc_id
        func = get_tc_field(tc, "function", {})
        func_name = get_tc_field(func, "name", "")
        if func_name:
            m["function"]["name"] = func_name
        func_args = get_tc_field(func, "arguments", "")
        if func_args:
            m["function"]["arguments"] += func_args
    return [merged[i] for i in sorted(merged)]


def format_tool_result(name: str, result: Dict[str, Any], max_len: int = 4000) -> str:
    """格式化工具结果给 LLM 的 JSON 字符串。

    对 shell_exec 输出做长度截断以保护上下文窗口，并在返回前对所有文本做
    敏感信息脱敏（借鉴 zhikuncode-main SensitiveDataFilter），避免密钥
    泄露到 LLM 上下文。

    Phase 7 Tier 1-1 BE：shell_exec 输出改用 SnipService 头尾保留截断
    （借鉴 zhikuncode SnipService），保留前 50% + 后 50%，
    修复"末尾 returncode / 错误堆栈丢失"痛点。
    """
    if name == "shell_exec":
        stdout = (result.get("stdout", "") or "")
        stderr = (result.get("stderr", "") or "")
        # Phase 7 Tier 1-1 BE：用 snip 头尾保留截断，确保末尾 returncode / 错误信息不丢失
        if len(stdout) > max_len:
            stdout = snip(stdout, max_len, head_ratio=0.5)
        if len(stderr) > max_len:
            stderr = snip(stderr, max_len, head_ratio=0.5)
        # 脱敏：shell_exec 输出最易泄露密钥（env / cat ~/.bashrc 等）
        redactor = get_default_filter()
        stdout = redactor.redact(stdout)
        stderr = redactor.redact(stderr)
        return json.dumps({
            "success": result.get("success", False),
            "stdout": stdout,
            "stderr": stderr,
            "returncode": result.get("returncode", None),
        }, ensure_ascii=False)
    # 其他工具：对整个 dict 递归脱敏（web_fetch / file_read 等可能含密钥）
    redacted = get_default_filter().redact_dict(result)
    return json.dumps(redacted, ensure_ascii=False)


class ToolRegistry:
    """工具注册中心：统一管理内置工具与外部扩展工具（MCP），追踪每个工具的来源。"""

    def __init__(self):
        self._tools: Dict[str, Tool] = {}
        self._tool_sources: Dict[str, str] = {}

    def register(self, tool: Tool, source: str = "unknown") -> None:
        """注册单个工具，按 tool.name 索引。可选 source 参数追溯工具来源。"""
        self._tools[tool.name] = tool
        self._tool_sources[tool.name] = source

    def unregister(self, name: str) -> None:
        """按名注销工具"""
        self._tools.pop(name, None)
        self._tool_sources.pop(name, None)

    def get_source(self, name: str) -> Optional[str]:
        """返回工具的来源标识，未注册返回 None。"""
        return self._tool_sources.get(name)

    def list_by_source(self, source: str) -> List[str]:
        """返回指定来源的所有工具名列表。"""
        return [name for name, src in self._tool_sources.items() if src == source]

    def unregister_by_source(self, source: str) -> int:
        """注销指定来源的所有工具。返回注销数量。"""
        names = self.list_by_source(source)
        for name in names:
            self.unregister(name)
        return len(names)

    def get(self, name: str) -> Optional[Tool]:
        """按名获取工具，未注册返回 None"""
        return self._tools.get(name)

    def list_names(self) -> List[str]:
        """返回所有已注册工具名"""
        return list(self._tools.keys())

    def to_openai_schemas(self) -> List[Dict[str, Any]]:
        """生成所有工具的 OpenAI function calling 定义"""
        return [t.to_openai_schema() for t in self._tools.values()]

    def register_builtins(self) -> None:
        """注册 7 个内置工具，source='builtin'"""
        for tool in (
            FileReadTool(), FileWriteTool(), FileEditTool(),
            ShellExecTool(), WebSearchTool(), WebFetchTool(),
            TodoWriteTool()
        ):
            self.register(tool, source="builtin")

    def __contains__(self, name: str) -> bool:
        return name in self._tools

    def execute_tool_call(self, tool_call) -> str:
        """执行单个 tool_call，返回给 AI 的结果 JSON 字符串。

        参数解析失败或工具未注册时返回 {"success": False, "error": ...}。
        """
        func = get_tc_field(tool_call, "function", {})
        name = get_tc_field(func, "name", "")
        args_str = get_tc_field(func, "arguments", "")
        try:
            arguments = json.loads(args_str) if args_str else {}
        except json.JSONDecodeError:
            return json.dumps({"success": False, "error": f"参数解析失败: {args_str}"})

        tool = self.get(name)
        if tool is None:
            return json.dumps({"success": False, "error": f"未知工具: {name}"})

        result = tool.execute(**arguments)
        return format_tool_result(name, result)


def create_default_registry() -> ToolRegistry:
    """创建主 Agent 工具注册表：6 个内置工具 + spawn_subagents（由调用方注册）"""
    reg = ToolRegistry()
    reg.register_builtins()
    return reg


def create_subagent_registry() -> ToolRegistry:
    """创建子 Agent 工具注册表：仅 6 个内置工具，不含 spawn_subagents。

    从物理层面禁止子 Agent 递归调用 spawn_subagents，实现递归防护。
    """
    reg = ToolRegistry()
    reg.register_builtins()
    return reg
