"""Local content-binding example, not authenticated approval or a mail sender."""
from dataclasses import dataclass
import hashlib
import json


@dataclass(frozen=True)
class Attachment:
    name: str
    content: bytes


@dataclass(frozen=True)
class MessageSnapshot:
    recipients: tuple[str, ...]
    subject: str
    body: str
    attachments: tuple[Attachment, ...]


def prepare(recipients, subject, body, attachments) -> MessageSnapshot:
    # Copy the attachment bytes now, not after the reviewer approves.
    return MessageSnapshot(tuple(recipients), subject, body,
                           tuple(Attachment(name, bytes(content)) for name, content in attachments))


def fingerprint(snapshot: MessageSnapshot) -> str:
    content = {
        "recipients": snapshot.recipients, "subject": snapshot.subject, "body": snapshot.body,
        "attachments": [{"name": attachment.name, "bytes": len(attachment.content),
                         "sha256": hashlib.sha256(attachment.content).hexdigest()}
                        for attachment in snapshot.attachments],
    }
    return hashlib.sha256(json.dumps(content, sort_keys=True, ensure_ascii=False).encode("utf-8")).hexdigest()


def checked_snapshot(snapshot: MessageSnapshot, approved_digest: str) -> MessageSnapshot:
    # approved_digest is supplied by the approval service, never by the model.
    if fingerprint(snapshot) != approved_digest:
        raise PermissionError("review again")
    return snapshot  # A sender must consume these bytes, not reopen a file path.


if __name__ == "__main__":
    first = prepare(["test@example.invalid"], "Draft report", "Please review.",
                    [("report.txt", b"Synthetic draft version 1")])
    approved = fingerprint(first)  # Simulates recorded content; no human approval occurred.
    replaced = prepare(first.recipients, first.subject, first.body,
                       [("report.txt", b"Synthetic draft version 2")])
    try:
        checked_snapshot(replaced, approved)
    except PermissionError as error:
        print(error)
