#!/usr/bin/env python3
"""Runnable, provider-free fixture for cached-prefix serialization invariants."""

from __future__ import annotations

import hashlib
import json
import unittest
from typing import Any

EXPECTED_SHA256 = "bf9615b9289861bf844473bbf4e38603556bcd496a30446f6021157040bf7100"

SEARCH = {
    "name": "search",
    "description": "Search public sources",
    "input_schema": {
        "type": "object",
        "properties": {"query": {"type": "string"}},
        "required": ["query"],
    },
}
CALCULATOR = {
    "name": "calculator",
    "description": "Evaluate an arithmetic expression",
    "input_schema": {
        "type": "object",
        "properties": {"expression": {"type": "string"}},
        "required": ["expression"],
    },
}


def canonical_prefix(*, tools: list[dict[str, Any]]) -> bytes:
    """Serialize stable fields; sorting tools by name is this fixture's policy."""
    payload = {
        "system": "Answer from evidence. Unicode fixture: 東京",
        "tools": sorted(tools, key=lambda tool: tool["name"]),
    }
    return json.dumps(
        payload,
        ensure_ascii=False,
        sort_keys=True,
        separators=(",", ":"),
        allow_nan=False,
    ).encode("utf-8")


class PrefixSerializationTest(unittest.TestCase):
    def test_registration_order_does_not_change_bytes(self) -> None:
        left = canonical_prefix(tools=[SEARCH, CALCULATOR])
        right = canonical_prefix(tools=[CALCULATOR, SEARCH])
        self.assertEqual(left, right)

    def test_dictionary_insertion_order_does_not_change_bytes(self) -> None:
        reversed_search = {
            "input_schema": {
                "required": ["query"],
                "properties": {"query": {"type": "string"}},
                "type": "object",
            },
            "description": "Search public sources",
            "name": "search",
        }
        self.assertEqual(
            canonical_prefix(tools=[SEARCH]),
            canonical_prefix(tools=[reversed_search]),
        )

    def test_reviewed_golden_digest(self) -> None:
        digest = hashlib.sha256(
            canonical_prefix(tools=[SEARCH, CALCULATOR])
        ).hexdigest()
        self.assertEqual(digest, EXPECTED_SHA256)

    def test_semantic_change_changes_bytes(self) -> None:
        changed = {**SEARCH, "description": "Search documentation only"}
        self.assertNotEqual(
            canonical_prefix(tools=[SEARCH, CALCULATOR]),
            canonical_prefix(tools=[changed, CALCULATOR]),
        )

    def test_omitted_field_is_not_null(self) -> None:
        with_null = {**SEARCH, "annotations": None}
        self.assertNotEqual(
            canonical_prefix(tools=[SEARCH]),
            canonical_prefix(tools=[with_null]),
        )


if __name__ == "__main__":
    prefix = canonical_prefix(tools=[SEARCH, CALCULATOR])
    print("sha256=" + hashlib.sha256(prefix).hexdigest())
    unittest.main()
