"""§2.5 batching — fold the journal's `artifact_hash` leaves into one `batch_root`, anchor it, and verify the
result independently (the #144 prover discipline, in Python).

Flow: collect a batch of `StoredEvent`s → Merkle-fold their `artifact_hash` leaves → `batch_root` (opaque) →
`AnchorBackend.anchor` (to A2) → INDEPENDENTLY verify the attestation. Verification is re-derivation, not
echo: the anchored root must equal OUR recomputed `batch_root`, and a leaf's inclusion proof must fold to it.
A wrong root → FAILED (the negative control proves the verifier discriminates).
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import List, Optional, Sequence

from cl.anchor.backend import AnchorAttestation, AnchorBackend
from cl.anchor.merkle import InclusionProof, batch_root, inclusion_proof
from cl.interfaces.storage import StoredEvent


@dataclass(frozen=True)
class Batch:
    artifact_hashes: tuple
    content_hashes: tuple
    root: bytes

    def inclusion_proof_for(self, leaf_index: int) -> InclusionProof:
        return inclusion_proof(list(self.artifact_hashes), leaf_index)


def build_batch(events: Sequence[StoredEvent]) -> Batch:
    """Build the anchorable batch from journal events (in journal order)."""
    arts = [e.artifact_hash for e in events]
    return Batch(
        artifact_hashes=tuple(arts),
        content_hashes=tuple(e.content_hash for e in events),
        root=batch_root(arts),
    )


@dataclass(frozen=True)
class AnchorVerification:
    verified: bool
    reason: Optional[str] = None


def verify_anchor(
    attestation: AnchorAttestation,
    expected_batch_root: bytes,
    leaf_proof: Optional[InclusionProof] = None,
) -> AnchorVerification:
    """Independently verify an attestation (the #144 prover chain, Python-side). Re-derivation, not echo."""
    # 1. the substrate anchored EXACTLY our batch_root (not some other value)
    if attestation.opaque_root != expected_batch_root:
        return AnchorVerification(False, "anchored opaque_root != our recomputed batch_root")
    # 2. A2 only (guardrail #1): a real height + a finality certificate must be present
    if attestation.anchored_at_height < 0 or not attestation.finality_certificate_hash:
        return AnchorVerification(False, "attestation is not A2 (missing height / finality certificate)")
    # 3. if a leaf proof is supplied, it must fold to the anchored root (the artifact is in the batch)
    if leaf_proof is not None and not leaf_proof.verifies_to(expected_batch_root):
        return AnchorVerification(False, "inclusion proof does not fold to the batch_root")
    return AnchorVerification(True)


@dataclass(frozen=True)
class AnchorResult:
    batch: Batch
    attestation: AnchorAttestation
    verification: AnchorVerification


def anchor_batch(events: Sequence[StoredEvent], backend: AnchorBackend) -> AnchorResult:
    """Build the batch, anchor its root via `backend` (to A2), and verify the attestation independently
    (including a sample leaf inclusion proof)."""
    batch = build_batch(events)
    attestation = backend.anchor(batch.root)
    sample_proof = batch.inclusion_proof_for(0) if batch.artifact_hashes else None
    verification = verify_anchor(attestation, batch.root, sample_proof)
    return AnchorResult(batch=batch, attestation=attestation, verification=verification)
