"""Attest emission — what the book already proves, said in 75 and 37 bytes.

Emission is event-driven and silent by default: a relation-period attest exists only
because a period was sealed, a handshake attest only because a relation was opened.
There are no heartbeats and no schedules — silence is free, and absence is the
counterparty's evidence (`matching.py`), never a timeout's.

Everything here is a pure function over what already exists: the sealed snapshot, the
book entries it sealed, and the shared-view machinery. Both parties run the same
functions over their own books, independently; equal attests are what agreement looks
like on the wire.
"""

from __future__ import annotations

from .book import Book, Entry
from .shared_view import agreement_root, shared_view_hash
from .snapshot import Snapshot
from .wire import EventPair, HandshakeAttest, RelationPeriodAttest, period_fields


def handshake_attest(relation_ref_hex: str, *, chamber_band: int, process_tag: int) -> HandshakeAttest:
    """The once-per-relation attest, emitted when the relation is opened. The band and
    process tag come from the handshake's own vocabulary (GRAMMAR_S); this function just
    says them in 37 bytes."""
    return HandshakeAttest(
        relation_ref=bytes.fromhex(relation_ref_hex),
        chamber_band=chamber_band,
        process_tag=process_tag,
    )


def sealed_entries(book: Book, snapshot: Snapshot) -> list[Entry]:
    """Exactly the entries one snapshot sealed — the book positions `from_seq..to_seq`,
    re-read from the public book surface so no caller ever reaches into edge internals."""
    return [e for e in book.entries() if snapshot.from_seq <= e.seq <= snapshot.to_seq]


def relation_period_attests(snapshot: Snapshot, entries: list[Entry]) -> list[RelationPeriodAttest]:
    """One attest per relation that had events in the sealed period, sorted by relation.

    The period on the wire is the snapshot's own declared label (RATIFIED P2); the root
    is the agreement root over the relation's `(event_key, shared_view_hash)` pairs. A
    relation with no events in the period simply has no attest — silence is free.
    """
    entries = list(entries)
    if not entries:
        raise ValueError("a sealed period is never empty — snapshots are not emitted for silence")
    expected = list(range(snapshot.from_seq, snapshot.to_seq + 1))
    if [e.seq for e in entries] != expected or len(entries) != snapshot.record_count:
        raise ValueError(
            "entries do not match the snapshot: expected exactly book positions "
            f"{snapshot.from_seq}..{snapshot.to_seq} ({snapshot.record_count} records)"
        )
    rule, label = period_fields(snapshot.period)

    by_relation: dict[str, list[Entry]] = {}
    for entry in entries:
        by_relation.setdefault(entry.record["relation_ref"], []).append(entry)

    attests = []
    for relation_hex in sorted(by_relation):
        pairs = [(e.event_key, shared_view_hash(e.record)) for e in by_relation[relation_hex]]
        attests.append(
            RelationPeriodAttest(
                relation_ref=bytes.fromhex(relation_hex),
                period_rule=rule,
                period_label=label,
                event_count=len(pairs),
                agreement_root=agreement_root(pairs),
            )
        )
    return attests


def event_pairs(entries: list[Entry], relation_ref_hex: str) -> list[EventPair]:
    """The bisection material for ONE disputed relation-period: its `(event_key,
    shared_view_hash)` pairs, sorted as the tree sorts them. Sent only when roots
    disagreed — never as a matter of course, because volume never leaves the edge."""
    pairs = sorted(
        (e.event_key, shared_view_hash(e.record))
        for e in entries
        if e.record["relation_ref"] == relation_ref_hex
    )
    return [EventPair(event_key=bytes.fromhex(key), shared_view_hash=view) for key, view in pairs]


def attests_for(edge, snapshot: Snapshot | None = None) -> list[RelationPeriodAttest]:
    """The attests for one of an edge's sealed periods (the latest by default) — the
    convenience wrapper a caller uses right after `edge.seal(...)`."""
    if snapshot is None:
        if not edge.sealed:
            raise ValueError("nothing sealed yet — attests exist only for sealed periods")
        snapshot = edge.sealed[-1]
    return relation_period_attests(snapshot, sealed_entries(edge.book, snapshot))
