"""Tests for brynq.knowledge_trust — Phase 67 cryptographic trust."""

import json
import pytest
from pathlib import Path


@pytest.fixture(autouse=True)
def _isolate_trust(tmp_path, monkeypatch):
    """Redirect trust storage to temp directory."""
    trust_dir = tmp_path / "knowledge_trust"
    trust_dir.mkdir()
    monkeypatch.setattr("brynq.knowledge_trust.TRUST_DIR", trust_dir)
    monkeypatch.setattr("brynq.knowledge_trust.SIGNATURES_PATH",
                        trust_dir / "signatures.json")
    monkeypatch.setattr("brynq.knowledge_trust.REPUTATION_PATH",
                        trust_dir / "signer_reputation.json")


@pytest.fixture
def knowledge_entry(tmp_path, monkeypatch):
    """Create a mock knowledge entry for signing."""
    kd = tmp_path / "knowledge"
    kd.mkdir()
    entry = {
        "knowledge_id": "test123",
        "topic": "Testing Topic",
        "content": "This is a test knowledge entry for verification.",
        "category": "general",
        "confidence": 0.9,
    }
    (kd / "test123.json").write_text(json.dumps(entry), encoding="utf-8")
    (kd / "index.jsonl").write_text("")
    monkeypatch.setattr("brynq.knowledge_mcp.KNOWLEDGE_DIR", kd)
    return "test123"


@pytest.fixture
def second_entry(tmp_path, monkeypatch):
    """Create a second knowledge entry."""
    # Reuse the patched KNOWLEDGE_DIR
    from brynq.knowledge_mcp import KNOWLEDGE_DIR
    entry = {
        "knowledge_id": "entry2",
        "topic": "Second entry",
        "content": "Another knowledge entry.",
    }
    (KNOWLEDGE_DIR / "entry2.json").write_text(json.dumps(entry), encoding="utf-8")
    return "entry2"


# ── sign_knowledge ────────────────────────────────────────────────────────


class TestSignKnowledge:
    def test_sign_returns_status(self, knowledge_entry):
        from brynq.knowledge_trust import sign_knowledge
        result = sign_knowledge(knowledge_entry, "agent-1")
        assert result["status"] == "signed"
        assert result["signer_id"] == "agent-1"

    def test_sign_creates_signature(self, knowledge_entry):
        from brynq.knowledge_trust import sign_knowledge
        result = sign_knowledge(knowledge_entry, "agent-1")
        assert "signature" in result
        assert len(result["signature"]) > 0

    def test_duplicate_sign(self, knowledge_entry):
        from brynq.knowledge_trust import sign_knowledge
        sign_knowledge(knowledge_entry, "agent-1")
        result = sign_knowledge(knowledge_entry, "agent-1")
        assert result["status"] == "already_signed"

    def test_different_signers(self, knowledge_entry):
        from brynq.knowledge_trust import sign_knowledge
        r1 = sign_knowledge(knowledge_entry, "agent-1")
        r2 = sign_knowledge(knowledge_entry, "agent-2")
        assert r1["status"] == "signed"
        assert r2["status"] == "signed"

    def test_missing_entry_raises(self):
        from brynq.knowledge_trust import sign_knowledge
        with pytest.raises(ValueError, match="not found"):
            sign_knowledge("nonexistent", "agent-1")

    def test_empty_knowledge_id_raises(self):
        from brynq.knowledge_trust import sign_knowledge
        with pytest.raises(ValueError, match="knowledge_id"):
            sign_knowledge("", "agent-1")

    def test_empty_signer_raises(self, knowledge_entry):
        from brynq.knowledge_trust import sign_knowledge
        with pytest.raises(ValueError, match="signer_id"):
            sign_knowledge(knowledge_entry, "")


# ── verify_signature ──────────────────────────────────────────────────────


class TestVerifySignature:
    def test_valid_signature(self, knowledge_entry):
        from brynq.knowledge_trust import sign_knowledge, verify_signature
        sign_knowledge(knowledge_entry, "agent-1")
        result = verify_signature(knowledge_entry, "agent-1")
        assert result["valid"] is True

    def test_no_signature(self, knowledge_entry):
        from brynq.knowledge_trust import verify_signature
        result = verify_signature(knowledge_entry, "agent-1")
        assert result["valid"] is False
        assert result["reason"] == "no_signature_found"

    def test_entry_not_found(self):
        from brynq.knowledge_trust import verify_signature
        result = verify_signature("nonexistent", "agent-1")
        assert result["valid"] is False
        assert result["reason"] == "entry_not_found"

    def test_tampered_content(self, knowledge_entry):
        from brynq.knowledge_trust import sign_knowledge, verify_signature
        from brynq.knowledge_mcp import KNOWLEDGE_DIR
        sign_knowledge(knowledge_entry, "agent-1")
        # Tamper with the entry
        path = KNOWLEDGE_DIR / f"{knowledge_entry}.json"
        entry = json.loads(path.read_text("utf-8"))
        entry["content"] = "TAMPERED CONTENT"
        path.write_text(json.dumps(entry), encoding="utf-8")
        result = verify_signature(knowledge_entry, "agent-1")
        assert result["valid"] is False

    def test_increments_verification_count(self, knowledge_entry):
        from brynq.knowledge_trust import sign_knowledge, verify_signature, SIGNATURES_PATH
        sign_knowledge(knowledge_entry, "agent-1")
        verify_signature(knowledge_entry, "agent-1")
        verify_signature(knowledge_entry, "agent-1")
        sigs = json.loads(SIGNATURES_PATH.read_text("utf-8"))
        assert sigs[knowledge_entry]["verification_count"] >= 2


# ── get_trust_score ───────────────────────────────────────────────────────


class TestGetTrustScore:
    def test_no_signatures(self):
        from brynq.knowledge_trust import get_trust_score
        result = get_trust_score("unsigned")
        assert result["trust_score"] == 0.0
        assert result["signers"] == 0

    def test_one_signer(self, knowledge_entry):
        from brynq.knowledge_trust import sign_knowledge, get_trust_score
        sign_knowledge(knowledge_entry, "agent-1")
        result = get_trust_score(knowledge_entry)
        assert result["trust_score"] > 0
        assert result["signers"] == 1

    def test_multiple_signers_increases_trust(self, knowledge_entry):
        from brynq.knowledge_trust import sign_knowledge, get_trust_score
        sign_knowledge(knowledge_entry, "agent-1")
        score1 = get_trust_score(knowledge_entry)["trust_score"]
        sign_knowledge(knowledge_entry, "agent-2")
        score2 = get_trust_score(knowledge_entry)["trust_score"]
        assert score2 > score1

    def test_trust_capped_at_1(self, knowledge_entry):
        from brynq.knowledge_trust import sign_knowledge, get_trust_score
        for i in range(10):
            sign_knowledge(knowledge_entry, f"agent-{i}")
        result = get_trust_score(knowledge_entry)
        assert result["trust_score"] <= 1.0


# ── get_signed_entries ────────────────────────────────────────────────────


class TestGetSignedEntries:
    def test_empty(self):
        from brynq.knowledge_trust import get_signed_entries
        assert get_signed_entries() == []

    def test_lists_by_signer(self, knowledge_entry, second_entry):
        from brynq.knowledge_trust import sign_knowledge, get_signed_entries
        sign_knowledge(knowledge_entry, "agent-1")
        sign_knowledge(second_entry, "agent-1")
        sign_knowledge(knowledge_entry, "agent-2")
        entries = get_signed_entries(signer_id="agent-1")
        assert len(entries) == 2

    def test_lists_all(self, knowledge_entry):
        from brynq.knowledge_trust import sign_knowledge, get_signed_entries
        sign_knowledge(knowledge_entry, "agent-1")
        sign_knowledge(knowledge_entry, "agent-2")
        entries = get_signed_entries()
        assert len(entries) == 2


# ── revoke_signature ──────────────────────────────────────────────────────


class TestRevokeSignature:
    def test_revoke(self, knowledge_entry):
        from brynq.knowledge_trust import sign_knowledge, revoke_signature
        sign_knowledge(knowledge_entry, "agent-1")
        result = revoke_signature(knowledge_entry, "agent-1", reason="outdated")
        assert result["status"] == "revoked"

    def test_revoke_not_found(self):
        from brynq.knowledge_trust import revoke_signature
        result = revoke_signature("no_entry", "no_signer")
        assert result["status"] == "not_found"

    def test_revoked_not_verified(self, knowledge_entry):
        from brynq.knowledge_trust import sign_knowledge, revoke_signature, verify_signature
        sign_knowledge(knowledge_entry, "agent-1")
        revoke_signature(knowledge_entry, "agent-1")
        result = verify_signature(knowledge_entry, "agent-1")
        assert result["valid"] is False

    def test_revoke_lowers_trust(self, knowledge_entry):
        from brynq.knowledge_trust import (
            sign_knowledge, get_trust_score, revoke_signature
        )
        sign_knowledge(knowledge_entry, "agent-1")
        sign_knowledge(knowledge_entry, "agent-2")
        score_before = get_trust_score(knowledge_entry)["trust_score"]
        revoke_signature(knowledge_entry, "agent-1")
        score_after = get_trust_score(knowledge_entry)["trust_score"]
        assert score_after < score_before

    def test_revoke_dings_reputation(self, knowledge_entry):
        from brynq.knowledge_trust import sign_knowledge, revoke_signature, REPUTATION_PATH
        sign_knowledge(knowledge_entry, "agent-1")
        revoke_signature(knowledge_entry, "agent-1")
        rep = json.loads(REPUTATION_PATH.read_text("utf-8"))
        assert rep["agent-1"]["revocations"] == 1
        assert rep["agent-1"]["score"] < 1.0
