"""Tests for response verification and tool pinning."""

import time
import pytest

from crewai_mcps.secure_tool import KeyManager, NonceTracker, ToolPinner
from crewai_mcps.verify import ResponseVerifier, VerificationResult, PinCheckResult


class TestToolPinner:
    """Test tool definition pinning and rug-pull detection."""

    def test_pin_tool(self):
        pinner = ToolPinner()
        defn = {"name": "search", "description": "Search files"}
        pin_hash = pinner.pin("search", defn)
        assert isinstance(pin_hash, str)
        assert len(pin_hash) == 64  # SHA-256 hex digest

    def test_verify_unchanged_tool(self):
        pinner = ToolPinner()
        defn = {"name": "search", "description": "Search files"}
        pinner.pin("search", defn)
        assert pinner.verify("search", defn) is True

    def test_detect_changed_tool(self):
        pinner = ToolPinner()
        original = {"name": "search", "description": "Search files"}
        pinner.pin("search", original)

        modified = {"name": "search", "description": "Execute arbitrary code"}
        assert pinner.verify("search", modified) is False

    def test_unpinned_tool_passes(self):
        pinner = ToolPinner()
        assert pinner.verify("unknown", {"any": "thing"}) is True

    def test_is_pinned(self):
        pinner = ToolPinner()
        assert pinner.is_pinned("search") is False
        pinner.pin("search", {"name": "search"})
        assert pinner.is_pinned("search") is True

    def test_get_pin(self):
        pinner = ToolPinner()
        pin_hash = pinner.pin("tool", {"name": "tool"})
        assert pinner.get_pin("tool") == pin_hash
        assert pinner.get_pin("nonexistent") is None

    def test_canonical_ordering(self):
        pinner = ToolPinner()
        defn1 = {"b": 2, "a": 1}
        defn2 = {"a": 1, "b": 2}
        hash1 = pinner.pin("t1", defn1)
        # Re-pin with different key order
        pinner._pins.clear()
        hash2 = pinner.pin("t1", defn2)
        assert hash1 == hash2  # Canonical JSON should match

    def test_detect_schema_change(self):
        pinner = ToolPinner()
        original = {
            "name": "write_file",
            "description": "Write a file",
            "schema": {"type": "object", "properties": {"path": {"type": "string"}}},
        }
        pinner.pin("write_file", original)

        # Attacker adds a new parameter
        tampered = {
            "name": "write_file",
            "description": "Write a file",
            "schema": {
                "type": "object",
                "properties": {
                    "path": {"type": "string"},
                    "exec_cmd": {"type": "string"},  # Injected!
                },
            },
        }
        assert pinner.verify("write_file", tampered) is False


class TestResponseVerifier:
    """Test response verification workflows."""

    def test_register_and_verify(self):
        verifier = ResponseVerifier()
        nonce = "test-nonce-123"
        verifier.register_request(
            nonce, "search", {"query": "test"}, {"nonce": nonce}
        )
        result = verifier.verify_response(nonce, "search", "some results")
        assert result.valid is True
        assert result.tool_name == "search"

    def test_unknown_nonce_rejected(self):
        verifier = ResponseVerifier()
        result = verifier.verify_response("unknown", "tool", "response")
        assert result.valid is False
        assert "No matching request" in result.errors[0]

    def test_tool_name_mismatch(self):
        verifier = ResponseVerifier()
        nonce = "nonce-abc"
        verifier.register_request(
            nonce, "tool_a", {}, {"nonce": nonce}
        )
        result = verifier.verify_response(nonce, "tool_b", "response")
        assert result.valid is False
        assert any("mismatch" in e for e in result.errors)

    def test_response_age_check(self):
        verifier = ResponseVerifier(max_response_age=0.001)
        nonce = "nonce-old"
        verifier.register_request(
            nonce, "tool", {}, {"nonce": nonce}
        )
        time.sleep(0.01)  # Let it age
        result = verifier.verify_response(nonce, "tool", "response")
        assert result.valid is False
        assert any("too old" in e for e in result.errors)

    def test_pending_count(self):
        verifier = ResponseVerifier()
        assert verifier.pending_count == 0
        verifier.register_request("n1", "t1", {}, {"nonce": "n1"})
        verifier.register_request("n2", "t2", {}, {"nonce": "n2"})
        assert verifier.pending_count == 2
        verifier.verify_response("n1", "t1", "ok")
        assert verifier.pending_count == 1

    def test_verification_result_bool(self):
        r = VerificationResult("tool", "nonce")
        assert bool(r) is False
        r.valid = True
        assert bool(r) is True

    def test_verification_result_repr(self):
        r = VerificationResult("search", "abc")
        r.valid = True
        assert "VALID" in repr(r)
        r.valid = False
        assert "INVALID" in repr(r)


class TestPinCheckResult:
    """Test pin check result objects."""

    def test_pin_check_not_pinned(self):
        verifier = ResponseVerifier()
        result = verifier.check_tool_pin("unknown", {"name": "unknown"})
        assert result.status == "not_pinned"
        assert not result.is_match
        assert not result.is_mismatch

    def test_pin_check_match(self):
        verifier = ResponseVerifier()
        defn = {"name": "tool", "description": "A tool"}
        verifier.pinner.pin("tool", defn)
        result = verifier.check_tool_pin("tool", defn)
        assert result.status == "match"
        assert result.is_match
        assert result.pinned_hash is not None

    def test_pin_check_mismatch(self):
        verifier = ResponseVerifier()
        original = {"name": "tool", "description": "Safe tool"}
        verifier.pinner.pin("tool", original)

        tampered = {"name": "tool", "description": "Unsafe tool"}
        result = verifier.check_tool_pin("tool", tampered)
        assert result.status == "mismatch"
        assert result.is_mismatch
        assert result.pinned_hash is not None
        assert result.current_hash is not None
        assert result.pinned_hash != result.current_hash

    def test_pin_check_repr(self):
        r = PinCheckResult("my_tool")
        r.status = "match"
        assert "match" in repr(r)
