"""
Response verification and tool hash-pinning.

Provides standalone verification utilities that can be used
independently of the full SecureMCPTool wrapper.
"""

import json
import time
import hashlib
from typing import Any, Dict, List, Optional, Set

from crewai_mcps.secure_tool import KeyManager, NonceTracker, ToolPinner


class ResponseVerifier:
    """
    Verifies the integrity of MCP tool responses.

    Checks that:
    - The response corresponds to a known signed request
    - The nonce has not been replayed
    - The tool definition has not changed since discovery
    - Response timing is within acceptable bounds
    """

    def __init__(
        self,
        key_manager: Optional[KeyManager] = None,
        nonce_tracker: Optional[NonceTracker] = None,
        tool_pinner: Optional[ToolPinner] = None,
        max_response_age: float = 30.0,
    ):
        self.keys = key_manager or KeyManager()
        self.nonces = nonce_tracker or NonceTracker()
        self.pinner = tool_pinner or ToolPinner()
        self.max_response_age = max_response_age
        self._pending_requests: Dict[str, Dict[str, Any]] = {}

    def register_request(
        self,
        nonce: str,
        tool_name: str,
        arguments: Dict[str, Any],
        envelope: Dict[str, str],
    ) -> None:
        """Register an outgoing request for later response matching."""
        self._pending_requests[nonce] = {
            "tool_name": tool_name,
            "arguments": arguments,
            "envelope": envelope,
            "sent_at": time.time(),
        }

    def verify_response(
        self,
        nonce: str,
        tool_name: str,
        response: Any,
    ) -> "VerificationResult":
        """
        Verify a tool response against the original request.

        Args:
            nonce: The nonce from the original request
            tool_name: The tool that produced the response
            response: The response content

        Returns:
            VerificationResult with status details
        """
        result = VerificationResult(tool_name=tool_name, nonce=nonce)

        # Check if we have a matching request
        if nonce not in self._pending_requests:
            result.valid = False
            result.errors.append("No matching request found for nonce")
            return result

        request = self._pending_requests[nonce]

        # Check tool name matches
        if request["tool_name"] != tool_name:
            result.valid = False
            result.errors.append(
                f"Tool name mismatch: expected '{request['tool_name']}', "
                f"got '{tool_name}'"
            )

        # Check response age
        age = time.time() - request["sent_at"]
        if age > self.max_response_age:
            result.valid = False
            result.errors.append(
                f"Response too old: {age:.1f}s > {self.max_response_age}s"
            )

        result.response_time = age

        # Clean up
        del self._pending_requests[nonce]

        if not result.errors:
            result.valid = True

        return result

    def check_tool_pin(
        self, tool_name: str, tool_definition: Dict[str, Any]
    ) -> "PinCheckResult":
        """
        Check a tool definition against its pin.

        Args:
            tool_name: Name of the tool
            tool_definition: Current tool definition dict

        Returns:
            PinCheckResult with match status
        """
        result = PinCheckResult(tool_name=tool_name)

        if not self.pinner.is_pinned(tool_name):
            result.status = "not_pinned"
            result.message = "Tool has not been pinned yet"
            return result

        if self.pinner.verify(tool_name, tool_definition):
            result.status = "match"
            result.message = "Tool definition matches pin"
            result.pinned_hash = self.pinner.get_pin(tool_name)
        else:
            result.status = "mismatch"
            result.message = (
                "Tool definition does NOT match pin. "
                "The tool may have been modified since discovery."
            )
            result.pinned_hash = self.pinner.get_pin(tool_name)
            current_canonical = json.dumps(
                tool_definition, sort_keys=True, separators=(",", ":")
            )
            result.current_hash = hashlib.sha256(
                current_canonical.encode("utf-8")
            ).hexdigest()

        return result

    @property
    def pending_count(self) -> int:
        """Number of requests awaiting responses."""
        return len(self._pending_requests)


class VerificationResult:
    """Result of a response verification check."""

    def __init__(self, tool_name: str, nonce: str):
        self.tool_name = tool_name
        self.nonce = nonce
        self.valid: bool = False
        self.errors: List[str] = []
        self.response_time: Optional[float] = None

    def __bool__(self) -> bool:
        return self.valid

    def __repr__(self) -> str:
        status = "VALID" if self.valid else "INVALID"
        return f"VerificationResult({self.tool_name}: {status})"


class PinCheckResult:
    """Result of a tool pin verification."""

    def __init__(self, tool_name: str):
        self.tool_name = tool_name
        self.status: str = "unknown"
        self.message: str = ""
        self.pinned_hash: Optional[str] = None
        self.current_hash: Optional[str] = None

    @property
    def is_match(self) -> bool:
        return self.status == "match"

    @property
    def is_mismatch(self) -> bool:
        return self.status == "mismatch"

    def __repr__(self) -> str:
        return f"PinCheckResult({self.tool_name}: {self.status})"
