"""Typed callback adapters for browser, cloud, credential, package, and publish tools."""

from __future__ import annotations

from collections.abc import Callable, Mapping
from typing import Any, TypeVar

from .canonical import sha256_value
from .engine import ScopeWritEngine
from .errors import GatewayError
from .gateway import GuardedExecutor, GuardedResult
from .models import ActionProposal

T = TypeVar("T")

_ADAPTER_ACTIONS: dict[str, tuple[str, ...]] = {
    "browser": ("browser.",),
    "cloud": ("cloud.",),
    "credential": ("credential.",),
    "package": ("package.",),
    "publish": ("publish.",),
}


class GuardedToolAdapter:
    """A fixed-class adapter for SDK operations that ScopeWrit cannot invoke itself.

    The adapter hashes arguments before admission and invokes the supplied SDK
    operation only after its exact ticket is consumed.
    """

    def __init__(self, engine: ScopeWritEngine, *, adapter: str):
        allowed_prefixes = _ADAPTER_ACTIONS.get(adapter)
        if allowed_prefixes is None:
            raise GatewayError(f"unsupported guarded tool adapter: {adapter}")
        self.engine = engine
        self.adapter = adapter
        self.allowed_prefixes = allowed_prefixes

    def execute(
        self,
        *,
        attestation: str,
        run_id: str,
        request_id: str,
        action: str,
        resource: str,
        arguments: Mapping[str, Any],
        operation: Callable[[Mapping[str, Any]], T],
        approval: str | None = None,
        justification: str | None = None,
        outcome: Callable[[T], Mapping[str, Any]] | None = None,
    ) -> GuardedResult[T]:
        if not any(action.startswith(prefix) for prefix in self.allowed_prefixes):
            raise GatewayError(f"action {action} cannot be executed by the {self.adapter} adapter")
        try:
            argument_digest = sha256_value(dict(arguments))
        except (TypeError, ValueError) as exc:
            raise GatewayError("tool arguments must be deterministic JSON") from exc
        proposal = ActionProposal(
            request_id=request_id,
            run_id=run_id,
            action=action,
            resource=resource,
            attributes={"arguments_sha256": argument_digest},
            justification=justification,
        )
        return GuardedExecutor(self.engine, adapter=self.adapter).execute(
            attestation=attestation,
            proposal=proposal,
            approval=approval,
            operation=lambda: operation(arguments),
            outcome=outcome,
        )
