# SPDX-License-Identifier: Apache-2.0
"""Tests for serialization roundtrips and future version rejection."""

import json

import pytest

from nlai import (
    Receipt, Anchor, Claim, Violation, Evidence, GateResult,
    canonical_json, verify_receipt, create_receipt, gate,
    attach_evidence, contest_claim,
)
from nlai.claims import STATUS_SUPPORTED, STATUS_CONTESTED
from nlai.receipt import RECEIPT_SCHEMA_VERSION


class TestReceiptRoundtrip:
    """Receipt serialize/deserialize."""

    def test_json_roundtrip(self):
        r = create_receipt(gate="test", verdict="pass",
                           subject_kind="text", subject_bytes=b"hello")
        j = json.dumps(r.to_dict())
        d = json.loads(j)
        r2 = Receipt.from_dict(d)
        assert r == r2
        assert verify_receipt(r2)

    def test_canonical_json_roundtrip(self):
        r = create_receipt(gate="test", verdict="pass",
                           subject_kind="text", subject_bytes=b"hello")
        b = canonical_json(r.to_dict())
        d = json.loads(b)
        r2 = Receipt.from_dict(d)
        assert r == r2


class TestAnchorRoundtrip:
    """Anchor serialize/deserialize."""

    def test_json_roundtrip(self):
        a = Anchor(id="test", description="Test",
                   required=("foo",), forbidden=("bar",),
                   severity="reject", constraint_class="invariant")
        j = json.dumps(a.to_dict())
        d = json.loads(j)
        a2 = Anchor.from_dict(d)
        assert a == a2

    def test_minimal_dict(self):
        """from_dict with only required fields."""
        d = {"id": "test", "description": "Test"}
        a = Anchor.from_dict(d)
        assert a.required == ()
        assert a.forbidden == ()


class TestClaimRoundtrip:
    """Claim serialize/deserialize."""

    def test_json_roundtrip(self):
        c = Claim(text="definitely", strength="assertive",
                  status="unsupported", span=(5, 15))
        j = json.dumps(c.to_dict())
        d = json.loads(j)
        c2 = Claim.from_dict(d)
        assert c == c2


class TestViolationRoundtrip:
    """Violation serialize/deserialize."""

    def test_json_roundtrip(self):
        v = Violation(anchor_id="test", severity="reject",
                      description="Bad thing",
                      evidence=("found 'foo'",))
        j = json.dumps(v.to_dict())
        d = json.loads(j)
        v2 = Violation.from_dict(d)
        assert v == v2


class TestFutureVersionRejection:
    """Future schema version handling."""

    def test_receipt_rejects_future(self):
        d = {
            "receipt_id": "abc",
            "schema_version": RECEIPT_SCHEMA_VERSION + 1,
            "timestamp": "now",
            "gate": "test",
            "verdict": "pass",
            "subject_hash": "abc",
            "evidence_hash": "def",
        }
        with pytest.raises(ValueError, match="newer than supported"):
            Receipt.from_dict(d)

    def test_receipt_accepts_current(self):
        d = {
            "receipt_id": "abc",
            "schema_version": RECEIPT_SCHEMA_VERSION,
            "timestamp": "now",
            "gate": "test",
            "verdict": "pass",
            "subject_hash": "abc",
            "evidence_hash": "def",
        }
        r = Receipt.from_dict(d)
        assert r.schema_version == RECEIPT_SCHEMA_VERSION

    def test_receipt_accepts_older(self):
        """Schema version 0 (hypothetical older) should still work."""
        d = {
            "receipt_id": "abc",
            "schema_version": 0,
            "timestamp": "now",
            "gate": "test",
            "verdict": "pass",
            "subject_hash": "abc",
            "evidence_hash": "def",
        }
        r = Receipt.from_dict(d)
        assert r.schema_version == 0


class TestBackwardCompat:
    """0.1.0 → 0.2.0 backward compatibility."""

    def test_claim_from_dict_without_evidence(self):
        """0.1.0 claim dicts lack evidence/conflicts_with fields."""
        d = {
            "text": "definitely",
            "strength": "assertive",
            "status": "unsupported",
            "span": [5, 15],
        }
        c = Claim.from_dict(d)
        assert c.evidence == ()
        assert c.conflicts_with == ()

    def test_v010_receipt_still_verifies(self):
        """Frozen 0.1.0-style receipt must still verify."""
        r = create_receipt(
            gate="nlai_gate",
            verdict="pass",
            subject_kind="text",
            subject_bytes=b"Hello world.",
            evidence={"claims_count": 0, "violations_count": 0, "anchor_count": 0},
        )
        assert verify_receipt(r) is True

    def test_claim_with_evidence_roundtrip(self):
        e = Evidence(kind="receipt", reference="abc123", source="test")
        c = Claim(text="definitely", strength="assertive",
                  status=STATUS_SUPPORTED, span=(5, 15), evidence=(e,))
        d = c.to_dict()
        c2 = Claim.from_dict(d)
        assert c == c2

    def test_claim_with_conflicts_roundtrip(self):
        c = Claim(text="definitely", strength="assertive",
                  status=STATUS_CONTESTED, conflicts_with=("opposing",))
        d = c.to_dict()
        c2 = Claim.from_dict(d)
        assert c == c2

    def test_evidence_roundtrip(self):
        e = Evidence(kind="tool_output", reference="ls output", source="bash")
        d = e.to_dict()
        e2 = Evidence.from_dict(d)
        assert e == e2


class TestGateResultRoundtrip:
    """GateResult serialize/deserialize."""

    def test_basic_roundtrip(self):
        result = gate("Hello world.")
        d = result.to_dict()
        result2 = GateResult.from_dict(d)
        assert result2.verdict == result.verdict
        assert result2.receipt == result.receipt
        assert result2.claims == result.claims
        assert result2.violations == result.violations

    def test_roundtrip_with_violations(self):
        anchors = [
            Anchor(id="no-bad", description="No bad", forbidden=("bad",)),
        ]
        result = gate("This is bad.", anchors=anchors)
        d = result.to_dict()
        result2 = GateResult.from_dict(d)
        assert result2.verdict == "block"
        assert len(result2.violations) == 1

    def test_roundtrip_with_claims(self):
        result = gate("It is definitely true.")
        d = result.to_dict()
        result2 = GateResult.from_dict(d)
        assert len(result2.claims) >= 1
        assert result2.claims[0].text == result.claims[0].text

    def test_json_roundtrip(self):
        import json
        result = gate("It is definitely true.")
        j = json.dumps(result.to_dict())
        d = json.loads(j)
        result2 = GateResult.from_dict(d)
        assert result2.verdict == result.verdict
        assert result2.receipt == result.receipt
