# SPDX-License-Identifier: Apache-2.0
"""Tests for canonical JSON serialization and content hashing."""

import hashlib

from nlai import canonical_json, content_hash, subject_hash


class TestCanonicalJson:
    """Deterministic JSON serialization."""

    def test_sorted_keys(self):
        result = canonical_json({"b": 2, "a": 1})
        assert result == b'{"a":1,"b":2}'

    def test_compact_separators(self):
        result = canonical_json({"key": "value"})
        assert b": " not in result
        assert b", " not in result

    def test_ascii_safe(self):
        result = canonical_json({"emoji": "\U0001f600"})
        assert b"\\u" in result  # Non-ASCII escaped

    def test_nan_rejected(self):
        import pytest
        with pytest.raises(ValueError):
            canonical_json({"bad": float("nan")})

    def test_empty_dict(self):
        assert canonical_json({}) == b"{}"

    def test_nested(self):
        result = canonical_json({"a": {"c": 3, "b": 2}})
        assert result == b'{"a":{"b":2,"c":3}}'

    def test_deterministic(self):
        """Same input always produces same output."""
        obj = {"z": 26, "a": 1, "m": 13}
        assert canonical_json(obj) == canonical_json(obj)

    def test_returns_bytes(self):
        result = canonical_json({"x": 1})
        assert isinstance(result, bytes)


class TestContentHash:
    """SHA-256 content hashing."""

    def test_known_hash(self):
        """Golden: hash of empty bytes."""
        expected = hashlib.sha256(b"").hexdigest()
        assert content_hash(b"") == expected

    def test_deterministic(self):
        h1 = content_hash(b"hello")
        h2 = content_hash(b"hello")
        assert h1 == h2

    def test_different_input_different_hash(self):
        assert content_hash(b"hello") != content_hash(b"world")

    def test_hex_format(self):
        result = content_hash(b"test")
        assert len(result) == 64  # SHA-256 hex length
        assert all(c in "0123456789abcdef" for c in result)

    def test_unsupported_algorithm(self):
        import pytest
        with pytest.raises(ValueError, match="Unsupported"):
            content_hash(b"test", alg="md5")


class TestSubjectHash:
    """Subject hashing with kind tag."""

    def test_kind_prefix(self):
        """Different kind = different hash, even for same content."""
        h1 = subject_hash("text", b"hello")
        h2 = subject_hash("diff", b"hello")
        assert h1 != h2

    def test_deterministic(self):
        h1 = subject_hash("text", b"hello")
        h2 = subject_hash("text", b"hello")
        assert h1 == h2

    def test_null_separator(self):
        """Kind and content separated by null byte."""
        expected = content_hash(b"text\x00hello")
        assert subject_hash("text", b"hello") == expected

    def test_empty_content(self):
        """Empty content is valid."""
        result = subject_hash("text", b"")
        assert len(result) == 64


class TestGoldenBytes:
    """Golden byte tests — these MUST NOT change across versions."""

    def test_canonical_json_golden(self):
        """Canonical JSON for receipt-like object matches golden bytes."""
        obj = {"gate": "test", "schema_version": 1, "verdict": "pass"}
        expected = b'{"gate":"test","schema_version":1,"verdict":"pass"}'
        assert canonical_json(obj) == expected

    def test_content_hash_golden(self):
        """Hash of canonical receipt-like JSON matches golden hex."""
        data = b'{"gate":"test","schema_version":1,"verdict":"pass"}'
        expected = hashlib.sha256(data).hexdigest()
        assert content_hash(data) == expected
