"""TAX-C14N1 and ordered path replay; API 1.5.36 reference, Python standard library only."""
import hashlib
import math
import struct

def logical_hash(value):
    """TAX-C14N1: tagged UTF-8/integers/IEEE754, sorted UTF-8 object keys.

    Unlike JSON pretty-printer bytes this has unambiguous cross-language float
    and large integer behavior. Implementations must reject nonfinite floats.
    """
    digest = hashlib.sha256()

    def walk(item):
        if item is None:
            digest.update(b"n")
        elif isinstance(item, bool):
            digest.update(b"t" if item else b"f")
        elif isinstance(item, int):
            digest.update(b"i" + str(item).encode() + b";")
        elif isinstance(item, float):
            if not math.isfinite(item):
                raise ValueError("nonfinite number")
            digest.update(b"d" + struct.pack(">d", item))
        elif isinstance(item, str):
            data = item.encode("utf-8")
            digest.update(b"s" + str(len(data)).encode() + b":" + data)
        elif isinstance(item, list):
            digest.update(b"[")
            for child in item:
                walk(child)
            digest.update(b"]")
        elif isinstance(item, dict):
            digest.update(b"{")
            for key in sorted(item, key=lambda k: k.encode("utf-8")):
                walk(key)
                walk(item[key])
            digest.update(b"}")
        else:
            raise TypeError(type(item).__name__)

    walk(value)
    return digest.hexdigest()

def apply_record(root, record, *, large_value=None):
    """Reference decoder; production clients should apply paths to durable storage."""
    if record["recordType"] == "DELIVERY_MANIFEST":
        return
    path = record["path"]
    parent = root
    for key in path[:-1]:
        parent = parent[key]
    key = path[-1]
    value = ({} if record["containerType"] == "object" else []) if record["operation"] == "CONTAINER" else (
        large_value if "largeRecord" in record else record["payload"])
    if isinstance(parent, list):
        if key == len(parent):
            parent.append(value)
        elif 0 <= key < len(parent):
            parent[key] = value
        else:
            raise ValueError("out-of-order array record")
    else:
        parent[key] = value
