"""
智能体工具集

定义智能体可以执行的操作。工具通过 function calling 机制由 LLM 自动选择调用。
"""

import os
import json
import subprocess
import tempfile
from pathlib import Path
from datetime import datetime
from typing import Any


def _python_tool_enabled() -> bool:
    return os.environ.get("coco_enable_python_tool", "").strip().lower() in {"1", "true", "yes", "on"}


RUN_PYTHON_TOOL = {
    "type": "function",
    "function": {
        "name": "run_python",
        "description": "执行 Python 代码并返回输出。代码可以 import 常用库。",
        "parameters": {
            "type": "object",
            "properties": {
                "code": {
                    "type": "string",
                    "description": "要执行的 Python 代码",
                },
            },
            "required": ["code"],
        },
    },
}


# ─── 工具定义（OpenAI function calling 格式） ────────────────

TOOLS = [
    {
        "type": "function",
        "function": {
            "name": "read_file",
            "description": "读取文件内容",
            "parameters": {
                "type": "object",
                "properties": {
                    "path": {
                        "type": "string",
                        "description": "文件路径（绝对路径或相对于智能体工作目录）",
                    },
                },
                "required": ["path"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "write_file",
            "description": "写入文件（会覆盖已有文件）",
            "parameters": {
                "type": "object",
                "properties": {
                    "path": {
                        "type": "string",
                        "description": "文件路径",
                    },
                    "content": {
                        "type": "string",
                        "description": "要写入的内容",
                    },
                },
                "required": ["path", "content"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "list_dir",
            "description": "列出目录内容",
            "parameters": {
                "type": "object",
                "properties": {
                    "path": {
                        "type": "string",
                        "description": "目录路径（留空表示当前目录）",
                    },
                },
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "web_search",
            "description": "搜索互联网获取实时信息",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "搜索关键词",
                    },
                },
                "required": ["query"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "get_time",
            "description": "获取当前日期和时间",
            "parameters": {
                "type": "object",
                "properties": {},
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "查询城市天气",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "城市名称，如 北京、上海",
                    },
                },
                "required": ["city"],
            },
        },
    },
]

if _python_tool_enabled():
    TOOLS.insert(3, RUN_PYTHON_TOOL)


# ─── 工具执行器 ──────────────────────────────────────────

class ToolExecutor:
    """执行工具调用，返回结果"""

    def __init__(self, workspace_dir: Path):
        self.workspace = Path(workspace_dir).resolve()
        self.workspace.mkdir(parents=True, exist_ok=True)

    def execute(self, name: str, args: dict) -> str:
        """执行一个工具调用"""
        handler = getattr(self, f"_tool_{name}", None)
        if handler is None:
            return f"[工具] 未知工具: {name}"

        try:
            result = handler(args)
            # 截断过长结果
            if isinstance(result, str) and len(result) > 4000:
                result = result[:4000] + "\n...(结果已截断)"
            return result
        except Exception as e:
            return f"[工具错误] {name}: {e}"

    def _resolve_path(self, path: str) -> Path:
        """解析路径：相对路径基于 workspace，并禁止逃逸。"""
        raw = Path(str(path or ".")).expanduser()
        target = raw.resolve() if raw.is_absolute() else (self.workspace / raw).resolve()
        workspace = os.path.normcase(str(self.workspace))
        target_text = os.path.normcase(str(target))
        try:
            common = os.path.commonpath([workspace, target_text])
        except ValueError:
            common = ""
        if common != workspace:
            raise ValueError("只能访问智能体 workspace 内的文件")
        return target

    def _tool_read_file(self, args: dict) -> str:
        path = self._resolve_path(args["path"])
        if not path.exists():
            return f"文件不存在: {path}"
        if path.stat().st_size > 50_000:
            return f"文件过大 ({path.stat().st_size} bytes)，只显示前 3000 字符:\n{path.read_text(encoding='utf-8', errors='replace')[:3000]}"
        return path.read_text(encoding="utf-8", errors="replace")

    def _tool_write_file(self, args: dict) -> str:
        path = self._resolve_path(args["path"])
        path.parent.mkdir(parents=True, exist_ok=True)
        content = args["content"]
        path.write_text(content, encoding="utf-8")
        return f"已写入: {path} ({len(content)} 字符)"

    def _tool_list_dir(self, args: dict) -> str:
        path = self._resolve_path(args.get("path", "."))
        if not path.exists() or not path.is_dir():
            return f"目录不存在: {path}"

        items = []
        for item in sorted(path.iterdir()):
            prefix = "📁 " if item.is_dir() else "📄 "
            items.append(f"{prefix}{item.name}")
        return "\n".join(items) if items else "空目录"

    def _tool_run_python(self, args: dict) -> str:
        if not _python_tool_enabled():
            return "run_python 工具默认关闭；需要时设置 coco_enable_python_tool=1 后重启。"
        code = args["code"]
        with tempfile.NamedTemporaryFile(
            suffix=".py", mode="w", encoding="utf-8", delete=False
        ) as f:
            f.write(code)
            tmp_path = f.name

        try:
            result = subprocess.run(
                ["python", tmp_path],
                capture_output=True,
                text=True,
                timeout=30,
                cwd=str(self.workspace),
            )
            output = result.stdout
            if result.stderr:
                output += f"\n[stderr]\n{result.stderr}"
            return output.strip() or "(无输出)"
        except subprocess.TimeoutExpired:
            return "执行超时（30 秒）"
        finally:
            Path(tmp_path).unlink(missing_ok=True)

    def _tool_web_search(self, args: dict) -> str:
        query = args["query"]
        import requests
        try:
            # 使用 DuckDuckGo 的免费搜索（无需 API key）
            resp = requests.get(
                "https://api.duckduckgo.com/",
                params={"q": query, "format": "json", "no_html": 1},
                timeout=10,
                headers={"User-Agent": "cocoAgent/1.0"},
            )
            data = resp.json()

            lines = []
            if data.get("AbstractText"):
                lines.append(f"摘要: {data['AbstractText']}")

            for r in data.get("RelatedTopics", [])[:5]:
                if isinstance(r, dict) and r.get("Text"):
                    lines.append(f"- {r['Text']}")

            return "\n".join(lines) if lines else f"未找到 '{query}' 的相关结果"
        except Exception as e:
            return f"搜索失败: {e}"

    def _tool_get_time(self, args: dict) -> str:
        now = datetime.now()
        return now.strftime("%Y年%m月%d日 %H:%M:%S") + f" 星期{['一','二','三','四','五','六','日'][now.weekday()]}"

    def _tool_get_weather(self, args: dict) -> str:
        city = args["city"]
        import urllib.request
        try:
            # 使用 wttr.in 免费天气 API
            url = f"https://wttr.in/{city}?format=%C+%t+%h+%w&lang=zh"
            resp = urllib.request.urlopen(url, timeout=10)
            return resp.read().decode("utf-8").strip()
        except Exception as e:
            return f"天气查询失败: {e}"
