"""
Burrow adapter for Anthropic Claude Agent SDK (Python).

Provides PreToolUse and PostToolUse hooks that scan tool inputs/outputs
through Burrow for prompt injection detection.

Usage::

    from burrow import BurrowGuard
    from burrow.integrations.claude_sdk import create_burrow_hooks

    guard = BurrowGuard(client_id="...", client_secret="...")
    hooks = create_burrow_hooks(guard)

    options = ClaudeAgentOptions(
        hooks=hooks,
    )

.. note:: Per-agent identity limitation

    The Claude Agent SDK ``HookContext`` does not currently carry agent
    identity. To get per-agent scanning in a multi-agent setup, create
    separate hooks for each subagent with a different ``agent_name``::

        orchestrator_hooks = create_burrow_hooks(guard, agent_name="claude-sdk:orchestrator")
        researcher_hooks = create_burrow_hooks(guard, agent_name="claude-sdk:researcher")
"""

from __future__ import annotations

import json
import logging
from collections.abc import Callable, Coroutine
from dataclasses import dataclass, field
from typing import Any

from burrow import BurrowGuard, ScanResult

__all__ = ["BurrowBlockedError", "create_burrow_hooks"]

logger = logging.getLogger("burrow.integrations.claude_sdk")

HookCallback = Callable[..., Coroutine[Any, Any, dict]]


@dataclass
class HookMatcher:
    """Local stand-in for ``claude_agent_sdk.HookMatcher``.

    Defined here so the module can be imported without the Claude Agent
    SDK installed. The fields are duck-type-compatible with the real class.
    """

    matcher: str | None = None
    hooks: list[HookCallback] = field(default_factory=list)


class BurrowBlockedError(Exception):
    """Raised when Burrow blocks a Claude SDK message."""

    def __init__(self, result: ScanResult):
        self.result = result
        super().__init__(f"Burrow blocked: {result.category} ({result.confidence:.0%} confidence)")


def _extract_tool_input_text(tool_input: dict) -> str:
    """Extract scannable text from tool input arguments.

    Prioritises known text-bearing fields, falls back to JSON serialisation.
    """
    text_fields = ("command", "content", "query", "text", "url", "file_path", "pattern")
    parts: list[str] = []
    for key in text_fields:
        if key in tool_input and tool_input[key] is not None:
            parts.append(str(tool_input[key]))
    return " ".join(parts).strip() if parts else json.dumps(tool_input)


def create_burrow_hooks(
    guard: BurrowGuard,
    agent_name: str = "claude-sdk",
    block_on_warn: bool = False,
    scan_tool_calls: bool = True,
    scan_tool_results: bool = True,
) -> dict[str, list[HookMatcher]]:
    """
    Create Claude Agent SDK hooks that scan tool calls with Burrow.

    Returns a hooks configuration dict compatible with ``ClaudeAgentOptions``.
    The hooks intercept ``PreToolUse`` and ``PostToolUse`` events to scan
    for prompt injection.

    Args:
        guard: BurrowGuard instance.
        agent_name: Agent name for scan context. Use the pattern
            ``"claude-sdk:<subagent>"`` for multi-agent systems.
        block_on_warn: If True, also block on "warn" verdicts.
        scan_tool_calls: Scan tool inputs before execution (default True).
        scan_tool_results: Scan tool outputs after execution (default True).

    Returns:
        Dict of ``{"PreToolUse": [...], "PostToolUse": [...]}`` suitable for
        passing to ``ClaudeAgentOptions(hooks=...)``.
    """

    def _should_block(result: ScanResult) -> bool:
        return result.is_blocked or (block_on_warn and result.is_warning)

    hooks: dict[str, list[HookMatcher]] = {}

    if scan_tool_calls:

        async def pre_tool_callback(
            input_data: dict,
            tool_use_id: str | None,
            context: Any,
        ) -> dict:
            tool_input = input_data.get("tool_input", {})
            tool_name = input_data.get("tool_name")

            if not isinstance(tool_input, dict):
                text = str(tool_input) if tool_input else ""
            else:
                text = _extract_tool_input_text(tool_input)

            if not text.strip():
                return {}

            result = await guard.scan_async(
                text,
                content_type="tool_call",
                agent=agent_name,
                tool_name=tool_name,
            )

            if _should_block(result):
                logger.warning(
                    "Burrow blocked tool call '%s' for agent '%s': %s (%.0f%% confidence)",
                    tool_name,
                    agent_name,
                    result.category,
                    result.confidence * 100,
                )
                return {
                    "hookSpecificOutput": {
                        "hookEventName": "PreToolUse",
                        "permissionDecision": "deny",
                        "permissionDecisionReason": (
                            f"Blocked by Burrow: {result.category} "
                            f"({result.confidence:.0%} confidence)"
                        ),
                    },
                }

            return {}

        hooks["PreToolUse"] = [HookMatcher(matcher=".*", hooks=[pre_tool_callback])]

    if scan_tool_results:

        async def post_tool_callback(
            input_data: dict,
            tool_use_id: str | None,
            context: Any,
        ) -> dict:
            tool_response = input_data.get("tool_response", "")
            tool_name = input_data.get("tool_name")

            text = str(tool_response)[:4096] if tool_response else ""
            if not text.strip():
                return {}

            result = await guard.scan_async(
                text,
                content_type="tool_response",
                agent=agent_name,
                tool_name=tool_name,
            )

            if _should_block(result):
                logger.warning(
                    "Burrow flagged tool response from '%s' for agent '%s': %s (%.0f%% confidence)",
                    tool_name,
                    agent_name,
                    result.category,
                    result.confidence * 100,
                )
                return {
                    "hookSpecificOutput": {
                        "hookEventName": "PostToolUse",
                        "burrowAction": result.action,
                        "burrowCategory": result.category,
                        "burrowConfidence": result.confidence,
                    },
                }

            return {}

        hooks["PostToolUse"] = [HookMatcher(matcher=".*", hooks=[post_tool_callback])]

    return hooks
