"""Vorim SDK — Anthropic/Claude integration.

Wraps Anthropic tool use with Vorim permission checks, audit trails,
and agent identity. Works with the Anthropic Python SDK (messages API
with tool use).

Requires: ``pip install vorim[anthropic]``

Usage::

    from vorim import Vorim
    from vorim.integrations.anthropic import VorimToolRegistry

    vorim = Vorim(api_key="agid_sk_live_...")

    registry = VorimToolRegistry(vorim=vorim, agent_id="agid_acme_...")
    registry.add(
        name="search_docs",
        description="Search internal documents",
        input_schema={"type": "object", "properties": {"query": {"type": "string"}}, "required": ["query"]},
        execute=lambda args: search_docs(args["query"]),
        permission="agent:read",
    )

    # Use with Anthropic messages API
    response = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=1024,
        messages=messages,
        tools=registry.to_anthropic_tools(),
    )

    # Execute tool_use blocks
    tool_results = registry.execute_tool_use_blocks(
        [b for b in response.content if b.type == "tool_use"]
    )
"""

from __future__ import annotations

import json
import time
from dataclasses import dataclass, field
from typing import Any, Callable

from ..client import Vorim
from ..types import AuditEventInput, PermissionScope


# ── Tool Definition ──────────────────────────────────────────────────────


@dataclass
class VorimToolDefinition:
    """Definition of a tool managed by the Vorim registry."""

    name: str
    description: str
    input_schema: dict[str, Any]
    execute: Callable[[Any], Any]
    permission: PermissionScope = "agent:execute"


# ── Tool Registry ────────────────────────────────────────────────────────


class VorimToolRegistry:
    """Manages tools with Vorim permission checks and audit logging for Anthropic/Claude.

    Args:
        vorim: Vorim client instance.
        agent_id: The Vorim agent_id.
        default_permission: Default scope for tools without an explicit one.
        async_audit: Whether to emit audit events in fire-and-forget mode.
    """

    def __init__(
        self,
        vorim: Vorim,
        agent_id: str,
        default_permission: PermissionScope = "agent:execute",
        async_audit: bool = True,
    ) -> None:
        self._vorim = vorim
        self._agent_id = agent_id
        self._default_permission = default_permission
        self._async_audit = async_audit
        self._tools: dict[str, VorimToolDefinition] = {}

    def add(
        self,
        name: str,
        description: str,
        input_schema: dict[str, Any],
        execute: Callable[[Any], Any],
        permission: PermissionScope = "agent:execute",
    ) -> "VorimToolRegistry":
        """Register a tool."""
        self._tools[name] = VorimToolDefinition(
            name=name,
            description=description,
            input_schema=input_schema,
            execute=execute,
            permission=permission,
        )
        return self

    def to_anthropic_tools(self) -> list[dict[str, Any]]:
        """Convert registered tools to Anthropic's tool format."""
        return [
            {
                "name": t.name,
                "description": t.description,
                "input_schema": t.input_schema,
            }
            for t in self._tools.values()
        ]

    def execute_tool_use_blocks(self, tool_use_blocks: list[Any]) -> list[dict[str, Any]]:
        """Execute tool_use blocks from a Claude message response.

        Each call is checked against Vorim permissions and audited.
        Returns a list of tool_result dicts ready to send back to Claude.
        """
        return [self._execute_single(block) for block in tool_use_blocks]

    def _execute_single(self, block: Any) -> dict[str, Any]:
        tool_name = block.name if hasattr(block, "name") else block["name"]
        tool_id = block.id if hasattr(block, "id") else block["id"]
        tool_input = block.input if hasattr(block, "input") else block["input"]

        definition = self._tools.get(tool_name)
        if not definition:
            return {
                "type": "tool_result",
                "tool_use_id": tool_id,
                "content": json.dumps({"error": f"Unknown tool: {tool_name}"}),
                "is_error": True,
            }

        scope = definition.permission or self._default_permission

        # 1. Permission check
        check = self._vorim.check(self._agent_id, scope)

        if not check.allowed:
            self._vorim.emit(
                agent_id=self._agent_id,
                event_type="tool_call",
                action=tool_name,
                resource=_truncate(json.dumps(tool_input), 500),
                permission=scope,
                result="denied",
                metadata={"reason": check.reason, "framework": "anthropic"},
            )
            reason_suffix = f" — {check.reason}" if check.reason else ""
            return {
                "type": "tool_result",
                "tool_use_id": tool_id,
                "content": json.dumps({"error": f"Permission denied: {scope}{reason_suffix}"}),
                "is_error": True,
            }

        # 2. Execute
        start = time.monotonic()
        try:
            result = definition.execute(tool_input)
            content = result if isinstance(result, str) else json.dumps(result)
            elapsed = round((time.monotonic() - start) * 1000)

            self._vorim.emit(
                agent_id=self._agent_id,
                event_type="tool_call",
                action=tool_name,
                resource=_truncate(json.dumps(tool_input), 500),
                permission=scope,
                result="success",
                latency_ms=elapsed,
                metadata={"framework": "anthropic"},
            )

            return {
                "type": "tool_result",
                "tool_use_id": tool_id,
                "content": content,
            }
        except Exception as exc:
            elapsed = round((time.monotonic() - start) * 1000)
            err_msg = str(exc)

            self._vorim.emit(
                agent_id=self._agent_id,
                event_type="tool_call",
                action=tool_name,
                resource=_truncate(json.dumps(tool_input), 500),
                permission=scope,
                result="error",
                latency_ms=elapsed,
                error_code=type(exc).__name__,
                metadata={"error": err_msg, "framework": "anthropic"},
            )

            return {
                "type": "tool_result",
                "tool_use_id": tool_id,
                "content": json.dumps({"error": err_msg}),
                "is_error": True,
            }


# ── Agent Registration Helper ────────────────────────────────────────────


def create_vorim_claude_agent(
    vorim: Vorim,
    name: str,
    capabilities: list[str],
    scopes: list[PermissionScope],
    tools: list[VorimToolDefinition],
    description: str | None = None,
) -> dict[str, Any]:
    """Register a new agent and return a ready-to-use tool registry.

    Returns::

        {
            "agent_id": str,
            "registration": AgentRegistrationResult,
            "registry": VorimToolRegistry,
            "private_key": str,
        }
    """
    registration = vorim.register(
        name=name,
        capabilities=capabilities,
        scopes=scopes,
        description=description,
    )

    agent_id = registration.agent.agent_id
    registry = VorimToolRegistry(vorim=vorim, agent_id=agent_id)
    for tool in tools:
        registry.add(
            name=tool.name,
            description=tool.description,
            input_schema=tool.input_schema,
            execute=tool.execute,
            permission=tool.permission,
        )

    return {
        "agent_id": agent_id,
        "registration": registration,
        "registry": registry,
        "private_key": registration.private_key,
    }


# ── Helpers ──────────────────────────────────────────────────────────────


def _truncate(s: str, max_len: int) -> str:
    return s[:max_len] + "…" if len(s) > max_len else s
