# SPDX-License-Identifier: Apache-2.0
"""Tests for the gate() function — the whole kernel surface."""

from nlai import gate, Anchor, Claim, Evidence, GateResult, verify_receipt, attach_evidence
from nlai.claims import STRENGTH_ASSERTIVE, STATUS_SUPPORTED, STATUS_CONTESTED


class TestGateBasic:
    """Basic gate behavior."""

    def test_returns_gate_result(self):
        result = gate("Hello world.")
        assert isinstance(result, GateResult)

    def test_pass_no_anchors(self):
        result = gate("Hello world.")
        assert result.verdict == "pass"

    def test_has_receipt(self):
        result = gate("Hello world.")
        assert result.receipt is not None
        assert result.receipt.gate == "nlai_gate"

    def test_receipt_verifies(self):
        result = gate("Hello world.")
        assert verify_receipt(result.receipt) is True

    def test_extracts_claims(self):
        result = gate("This is definitely the right approach.")
        assert len(result.claims) >= 1

    def test_no_claims_still_passes(self):
        result = gate("The weather is nice.")
        assert result.verdict == "pass"
        assert result.violations == []


class TestGateWithAnchors:
    """Gate with anchor constraints."""

    def test_forbidden_blocks(self):
        anchors = [
            Anchor(id="no-thread", description="No thread-safety claims",
                   forbidden=("thread-safe", "thread safe")),
        ]
        result = gate("The code is thread-safe.", anchors=anchors)
        assert result.verdict == "block"
        assert len(result.violations) >= 1

    def test_required_missing_blocks(self):
        anchors = [
            Anchor(id="must-test", description="Must mention tests",
                   required=("tests pass",)),
        ]
        result = gate("The code compiles.", anchors=anchors)
        assert result.verdict == "block"

    def test_required_present_passes(self):
        anchors = [
            Anchor(id="must-test", description="Must mention tests",
                   required=("tests pass",)),
        ]
        result = gate("All tests pass successfully.", anchors=anchors)
        assert result.verdict == "pass"

    def test_warn_severity(self):
        anchors = [
            Anchor(id="soft", description="Soft check",
                   forbidden=("bad",), severity="warn"),
        ]
        result = gate("This is bad.", anchors=anchors)
        assert result.verdict == "warn"

    def test_multiple_anchors(self):
        anchors = [
            Anchor(id="a1", description="A1", forbidden=("bad",)),
            Anchor(id="a2", description="A2", required=("good",)),
        ]
        result = gate("This is bad.", anchors=anchors)
        assert result.verdict == "block"
        assert len(result.violations) == 2  # forbidden + missing required

    def test_all_pass(self):
        anchors = [
            Anchor(id="a1", description="A1", required=("hello",)),
            Anchor(id="a2", description="A2", forbidden=("goodbye",)),
        ]
        result = gate("hello world", anchors=anchors)
        assert result.verdict == "pass"
        assert result.violations == []


class TestGateContentAddressing:
    """Receipt content-addressing through gate()."""

    def test_same_text_same_receipt_id(self):
        r1 = gate("Hello world.")
        r2 = gate("Hello world.")
        assert r1.receipt.receipt_id == r2.receipt.receipt_id

    def test_different_text_different_receipt_id(self):
        r1 = gate("Hello world.")
        r2 = gate("Goodbye world.")
        assert r1.receipt.receipt_id != r2.receipt.receipt_id

    def test_anchors_change_evidence_hash(self):
        """Different anchor configs produce different evidence hashes."""
        r1 = gate("Hello world.")
        r2 = gate("Hello world.", anchors=[
            Anchor(id="test", description="Test"),
        ])
        # Evidence hash differs because anchor_count differs
        assert r1.receipt.evidence_hash != r2.receipt.evidence_hash


class TestGateWithPriorClaims:
    """Gate with contradiction detection against prior claims."""

    def test_detects_contradictions(self):
        """Sentence-level claims enable end-to-end contradiction detection."""
        prior = Claim(text="The system definitely passes all tests",
                      strength=STRENGTH_ASSERTIVE)
        prior = attach_evidence(prior, Evidence(kind="test_result", reference="exit 0"))

        # "fails" contradicts "passes" in the prior
        result = gate("The system definitely fails all tests.", prior_claims=[prior])
        assert len(result.contested_claims) >= 1

    def test_contested_claims_property(self):
        result = gate("Hello world.")
        assert result.contested_claims == []

    def test_prior_claims_none_backward_compat(self):
        result = gate("Hello world.", prior_claims=None)
        assert result.verdict == "pass"

    def test_prior_claims_empty_list(self):
        result = gate("Hello world.", prior_claims=[])
        assert result.verdict == "pass"

    def test_receipt_verifies_with_prior_claims(self):
        prior = Claim(text="definitely passes", strength=STRENGTH_ASSERTIVE,
                      status=STATUS_SUPPORTED)
        result = gate("It definitely fails.", prior_claims=[prior])
        assert verify_receipt(result.receipt) is True

    def test_evidence_hash_changes_with_contradictions(self):
        """Prior claims that cause contradictions change evidence hash."""
        r1 = gate("The system definitely passes.")

        prior = Claim(text="The system definitely fails",
                      strength=STRENGTH_ASSERTIVE, status=STATUS_SUPPORTED)
        r2 = gate("The system definitely passes.", prior_claims=[prior])

        assert r1.receipt.evidence_hash != r2.receipt.evidence_hash

    def test_contradictions_dont_block(self):
        """Contradictions CONTEST claims but don't change the verdict."""
        prior = Claim(text="The system definitely passes",
                      strength=STRENGTH_ASSERTIVE, status=STATUS_SUPPORTED)
        # "fails" contradicts "passes" — but no anchors violated
        result = gate("The system definitely fails.", prior_claims=[prior])
        assert result.verdict == "pass"  # contradictions don't block
        assert len(result.contested_claims) >= 1

    def test_to_dict_includes_claim_evidence_fields(self):
        """to_dict output includes evidence and conflicts_with on claims."""
        result = gate("It is definitely true.")
        d = result.to_dict()
        assert len(d["claims"]) >= 1
        claim_dict = d["claims"][0]
        assert "evidence" in claim_dict
        assert "conflicts_with" in claim_dict


class TestGateResultSerialization:
    """GateResult.to_dict()."""

    def test_to_dict(self):
        result = gate("Hello world.")
        d = result.to_dict()
        assert "verdict" in d
        assert "receipt" in d
        assert "claims" in d
        assert "violations" in d
        assert isinstance(d["receipt"], dict)
