"""Writes a statement of account claiming the fourth part of the archival
standard, at the level that carries files.

The fourth part on its own carries nothing; the level named `f` after it is
what lets a record hold a file of any kind. Here that file is the same
statement the page states, in the form a machine reads.

Whether the result really is PDF/A-4F is not for us to say. `veraPDF` says,
and `scripts/check_archival.sh` asks it.

The words of the page and of its metadata are held in `Words`, once per language,
and `HQF_PDF_LANG` picks which set the record is written in.

Usage: `python examples/write_pdfa4f.py [out.pdf] [font.ttf]`
       `HQF_PDF_LANG=fr python examples/write_pdfa4f.py`
"""

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path

import _language
import _licence
import _out

import hqf_pdf

# When the record was drawn up. The library never reads the clock — a document that
# stamped itself with the time would be a different file on every build — so the caller
# says when.
ISSUED = "2026-06-02T08:15:00+02:00"

# Who the file says wrote it, and what produced it, in every language alike.
AUTHOR = "HQF Development"
PRODUCER = "hqf-pdf-server"

# The file the record carries. What it is to the record is said in `Words`.
CARRIED_NAME = "statement.xml"
CARRIED_TYPE = "text/xml"
CHANGED = "2026-06-02T08:10:00+02:00"
CARRIED = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<statement period=\"2026-05\">\n  <line name=\"Carriage\" amount=\"1240.00\"/>\n  <line name=\"Handling\" amount=\"186.50\"/>\n  <due amount=\"1426.50\"/>\n</statement>\n"

# The amounts themselves, beside what each one is for. Figures are the same in every
# language.
AMOUNTS = "1 240.00\n186.50\n1 426.50"

@dataclass(frozen=True)
class Words:
    """Every word the statement is written in, in one language."""

    # What the file says it is, in its metadata.
    title: str
    # What the file says it is about, in its metadata.
    subject: str
    # What the carried file says it is to the record.
    carried_what: str
    # The heading at the top of the page.
    heading: str
    # What the page says of the part it claims.
    body: str
    # What each line of the record asks, one a line.
    labels: str


# The statement in English.
ENGLISH = Words(
    title="Statement of account, May 2026",
    subject=(
        "A record on the second version of PDF, carrying the figures a machine reads"
    ),
    carried_what="The lines this page states, as a machine reads them",
    heading="Statement of account, May 2026",
    body=(
        "This record claims the fourth part of the archival standard at the level that "
        "carries files. The part it is built on takes a file of any kind, whatever the "
        "file itself conforms to, so the same record answers a person and a program. "
        "The lines below are carried inside the file as well, in the form a machine "
        "reads."
    ),
    labels="Carriage\nHandling\nDue",
)


# The statement in French.
FRENCH = Words(
    title="Relevé de compte, mai 2026",
    subject=(
        "Un document sur la deuxième version de PDF, qui contient les chiffres que lit "
        "une machine"
    ),
    carried_what="Les lignes que cette page indique, sous la forme que lit une machine",
    heading="Relevé de compte, mai 2026",
    body=(
        ("Ce document déclare suivre la quatrième partie de la norme d’archivage, au "
         "niveau qui admet des fichiers joints. La partie sur laquelle il repose "
         "accepte un fichier de toute nature, quelle que soit la norme que ce fichier "
         "suit lui-même : le même document répond ainsi à une personne et à un "
         "programme. Les lignes ci-dessous sont aussi contenues dans le fichier, sous "
         "la forme que lit une machine.")
    ),
    labels="Transport\nManutention\nÀ payer",
)


# The statement in German.
GERMAN = Words(
    title="Kontoauszug, Mai 2026",
    subject=(
        ("Ein Dokument in der zweiten PDF-Version, das die Zahlen für eine Maschine "
         "enthält")
    ),
    carried_what="Die Posten dieser Seite, so wie eine Maschine sie liest",
    heading="Kontoauszug, Mai 2026",
    body=(
        ("Dieses Dokument erklärt Konformität mit dem vierten Teil der Archivnorm, auf"
         " der Stufe, die Dateien mitführt. Der zugrunde liegende Teil nimmt eine "
         "Datei beliebiger Art auf, gleich welcher Norm sie selbst folgt, und so "
         "antwortet dasselbe Dokument einem Menschen und einem Programm. Die Posten "
         "unten stecken außerdem in der Datei, in der Form, die eine Maschine liest.")
    ),
    labels="Fracht\nUmschlag\nFällig",
)


# The statement in Spanish.
SPANISH = Words(
    title="Extracto de cuenta, mayo de 2026",
    subject=(
        ("Un documento basado en la segunda versión de PDF que contiene las cifras que"
         " lee una máquina")
    ),
    carried_what="Las líneas que indica esta página, tal como las lee una máquina",
    heading="Extracto de cuenta, mayo de 2026",
    body=(
        ("Este documento declara cumplir la cuarta parte de la norma de archivo, en el"
         " nivel que admite ficheros adjuntos. La parte en la que se basa admite un "
         "fichero de cualquier tipo, cumpla la norma que cumpla ese fichero, así que "
         "el mismo documento sirve a la vez a una persona y a un programa. Las líneas "
         "de abajo van también dentro del fichero, en la forma que lee una máquina.")
    ),
    labels="Transporte\nManipulación\nA pagar",
)


# The statement in Italian.
ITALIAN = Words(
    title="Estratto conto, maggio 2026",
    subject=(
        "Un documento sulla seconda versione di PDF, che contiene le cifre lette da "
        "una macchina"
    ),
    carried_what="Le righe indicate in questa pagina, come le legge una macchina",
    heading="Estratto conto, maggio 2026",
    body=(
        ("Questo documento dichiara di rispettare la quarta parte della norma di "
         "archiviazione, al livello che ammette file allegati. La parte su cui poggia "
         "accetta un file di qualsiasi tipo, a qualunque norma esso stesso si "
         "conformi, così lo stesso documento risponde a una persona e a un programma. "
         "Le righe qui sotto sono contenute anche nel file, nella forma che una "
         "macchina legge.")
    ),
    labels="Trasporto\nMovimentazione\nDovuto",
)


# The statement in Portuguese.
PORTUGUESE = Words(
    title="Extrato de conta, maio de 2026",
    subject=(
        "Um documento na segunda versão do PDF, que contém os números que uma máquina "
        "lê"
    ),
    carried_what="As linhas que esta página indica, tal como uma máquina as lê",
    heading="Extrato de conta, maio de 2026",
    body=(
        "Este documento declara cumprir a quarta parte da norma de arquivo, no nível "
        "que contém ficheiros. A parte em que assenta aceita um ficheiro de qualquer "
        "tipo, seja qual for a norma que esse ficheiro cumpre, e assim o mesmo "
        "documento responde a uma pessoa e a um programa. As linhas abaixo também "
        "estão dentro do ficheiro, na forma que uma máquina lê."
    ),
    labels="Transporte\nManuseamento\nA pagar",
)


# The statement in Dutch.
DUTCH = Words(
    title="Rekeningoverzicht, mei 2026",
    subject=(
        ("Een document op de tweede versie van PDF, met de cijfers die een machine "
         "leest")
    ),
    carried_what="De regels op deze pagina, zoals een machine ze leest",
    heading="Rekeningoverzicht, mei 2026",
    body=(
        ("Dit document verklaart te voldoen aan het vierde deel van de archiefnorm, op"
         " het niveau dat bestanden meevoert. Het deel waarop het gebaseerd is, neemt "
         "een bestand van elke soort op, aan welke norm dat bestand zelf ook voldoet, "
         "zodat hetzelfde document zowel een mens als een programma bedient. De regels"
         " hieronder zitten ook in het bestand, in de vorm die een machine leest.")
    ),
    labels="Vervoer\nBehandeling\nTe betalen",
)

# Every language the example is written in. A language is added by writing its own set
# of words and naming it here.
WORDS = {
    _language.ENGLISH: ENGLISH,
    _language.FRENCH: FRENCH,
    _language.GERMAN: GERMAN,
    _language.SPANISH: SPANISH,
    _language.ITALIAN: ITALIAN,
    _language.PORTUGUESE: PORTUGUESE,
    _language.DUTCH: DUTCH,
}


# The blocks of words the page carries, in the order it draws them: the box each goes
# in, the size it is set at, and the leading and alignment where it states them. A block
# the request states in several runs is one run of words here.
BLOCKS = (
    {
        "text": lambda words: words.heading,
        "size": 20.0,
        "left": 72.0,
        "top": 782.0,
        "width": 451.0,
        "set": None,
    },
    {
        "text": lambda words: words.body,
        "size": 11.0,
        "left": 72.0,
        "top": 736.0,
        "width": 451.0,
        "set": (16.0, hqf_pdf.Align.Justify),
    },
    {
        "text": lambda words: words.labels,
        "size": 11.0,
        "left": 72.0,
        "top": 616.0,
        "width": 228.0,
        "set": (17.0, hqf_pdf.Align.Left),
    },
    {
        "text": lambda words: AMOUNTS,
        "size": 11.0,
        "left": 300.0,
        "top": 616.0,
        "width": 223.0,
        "set": (17.0, hqf_pdf.Align.Left),
    },
)


def sheet(handle: hqf_pdf.FontHandle, words: Words) -> hqf_pdf.Page:
    """The one page, block by block."""
    content = hqf_pdf.Content()
    for block in BLOCKS:
        settings = {}
        if block["set"] is not None:
            leading, align = block["set"]
            settings = {"leading": leading, "align": align}
        flow = hqf_pdf.TextFlow(handle, block["size"], **settings)
        lines = flow.break_lines(block["text"](words), block["width"])
        # The Python side opens and closes the text object itself, where the Rust side
        # leaves that to the caller.
        flow.draw(content, lines, block["left"], block["top"], block["width"])

    page = hqf_pdf.Page.a4()
    page.set_content(content)
    return page


def main() -> None:
    """Writes the file, and says where it went."""
    language = _language.from_environment()
    words = _language.words_of(WORDS, language)

    # A named file is written as named; the default one carries the language, so the
    # languages do not overwrite each other in `tmp/`.
    out = _out.output_path(Path(_language.file_name("pdfa4f.pdf", language)).stem)

    document = hqf_pdf.Document()
    document.set_license(_licence.licensed())
    # The fourth part is built on the second version of PDF, and the standard refuses a
    # file that declares an earlier one.
    document.set_version(hqf_pdf.Version.V2_0)
    document.set_conformance(hqf_pdf.PdfA.A4F)
    document.set_metadata(
        hqf_pdf.Metadata(
            title=words.title,
            author=AUTHOR,
            subject=words.subject,
            producer=PRODUCER,
            created=ISSUED,
        )
    )
    document.attach(
        hqf_pdf.Attachment(
            CARRIED_NAME,
            words.carried_what,
            CARRIED_TYPE,
            hqf_pdf.Relationship.Alternative,
            CHANGED,
            CARRIED.encode(),
        )
    )

    handle = document.add_font(hqf_pdf.Font.from_path(_out.font_path()))
    document.add_page(sheet(handle, words))

    data = document.to_bytes()
    out.parent.mkdir(parents=True, exist_ok=True)
    out.write_bytes(data)
    print(f"wrote {out}: {len(data)} bytes")


if __name__ == "__main__":
    main()
