"""AnchorBackend — the thin seam between the Ledger and any anchoring substrate (§2.5, guardrail #5).

`anchor(batch_root)` submits the OPAQUE batch root and returns an attestation ONLY once it is A2-anchored
(guardrail #1: A0 received / A1 sequenced are NOT anchored and never yield an attestation). The NEO-specific
HTTP/admission details live entirely inside the `NEOAnchorBackend` implementation, so a contract tweak from
the #145 audit is a localized change here, never a rewrite of the pipeline.
"""

from __future__ import annotations

import abc
from dataclasses import dataclass


class AnchorError(RuntimeError):
    """Anchoring did not reach A2 (timeout / rejection) — fail-closed, never a partial 'anchored'."""


@dataclass(frozen=True)
class AnchorAttestation:
    """The A2 anchoring receipt. For NEO, ``opaque_root`` is the independently observed and verified
    ``fold([batch_root])`` from the finalized header, never an echo of the submitted batch root."""

    opaque_root: bytes           # the 32-byte batch_root that was anchored
    anchored_at_height: int      # the substrate height the root was committed at
    finality_certificate_hash: bytes
    assurance_level: str         # A3, e.g. "signature_authentic" | "structural"
    block_hash: bytes = b""


class AnchorBackend(abc.ABC):
    @abc.abstractmethod
    def anchor(self, batch_root: bytes) -> AnchorAttestation:
        """Submit `batch_root`, poll to A2, and return the A2 attestation. Raises `AnchorError` if A2 is not
        reached. Only the opaque 32-byte root crosses the seam (guardrail #2)."""
