"""
Burrow SDK adapter for Microsoft Semantic Kernel.

Uses Semantic Kernel's function invocation filter system to scan tool calls
through Burrow for prompt injection detection. Automatically resolves
per-agent identity from the kernel context when available.

Usage:
    from burrow import BurrowGuard
    from burrow.integrations.semantic_kernel import create_burrow_filter

    guard = BurrowGuard(client_id="...", client_secret="...")
    burrow_filter = create_burrow_filter(guard)

    kernel.add_filter("function_invocation", burrow_filter)
"""

from __future__ import annotations

import json
import logging
from typing import Any

__all__ = ["BurrowBlockedError", "create_burrow_filter"]

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


class BurrowBlockedError(Exception):
    """Raised when Burrow blocks a Semantic Kernel function invocation."""

    def __init__(self, category: str, confidence: float, function_name: str = ""):
        self.category = category
        self.confidence = confidence
        self.function_name = function_name
        super().__init__(
            f"Burrow blocked function '{function_name}': {category} ({confidence:.0%} confidence)"
        )


def create_burrow_filter(
    guard: Any,
    block_on_warn: bool = False,
) -> Any:
    """Create a Semantic Kernel function invocation filter that scans with Burrow.

    Automatically resolves per-agent identity from the invocation context
    (plugin_name or kernel.agent_name) to produce identifiers like
    ``semantic-kernel:planner``. Falls back to ``semantic-kernel``.

    Args:
        guard: BurrowGuard instance.
        block_on_warn: If True, also block on "warn" verdicts.

    Returns:
        Filter function for kernel.add_filter("function_invocation", ...).
    """

    async def burrow_function_filter(context: Any, next: Any) -> None:
        # Parameter MUST be named `next` (not `next_fn`) because SK's
        # construct_call_stack passes it as a keyword argument: next=stack[0]
        function_name = getattr(context, "function_name", None) or "unknown"
        arguments = getattr(context, "arguments", None)

        text = _extract_text(arguments)
        if text.strip():
            agent_label = _resolve_agent(context)

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

            if result.is_blocked or (block_on_warn and result.is_warning):
                logger.warning(
                    "Burrow blocked function %s for agent '%s': %s (%.0f%% confidence)",
                    function_name,
                    agent_label,
                    result.category,
                    result.confidence * 100,
                )
                raise BurrowBlockedError(result.category, result.confidence, function_name)

        await next(context)

    return burrow_function_filter


def _resolve_agent(context: Any) -> str:
    plugin_name = getattr(context, "plugin_name", None)
    if plugin_name:
        return f"semantic-kernel:{plugin_name}"
    kernel = getattr(context, "kernel", None)
    if kernel is not None:
        agent_name = getattr(kernel, "agent_name", None)
        if agent_name:
            return f"semantic-kernel:{agent_name}"
    return "semantic-kernel"


def _extract_text(arguments: Any) -> str:
    if arguments is None:
        return ""
    if isinstance(arguments, dict):
        for key in ("input", "command", "content", "query", "text", "url", "file_path", "pattern"):
            if key in arguments:
                return str(arguments[key])
        return json.dumps(arguments)
    if isinstance(arguments, str):
        return arguments
    # Semantic Kernel KernelArguments may support dict-like access
    if hasattr(arguments, "__getitem__"):
        try:
            for key in ("input", "command", "content", "query", "text"):
                try:
                    val = arguments[key]
                    if val:
                        return str(val)
                except (KeyError, TypeError):
                    continue
        except Exception:
            pass
    return str(arguments)
