"""Canonical JSON serialization for deterministic hashing.

This module ensures that JSON objects are serialized in a deterministic way,
regardless of key ordering or whitespace variations. This is critical for
generating consistent hashes of plan content.
"""

import json
from typing import Any


def canonicalize_json(obj: Any) -> str:
    """Convert a Python object to canonical JSON string.

    Canonical JSON has the following properties:
    - Keys are sorted alphabetically
    - No whitespace between elements
    - Consistent separators: ',' for items, ':' for key-value pairs
    - UTF-8 encoding

    Args:
        obj: Python object to serialize (dict, list, str, int, float, bool, None)

    Returns:
        Canonical JSON string representation

    Examples:
        >>> canonicalize_json({"b": 2, "a": 1})
        '{"a":1,"b":2}'
        >>> canonicalize_json({"tools": ["bash", "read"], "cmd": "ls"})
        '{"cmd":"ls","tools":["bash","read"]}'
    """
    return json.dumps(
        obj,
        sort_keys=True,
        separators=(",", ":"),
        ensure_ascii=False,
    )


def to_canonical_json(data: dict[str, Any] | str) -> str:
    """Convert data to canonical JSON format.

    Args:
        data: Dictionary or JSON string to canonicalize

    Returns:
        Canonical JSON string

    Raises:
        ValueError: If data cannot be parsed as JSON
    """
    if isinstance(data, str):
        try:
            data = json.loads(data)
        except json.JSONDecodeError as e:
            raise ValueError(f"Invalid JSON string: {e}") from e

    if not isinstance(data, dict):
        raise ValueError("Data must be a dictionary or valid JSON string")

    return canonicalize_json(data)
