from __future__ import annotations

import functools
import inspect
from typing import Any, Callable, ParamSpec, TypeVar

from acp.client import ACPClient

P = ParamSpec("P")
R = TypeVar("R")


def governed_tool(
    *,
    agent_id: str,
    tool: str,
    action: str,
    client: ACPClient | None = None,
    risk_score: float | None = None,
    trace_id: str | None = None,
) -> Callable[[Callable[P, R]], Callable[P, R]]:
    """
    Decorator: preflight to ACP interceptor, then execute wrapped function only on allow.

    Framework-agnostic: any callable can be wrapped; agent frameworks simply register the
    wrapped function as their "tool".
    """

    def decorator(fn: Callable[P, R]) -> Callable[P, R]:
        c = client or ACPClient()

        @functools.wraps(fn)
        def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
            sig = inspect.signature(fn)
            bound = sig.bind_partial(*args, **kwargs)
            bound.apply_defaults()
            arguments: dict[str, Any] = dict(bound.arguments)

            amount: int | None = None
            if "amount" in arguments and isinstance(arguments["amount"], int):
                amount = arguments["amount"]

            extra = {
                "fn": getattr(fn, "__qualname__", fn.__name__),
                "args": arguments,
            }

            c.enforce_or_raise(
                agent_id=agent_id,
                tool=tool,
                action=action,
                amount=amount,
                risk_score=risk_score,
                extra=extra,
                trace_id=trace_id,
            )
            return fn(*args, **kwargs)

        return wrapper

    return decorator
