/**
 * SEPA direct debit mandate PDF (pdf-lib).
 *
 * Why pdf-lib and not pdfmake: the mandate must carry NATIVE fillable form
 * fields (AcroForm) for account holder, IBAN, BIC, bank, address, place/date —
 * so the customer can complete it in any PDF viewer or on paper — and pdfmake
 * cannot emit form fields. The signed variant sets the values, draws the
 * signature image, appends a protocol line and flattens the form.
 *
 * One A4 page. Standard Helvetica (WinAnsi) — umlauts fine, exotic glyphs are
 * mapped by `winAnsi()` like in quoteConfirmation.ts.
 */
import { PDFDocument, StandardFonts, rgb, type PDFFont, type PDFPage } from 'pdf-lib'
import { getQuoteLogos, LEGAL_FOOTER_LINES } from './quotePdf'
import { normalizePdfImage, parseDataUrl } from './pdfImages'
import { sepaTexts, type SepaMandateData, type SepaLang } from './sepaTexts'
import { formatIbanDisplay } from '../../../app/utils/iban'

const A4: [number, number] = [595.28, 841.89]
const M = 48                     // page margin
const NAVY = rgb(0.11, 0.196, 0.294)
const ORANGE = rgb(0.945, 0.349, 0.141)
const GRAY = rgb(0.45, 0.5, 0.55)
const LINE = rgb(0.85, 0.87, 0.9)
const FIELD_BG = rgb(0.955, 0.965, 0.98)
const TEXT = rgb(0.13, 0.16, 0.2)

const winAnsi = (s: any) => String(s ?? '')
  .replace(/→/g, '->').replace(/—/g, '-').replace(/–/g, '-')
  .replace(/…/g, '...').replace(/[‘’]/g, "'").replace(/[“”„]/g, '"')
  .replace(/[^\x00-\xFF€]/g, '?')

const wrap = (text: string, font: PDFFont, size: number, maxWidth: number): string[] => {
  const out: string[] = []
  for (const para of String(text || '').split('\n')) {
    const words = para.split(/\s+/).filter(Boolean)
    if (!words.length) { out.push(''); continue }
    let line = ''
    for (const w of words) {
      const probe = line ? `${line} ${w}` : w
      if (font.widthOfTextAtSize(probe, size) <= maxWidth) { line = probe; continue }
      if (line) out.push(line)
      line = w
    }
    if (line) out.push(line)
  }
  return out
}

export interface SepaFilled {
  company?: string
  accountHolder?: string
  street?: string
  zipCity?: string
  country?: string
  iban?: string
  bic?: string
  bank?: string
  place?: string
  date?: string
}

export interface SepaSignature {
  imageDataUrl: string
  signedAt: string          // ISO
  signerName: string
  ip?: string
  token?: string
  sha256OfBlank?: string
}

export interface SepaPdfOptions {
  data: SepaMandateData
  language: SepaLang
  /** Values to place into the fields (portal input / prefill). */
  filled?: SepaFilled
  /** When present: values are set, signature drawn, protocol added, form flattened. */
  signature?: SepaSignature
}

const fmtDateHuman = (iso: string, lang: SepaLang) => {
  const d = new Date(iso)
  if (isNaN(d.getTime())) return iso
  return d.toLocaleString(lang === 'en' ? 'en-GB' : 'de-DE', { timeZone: 'Europe/Berlin', day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' })
}

export const sepaFilename = (language: SepaLang, mandateReference: string, suffix = ''): string => {
  const ref = String(mandateReference || 'Mandat').replace(/[^A-Za-z0-9\-]/g, '_').slice(0, 40)
  return `${language === 'en' ? 'SEPA_Mandate' : 'SEPA-Lastschriftmandat'}_${ref}${suffix}.pdf`
}

export const buildSepaMandatePdf = async (opts: SepaPdfOptions): Promise<Buffer> => {
  const { data } = opts
  const lang: SepaLang = opts.language === 'en' ? 'en' : 'de'
  const t = sepaTexts(lang)
  const filled: SepaFilled = { ...(opts.filled || {}) }
  const pdf = await PDFDocument.create()
  pdf.setTitle(`${t.title} ${data.mandateReference}`)
  pdf.setAuthor(data.creditor.name)
  pdf.setLanguage(lang === 'en' ? 'en-GB' : 'de-DE')
  const font = await pdf.embedFont(StandardFonts.Helvetica)
  const bold = await pdf.embedFont(StandardFonts.HelveticaBold)
  const page = pdf.addPage(A4)
  const W = A4[0]
  const contentW = W - 2 * M
  let y = A4[1] - M

  const text = (s: string, x: number, yy: number, size: number, f: PDFFont = font, color = TEXT) => {
    page.drawText(winAnsi(s), { x, y: yy, size, font: f, color })
  }
  const paragraph = (s: string, size: number, f: PDFFont = font, color = TEXT, lineGap = 1.35): number => {
    const lines = wrap(winAnsi(s), f, size, contentW)
    for (const l of lines) { text(l, M, y, size, f, color); y -= size * lineGap }
    return lines.length
  }

  // ---- header: logo + title
  try {
    const logos = await getQuoteLogos()
    const src = logos.light ? parseDataUrl(logos.light) : null
    if (src?.buffer) {
      const img = await pdf.embedPng(src.buffer)
      const h = 26
      const w = (img.width / img.height) * h
      page.drawImage(img, { x: W - M - w, y: y - h + 6, width: w, height: h })
    }
  } catch { /* logo optional */ }
  text(t.title, M, y - 14, 17, bold, NAVY)
  y -= 30
  text(data.scheme === 'B2B' ? t.schemeB2B : t.schemeCore, M, y, 9.5, font, ORANGE)
  y -= 10
  page.drawLine({ start: { x: M, y }, end: { x: W - M, y }, thickness: 1, color: ORANGE })
  y -= 18

  // ---- creditor block (two columns of key/value)
  const kv = (label: string, value: string, x: number, yy: number, colW: number) => {
    text(label.toUpperCase(), x, yy, 6.5, bold, GRAY)
    const lines = wrap(winAnsi(value), font, 9.5, colW)
    let yy2 = yy - 11
    for (const l of lines) { text(l, x, yy2, 9.5, font, TEXT); yy2 -= 12 }
    return yy - yy2
  }
  text(t.creditor, M, y, 8, bold, NAVY)
  y -= 14
  const colW = (contentW - 16) / 2
  const c = data.creditor
  const leftH = kv(t.creditor, `${c.name}\n${c.street}\n${c.zipCity}${c.country ? `\n${c.country}` : ''}`, M, y, colW)
  const rightY = y
  kv(t.creditorId, c.creditorId || '—', M + colW + 16, rightY, colW)
  kv(t.mandateRef, data.mandateReference, M + colW + 16, rightY - 30, colW)
  kv(t.paymentType, data.recurring ? t.recurring : t.oneOff, M + colW + 16, rightY - 60, colW)
  y -= Math.max(leftH, 90) + 4
  page.drawLine({ start: { x: M, y }, end: { x: W - M, y }, thickness: 0.5, color: LINE })
  y -= 16

  // ---- authorisation + notice
  const auth = data.scheme === 'B2B' ? t.authB2B(c.name) : t.authCore(c.name)
  const notice = data.scheme === 'B2B' ? t.noticeB2B : t.noticeCore
  paragraph(auth, 9.5, font, TEXT)
  y -= 4
  paragraph(notice, 8.5, font, GRAY)
  y -= 4
  paragraph(t.prenote(c.prenotificationDays), 8.5, font, GRAY)
  y -= 10
  page.drawLine({ start: { x: M, y }, end: { x: W - M, y }, thickness: 0.5, color: LINE })
  y -= 16

  // ---- debtor fields (AcroForm)
  text(t.debtor, M, y, 8, bold, NAVY)
  text(t.fillHint, M + 160, y, 7, font, GRAY)
  y -= 8
  const form = pdf.getForm()
  const fieldH = 20
  const field = (name: string, label: string, x: number, yy: number, w: number, value: string) => {
    text(label.toUpperCase(), x, yy - 8, 6.5, bold, GRAY)
    const fy = yy - 8 - fieldH - 3
    page.drawRectangle({ x, y: fy, width: w, height: fieldH, color: FIELD_BG, borderColor: LINE, borderWidth: 0.6 })
    const tf = form.createTextField(`sepa.${name}`)
    tf.addToPage(page, { x: x + 1, y: fy + 1, width: w - 2, height: fieldH - 2, borderWidth: 0, backgroundColor: FIELD_BG, textColor: TEXT, font })
    tf.setFontSize(9.5)   // after addToPage: the widget's /DA entry must exist first
    tf.setText(winAnsi(value || ''))
    return fy
  }
  const rowGap = 12
  let fy = field('company', t.company, M, y, contentW, filled.company || '')
  y = fy - rowGap
  fy = field('accountHolder', t.accountHolder, M, y, contentW, filled.accountHolder || '')
  y = fy - rowGap
  fy = field('street', t.street, M, y, colW, filled.street || '')
  field('zipCity', t.zipCity, M + colW + 16, y, colW, filled.zipCity || '')
  y = fy - rowGap
  fy = field('country', t.country, M, y, colW, filled.country || '')
  field('bank', t.bank, M + colW + 16, y, colW, filled.bank || '')
  y = fy - rowGap
  fy = field('iban', t.iban, M, y, colW + 60, filled.iban ? formatIbanDisplay(filled.iban) : '')
  field('bic', t.bic, M + colW + 76, y, colW - 60, filled.bic || '')
  y = fy - 18

  // ---- place/date + signature
  const sigTop = y
  fy = field('placeDate', t.placeDate, M, sigTop, colW, filled.place || filled.date ? `${filled.place || ''}${filled.place && filled.date ? ', ' : ''}${filled.date || ''}` : '')
  const sigX = M + colW + 16
  const sigBoxH = 58
  text(t.signature.toUpperCase(), sigX, sigTop - 8, 6.5, bold, GRAY)
  page.drawRectangle({ x: sigX, y: sigTop - 8 - sigBoxH - 3, width: colW, height: sigBoxH, color: rgb(1, 1, 1), borderColor: LINE, borderWidth: 0.6 })
  page.drawLine({ start: { x: sigX + 8, y: sigTop - 8 - sigBoxH + 10 }, end: { x: sigX + colW - 8, y: sigTop - 8 - sigBoxH + 10 }, thickness: 0.5, color: GRAY })
  y = Math.min(fy, sigTop - 8 - sigBoxH - 3) - 14

  if (opts.signature) {
    try {
      const norm = await normalizePdfImage(opts.signature.imageDataUrl)
      const src = norm ? parseDataUrl(norm) : null
      if (src?.buffer) {
        const img = /jpe?g/i.test(src.mime) ? await pdf.embedJpg(src.buffer) : await pdf.embedPng(src.buffer)
        const maxW = colW - 20, maxH = sigBoxH - 16
        const scale = Math.min(maxW / img.width, maxH / img.height)
        const w = img.width * scale, h = img.height * scale
        page.drawImage(img, { x: sigX + 10, y: sigTop - 8 - sigBoxH + 12, width: w, height: h })
      }
    } catch (err: any) {
      console.warn('[sepa] signature image skipped:', err?.message || err)
    }
    text(`${opts.signature.signerName} · ${fmtDateHuman(opts.signature.signedAt, lang)}`, sigX + 8, sigTop - 8 - sigBoxH + 1, 6.5, font, GRAY)
  }

  // ---- footer texts
  paragraph(t.footer, 7.5, font, GRAY)
  if (opts.signature) {
    y -= 4
    const proto = lang === 'en'
      ? `Electronically granted via the LogYou portal on ${fmtDateHuman(opts.signature.signedAt, 'en')}${opts.signature.ip ? ` · IP ${opts.signature.ip}` : ''}${opts.signature.token ? ` · Link ${opts.signature.token.slice(0, 12)}…` : ''}${opts.signature.sha256OfBlank ? ` · Template SHA-256 ${opts.signature.sha256OfBlank.slice(0, 16)}…` : ''}`
      : `Elektronisch erteilt über das LogYou-Portal am ${fmtDateHuman(opts.signature.signedAt, 'de')}${opts.signature.ip ? ` · IP ${opts.signature.ip}` : ''}${opts.signature.token ? ` · Link ${opts.signature.token.slice(0, 12)}…` : ''}${opts.signature.sha256OfBlank ? ` · Vorlage SHA-256 ${opts.signature.sha256OfBlank.slice(0, 16)}…` : ''}`
    paragraph(proto, 7, font, NAVY)
  }

  // ---- legal footer (bottom of page)
  let fyy = M - 8
  for (const line of [...LEGAL_FOOTER_LINES].reverse()) {
    text(line, M, fyy, 6.5, font, GRAY)
    fyy += 9
  }
  page.drawLine({ start: { x: M, y: fyy + 2 }, end: { x: W - M, y: fyy + 2 }, thickness: 0.5, color: LINE })

  form.updateFieldAppearances(font)
  if (opts.signature) form.flatten()
  return Buffer.from(await pdf.save())
}
