/**
 * Customer-facing shipping price sheet (Preisliste Versand) — pdfmake, server-side,
 * LogYou brand (same header/footer as the offer PDFs).
 *
 * Input is the fully resolved `PriceSheet` (app/utils/shippingPriceSheet.ts): every
 * cell already carries the SELL price (Tier 2 → Tier 1 → derived contract price + markup).
 * This file only lays it out — no pricing logic lives here.
 *
 * Structure (2026-10-30: rates first, explanations last): cover (title, merchant, carrier logos,
 * validity, intro, table of contents) · one rate section per SECTION_ORDER key (national / EU /
 * non-EU / Warenpost; rows grouped per carrier with its logo on merged sheets) · surcharges ·
 * service overview · notes (the section notes + customer notes + general notes).
 * `sheet.warnings` is internal and never printed.
 * Country rows carry a flag (PNG rasterised from public/flags by flagImages.ts, passed in as
 * `flags` — a missing flag just prints the name).
 *
 * pdfmake rules: tables are `headerRows: 1` + `dontBreakRows: true`, NEVER `unbreakable`
 * (overflowing unbreakable blocks are dropped silently); Roboto has no `→` — use `·`.
 */
import { BRAND, buildBrandHeader, buildBrandFooter, getQuoteLogos } from '../offers/quotePdf'
import { PRICE_SHEET_TEXTS } from './priceSheetTexts'
import { loadFlagImages, FLAG_PDF_MAX_WIDTH, type FlagImage } from './flagImages'
import { loadCarrierLogos, type CarrierLogo } from './carrierLogos'
import {
  SECTION_ORDER,
  carrierCodeOf,
  groupRowsByCarrier,
  priceAt,
  priceSheetFilename,
  type FixtureService,
  type PriceSheet,
  type SectionKey,
  type SheetRow,
  type SurchargeRow
} from '../../../app/utils/shippingPriceSheet'

const A4_PORTRAIT_WIDTH = 595.28
const A4_LANDSCAPE_WIDTH = 841.89
const PAGE_MARGINS: [number, number, number, number] = [40, 100, 40, 86]
/** Weight columns used for Warenpost rows when the sheet columns carry no ≤ 1 kg steps. */
const WARENPOST_DERIVED_COLUMNS = [0.1, 0.25, 0.5, 0.75, 1]
/** Landscape when the sheet has more than this many WEIGHT columns. */
const MAX_PORTRAIT_WEIGHT_COLUMNS = 8
const SUBHEAD_FILL = '#E6EBF1'
const GENERAL_SERVICE_SUFFIX = '_GENERAL'

type Lang = 'de' | 'en'

const formatDateLocal = (iso: string, language: Lang): string => {
  if (!iso) return '—'
  const d = new Date(iso)
  if (isNaN(d.getTime())) return iso
  const dd = String(d.getDate()).padStart(2, '0')
  const mm = String(d.getMonth() + 1).padStart(2, '0')
  return language === 'en' ? `${d.getFullYear()}-${mm}-${dd}` : `${dd}.${mm}.${d.getFullYear()}`
}

const serviceLabel = (service: FixtureService | undefined, fallback: string, language: Lang): string => {
  if (!service) return fallback
  return (language === 'en' ? service.nameEn : service.name) || service.name || fallback
}

export const buildPriceSheetDocDefinition = (
  sheet: PriceSheet,
  logos: { light: string | null; dark: string | null },
  flags: Record<string, FlagImage> = {},
  carrierLogos: Record<string, CarrierLogo> = {}
): any => {
  const language: Lang = sheet?.options?.language === 'en' ? 'en' : 'de'
  const T = PRICE_SHEET_TEXTS[language]
  const locale = language === 'en' ? 'en-IE' : 'de-DE'
  const euro = (amount: any) => new Intl.NumberFormat(locale, { style: 'currency', currency: 'EUR' }).format(Number(amount) || 0)
  const num = (n: any, maxFrac = 2) => new Intl.NumberFormat(locale, { maximumFractionDigits: maxFrac }).format(Number(n) || 0)
  const weightLabel = (w: number) => (w < 1 ? `${num(w * 1000, 0)} g` : `${num(w)} ${T.kg}`)
  /** Two-line header ("bis" / "31,5 kg") so every weight column header has the same height. */
  const columnHeader = (w: number) => `${T.upTo}\n${weightLabel(w)}`

  const generatedAt = sheet?.generatedAt || new Date().toISOString()
  const validOn = sheet?.options?.validOn || generatedAt.slice(0, 10)
  const merchantName = String(sheet?.merchant?.name || sheet?.options?.customerName || '').trim()
  const subtitle = merchantName || T.standardSheet
  const carrierName = String(sheet?.shipperName || '').trim()
  const columns: number[] = (Array.isArray(sheet?.options?.columns) ? sheet.options.columns : [])
    .map(Number).filter(n => Number.isFinite(n) && n > 0).sort((a, b) => a - b)
  const servicesByCode = new Map<string, FixtureService>()
  for (const s of sheet?.services || []) servicesByCode.set(String(s.code), s)
  // Merged multi-carrier sheet (sheet.carriers > 1): every service / row label carries its carrier ("DHL · Paket").
  const multiCarrier = (sheet?.carriers?.length || 0) > 1
  // No prefix when the service name already starts with the carrier ("DHL Paket" stays "DHL Paket").
  const carrierPrefix = (name: string | undefined, label: string = '') => (multiCarrier && name && !String(label).toLowerCase().startsWith(String(name).toLowerCase()) ? `${name} · ` : '')
  const serviceName = (code: string, fallback: string) => { const label = serviceLabel(servicesByCode.get(code), fallback, language); return carrierPrefix(servicesByCode.get(code)?.shipperName, label) + label }
  // Carrier logo (PNG data URL, scaled with `fit`); the text prefix above is only used when no logo exists.
  const carrierLogoOf = (name: string | null | undefined): CarrierLogo | null => carrierLogos[carrierCodeOf(name, name || '')] || null
  const LOGO_FIT: [number, number] = [34, 11]
  const LOGO_FIT_LG: [number, number] = [46, 15]
  /** `[logo] Name` as a pdfmake columns node (plain text when the carrier has no logo). */
  const carrierLabel = (name: string, opts: any = {}, fit: [number, number] = LOGO_FIT_LG) => {
    const logo = carrierLogoOf(name)
    if (!logo) return { text: String(name || ''), ...opts }
    return { columns: [{ width: fit[0], image: logo.dataUrl, fit, margin: [0, 0, 0, 0] }, { width: '*', text: String(name || ''), margin: [5, 1, 0, 0], ...opts }], columnGap: 0 }
  }
  /** Service label with the carrier logo in front (merged sheets); text prefix only without a logo. */
  const serviceCell = (code: string, fallback: string, opts: any = {}) => {
    const svc = servicesByCode.get(code)
    const label = serviceLabel(svc, fallback, language)
    // Tables are grouped per carrier with a logo heading row → the rows themselves stay plain
    // (logo only once per group); the text prefix remains for carriers without a logo.
    if (multiCarrier && carrierLogoOf(svc?.shipperName)) return td(label, opts)
    return td(carrierPrefix(svc?.shipperName, label) + label, opts)
  }
  /** Full-width carrier heading row inside a table (merged sheets only). */
  const carrierHeadRow = (name: string, colCount: number) => [
    { ...carrierLabel(name, { fontSize: 9, bold: true, color: BRAND.navy }), colSpan: colCount, fillColor: '#F3F6FA', margin: [0, 3, 0, 3] },
    ...Array.from({ length: colCount - 1 }, () => ({}))
  ]
  /** Empty spacer row between two carrier groups (white, no zebra fill). */
  const spacerRow = (colCount: number) => [
    { text: ' ', colSpan: colCount, fillColor: BRAND.white, fontSize: 5, margin: [0, 4, 0, 4] },
    ...Array.from({ length: colCount - 1 }, () => ({}))
  ]
  const sections: Record<SectionKey, SheetRow[]> = (sheet?.sections || {}) as any
  const rowsOf = (key: SectionKey): SheetRow[] => (Array.isArray(sections?.[key]) ? sections[key] : [])
  const surcharges: SurchargeRow[] = Array.isArray(sheet?.surcharges) ? sheet.surcharges : []
  const serviceEnabled = (code: string) => sheet?.options?.services?.[code] !== false

  // ---------- shared pdfmake helpers (copied from softwareQuotePdf.ts — local consts there) ----------
  const sectionTitle = (text: string, extra: any = {}) => ({
    stack: [
      { canvas: [{ type: 'rect', x: 0, y: 0, w: 26, h: 3, color: BRAND.orange }] },
      { text: String(text), fontSize: 13, bold: true, color: BRAND.navy, characterSpacing: 0.3, margin: [0, 5, 0, 0], ...extra }
    ],
    margin: [0, 4, 0, 10]
  })
  const zebraLayout = (padX: number = 8) => ({
    hLineWidth: (i: number) => (i === 1 ? 0.75 : 0),
    vLineWidth: () => 0,
    hLineColor: () => BRAND.grayLine,
    fillColor: (rowIndex: number) => (rowIndex === 0 ? BRAND.navy : rowIndex % 2 === 0 ? BRAND.zebra : null),
    paddingLeft: () => padX,
    paddingRight: () => padX,
    paddingTop: () => 4,
    paddingBottom: () => 4
  })
  const th = (text: string, alignment: string = 'left', extra: any = {}) => ({ text, style: 'tableHeader', alignment, ...extra })
  const td = (text: any, opts: any = {}) => ({ text: String(text ?? ''), fontSize: 8, ...opts })
  /** Flag + text side by side. The flag column is a FIXED width (not the flag's own width) so the names align. */
  const flagOf = (countryCode: any): FlagImage | null => flags[String(countryCode || '').toUpperCase()] || null
  const withFlag = (countryCode: any, content: any) => {
    const flag = flagOf(countryCode)
    if (!flag) return content
    return {
      columns: [
        { width: FLAG_PDF_MAX_WIDTH, stack: [{ image: flag.dataUrl, width: flag.width, height: flag.height, margin: [0, 0.5, 0, 0] }] },
        content
      ],
      columnGap: 4
    }
  }
  const tdPrice = (value: any, opts: any = {}) =>
    typeof value === 'number'
      ? td(euro(value), { alignment: 'right', ...opts })
      : td(value ?? T.noPrice, { alignment: 'right', color: BRAND.grayText, ...opts })
  const priceTable = (widths: any[], headers: any[], rows: any[][], padX: number = 8) => ({
    table: { headerRows: 1, dontBreakRows: true, widths, body: [headers, ...rows] },
    layout: zebraLayout(padX),
    margin: [0, 0, 0, 8]
  })
  /** Full-width sub-heading row inside a table (service name over a group of countries). */
  const subheadRow = (text: string, colCount: number) => [
    { text, colSpan: colCount, fontSize: 8.5, bold: true, color: BRAND.navy, fillColor: SUBHEAD_FILL, margin: [0, 1, 0, 1] },
    ...Array.from({ length: colCount - 1 }, () => ({}))
  ]
  /** Section notes are collected here and printed on the final "Hinweise" page (rates first, explanations last). */
  const collectedNotes: string[] = []
  const collectNotes = (lines: string[]) => { for (const l of lines) if (l && !collectedNotes.includes(l)) collectedNotes.push(l) }
  /** "Additional costs & notes" block (kept for the notes page). */
  const noteBlock = (lines: string[]) => ({
    stack: [
      { text: T.additionalCosts, fontSize: 8, bold: true, color: BRAND.orange, margin: [0, 2, 0, 2] },
      ...lines.filter(Boolean).map(l => ({ text: l, fontSize: 7.5, color: BRAND.grayText, margin: [0, 0, 0, 2] }))
    ],
    margin: [0, 0, 0, 10],
    unbreakable: true // 2–3 short lines only — keeps the heading with its note (never do this for tables)
  })
  const pageBreak = (landscape: boolean) => ({ text: '', pageBreak: 'before', pageOrientation: landscape ? 'landscape' : 'portrait' })
  const needsLandscape = (weightColumns: number) => weightColumns > MAX_PORTRAIT_WEIGHT_COLUMNS

  // ---------- cell lookups ----------
  const cellPrice = (row: SheetRow, weight: number): number | null => {
    if (Number(row.maxWeight) > 0 && weight > Number(row.maxWeight) + 1e-9) return null
    const cell = (row.cells || []).find(c => Math.abs(Number(c.weight) - weight) < 1e-9)
    if (cell && typeof cell.price === 'number') return cell.price
    if (cell && cell.price === null) return null
    const base = row.base?.value, perKg = row.perKg?.value
    if (typeof base === 'number' && typeof perKg === 'number') return priceAt(base, perKg, weight)
    return null
  }
  const rowSurchargeNote = (row: SheetRow): string =>
    (row.surcharges || []).filter(s => s && s.label).map(s => `+ ${s.label} ${euro(s.price)}`).join(' · ')

  const sortByCountry = (rows: SheetRow[]) =>
    [...rows].sort((a, b) =>
      String(a.countryName || a.countryCode || '').localeCompare(String(b.countryName || b.countryCode || ''), locale) ||
      String(a.serviceCode).localeCompare(String(b.serviceCode)))

  /** Groups rows per service (in sheet.services order, unknown services last) — only when > 1 service. */
  const groupByService = (rows: SheetRow[]): { code: string; name: string; rows: SheetRow[] }[] => {
    const order = (sheet?.services || []).map(s => String(s.code))
    const map = new Map<string, SheetRow[]>()
    for (const r of rows) {
      const k = String(r.serviceCode || '')
      if (!map.has(k)) map.set(k, [])
      map.get(k)!.push(r)
    }
    const codes = [...map.keys()].sort((a, b) => {
      const ia = order.indexOf(a), ib = order.indexOf(b)
      return (ia < 0 ? 9999 : ia) - (ib < 0 ? 9999 : ib) || a.localeCompare(b)
    })
    return codes.map(code => ({ code, name: (multiCarrier && carrierLogoOf(servicesByCode.get(code)?.shipperName)) ? serviceLabel(servicesByCode.get(code), map.get(code)![0]?.serviceName || code, language) : serviceName(code, map.get(code)![0]?.serviceName || code), rows: map.get(code)! }))
  }

  // ---------- cover ----------
  const coverFacts = [
    [td(T.carrier, { color: BRAND.grayText, fontSize: 9 }), (sheet?.carriers?.length ? { columns: sheet.carriers.flatMap((c, i) => [...(i > 0 ? [{ width: 'auto', text: '+', fontSize: 9, color: BRAND.grayText, margin: [6, 1, 6, 0] }] : []), { width: 'auto', ...carrierLabel(c.shipperName, { bold: true, fontSize: 9 }) }]), columnGap: 0 } : carrierLabel(carrierName || '—', { bold: true, fontSize: 9 }))],
    [td(T.dateWord, { color: BRAND.grayText, fontSize: 9 }), td(formatDateLocal(generatedAt, language), { bold: true, fontSize: 9 })],
    [td(T.validFrom, { color: BRAND.grayText, fontSize: 9 }), td(formatDateLocal(validOn, language), { bold: true, fontSize: 9 })]
  ]
  const cover: any[] = [
    { text: sheet?.options?.title || T.docTitle, fontSize: 28, bold: true, color: BRAND.navy, margin: [0, 70, 0, 6] },
    { canvas: [{ type: 'rect', x: 0, y: 0, w: 60, h: 4, color: BRAND.orange }], margin: [0, 0, 0, 14] },
    { text: subtitle, fontSize: 16, color: BRAND.navySoft, margin: [0, 0, 0, 4] },
    { text: T.issuedBy, fontSize: 9, color: BRAND.grayText, margin: [0, 0, 0, 24] },
    {
      table: { widths: [110, '*'], body: coverFacts },
      layout: { hLineWidth: () => 0, vLineWidth: () => 0, paddingLeft: () => 0, paddingRight: () => 8, paddingTop: () => 2, paddingBottom: () => 2 },
      margin: [0, 0, 0, 22]
    },
    { text: T.intro, fontSize: 9.5, lineHeight: 1.3, color: BRAND.text, margin: [0, 0, 0, 30] },
    {
      toc: {
        title: { text: T.toc, fontSize: 13, bold: true, color: BRAND.navy, margin: [0, 0, 0, 8] },
        textStyle: { fontSize: 10, color: BRAND.text },
        numberStyle: { fontSize: 10, bold: true, color: BRAND.navy },
        textMargin: [0, 2, 0, 2]
      }
    }
  ]

  // ---------- service overview ----------
  const sectionRowsByService = new Map<string, number>()
  for (const key of SECTION_ORDER) for (const r of rowsOf(key)) sectionRowsByService.set(r.serviceCode, (sectionRowsByService.get(r.serviceCode) || 0) + 1)
  const overviewServices = (sheet?.services || []).filter(s => serviceEnabled(String(s.code)) && (sectionRowsByService.get(String(s.code)) || 0) > 0)
  const overview: any[] = []
  if (overviewServices.length) {
    overview.push(pageBreak(false))
    overview.push(sectionTitle(T.overviewTitle, { tocItem: true }))
    overview.push(priceTable(
      [140, 62, 118, 50, '*'],
      [th(T.overviewService), th(T.overviewScope), th(T.overviewDelivery), th(T.overviewMaxWeight, 'right'), th(T.overviewNote)],
      overviewServices.map(s => [
        serviceCell(String(s.code), serviceLabel(s, s.code, language), { bold: true }),
        td(s.scope === 'national' ? T.scopeNational : T.scopeInternational),
        td(s.deliveryDays?.[language] || s.deliveryDays?.de || '—'),
        td(Number(s.maxWeightKg) > 0 ? `${num(s.maxWeightKg)} ${T.kg}` : '—', { alignment: 'right' }),
        td(s.note?.[language] || s.note?.de || '', { color: BRAND.grayText, fontSize: 7.5 })
      ])
    ))
  }

  // ---------- price sections ----------
  const body: any[] = []
  let currentLandscape = false
  const sectionTitleText: Record<SectionKey, string> = {
    national: T.sectionNational, eu: T.sectionEu, nonEu: T.sectionNonEu, warenpost: T.sectionWarenpost, surcharges: T.sectionSurcharges
  }

  const nationalSection = (rows: SheetRow[]) => {
    const cols = columns
    const widths: any[] = [150, ...cols.map(() => '*')]
    const header = [th(T.colService), ...cols.map(w => th(columnHeader(w), 'right'))]
    const tableRows: any[][] = []
    const groups = groupRowsByCarrier(rows, sheet?.carriers)
    groups.forEach((g, gi) => {
      if (multiCarrier && gi > 0) tableRows.push(spacerRow(cols.length + 1))
      if (multiCarrier) tableRows.push(carrierHeadRow(g.shipperName, cols.length + 1))
      for (const r of g.rows) tableRows.push([
        // r.note is INTERNAL (Tier-table / "derived from contract" bookkeeping for the matrix) — never printed:
        // the sheet goes to merchants and is attached to offers / contracts.
        serviceCell(r.serviceCode, r.serviceName, { bold: true }),
        ...cols.map(w => tdPrice(cellPrice(r, w)))
      ])
    })
    collectNotes([T.nationalNote])
    return { landscape: needsLandscape(cols.length), nodes: [priceTable(widths, header, tableRows, 6)] }
  }

  const countrySection = (rows: SheetRow[], cols: number[], extraNote: string) => {
    const colCount = cols.length + 2
    const widths: any[] = [100 + FLAG_PDF_MAX_WIDTH, 40, ...cols.map(() => '*')]
    const header = [th(T.colCountry), th(T.colZone, 'center'), ...cols.map(w => th(columnHeader(w), 'right'))]
    const tableRows: any[][] = []
    let anyRowSurcharge = false
    groupRowsByCarrier(rows, sheet?.carriers).forEach((cg, gi) => {
    if (multiCarrier && gi > 0) tableRows.push(spacerRow(colCount))
    if (multiCarrier) tableRows.push(carrierHeadRow(cg.shipperName, colCount))
    const groups = groupByService(cg.rows)
    for (const g of groups) {
      if (groups.length > 1) tableRows.push(subheadRow(g.name, colCount))
      for (const r of sortByCountry(g.rows)) {
        const note = rowSurchargeNote(r)   // customer-facing surcharge hint only; r.note (internal Tier bookkeeping) is never printed
        if (note) anyRowSurcharge = true
        tableRows.push([
          withFlag(r.countryCode, note
            ? { stack: [td(r.countryName || r.countryCode, { bold: true }), td(note, { fontSize: 6.5, color: BRAND.grayText })] }
            : td(r.countryName || r.countryCode, { bold: true })),
          td(r.zone || '', { alignment: 'center', color: BRAND.grayText }),
          ...cols.map(w => tdPrice(cellPrice(r, w)))
        ])
      }
    }
    })
    collectNotes([extraNote, anyRowSurcharge ? T.haustuerNote : ''])
    return { landscape: needsLandscape(cols.length), nodes: [priceTable(widths, header, tableRows, 5)] }
  }

  const surchargeSection = (list: SurchargeRow[]) => {
    const order = (sheet?.services || []).map(s => String(s.code))
    const rank = (code: string) => (code.endsWith(GENERAL_SERVICE_SUFFIX) ? 9998 : order.indexOf(code) < 0 ? 9999 : order.indexOf(code))
    const map = new Map<string, SurchargeRow[]>()
    for (const s of list) {
      const k = String(s.serviceCode || '')
      if (!map.has(k)) map.set(k, [])
      map.get(k)!.push(s)
    }
    const codes = [...map.keys()].sort((a, b) => rank(a) - rank(b) || a.localeCompare(b))
    const header = [th(T.colSurcharge), th(T.colPrice, 'right'), th(T.colDescription)]
    const tableRows: any[][] = []
    groupRowsByCarrier(list, sheet?.carriers).forEach((cg, gi) => {
    if (multiCarrier && gi > 0) tableRows.push(spacerRow(3))
    if (multiCarrier) tableRows.push(carrierHeadRow(cg.shipperName, 3))
    const inGroup = new Set(cg.rows)
    for (const code of codes) {
      const group = map.get(code)!.filter(x => inGroup.has(x))
      if (!group.length) continue
      const plain = (fallback: string) => (multiCarrier && carrierLogoOf(servicesByCode.get(code)?.shipperName)) ? serviceLabel(servicesByCode.get(code), fallback, language) : serviceName(code, fallback)
      const name = plain(group[0]?.serviceName || (code.endsWith(GENERAL_SERVICE_SUFFIX) ? (language === 'en' ? 'General' : 'Allgemein') : code))
      if (codes.length > 1) tableRows.push(subheadRow(name, 3))
      for (const s of group) {
        const price = s.isPercentage
          ? (Number(s.price) > 0 ? `${num(s.price)} ${T.percentOfBase}` : `% ${language === 'en' ? 'of the base price' : 'des Basispreises'}`)
          : Number(s.price)
        const label = s.countryCode ? `${s.label} (${s.countryCode})` : s.label
        tableRows.push([
          withFlag(s.countryCode, td(label, { bold: true })),
          typeof price === 'number' ? tdPrice(price) : td(price, { alignment: 'right' }),
          td(s.description || '', { color: BRAND.grayText })
        ])
      }
    }
    })
    collectNotes([T.surchargeNote])
    // label column 25 % narrower (142 instead of 190 pt) — long labels wrap, the description gets the rest
    return { landscape: false, nodes: [priceTable([142, 80, '*'], header, tableRows, 6)] }
  }

  const warenpostColumns = (): number[] => {
    const own = columns.filter(w => w <= 1 + 1e-9)
    return own.length >= 2 ? own : [...WARENPOST_DERIVED_COLUMNS]
  }

  for (const key of SECTION_ORDER) {
    let built: { landscape: boolean; nodes: any[] } | null = null
    if (key === 'surcharges') {
      if (sheet?.options?.includeSurcharges !== false && surcharges.length) built = surchargeSection(surcharges)
    } else {
      const rows = rowsOf(key).filter(r => serviceEnabled(String(r.serviceCode)))
      if (!rows.length) continue
      if (key === 'national') built = nationalSection(rows)
      else if (key === 'warenpost') built = countrySection(rows, warenpostColumns(), T.warenpostNote)
      else built = countrySection(rows, columns, T.internationalNote)
    }
    if (!built) continue
    body.push(pageBreak(built.landscape))
    currentLandscape = built.landscape
    body.push(sectionTitle(sectionTitleText[key], { tocItem: true }))
    body.push(...built.nodes)
  }

  // ---------- notes ----------
  const customerNotes = String(sheet?.options?.notes || '').split(/\r?\n/).map(l => l.trim()).filter(Boolean)
  const notes: any[] = [
    pageBreak(false),
    sectionTitle(T.sectionNotes, { tocItem: true }),
    ...(collectedNotes.length ? [noteBlock(collectedNotes)] : []),
    ...(customerNotes.length
      ? [
          { text: T.customerNotes, fontSize: 9.5, bold: true, color: BRAND.navy, margin: [0, 0, 0, 4] },
          { ul: customerNotes.map(l => ({ text: l, fontSize: 9, lineHeight: 1.25, margin: [0, 0, 0, 2] })), margin: [0, 0, 0, 14] }
        ]
      : []),
    { ul: [T.notesNet, T.notesWeight, T.notesVolumetric, T.notesValidity, T.notesChanges, T.notesContact].map(l => ({ text: l, fontSize: 9, lineHeight: 1.25, margin: [0, 0, 0, 3] })) }
  ]
  void currentLandscape

  // Header / footer adapt to the page orientation (landscape country tables).
  const headerSub = `${subtitle}  ·  ${T.dateWord} ${formatDateLocal(generatedAt, language)}`
  const headerPortrait = buildBrandHeader(T.docTitle, headerSub, logos.dark, A4_PORTRAIT_WIDTH)
  const headerLandscape = buildBrandHeader(T.docTitle, headerSub, logos.dark, A4_LANDSCAPE_WIDTH)
  const footerPortrait = buildBrandFooter(T.page, T.of, A4_PORTRAIT_WIDTH)
  const footerLandscape = buildBrandFooter(T.page, T.of, A4_LANDSCAPE_WIDTH)
  const isLandscape = (pageSize: any) => Number(pageSize?.width) > A4_PORTRAIT_WIDTH + 1

  return {
    pageSize: 'A4',
    pageMargins: PAGE_MARGINS,
    info: { title: `${T.docTitle} – ${subtitle}`, author: 'LogYou GmbH', subject: T.docTitle },
    header: (currentPage: number, pageCount: number, pageSize: any) => (isLandscape(pageSize) ? headerLandscape : headerPortrait)(),
    footer: (currentPage: number, pageCount: number, pageSize: any) => (isLandscape(pageSize) ? footerLandscape : footerPortrait)(currentPage, pageCount),
    content: [...cover, ...body, ...overview, ...notes],
    styles: { tableHeader: { fontSize: 8, bold: true, color: BRAND.white } },
    defaultStyle: { font: 'Roboto', fontSize: 9, color: BRAND.text }
  }
}

/** Build the final PDF buffer + authoritative filename (never `Angebot_…`). */
export const generatePriceSheetPdf = async (sheet: PriceSheet): Promise<{ buffer: Buffer; filename: string }> => {
  // Same defensive vfs extraction as generateQuotePdf (export shape varies by bundler).
  const pdfMake = await import('pdfmake/build/pdfmake.js')
  const pdfFonts = await import('pdfmake/build/vfs_fonts.js')
  // @ts-ignore
  const pdfMakeInstance = pdfMake.default || pdfMake
  let vfs: any = null
  if ((pdfFonts as any).pdfMake?.vfs) vfs = (pdfFonts as any).pdfMake.vfs
  else if ((pdfFonts as any).default?.pdfMake?.vfs) vfs = (pdfFonts as any).default.pdfMake.vfs
  else if ((pdfFonts as any).default?.vfs) vfs = (pdfFonts as any).default.vfs
  else if ((pdfFonts as any).vfs) vfs = (pdfFonts as any).vfs
  else if ((pdfFonts as any).default && Object.keys((pdfFonts as any).default).some((k: string) => k.endsWith('.ttf'))) vfs = (pdfFonts as any).default
  if (!vfs) throw new Error('Could not find vfs fonts in pdfmake fonts module')
  pdfMakeInstance.vfs = vfs

  const logos = await getQuoteLogos()
  const flagCodes: string[] = []
  for (const key of SECTION_ORDER) for (const r of (sheet?.sections as any)?.[key] || []) if (r?.countryCode) flagCodes.push(String(r.countryCode))
  for (const s of sheet?.surcharges || []) if (s?.countryCode) flagCodes.push(String(s.countryCode))
  const flags = await loadFlagImages(flagCodes)
  const carrierLogos = await loadCarrierLogos([sheet?.shipperName, ...(sheet?.carriers || []).map(c => c.shipperName)])
  const docDefinition = buildPriceSheetDocDefinition(sheet, logos, flags, carrierLogos)
  const language: Lang = sheet?.options?.language === 'en' ? 'en' : 'de'
  const filename = priceSheetFilename(language, sheet?.merchant?.name || sheet?.options?.customerName || '', sheet?.generatedAt || new Date().toISOString())
  const buffer = await new Promise<Buffer>((resolve, reject) => {
    try {
      pdfMakeInstance.createPdf(docDefinition).getBuffer((buf: Buffer) => resolve(buf))
    } catch (error) {
      reject(error)
    }
  })
  return { buffer, filename }
}
