import path from "path";
import { Document, Page, Text, View, StyleSheet, Font, renderToBuffer } from "@react-pdf/renderer";
import type { ContractDoc } from "@/models/Contract";
import { formatEUR, formatAddress } from "@/lib/orderConstants";
import { formatNameWithSalutation } from "@/lib/salutation";

export interface ContractPartyInfo {
  clientName: string;
  // "" (kein Herr/Frau-Präfix) bei "divers" oder noch nicht gepflegter Anrede.
  clientSalutation: string;
  clientEmail: string;
  clientStreet: string;
  clientPostCode: string;
  clientCity: string;
  photographerEmail: string;
  photographerInstagram: string;
  photographerWebsite: string;
}

export interface ContractLetterhead {
  businessName: string;
  legalName: string;
  street: string;
  postCode: string;
  city: string;
  countryCode: string;
}

// Bereits sprachaufgelöste Abschnitte (siehe resolveSectionsForLocale in
// lib/contractTemplateDefaults.ts) — contractPdf.tsx selbst kennt die
// zweisprachige Roh-Section-Form (headingEn/bodyEn) nicht, bekommt für die
// gewählte Vertragssprache immer schon die passende Fassung.
export type ResolvedContractSection = { heading: string; body: string; appendix?: boolean };

type Locale = "de" | "en";

// Bevorzugt die strukturierten Straße/PLZ/Ort-Felder — fällt bei vor der
// Umstellung angelegten Verträgen auf die alte Freitext-Zeile zurück, damit
// bestehende Vertrags-PDFs weiterhin korrekt anzeigen.
function partyAddress(d: { street?: string; postCode?: string; city?: string; address?: string }): string {
  const structured = formatAddress({ street: d.street, postCode: d.postCode, city: d.city });
  return structured || d.address || "";
}

// Der Markenname "Rakku Photography" allein reicht als Vertragspartei nicht
// — bei einem Einzelunternehmen unterschreibt die reale Person (Nutzer-
// Wunsch 15.09.2026: "ich muss ja auch als Till Kuhlmann unterschreiben"),
// daher immer den bürgerlichen Namen mit ausweisen, falls gepflegt.
function photographerDisplayName(letterhead: ContractLetterhead): string {
  if (letterhead.legalName && letterhead.legalName !== letterhead.businessName) {
    return `${letterhead.businessName} (${letterhead.legalName})`;
  }
  return letterhead.businessName;
}

function formatSignedAt(date: Date, locale: Locale): string {
  return new Date(date).toLocaleString(locale === "en" ? "en-GB" : "de-DE", {
    day: "2-digit",
    month: "2-digit",
    year: "numeric",
    hour: "2-digit",
    minute: "2-digit",
  });
}

// Gleiche Wortmarken-Schrift wie im Dokumenten-Briefkopf (documentPdf.tsx) —
// beide Generatoren registrieren sie unabhängig voneinander, doppeltes
// Registrieren mit demselben Namen ist bei react-pdf unproblematisch.
Font.register({
  family: "Quantify",
  src: path.join(process.cwd(), "src/app/fonts/Quantify.ttf"),
});

const styles = StyleSheet.create({
  page: { padding: 48, paddingBottom: 72, fontSize: 10, fontFamily: "Helvetica", color: "#1a1a1a" },
  // Briefkopf — bewusst identisch zu documentPdf.tsx aufgebaut (Wortmarke,
  // Rechtsname, Adresse, Trennlinie, Eyebrow), damit Verträge optisch wie
  // die übrigen generierten Dokumente aussehen. Ohne die Empfänger-Spalte
  // aus documentPdf.tsx — die Vertragsparteien stehen weiter unten ohnehin
  // ausführlich im "Vertragsparteien"-Block.
  wordmark: { fontFamily: "Quantify", fontSize: 20, letterSpacing: 2, marginBottom: 4 },
  legalName: { fontSize: 9, color: "#555", marginBottom: 2 },
  address: { fontSize: 9, color: "#555" },
  rule: { borderBottom: "1px solid #e5e5e5", marginTop: 10, marginBottom: 10 },
  eyebrow: { fontSize: 8, color: "#777", textTransform: "uppercase", letterSpacing: 1.5 },
  title: { fontSize: 16, fontFamily: "Helvetica-Bold", marginTop: 14, marginBottom: 18 },
  h2: { fontSize: 11, fontFamily: "Helvetica-Bold", marginTop: 14, marginBottom: 4 },
  p: { marginBottom: 6, lineHeight: 1.5 },
  row: { flexDirection: "row", marginBottom: 3 },
  label: { width: 140, color: "#555" },
  value: { flex: 1 },
  sigBlock: { marginTop: 10, borderTop: "1px solid #ccc", paddingTop: 6 },
  sigRow: { flexDirection: "row", justifyContent: "space-between", marginTop: 18 },
  sigCol: { width: "45%" },
  sigLine: { borderTop: "1px solid #999", marginTop: 24, paddingTop: 3, fontSize: 8, color: "#555" },
  // Gleiche Fußzeile wie documentPdf.tsx (Position/Abstände identisch), für
  // optisch einheitliches Erscheinungsbild aller generierten PDFs.
  footerMeta: {
    position: "absolute",
    bottom: 24,
    left: 48,
    right: 48,
    fontSize: 8,
    color: "#999",
    textAlign: "center",
    borderTop: "1px solid #eee",
    paddingTop: 8,
    flexDirection: "row",
    justifyContent: "space-between",
  },
});

// Alle festverdrahteten Gerüst-Texte rund um die editierbaren
// Vertragsabschnitte (die kommen weiter aus ContractTemplate) — 1:1
// Übersetzung, strukturelle UI-Beschriftungen ohne eigenen Rechtsgehalt
// (anders als die AGB-/Vertragsklauseln selbst, die weiterhin unverändert
// aus der zweisprachigen Vorlage kommen).
const LABELS: Record<
  Locale,
  {
    pageOf: (page: number, total: number) => string;
    appendixEyebrow: string;
    contractingParties: string;
    photographerHeading: string;
    fieldName: string;
    fieldEmail: string;
    fieldInstagram: string;
    fieldWebsite: string;
    partiesFootnote: (photographer: string, client: string) => string;
    signedOn: (date: string) => string;
    notYetSigned: string;
    contractEyebrow: string;
    contractTitle: string;
    standardClientLabel: string;
    standardShortPhotographer: string;
    standardShortClient: string;
    addressLabel: string;
    orderInfoHeading: string;
    fieldCharacter: string;
    fieldShootingType: string;
    fieldLocation: string;
    fieldDatePeriod: string;
    fieldAgreedPrice: string;
    grossSuffix: string;
    usageRightsBase: string;
    usageRightsNo: string;
    usageRightsAnonymized: string;
    usageRightsYes: string;
    signaturesHeading: string;
    standardDocName: string;
    tfpEyebrow: string;
    tfpTitle: string;
    modelReleaseSubtitle: (businessName: string) => string;
    tfpClientLabel: string;
    tfpShortPhotographer: string;
    tfpShortClient: string;
    fieldBirthdate: string;
    shootingInfoHeading: string;
    fieldTheme: string;
    fieldKind: string;
    fieldImageSelection: string;
    fieldPhotographerUsage: string;
    fieldPublicationChannels: string;
    fieldRestrictions: string;
    fieldCreditPreference: string;
    creditWanted: string;
    creditNotWanted: string;
    creditByArrangement: string;
    photographerCreditRequired: (businessName: string) => string;
    photographerCreditNotRequired: (businessName: string) => string;
    fieldCompanionAllowed: string;
    yes: string;
    no: string;
    stylistHeading: string;
    fieldUsageAllowed: string;
    fieldCredit: string;
    minorHeading: string;
    guardianLabel: (n: number) => string;
    specialAgreementsHeading: string;
    tfpDocName: string;
    dash: string;
  }
> = {
  de: {
    pageOf: (page, total) => `Seite ${page} von ${total}`,
    appendixEyebrow: "Anlage",
    contractingParties: "Vertragsparteien",
    photographerHeading: "Fotograf/in",
    fieldName: "Name",
    fieldEmail: "E-Mail",
    fieldInstagram: "Instagram",
    fieldWebsite: "Webseite",
    partiesFootnote: (photographer, client) =>
      `Im Folgenden werden die Vertragsparteien als „${photographer}" und „${client}" bezeichnet.`,
    signedOn: (date) => `digital unterschrieben am ${date}`,
    notYetSigned: "noch nicht unterschrieben",
    contractEyebrow: "Vertrag",
    contractTitle: "Fotografie-Dienstleistungsvertrag",
    standardClientLabel: "Auftraggeber/in",
    standardShortPhotographer: "Auftragnehmer",
    standardShortClient: "Auftraggeber/in",
    addressLabel: "Anschrift",
    orderInfoHeading: "Angaben zum Auftrag",
    fieldCharacter: "Charakter / Idee",
    fieldShootingType: "Art des Shootings",
    fieldLocation: "Ort / Event",
    fieldDatePeriod: "Zeitraum",
    fieldAgreedPrice: "Vereinbarter Preis",
    grossSuffix: "(brutto)",
    usageRightsBase:
      "Der Auftraggeber erhält nach vollständiger Bezahlung ein einfaches, nicht exklusives Nutzungsrecht an den final bearbeiteten Bildern für private, nicht-kommerzielle Zwecke. Der Auftragnehmer bleibt Urheber.",
    usageRightsNo: "Der Auftragnehmer nutzt die Bilder nicht für eigene Portfolio- oder Eigenwerbungszwecke.",
    usageRightsAnonymized:
      "Der Auftragnehmer darf die Bilder nur in anonymisierter Form (z. B. ohne erkennbares Gesicht) für eigene Portfolio- und Eigenwerbungszwecke nutzen (Website, Social Media, Google-Unternehmensprofil, Print-Portfolio).",
    usageRightsYes:
      "Der Auftragnehmer darf die Bilder für eigene Portfolio- und Eigenwerbungszwecke nutzen (Website, Social Media, Google-Unternehmensprofil, Print-Portfolio), sofern nicht ausdrücklich anders vereinbart.",
    signaturesHeading: "Unterschriften",
    standardDocName: "Fotografie-Dienstleistungsvertrag",
    tfpEyebrow: "TFP-Vertrag",
    tfpTitle: "TFP-Shooting-Vertrag",
    modelReleaseSubtitle: (businessName) => `Model Release für Portrait-, Cosplay- und Convention-Fotografie · ${businessName}`,
    tfpClientLabel: "Model / teilnehmende Person",
    tfpShortPhotographer: "Fotograf",
    tfpShortClient: "Model",
    fieldBirthdate: "Geburtsdatum",
    shootingInfoHeading: "Angaben zum Shooting",
    fieldTheme: "Charakter / Thema",
    fieldKind: "Art",
    fieldImageSelection: "Bildauswahl",
    fieldPhotographerUsage: "Nutzungsrechte Fotograf",
    fieldPublicationChannels: "Veröffentlichung — Kanäle",
    fieldRestrictions: "Einschränkungen",
    fieldCreditPreference: "Credit-Wunsch",
    creditWanted: "Nennen/Verlinken gewünscht",
    creditNotWanted: "Nicht nennen/verlinken",
    creditByArrangement: "Je nach Bild/Plattform nach Absprache",
    photographerCreditRequired: (businessName) =>
      `Bei eigener Veröffentlichung durch das Model ist ${businessName} als Fotograf zu nennen bzw. zu verlinken, soweit dies auf der jeweiligen Plattform möglich ist.`,
    photographerCreditNotRequired: (businessName) =>
      `Eine Nennung oder Verlinkung von ${businessName} bei eigener Veröffentlichung durch das Model ist nicht erforderlich.`,
    fieldCompanionAllowed: "Begleitperson erlaubt",
    yes: "Ja",
    no: "Nein",
    stylistHeading: "Stylist/in",
    fieldUsageAllowed: "Nutzung erlaubt",
    fieldCredit: "Credit",
    minorHeading: "Minderjährig — gesetzliche Vertretung",
    guardianLabel: (n) => `Vertreter/in ${n}`,
    specialAgreementsHeading: "Besondere Vereinbarungen",
    tfpDocName: "TFP-Shooting-Vertrag",
    dash: "—",
  },
  en: {
    pageOf: (page, total) => `Page ${page} of ${total}`,
    appendixEyebrow: "Appendix",
    contractingParties: "Contracting Parties",
    photographerHeading: "Photographer",
    fieldName: "Name",
    fieldEmail: "Email",
    fieldInstagram: "Instagram",
    fieldWebsite: "Website",
    partiesFootnote: (photographer, client) =>
      `The contracting parties are hereinafter referred to as "${photographer}" and "${client}".`,
    signedOn: (date) => `signed digitally on ${date}`,
    notYetSigned: "not yet signed",
    contractEyebrow: "Contract",
    contractTitle: "Photography Services Agreement",
    standardClientLabel: "Client",
    standardShortPhotographer: "Contractor",
    standardShortClient: "Client",
    addressLabel: "Address",
    orderInfoHeading: "Order Details",
    fieldCharacter: "Character / Concept",
    fieldShootingType: "Type of Shoot",
    fieldLocation: "Location / Event",
    fieldDatePeriod: "Date / Period",
    fieldAgreedPrice: "Agreed Price",
    grossSuffix: "(gross)",
    usageRightsBase:
      "Upon full payment, the Client receives a simple, non-exclusive right of use to the final edited images for private, non-commercial purposes. The Contractor remains the copyright holder.",
    usageRightsNo: "The Contractor does not use the images for its own portfolio or self-promotion purposes.",
    usageRightsAnonymized:
      "The Contractor may only use the images in anonymized form (e.g. without a recognizable face) for its own portfolio and self-promotion purposes (website, social media, Google Business Profile, print portfolio).",
    usageRightsYes:
      "The Contractor may use the images for its own portfolio and self-promotion purposes (website, social media, Google Business Profile, print portfolio), unless expressly agreed otherwise.",
    signaturesHeading: "Signatures",
    standardDocName: "Photography Services Agreement",
    tfpEyebrow: "TFP Agreement",
    tfpTitle: "TFP Shoot Agreement",
    modelReleaseSubtitle: (businessName) => `Model release for portrait, cosplay and convention photography · ${businessName}`,
    tfpClientLabel: "Model / Participant",
    tfpShortPhotographer: "Photographer",
    tfpShortClient: "Model",
    fieldBirthdate: "Date of Birth",
    shootingInfoHeading: "Shoot Details",
    fieldTheme: "Character / Theme",
    fieldKind: "Type",
    fieldImageSelection: "Image Selection",
    fieldPhotographerUsage: "Photographer's Usage Rights",
    fieldPublicationChannels: "Publication — Channels",
    fieldRestrictions: "Restrictions",
    fieldCreditPreference: "Credit Preference",
    creditWanted: "Credit/tag requested",
    creditNotWanted: "No credit/tag",
    creditByArrangement: "By arrangement, depending on image/platform",
    photographerCreditRequired: (businessName) =>
      `When the Model publishes images independently, ${businessName} must be credited/tagged as the photographer, where this is possible on the respective platform.`,
    photographerCreditNotRequired: (businessName) =>
      `Crediting or tagging ${businessName} when the Model publishes images independently is not required.`,
    fieldCompanionAllowed: "Companion Allowed",
    yes: "Yes",
    no: "No",
    stylistHeading: "Stylist",
    fieldUsageAllowed: "Usage Allowed",
    fieldCredit: "Credit",
    minorHeading: "Minor — Legal Representation",
    guardianLabel: (n) => `Legal Representative ${n}`,
    specialAgreementsHeading: "Special Agreements",
    tfpDocName: "TFP Shoot Agreement",
    dash: "—",
  },
};

function Letterhead({ letterhead, eyebrow }: { letterhead: ContractLetterhead; eyebrow: string }) {
  return (
    <View>
      <Text style={styles.wordmark}>{letterhead.businessName.toUpperCase()}</Text>
      {letterhead.legalName && letterhead.legalName !== letterhead.businessName && (
        <Text style={styles.legalName}>{letterhead.legalName}</Text>
      )}
      <Text style={styles.address}>
        {formatAddress({ street: letterhead.street, postCode: letterhead.postCode, city: letterhead.city, countryCode: letterhead.countryCode })}
      </Text>
      <View style={styles.rule} />
      <Text style={styles.eyebrow}>{eyebrow}</Text>
    </View>
  );
}

function FooterMeta({ documentName, locale }: { documentName: string; locale: Locale }) {
  const t = LABELS[locale];
  return (
    <View style={styles.footerMeta} fixed>
      <Text>{documentName}</Text>
      <Text render={({ pageNumber, totalPages }) => t.pageOf(pageNumber, totalPages)} />
    </View>
  );
}

function Field({ label, value }: { label: string; value?: string }) {
  if (!value) return null;
  return (
    <View style={styles.row}>
      <Text style={styles.label}>{label}</Text>
      <Text style={styles.value}>{value}</Text>
    </View>
  );
}

// Rendert die in der Vorlagen-Verwaltung (admin/contract-templates) frei
// bearbeitbaren Vertragsabschnitte — das ist der eigentliche Rechtstext.
// Rein datengetriebene Angaben (Preis, Termin, Nutzungsrechte-Auswahl usw.)
// stehen bewusst nicht hier, sondern im "Angaben zum Auftrag"-Block darüber.
// `sections` ist hier schon die für die gewählte Vertragssprache aufgelöste
// Fassung (siehe resolveSectionsForLocale), keine erneute Sprachwahl nötig.
//
// Überschrift + erster Absatz stecken zusammen in einer wrap={false}-View,
// damit react-pdf die Überschrift nie allein am Seitenende stehen lässt,
// während der zugehörige Text erst auf der Folgeseite weitergeht (Waisen-
// Effekt). Weitere Absätze bleiben normal umbrechbar, sonst müsste z. B. die
// gesamte AGB als ein einziger unteilbarer Block auf eine Seite passen.
function TemplateSections({ sections }: { sections: ResolvedContractSection[] }) {
  return (
    <>
      {sections
        .filter((s) => !s.appendix)
        .map((s, i) => {
          const [first, ...rest] = s.body.split(/\n\n+/);
          return (
            <View key={i}>
              <View wrap={false}>
                <Text style={styles.h2}>{s.heading}</Text>
                {first && <Text style={styles.p}>{first}</Text>}
              </View>
              {rest.map((para, j) => (
                <Text key={j} style={styles.p}>
                  {para}
                </Text>
              ))}
            </View>
          );
        })}
    </>
  );
}

// Anhang-Abschnitte (AGB) — beginnen auf einer eigenen Seite NACH dem
// Unterschriftenblock, statt zwischen den Vertragsklauseln zu stehen.
function AppendixPage({
  letterhead,
  sections,
  documentName,
  locale,
}: {
  letterhead: ContractLetterhead;
  sections: ResolvedContractSection[];
  documentName: string;
  locale: Locale;
}) {
  const t = LABELS[locale];
  const appendixSections = sections.filter((s) => s.appendix);
  if (appendixSections.length === 0) return null;
  return (
    <Page size="A4" style={styles.page}>
      <Letterhead letterhead={letterhead} eyebrow={t.appendixEyebrow} />
      {appendixSections.map((s, i) => {
        const [first, ...rest] = s.body.split(/\n\n+/);
        return (
          <View key={i}>
            <View wrap={false}>
              <Text style={styles.h2}>{s.heading}</Text>
              {first && <Text style={styles.p}>{first}</Text>}
            </View>
            {rest.map((para, j) => (
              <Text key={j} style={styles.p}>
                {para}
              </Text>
            ))}
          </View>
        );
      })}
      <FooterMeta documentName={documentName} locale={locale} />
    </Page>
  );
}

function PartiesBlock({
  info,
  letterhead,
  clientLabel,
  shortNames,
  extra,
  locale,
}: {
  info: ContractPartyInfo;
  letterhead: ContractLetterhead;
  clientLabel: string;
  // Definiert die Kurzbezeichnungen, mit denen die Vertragsparteien im
  // restlichen Dokumenttext angesprochen werden (z. B. "Fotograf"/"Model"
  // im TFP-Vertrag, "Auftragnehmer"/"Auftraggeber/in" im Standardvertrag) —
  // muss zu den tatsächlich im Fließtext verwendeten Begriffen passen.
  shortNames: { photographer: string; client: string };
  extra?: { label: string; value?: string }[];
  locale: Locale;
}) {
  const t = LABELS[locale];
  return (
    <>
      <Text style={styles.h2}>{t.contractingParties}</Text>
      <View style={{ flexDirection: "row", marginBottom: 6 }}>
        <View style={{ flex: 1 }}>
          <Text style={{ fontFamily: "Helvetica-Bold", marginBottom: 3 }}>{t.photographerHeading}</Text>
          <Field label={t.fieldName} value={photographerDisplayName(letterhead)} />
          <Field label={t.fieldEmail} value={info.photographerEmail} />
          <Field label={t.fieldInstagram} value={info.photographerInstagram} />
          <Field label={t.fieldWebsite} value={info.photographerWebsite} />
        </View>
        <View style={{ flex: 1 }}>
          <Text style={{ fontFamily: "Helvetica-Bold", marginBottom: 3 }}>{clientLabel}</Text>
          <Field label={t.fieldName} value={formatNameWithSalutation(info.clientSalutation, info.clientName)} />
          <Field label={t.fieldEmail} value={info.clientEmail} />
          {extra?.map((f) => <Field key={f.label} label={f.label} value={f.value} />)}
        </View>
      </View>
      <Text style={{ fontSize: 8, color: "#777", marginBottom: 10 }}>
        {t.partiesFootnote(shortNames.photographer, shortNames.client)}
      </Text>
    </>
  );
}

function SignatureBlock({
  label,
  sig,
  locale,
}: {
  label: string;
  sig?: { name: string; signedAt: Date };
  locale: Locale;
}) {
  const t = LABELS[locale];
  return (
    <View style={styles.sigCol}>
      <Text style={{ fontFamily: "Helvetica-Bold", marginBottom: 4 }}>{label}</Text>
      {sig ? (
        <>
          <Text>{sig.name}</Text>
          <Text style={{ color: "#555", fontSize: 8 }}>{t.signedOn(formatSignedAt(new Date(sig.signedAt), locale))}</Text>
        </>
      ) : (
        <View style={styles.sigLine}>
          <Text>{t.notYetSigned}</Text>
        </View>
      )}
    </View>
  );
}

function StandardContractPdf({
  contract,
  info,
  letterhead,
  sections,
  locale,
}: {
  contract: ContractDoc;
  info: ContractPartyInfo;
  letterhead: ContractLetterhead;
  sections: ResolvedContractSection[];
  locale: Locale;
}) {
  const t = LABELS[locale];
  const d = contract.standardData!;
  const photographerSig = contract.signatures.find((s) => s.role === "photographer");
  const modelSig = contract.signatures.find((s) => s.role === "model");

  return (
    <>
    <Page size="A4" style={styles.page}>
      <Letterhead letterhead={letterhead} eyebrow={t.contractEyebrow} />
      <Text style={styles.title}>{t.contractTitle}</Text>

      <PartiesBlock
        info={info}
        letterhead={letterhead}
        clientLabel={t.standardClientLabel}
        shortNames={{ photographer: t.standardShortPhotographer, client: t.standardShortClient }}
        extra={[{ label: t.addressLabel, value: formatAddress({ street: info.clientStreet, postCode: info.clientPostCode, city: info.clientCity }) }]}
        locale={locale}
      />

      <Text style={styles.h2}>{t.orderInfoHeading}</Text>
      <Field label={t.fieldCharacter} value={d.characterOrConcept} />
      <Field label={t.fieldShootingType} value={d.shootingType} />
      <Field label={t.fieldLocation} value={d.eventLocation} />
      <Field label={t.fieldDatePeriod} value={d.datePeriod} />
      <Field label={t.fieldAgreedPrice} value={`${formatEUR(d.amount)} ${t.grossSuffix}`} />
      <Text style={styles.p}>
        {t.usageRightsBase}{" "}
        {d.usageRights === "no" ? t.usageRightsNo : d.usageRights === "anonymized" ? t.usageRightsAnonymized : t.usageRightsYes}
      </Text>

      <TemplateSections sections={sections} />

      <View style={styles.sigBlock}>
        <Text style={{ fontFamily: "Helvetica-Bold" }}>{t.signaturesHeading}</Text>
        <View style={styles.sigRow}>
          <SignatureBlock label={t.standardShortPhotographer} sig={photographerSig} locale={locale} />
          <SignatureBlock label={t.standardShortClient} sig={modelSig} locale={locale} />
        </View>
      </View>
      <FooterMeta documentName={t.standardDocName} locale={locale} />
    </Page>
    <AppendixPage letterhead={letterhead} sections={sections} documentName={t.standardDocName} locale={locale} />
    </>
  );
}

function TfpContractPdf({
  contract,
  info,
  letterhead,
  sections,
  locale,
}: {
  contract: ContractDoc;
  info: ContractPartyInfo;
  letterhead: ContractLetterhead;
  sections: ResolvedContractSection[];
  locale: Locale;
}) {
  const t = LABELS[locale];
  const d = contract.tfpData!;
  const photographerSig = contract.signatures.find((s) => s.role === "photographer");
  const modelSig = contract.signatures.find((s) => s.role === "model");
  const guardian1Sig = contract.signatures.find((s) => s.role === "guardian1");
  const guardian2Sig = contract.signatures.find((s) => s.role === "guardian2");

  const creditLabel =
    d.creditPreference === "nennen" ? t.creditWanted : d.creditPreference === "nicht_nennen" ? t.creditNotWanted : t.creditByArrangement;

  return (
    <>
    <Page size="A4" style={styles.page}>
      <Letterhead letterhead={letterhead} eyebrow={t.tfpEyebrow} />
      <Text style={styles.title}>{t.tfpTitle}</Text>
      <Text style={{ ...styles.p, color: "#555" }}>{t.modelReleaseSubtitle(letterhead.businessName)}</Text>

      <PartiesBlock
        info={info}
        letterhead={letterhead}
        clientLabel={t.tfpClientLabel}
        shortNames={{ photographer: t.tfpShortPhotographer, client: t.tfpShortClient }}
        extra={[
          { label: t.fieldBirthdate, value: d.modelBirthdate },
          { label: t.addressLabel, value: partyAddress({ street: d.modelStreet, postCode: d.modelPostCode, city: d.modelCity, address: d.modelAddress }) },
          { label: t.fieldInstagram, value: d.modelInstagram },
        ]}
        locale={locale}
      />

      <Text style={styles.h2}>{t.shootingInfoHeading}</Text>
      <Field label={t.fieldTheme} value={d.shootingTheme} />
      <Field label={t.fieldLocation} value={d.shootingLocation} />
      <Field label={t.fieldDatePeriod} value={d.shootingDate} />
      <Field label={t.fieldKind} value={d.shootingKinds.join(", ")} />
      <Field label={t.fieldImageSelection} value={d.finalImageCount} />
      <Field label={t.fieldPhotographerUsage} value={d.photographerUsage.join(", ") || t.dash} />
      <Field label={t.fieldPublicationChannels} value={d.publicationChannels.join(", ") || t.dash} />
      <Field label={t.fieldRestrictions} value={d.publicationRestrictions} />
      <Field label={t.fieldCreditPreference} value={`${creditLabel}${d.creditHandle ? " — " + d.creditHandle : ""}`} />
      <Text style={styles.p}>
        {d.photographerCreditRequired
          ? t.photographerCreditRequired(letterhead.businessName)
          : t.photographerCreditNotRequired(letterhead.businessName)}
      </Text>
      <Field label={t.fieldCompanionAllowed} value={d.companionAllowed ? t.yes : t.no} />

      {d.stylist && (
        <>
          <Text style={{ fontFamily: "Helvetica-Bold", marginTop: 8, marginBottom: 3 }}>{t.stylistHeading}</Text>
          <Field label={t.fieldName} value={d.stylist.name} />
          <Field label={t.addressLabel} value={partyAddress(d.stylist)} />
          <Field label={t.fieldInstagram} value={d.stylist.instagram} />
          <Field label={t.fieldUsageAllowed} value={d.stylist.usage.join(", ")} />
          <Field label={t.fieldCredit} value={d.stylist.credit} />
        </>
      )}

      {d.isMinor && (
        <>
          <Text style={{ fontFamily: "Helvetica-Bold", marginTop: 8, marginBottom: 3 }}>{t.minorHeading}</Text>
          {d.guardians.map((g, i) => (
            <View key={i} style={{ marginBottom: 4 }}>
              <Field label={t.guardianLabel(i + 1)} value={`${g.name} — ${partyAddress(g)} — ${g.contact}`} />
            </View>
          ))}
        </>
      )}

      {d.specialAgreements && (
        <>
          <Text style={styles.h2}>{t.specialAgreementsHeading}</Text>
          <Text style={styles.p}>{d.specialAgreements}</Text>
        </>
      )}

      <TemplateSections sections={sections} />

      <View style={styles.sigBlock}>
        <Text style={{ fontFamily: "Helvetica-Bold" }}>{t.signaturesHeading}</Text>
        <View style={styles.sigRow}>
          <SignatureBlock label={t.tfpShortPhotographer} sig={photographerSig} locale={locale} />
          <SignatureBlock label={t.tfpShortClient} sig={modelSig} locale={locale} />
        </View>
        {d.isMinor && (
          <View style={styles.sigRow}>
            <SignatureBlock label={t.guardianLabel(1)} sig={guardian1Sig} locale={locale} />
            <SignatureBlock label={t.guardianLabel(2)} sig={guardian2Sig} locale={locale} />
          </View>
        )}
      </View>
      <FooterMeta documentName={t.tfpDocName} locale={locale} />
    </Page>
    <AppendixPage letterhead={letterhead} sections={sections} documentName={t.tfpDocName} locale={locale} />
    </>
  );
}

export async function renderContractPdf(
  contract: ContractDoc,
  info: ContractPartyInfo,
  letterhead: ContractLetterhead,
  sections: ResolvedContractSection[]
): Promise<Buffer> {
  const locale: Locale = contract.locale === "en" ? "en" : "de";
  const doc = (
    <Document>
      {contract.type === "standard" ? (
        <StandardContractPdf contract={contract} info={info} letterhead={letterhead} sections={sections} locale={locale} />
      ) : (
        <TfpContractPdf contract={contract} info={info} letterhead={letterhead} sections={sections} locale={locale} />
      )}
    </Document>
  );
  return renderToBuffer(doc);
}
