from __future__ import annotations

import inspect
import json
from pathlib import Path
from typing import Any

from ..schemas import ToolDefinition, ToolExecutionContext
from ..util import ensure_dir, truncate_text, utcnow_iso


class ToolRegistry:
    def __init__(self, audit_path: str | Path):
        self._tools: dict[str, ToolDefinition] = {}
        self.audit_path = ensure_dir(Path(audit_path).parent) / Path(audit_path).name

    def register(self, definition: ToolDefinition) -> None:
        self._tools[definition.name] = definition

    def register_many(self, definitions: list[ToolDefinition]) -> None:
        for definition in definitions:
            self.register(definition)

    def list(self) -> list[ToolDefinition]:
        return [self._tools[name] for name in sorted(self._tools)]

    def export_openai_schemas(self) -> list[dict[str, Any]]:
        return [tool.openai_schema() for tool in self.list()]

    async def execute(self, name: str, arguments: dict[str, Any], context: ToolExecutionContext) -> Any:
        if name not in self._tools:
            raise KeyError(f"Unknown tool: {name}")
        tool = self._tools[name]
        if tool.owner_only and not context.is_owner:
            raise PermissionError(f"Tool requires owner access: {name}")
        self._write_audit(
            {
                "time": utcnow_iso(),
                "event": "tool_call",
                "tool": name,
                "chat_id": context.chat_id,
                "user_id": context.user_id,
                "arguments": arguments,
                "security_mode": context.session.security_mode,
            }
        )
        try:
            result = tool.handler(context, arguments)
            if inspect.isawaitable(result):
                result = await result
        except Exception as exc:
            self._write_audit(
                {
                    "time": utcnow_iso(),
                    "event": "tool_error",
                    "tool": name,
                    "chat_id": context.chat_id,
                    "user_id": context.user_id,
                    "error": str(exc),
                }
            )
            raise
        preview = truncate_text(json.dumps(result, ensure_ascii=False, default=str), 1500)
        self._write_audit(
            {
                "time": utcnow_iso(),
                "event": "tool_result",
                "tool": name,
                "chat_id": context.chat_id,
                "user_id": context.user_id,
                "result_preview": preview,
            }
        )
        return result

    def _write_audit(self, payload: dict[str, Any]) -> None:
        with self.audit_path.open("a", encoding="utf-8") as handle:
            handle.write(json.dumps(payload, ensure_ascii=False, default=str) + "\n")
