"""E — per-leaf Merkle inclusion proofs over an opaque root.

This is a thin, ADDITIVE wrapper over the EXISTING `cl.anchor.merkle` (RFC 6962-style, domain-separated, v1
profile) — it does NOT re-implement or rewrite the tree. The point of the seam: the ingest / anchor result
must be able to RETURN the `opaque_root` (the bytes that cross to the substrate), not only a re-derivation
boolean, so a relying party can verify a single leaf against the returned root.
"""

from __future__ import annotations

from typing import List

from cl.anchor.merkle import InclusionProof, batch_root, inclusion_proof


def opaque_root(leaves: List[bytes]) -> bytes:
    """The opaque Merkle root over `leaves` (the value that crosses to the substrate). Wraps `batch_root`."""
    return batch_root(list(leaves))


def inclusion_proof_for(leaves: List[bytes], leaf: bytes) -> InclusionProof:
    """Build the per-leaf inclusion proof for `leaf` (first occurrence) among `leaves`. Wraps the existing
    `inclusion_proof` by resolving the leaf to its index."""
    seq = list(leaves)
    try:
        index = seq.index(leaf)
    except ValueError as e:
        raise ValueError("leaf is not a member of the provided leaves") from e
    return inclusion_proof(seq, index)


def verify_inclusion(leaf: bytes, proof: InclusionProof, root: bytes) -> bool:
    """True iff `proof` is for `leaf` AND folds to `root`. A wrong leaf (proof not built for it) fails."""
    from cl.anchor.merkle import _leaf_hash  # the same domain-separated leaf hashing the tree uses
    from cl.canonical.hash_profile import DEFAULT_PROFILE

    if proof.leaf_hash != _leaf_hash(leaf, DEFAULT_PROFILE):
        return False
    return proof.verifies_to(root)
