/**
 * Custom forecast ("Individuelle Prognose") PDF — pdfmake, server-side, same
 * brand look as the quote (header/footer/colours reused from quotePdf.ts).
 *
 * Pure renderer: labels, calculation texts and every amount arrive pre-computed
 * in the payload (app/composables/useOfferForecast.ts → buildForecastPayload),
 * so the PDF always matches what the user saw in the modal.
 *
 * Up to 2 volume scenarios print on A4 portrait; 3–4 switch to landscape so the
 * amount columns stay readable.
 */
import { BRAND, buildBrandHeader, buildBrandFooter, getQuoteLogos } from './quotePdf'

const A4_PORTRAIT_WIDTH = 595.28
const A4_LANDSCAPE_WIDTH = 841.89

export const FORECAST_TEXTS: any = {
  de: {
    docTitle: 'Individuelle Kostenprognose',
    dateWord: 'Prognose vom',
    date: 'Datum',
    basis: 'Grundlage',
    basisValue: 'Unverbindliche Beispielrechnung',
    intro: 'Auf Basis der mit Ihnen besprochenen Mengen haben wir Ihre voraussichtlichen monatlichen Kosten für das Fulfillment bei LogYou kalkuliert.',
    tableTitle: 'Ihre voraussichtlichen monatlichen Kosten',
    position: 'Position',
    calculation: 'Berechnungsgrundlage',
    variableTotal: 'Zwischensumme variable Kosten (mengenabhängig)',
    fixedTotal: 'Zwischensumme fixe monatliche Kosten',
    total: 'Gesamt pro Monat',
    perOrder: 'Gesamtkosten pro Auftrag',
    yearly: 'Gesamt pro Jahr (12 Monate)',
    notesTitle: 'Hinweise',
    disclaimer: 'Unverbindliche, beispielhafte Kalkulation auf Basis der genannten Annahmen — kein Vertragsbestandteil. Abgerechnet wird ausschließlich nach tatsächlichem Verbrauch. Alle Preise verstehen sich netto zzgl. 19 % MwSt.',
    closing: 'Wir freuen uns auf die Zusammenarbeit!',
    closingTeam: 'Ihr LogYou Team',
    page: 'Seite',
    of: 'von'
  },
  en: {
    docTitle: 'Individual cost forecast',
    dateWord: 'Forecast of',
    date: 'Date',
    basis: 'Basis',
    basisValue: 'Non-binding example calculation',
    intro: 'Based on the volumes we discussed, we have calculated your expected monthly costs for fulfillment with LogYou.',
    tableTitle: 'Your expected monthly costs',
    position: 'Position',
    calculation: 'Calculation basis',
    variableTotal: 'Subtotal variable costs (volume-dependent)',
    fixedTotal: 'Subtotal fixed monthly costs',
    total: 'Total per month',
    perOrder: 'Total cost per order',
    yearly: 'Total per year (12 months)',
    notesTitle: 'Notes',
    disclaimer: 'Non-binding example calculation based on the stated assumptions — not part of the contract. Billing is based solely on actual consumption. All prices are net plus 19 % German VAT.',
    closing: 'We look forward to working with you!',
    closingTeam: 'Your LogYou team',
    page: 'Page',
    of: 'of'
  }
}

// Authoritative filename — never trust the client's.
export const sanitizeForecastFilename = (company: string, date?: string): string => {
  const safe = String(company || 'Kunde')
    .replace(/ä/g, 'ae').replace(/ö/g, 'oe').replace(/ü/g, 'ue')
    .replace(/Ä/g, 'Ae').replace(/Ö/g, 'Oe').replace(/Ü/g, 'Ue')
    .replace(/ß/g, 'ss')
    .replace(/[^A-Za-z0-9-_ ]/g, '')
    .trim()
    .replace(/\s+/g, '-') || 'Kunde'
  const d = date || new Date().toISOString().slice(0, 10)
  return `Prognose_LogYou_${safe}_${d}.pdf`
}

const formatDateLocal = (iso: string, language: string): string => {
  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()}`
}

export const buildForecastDocDefinition = (payload: any, logos: { light: string | null; dark: string | null }) => {
  const language = payload?.meta?.language === 'en' ? 'en' : 'de'
  const T = FORECAST_TEXTS[language]
  const c = payload?.customer || {}
  const display = payload?.display || {}
  const dateIso = payload?.meta?.date || new Date().toISOString().slice(0, 10)
  const scenarios: any[] = (Array.isArray(payload?.scenarios) ? payload.scenarios : []).slice(0, 4)
  const lines: any[] = (Array.isArray(payload?.lines) ? payload.lines : []).filter((l: any) => l?.enabled !== false)
  const totals = payload?.totals || {}
  const showCalc = display.showCalculation !== false
  const landscape = scenarios.length > 2
  const pageWidth = landscape ? A4_LANDSCAPE_WIDTH : A4_PORTRAIT_WIDTH
  const docTitle = String(payload?.title || '').trim() || T.docTitle
  // Landscape has ~400 pt of usable height — tighter rows keep a typical
  // forecast (≈10 positions + subtotals + totals) on ONE page.
  const rowFont = landscape ? 8.5 : 9
  const rowPad = landscape ? 3 : 5

  const euro = (amount: any) =>
    new Intl.NumberFormat(language === 'en' ? 'en-IE' : 'de-DE', { style: 'currency', currency: 'EUR' }).format(Number(amount) || 0)

  const sectionTitle = (text: string) => ({
    stack: [
      { canvas: [{ type: 'rect', x: 0, y: 0, w: 26, h: 3, color: BRAND.orange }] },
      { text: String(text).toUpperCase(), fontSize: 11, bold: true, color: BRAND.navy, characterSpacing: 0.4, margin: [0, 5, 0, 0] }
    ],
    margin: [0, landscape ? 10 : 16, 0, landscape ? 6 : 8]
  })

  const th = (text: string, alignment: string = 'left') => ({ text, style: 'tableHeader', alignment })
  const td = (text: any, opts: any = {}) => ({ text: String(text), fontSize: rowFont, ...opts })
  const amountCells = (values: any, opts: any = {}) =>
    scenarios.map((_s: any, i: number) => td(euro(Array.isArray(values) ? values[i] : 0), { alignment: 'right', bold: true, ...opts }))

  // Special (non-zebra) rows are styled per cell; remember their indexes so the
  // zebra fill skips them.
  const body: any[][] = []
  const styledRows = new Set<number>()
  const pushLine = (line: any) => {
    body.push([
      td(line.displayLabel || line.label || line.key || '—'),
      ...(showCalc ? [td(line.calculation || '', { color: BRAND.grayText, fontSize: rowFont - 0.5 })] : []),
      ...amountCells(line.amounts)
    ])
  }
  const pushSummary = (label: string, values: any, style: 'subtotal' | 'total' | 'extra') => {
    const fill = style === 'total' ? BRAND.navy : style === 'subtotal' ? '#E9EEF3' : null
    const labelCell: any = {
      text: label,
      bold: true,
      fontSize: style === 'total' ? rowFont + 1 : rowFont,
      color: style === 'total' ? BRAND.white : BRAND.navy,
      colSpan: showCalc ? 2 : 1,
      ...(fill ? { fillColor: fill } : {})
    }
    const row: any[] = [labelCell]
    if (showCalc) row.push({ text: '' })
    row.push(...amountCells(values, {
      fontSize: style === 'total' ? rowFont + 1 : rowFont,
      color: style === 'total' ? BRAND.orange : BRAND.navy,
      ...(fill ? { fillColor: fill } : {})
    }))
    if (fill) styledRows.add(body.length + 1) // +1 = header row offset
    body.push(row)
  }

  const variableLines = lines.filter((l: any) => l.basis !== 'fixed')
  const fixedLines = lines.filter((l: any) => l.basis === 'fixed')
  if (display.showSubtotals !== false && variableLines.length && fixedLines.length) {
    variableLines.forEach(pushLine)
    pushSummary(T.variableTotal, totals.variable, 'subtotal')
    fixedLines.forEach(pushLine)
    pushSummary(T.fixedTotal, totals.fixed, 'subtotal')
  } else {
    lines.forEach(pushLine)
  }
  pushSummary(T.total, totals.total, 'total')
  if (display.showPerOrder !== false) pushSummary(T.perOrder, totals.perOrder, 'extra')
  if (display.showYearly === true) pushSummary(T.yearly, totals.yearly, 'extra')

  const amountWidth = landscape ? 92 : (scenarios.length > 1 ? 84 : 110)
  const widths: any[] = ['*', ...(showCalc ? [landscape ? 170 : (scenarios.length > 1 ? 118 : 150)] : []), ...scenarios.map(() => amountWidth)]
  const header = [
    th(T.position),
    ...(showCalc ? [th(T.calculation)] : []),
    ...scenarios.map((s: any) => th(String(s?.heading || s?.label || ''), 'right'))
  ]

  const table = {
    table: { headerRows: 1, widths, body: [header, ...body], dontBreakRows: true },
    layout: {
      hLineWidth: (i: number) => (i === 1 ? 0.75 : 0),
      vLineWidth: () => 0,
      hLineColor: () => BRAND.grayLine,
      fillColor: (rowIndex: number) => {
        if (rowIndex === 0) return BRAND.navy
        if (styledRows.has(rowIndex)) return null
        return rowIndex % 2 === 0 ? BRAND.zebra : null
      },
      paddingLeft: () => 7,
      paddingRight: () => 7,
      paddingTop: () => rowPad,
      paddingBottom: () => rowPad
    }
  }

  const kv = (label: string, value: any) => [
    td(label, { color: BRAND.grayText }),
    td(value || '—', { bold: true })
  ]

  const intro = String(payload?.intro || '').trim() || T.intro
  const notes = String(payload?.notes || '').trim()

  const content: any[] = [
    {
      columns: [
        {
          width: '*',
          stack: [
            { text: c.company || '', bold: true, fontSize: 12, color: BRAND.navy },
            ...(c.contactName ? [{ text: c.contactName, margin: [0, 3, 0, 0] }] : [])
          ]
        },
        {
          width: 240,
          table: {
            widths: [70, '*'],
            body: [
              kv(T.date, formatDateLocal(dateIso, language)),
              kv(T.basis, T.basisValue)
            ]
          },
          layout: {
            hLineWidth: () => 0,
            vLineWidth: () => 0,
            fillColor: () => BRAND.zebra,
            paddingLeft: () => 8,
            paddingRight: () => 8,
            paddingTop: () => 4,
            paddingBottom: () => 4
          }
        }
      ],
      columnGap: 24,
      margin: [0, 4, 0, 0]
    },
    { text: intro, color: BRAND.grayText, margin: [0, landscape ? 8 : 14, 0, 0] },
    sectionTitle(T.tableTitle),
    table
  ]
  if (display.showDisclaimer !== false) {
    content.push({ text: T.disclaimer, fontSize: 7.5, italics: true, color: BRAND.grayText, margin: [0, 6, 0, 0] })
  }
  if (notes) {
    content.push({
      unbreakable: notes.length < 1500,
      stack: [sectionTitle(T.notesTitle), { text: notes, fontSize: 9.5, margin: [0, 2, 0, 0] }]
    })
  }
  content.push({
    unbreakable: true,
    columns: [
      { width: 'auto', canvas: [{ type: 'rect', x: 0, y: 2, w: 3, h: 26, color: BRAND.orange }] },
      {
        width: '*',
        stack: [
          { text: T.closing, bold: true, fontSize: 11, color: BRAND.navy },
          { text: T.closingTeam, fontSize: 9.5, color: BRAND.grayText, margin: [0, 3, 0, 0] }
        ],
        margin: [10, 0, 0, 0]
      }
    ],
    margin: [0, 22, 0, 0]
  })

  return {
    pageSize: 'A4',
    pageOrientation: landscape ? 'landscape' : 'portrait',
    pageMargins: [40, 106, 40, 86],
    info: {
      title: `${docTitle} – ${c.company || ''}`,
      author: 'LogYou GmbH',
      subject: docTitle
    },
    header: buildBrandHeader(docTitle, `logyou.de  ·  ${T.dateWord} ${formatDateLocal(dateIso, language)}`, logos.dark, pageWidth),
    footer: buildBrandFooter(T.page, T.of, pageWidth),
    content,
    styles: {
      tableHeader: { fontSize: 8.5, bold: true, color: BRAND.white }
    },
    defaultStyle: {
      font: 'Roboto',
      fontSize: 9.5,
      color: BRAND.text,
      lineHeight: 1.25
    }
  }
}

/** Build the final PDF buffer + authoritative filename. */
export const generateForecastPdf = async (payload: any): Promise<{ buffer: Buffer; filename: string }> => {
  // Defensive vfs extraction — the export shape varies by pdfmake version/bundler
  // (same block as quotePdf.ts / agbPdf.ts, do not simplify).
  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 docDefinition = buildForecastDocDefinition(payload, logos)
  const filename = sanitizeForecastFilename(payload?.customer?.company, payload?.meta?.date)

  const buffer = await new Promise<Buffer>((resolve, reject) => {
    try {
      pdfMakeInstance.createPdf(docDefinition).getBuffer((buf: Buffer) => resolve(buf))
    } catch (error) {
      reject(error)
    }
  })
  return { buffer, filename }
}

/** Shape-check shared by the preview and send routes. */
export const validateForecastPayload = (body: any): string => {
  if (!String(body?.customer?.company || '').trim()) return 'Company is required'
  if (!Array.isArray(body?.scenarios) || !body.scenarios.length) return 'At least one scenario is required'
  if (!Array.isArray(body?.lines) || !body.lines.some((l: any) => l?.enabled !== false)) return 'At least one active position is required'
  return ''
}

/** Persistable editor state (offer_conditions.forecast) — computed values stripped. */
export const conditionsFromForecastPayload = (p: any) => ({
  language: p?.meta?.language === 'en' ? 'en' : 'de',
  customer: p?.customer || {},
  title: String(p?.title || ''),
  intro: String(p?.intro || ''),
  notes: String(p?.notes || ''),
  scenarios: (Array.isArray(p?.scenarios) ? p.scenarios : []).slice(0, 4).map((s: any) => ({
    label: String(s?.label || ''),
    orders: Number(s?.orders) || 0
  })),
  lines: (Array.isArray(p?.lines) ? p.lines : []).map((l: any) => ({
    key: String(l?.key || 'custom'),
    enabled: l?.enabled !== false,
    label: String(l?.label || ''),
    basis: l?.basis === 'fixed' ? 'fixed' : 'perOrder',
    factor: Number(l?.factor) || 0,
    factorMode: l?.factorMode === 'pct' ? 'pct' : 'x',
    qty: Number(l?.qty) || 0,
    unitPrice: Number(l?.unitPrice) || 0
  })),
  display: p?.display && typeof p.display === 'object' ? p.display : {}
})
