"""
trust_gate — Intercept tool calls with Nerq preflight trust checks.
"""

import logging
import time
from functools import wraps
from typing import Any

import requests

logger = logging.getLogger("nerq.trust_gate")

NERQ_API = "https://nerq.ai"
PREFLIGHT_TIMEOUT = 5.0
CACHE_TTL = 300  # 5 minutes


class TrustError(Exception):
    """Raised when a tool call is denied due to low trust score."""

    def __init__(self, tool_name: str, trust_score: float | None, recommendation: str):
        self.tool_name = tool_name
        self.trust_score = trust_score
        self.recommendation = recommendation
        super().__init__(
            f"Trust gate denied '{tool_name}': "
            f"trust={trust_score}, recommendation={recommendation}"
        )


class _PreflightCache:
    """Simple TTL cache for preflight results."""

    def __init__(self, ttl: int = CACHE_TTL):
        self._cache: dict[str, tuple[dict, float]] = {}
        self._ttl = ttl

    def get(self, key: str) -> dict | None:
        if key in self._cache:
            result, ts = self._cache[key]
            if time.time() - ts < self._ttl:
                return result
            del self._cache[key]
        return None

    def set(self, key: str, value: dict):
        self._cache[key] = (value, time.time())
        # Evict if too large
        if len(self._cache) > 5000:
            self._cache.clear()


_cache = _PreflightCache()


def _preflight_check(target: str, caller: str | None = None, api_base: str = NERQ_API) -> dict:
    """Run preflight trust check against Nerq API."""
    cached = _cache.get(target.lower())
    if cached:
        return cached

    try:
        params = {"target": target}
        if caller:
            params["caller"] = caller
        resp = requests.get(
            f"{api_base}/v1/preflight",
            params=params,
            timeout=PREFLIGHT_TIMEOUT,
        )
        resp.raise_for_status()
        result = resp.json()
        _cache.set(target.lower(), result)
        return result
    except Exception as e:
        logger.warning(f"Nerq preflight unreachable for '{target}': {e}")
        return {
            "target": target,
            "target_trust": None,
            "target_grade": None,
            "recommendation": "UNKNOWN",
            "interaction_risk": "UNKNOWN",
            "_error": str(e),
        }


def trust_gate(agent, min_trust: float = 60, caller: str | None = None, api_base: str = NERQ_API):
    """Wrap a LangChain agent with Nerq trust-gating on tool calls.

    Before each tool call, checks the tool's trust score via Nerq preflight.
    - DENY (trust < 40): raises TrustError
    - CAUTION (trust 40-69): logs warning, proceeds
    - PROCEED (trust >= 70): proceeds silently
    - UNKNOWN (not found / API unreachable): proceeds with warning

    Args:
        agent: A LangChain agent (AgentExecutor, RunnableAgent, etc.)
        min_trust: Minimum trust score to allow (default 60). Tools below this raise TrustError.
        caller: Optional caller agent name for interaction risk calculation.
        api_base: Nerq API base URL (default https://nerq.ai).

    Returns:
        Wrapped agent with trust-gating enabled.
    """
    # Wrap the invoke/run method
    original_invoke = getattr(agent, "invoke", None)
    original_run = getattr(agent, "run", None)

    def _check_tool(tool_name: str):
        """Check a tool's trust before allowing the call."""
        result = _preflight_check(tool_name, caller=caller, api_base=api_base)
        recommendation = result.get("recommendation", "UNKNOWN")
        trust = result.get("target_trust")

        if recommendation == "DENY":
            raise TrustError(tool_name, trust, recommendation)

        if trust is not None and trust < min_trust:
            raise TrustError(tool_name, trust, f"BELOW_MIN_TRUST ({min_trust})")

        if recommendation == "CAUTION":
            logger.warning(
                f"Nerq trust gate: CAUTION for '{tool_name}' "
                f"(trust={trust}, grade={result.get('target_grade')})"
            )

        if recommendation == "UNKNOWN":
            logger.warning(
                f"Nerq trust gate: '{tool_name}' not found in index, proceeding anyway"
            )

    # Patch tools if they exist
    if hasattr(agent, "tools"):
        for tool in agent.tools:
            original_run_tool = tool._run

            @wraps(original_run_tool)
            def _gated_run(*args, _orig=original_run_tool, _name=tool.name, **kwargs):
                _check_tool(_name)
                return _orig(*args, **kwargs)

            tool._run = _gated_run

            if hasattr(tool, "_arun"):
                original_arun_tool = tool._arun

                @wraps(original_arun_tool)
                async def _gated_arun(*args, _orig=original_arun_tool, _name=tool.name, **kwargs):
                    _check_tool(_name)
                    return await _orig(*args, **kwargs)

                tool._arun = _gated_arun

    return agent
