import { PDFDocument, PDFTextField, PDFCheckBox, PDFDropdown, PDFName } from 'pdf-lib';
import fs from 'fs';
import path from 'path';
import prisma from '../lib/prisma.js';

// ==================== VERFÜGBARE CRM-FELDER ====================

export const CRM_FIELDS = [
  // Kunde
  { path: 'customer.salutation', label: 'Anrede', group: 'Kunde' },
  { path: 'customer.firstName', label: 'Vorname', group: 'Kunde' },
  { path: 'customer.lastName', label: 'Nachname', group: 'Kunde' },
  { path: 'customer.fullName', label: 'Voller Name (Vor- + Nachname)', group: 'Kunde' },
  { path: 'customer.customerNumber', label: 'Kundennummer', group: 'Kunde' },
  { path: 'customer.email', label: 'E-Mail', group: 'Kunde' },
  { path: 'customer.phone', label: 'Telefon', group: 'Kunde' },
  { path: 'customer.mobile', label: 'Mobil', group: 'Kunde' },
  { path: 'customer.birthDate', label: 'Geburtsdatum', group: 'Kunde' },
  { path: 'customer.birthPlace', label: 'Geburtsort', group: 'Kunde' },
  { path: 'customer.companyName', label: 'Firma', group: 'Kunde' },
  { path: 'customer.type', label: 'Kundentyp (Privat/Firma)', group: 'Kunde' },
  { path: 'customer.taxNumber', label: 'Steuernummer', group: 'Kunde' },
  // Adresse (Lieferadresse)
  { path: 'address.street', label: 'Straße', group: 'Adresse' },
  { path: 'address.houseNumber', label: 'Hausnummer', group: 'Adresse' },
  { path: 'address.streetFull', label: 'Straße + Hausnummer', group: 'Adresse' },
  { path: 'address.postalCode', label: 'PLZ', group: 'Adresse' },
  { path: 'address.city', label: 'Stadt', group: 'Adresse' },
  { path: 'address.postalCodeCity', label: 'PLZ + Stadt', group: 'Adresse' },
  { path: 'address.country', label: 'Land', group: 'Adresse' },
  { path: 'address.full', label: 'Vollständige Adresse', group: 'Adresse' },
  // Eigentümer (der Lieferadresse)
  { path: 'owner.company', label: 'Firma', group: 'Eigentümer' },
  { path: 'owner.firstName', label: 'Vorname', group: 'Eigentümer' },
  { path: 'owner.lastName', label: 'Nachname', group: 'Eigentümer' },
  { path: 'owner.fullName', label: 'Vorname Nachname', group: 'Eigentümer' },
  { path: 'owner.companyFirstNameLastName', label: 'Firma + Vorname + Nachname', group: 'Eigentümer' },
  { path: 'owner.companyLastNameFirstName', label: 'Firma + Nachname + Vorname', group: 'Eigentümer' },
  { path: 'owner.companyFirstName', label: 'Firma + Vorname', group: 'Eigentümer' },
  { path: 'owner.companyLastName', label: 'Firma + Nachname', group: 'Eigentümer' },
  { path: 'owner.street', label: 'Straße', group: 'Eigentümer' },
  { path: 'owner.houseNumber', label: 'Hausnummer', group: 'Eigentümer' },
  { path: 'owner.postalCode', label: 'PLZ', group: 'Eigentümer' },
  { path: 'owner.city', label: 'Ort', group: 'Eigentümer' },
  { path: 'owner.phone', label: 'Telefon', group: 'Eigentümer' },
  { path: 'owner.mobile', label: 'Mobil', group: 'Eigentümer' },
  { path: 'owner.email', label: 'E-Mail', group: 'Eigentümer' },
  // Rechnungsadresse (Fallback auf Lieferadresse, wenn keine separate
  // Rechnungsadresse gepflegt ist – siehe billingAddress-Resolve im Code)
  { path: 'billingAddress.street', label: 'Straße (Rechnung)', group: 'Rechnungsadresse' },
  { path: 'billingAddress.houseNumber', label: 'Hausnummer (Rechnung)', group: 'Rechnungsadresse' },
  { path: 'billingAddress.streetFull', label: 'Straße + Nr. (Rechnung)', group: 'Rechnungsadresse' },
  { path: 'billingAddress.postalCode', label: 'PLZ (Rechnung)', group: 'Rechnungsadresse' },
  { path: 'billingAddress.city', label: 'Stadt (Rechnung)', group: 'Rechnungsadresse' },
  { path: 'billingAddress.postalCodeCity', label: 'PLZ + Stadt (Rechnung)', group: 'Rechnungsadresse' },
  { path: 'billingAddress.country', label: 'Land (Rechnung)', group: 'Rechnungsadresse' },
  { path: 'billingAddress.full', label: 'Vollständige Adresse (Rechnung)', group: 'Rechnungsadresse' },
  // Bankverbindung
  { path: 'bankCard.iban', label: 'IBAN', group: 'Bank' },
  { path: 'bankCard.bic', label: 'BIC', group: 'Bank' },
  { path: 'bankCard.bankName', label: 'Bank', group: 'Bank' },
  { path: 'bankCard.accountHolder', label: 'Kontoinhaber', group: 'Bank' },
  // Vertrag
  { path: 'contract.contractNumber', label: 'Vertragsnummer', group: 'Vertrag' },
  { path: 'contract.type', label: 'Vertragstyp', group: 'Vertrag' },
  { path: 'contract.status', label: 'Status', group: 'Vertrag' },
  { path: 'contract.startDate', label: 'Vertragsbeginn', group: 'Vertrag' },
  { path: 'contract.endDate', label: 'Vertragsende', group: 'Vertrag' },
  { path: 'contract.providerName', label: 'Anbieter', group: 'Vertrag' },
  { path: 'contract.tariffName', label: 'Tarif', group: 'Vertrag' },
  { path: 'contract.customerNumberAtProvider', label: 'Kundennr. beim Anbieter', group: 'Vertrag' },
  { path: 'contract.cancellationDate', label: 'Kündigungsdatum', group: 'Vertrag' },
  { path: 'contract.commission', label: 'Provision', group: 'Vertrag' },
  { path: 'contract.platformName', label: 'Vertriebsplattform', group: 'Vertrag' },
  { path: 'contract.notes', label: 'Notizen', group: 'Vertrag' },
  // Altanbieter
  { path: 'contract.previousProviderName', label: 'Altanbieter', group: 'Altanbieter' },
  { path: 'contract.previousCustomerNumber', label: 'Kundennr. Altanbieter', group: 'Altanbieter' },
  // Ausweis
  { path: 'identityDocument.type', label: 'Dokumenttyp', group: 'Ausweis' },
  { path: 'identityDocument.documentNumber', label: 'Ausweisnummer', group: 'Ausweis' },
  { path: 'identityDocument.issuingAuthority', label: 'Ausstellungsbehörde', group: 'Ausweis' },
  { path: 'identityDocument.issueDate', label: 'Ausstellungsdatum', group: 'Ausweis' },
  { path: 'identityDocument.expiryDate', label: 'Gültig bis', group: 'Ausweis' },
  // Zähler (Energie)
  { path: 'meter.meterNumber', label: 'Zählernummer', group: 'Energie' },
  { path: 'meter.type', label: 'Zählertyp (Strom/Gas)', group: 'Energie' },
  { path: 'energyDetails.maloId', label: 'MaLo-ID', group: 'Energie' },
  { path: 'energyDetails.annualConsumption', label: 'Jahresverbrauch', group: 'Energie' },
  { path: 'energyDetails.basePrice', label: 'Grundpreis (€/Monat)', group: 'Energie' },
  { path: 'energyDetails.unitPrice', label: 'Arbeitspreis (€/kWh)', group: 'Energie' },
  { path: 'energyDetails.unitPriceNt', label: 'NT-Arbeitspreis (€/kWh)', group: 'Energie' },
  { path: 'energyDetails.instantBonus', label: 'Sofort-Bonus (€)', group: 'Energie' },
  { path: 'energyDetails.newCustomerBonus', label: 'Neukunden-Bonus (€)', group: 'Energie' },
  { path: 'energyDetails.totalBonus', label: 'Gesamtbonus (€)', group: 'Energie' },
  // Internet/DSL/Glasfaser/Kabel
  { path: 'internetDetails.downloadSpeed', label: 'Download-Speed (Mbit/s)', group: 'Internet' },
  { path: 'internetDetails.uploadSpeed', label: 'Upload-Speed (Mbit/s)', group: 'Internet' },
  { path: 'internetDetails.routerModel', label: 'Router-Modell', group: 'Internet' },
  { path: 'internetDetails.routerSerialNumber', label: 'Router-Seriennummer', group: 'Internet' },
  { path: 'internetDetails.installationDate', label: 'Installationsdatum', group: 'Internet' },
  { path: 'internetDetails.internetUsername', label: 'Internet-Benutzername', group: 'Internet' },
  { path: 'internetDetails.homeId', label: 'Home-ID', group: 'Internet' },
  { path: 'internetDetails.activationCode', label: 'Aktivierungscode', group: 'Internet' },
  { path: 'internetDetails.propertyType', label: 'Objekttyp', group: 'Internet' },
  { path: 'internetDetails.propertyLocation', label: 'Lage', group: 'Internet' },
  { path: 'internetDetails.connectionLocation', label: 'Anschluss-Lage', group: 'Internet' },
  // Mobilfunk
  { path: 'mobileDetails.phoneNumber', label: 'Mobilfunknummer', group: 'Mobilfunk' },
  { path: 'mobileDetails.simCardNumber', label: 'SIM-Kartennummer', group: 'Mobilfunk' },
  { path: 'mobileDetails.dataVolume', label: 'Datenvolumen', group: 'Mobilfunk' },
  { path: 'mobileDetails.deviceName', label: 'Gerätename', group: 'Mobilfunk' },
  { path: 'mobileDetails.deviceImei', label: 'IMEI', group: 'Mobilfunk' },
  // Rufnummern werden dynamisch generiert über getCrmFieldsForTemplate()
  // Stressfrei-Wechseln E-Mail
  { path: 'stressfreiEmail', label: 'Stressfrei-E-Mail (Auswahl beim Generieren)', group: 'Stressfrei-Wechseln' },
  { path: 'stressfreiEmail.password', label: 'Stressfrei-E-Mail Passwort', group: 'Stressfrei-Wechseln' },
  // Sonstiges (individuelle Werte)
  { path: 'today', label: 'Heutiges Datum', group: 'Sonstiges' },
  { path: 'static:true', label: 'Checkbox: Angehakt', group: 'Sonstiges' },
  { path: 'static:false', label: 'Checkbox: Nicht angehakt', group: 'Sonstiges' },
  { path: 'static:X', label: 'Text: X (Kreuz)', group: 'Sonstiges' },
  { path: 'static:Ja', label: 'Text: Ja', group: 'Sonstiges' },
  { path: 'static:Nein', label: 'Text: Nein', group: 'Sonstiges' },
  { path: 'static:SEPA', label: 'Text: SEPA-Lastschrift', group: 'Sonstiges' },
  { path: 'static:Privatkunde', label: 'Text: Privatkunde', group: 'Sonstiges' },
  { path: 'static:Geschäftskunde', label: 'Text: Geschäftskunde', group: 'Sonstiges' },
  { path: 'static:Herr', label: 'Text: Herr', group: 'Sonstiges' },
  { path: 'static:Frau', label: 'Text: Frau', group: 'Sonstiges' },
  { path: 'static:Anbieterwechsel', label: 'Text: Anbieterwechsel', group: 'Sonstiges' },
  { path: 'static:Neuanschluss', label: 'Text: Neuanschluss', group: 'Sonstiges' },
  { path: 'static:Umzug', label: 'Text: Umzug', group: 'Sonstiges' },
  // Freitextfelder (leer lassen zum manuellen Ausfüllen)
  { path: 'manual:1', label: 'Freitext 1 (manuell beim Generieren)', group: 'Freitext' },
  { path: 'manual:2', label: 'Freitext 2 (manuell beim Generieren)', group: 'Freitext' },
  { path: 'manual:3', label: 'Freitext 3 (manuell beim Generieren)', group: 'Freitext' },
  { path: 'manual:4', label: 'Freitext 4 (manuell beim Generieren)', group: 'Freitext' },
  { path: 'manual:5', label: 'Freitext 5 (manuell beim Generieren)', group: 'Freitext' },
];

/**
 * Generiert die vollständige CRM-Feldliste inkl. dynamischer Rufnummern
 */
export function getCrmFieldsForTemplate(maxPhoneFields: number = 8) {
  const phoneFields = [];
  for (let i = 0; i < maxPhoneFields; i++) {
    const n = i + 1;
    phoneFields.push(
      { path: `phoneNumbers[${i}]`, label: `Vorwahl+Rufnummer ${n}`, group: 'Rufnummern' },
      { path: `phoneAreaCode[${i}]`, label: `Vorwahl ${n}`, group: 'Rufnummern' },
      { path: `phoneLocal[${i}]`, label: `Rufnummer ${n} (ohne Vorwahl)`, group: 'Rufnummern' },
    );
  }
  return [...CRM_FIELDS, ...phoneFields];
}

// ==================== PDF-FELDER AUSLESEN ====================

export async function extractPdfFields(pdfPath: string): Promise<{ fields: { name: string; type: string; page: number; y: number }[]; totalPages: number }> {
  const pdfBytes = fs.readFileSync(path.join(process.cwd(), pdfPath));
  const pdfDoc = await PDFDocument.load(pdfBytes);
  const form = pdfDoc.getForm();
  const fields = form.getFields();
  const pages = pdfDoc.getPages();

  const totalPages = pages.length;

  // Page-Refs sammeln: Jede Seite hat eine interne Referenz
  const pageRefs: string[] = pages.map(p => String((p as any).ref));

  const result: { name: string; type: string; page: number; y: number }[] = [];

  for (const field of fields) {
    const widgets = field.acroField.getWidgets();
    let pageIndex = 0;
    let yPos = 0;

    if (widgets.length > 0) {
      const widget = widgets[0];
      const rect = widget.getRectangle();

      // Seitenzuordnung über /P Eintrag im Widget
      try {
        const pRef = widget.dict.get(PDFName.of('P'));
        if (pRef) {
          const pRefStr = String(pRef);
          const idx = pageRefs.indexOf(pRefStr);
          if (idx >= 0) pageIndex = idx;
        }
      } catch { /* ignore */ }

      const pageHeight = pages[pageIndex]?.getHeight() || 842;
      yPos = Math.round(pageHeight - rect.y);
    }

    result.push({
      name: field.getName(),
      type: field.constructor.name.replace('PDF', '').replace('Field', ''),
      page: pageIndex,
      y: yPos,
    });
  }

  // Sortieren: erst nach Seite, dann nach Y-Position (oben nach unten)
  result.sort((a, b) => a.page !== b.page ? a.page - b.page : a.y - b.y);

  return { fields: result, totalPages };
}

/**
 * Generiert eine annotierte PDF-Vorschau: Alle Formularfelder werden mit ihrem
 * Feldnamen als Text befüllt, damit man sieht wo welches Feld ist.
 */
export async function generateAnnotatedPreview(pdfPath: string): Promise<Buffer> {
  const pdfBytes = fs.readFileSync(path.join(process.cwd(), pdfPath));
  const pdfDoc = await PDFDocument.load(pdfBytes);
  const form = pdfDoc.getForm();
  const fields = form.getFields();

  for (const field of fields) {
    const name = field.getName();
    try {
      if (field instanceof PDFTextField) {
        field.setText(`[${name}]`);
      } else if (field instanceof PDFCheckBox) {
        // Checkboxen angehakt lassen damit man sie sieht
        field.check();
      } else if (field instanceof PDFDropdown) {
        // Dropdown-Name als Text nicht möglich, ignorieren
      }
    } catch { /* ignore */ }
  }

  // Alle Felder flatten damit sie als Text sichtbar sind
  form.flatten();

  return Buffer.from(await pdfDoc.save());
}

// ==================== TEMPLATE CRUD ====================

export async function getAllTemplates() {
  return prisma.pdfTemplate.findMany({
    orderBy: { name: 'asc' },
  });
}

export async function getTemplateById(id: number) {
  return prisma.pdfTemplate.findUnique({ where: { id } });
}

export async function createTemplate(data: {
  name: string;
  description?: string;
  providerName?: string;
  templatePath: string;
  originalName: string;
  fieldMapping?: string;
  phoneFieldPrefix?: string;
  maxPhoneFields?: number;
}) {
  return prisma.pdfTemplate.create({
    data: {
      name: data.name,
      description: data.description,
      providerName: data.providerName,
      templatePath: data.templatePath,
      originalName: data.originalName,
      fieldMapping: data.fieldMapping || '{}',
      phoneFieldPrefix: data.phoneFieldPrefix,
      maxPhoneFields: data.maxPhoneFields ?? 8,
    },
  });
}

export async function updateTemplate(id: number, data: {
  name?: string;
  description?: string;
  providerName?: string;
  fieldMapping?: string;
  phoneFieldPrefix?: string;
  maxPhoneFields?: number;
  isActive?: boolean;
}) {
  return prisma.pdfTemplate.update({
    where: { id },
    data,
  });
}

export async function deleteTemplate(id: number) {
  const template = await prisma.pdfTemplate.findUnique({ where: { id } });
  if (template?.templatePath) {
    const filePath = path.join(process.cwd(), template.templatePath);
    if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
  }
  return prisma.pdfTemplate.delete({ where: { id } });
}

// ==================== PDF GENERIEREN ====================

/**
 * Generiert ein ausgefülltes PDF aus einem Template + Kundendaten
 */
/**
 * Ermittelt welche Felder beim Generieren manuell eingegeben werden müssen
 */
export async function getRequiredInputs(templateId: number, contractId: number): Promise<{
  needsStressfreiEmail: boolean;
  stressfreiEmails: { id: number; email: string }[];
  manualFields: { key: string; pdfFieldName: string }[];
}> {
  const template = await prisma.pdfTemplate.findUnique({ where: { id: templateId } });
  if (!template) throw new Error('Vorlage nicht gefunden');

  const mapping: Record<string, string> = JSON.parse(template.fieldMapping || '{}');

  const needsStressfreiEmail = Object.values(mapping).some(v => v.startsWith('stressfreiEmail'));
  const manualFields = Object.entries(mapping)
    .filter(([_, v]) => v.startsWith('manual:'))
    .map(([pdfFieldName, v]) => ({ key: v, pdfFieldName }));

  let stressfreiEmails: { id: number; email: string }[] = [];
  if (needsStressfreiEmail) {
    const contract = await prisma.contract.findUnique({
      where: { id: contractId },
      select: { customerId: true },
    });
    if (contract) {
      const emails = await prisma.stressfreiEmail.findMany({
        where: { customerId: contract.customerId, isActive: true },
        select: { id: true, email: true },
      });
      stressfreiEmails = emails;
    }
  }

  return { needsStressfreiEmail, stressfreiEmails, manualFields };
}

export async function generateFilledPdf(
  templateId: number,
  contractId: number,
  extras?: {
    stressfreiEmailId?: number;
    manualValues?: Record<string, string>;
  }
): Promise<Buffer> {
  const template = await prisma.pdfTemplate.findUnique({ where: { id: templateId } });
  if (!template) throw new Error('Vorlage nicht gefunden');

  // Vertrag mit allen Relationen laden
  const contract = await prisma.contract.findUnique({
    where: { id: contractId },
    include: {
      customer: true,
      address: true,
      billingAddress: true,
      bankCard: true,
      identityDocument: true,
      provider: true,
      tariff: true,
      salesPlatform: true,
      energyDetails: { include: { meter: true } },
      internetDetails: { include: { phoneNumbers: true } },
      mobileDetails: { include: { simCards: true } },
      stressfreiEmail: true,
    },
  });

  if (!contract) throw new Error('Vertrag nicht gefunden');

  // Fallback: Wenn keine Bankverbindung am Vertrag, erste aktive des Kunden nehmen
  let bankCard = contract.bankCard;
  if (!bankCard && contract.customer) {
    bankCard = await prisma.bankCard.findFirst({
      where: { customerId: contract.customer.id, isActive: true },
      orderBy: { createdAt: 'desc' },
    });
  }

  // Daten-Kontext aufbauen
  const formatDate = (d: Date | null | undefined) => {
    if (!d) return '';
    return new Date(d).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' });
  };

  const typeLabels: Record<string, string> = { ELECTRICITY: 'Strom', GAS: 'Gas', DSL: 'DSL', CABLE: 'Kabelinternet', FIBER: 'Glasfaser', MOBILE: 'Mobilfunk', TV: 'TV', CAR_INSURANCE: 'KFZ-Versicherung' };
  const docTypeLabels: Record<string, string> = { ID_CARD: 'Personalausweis', PASSPORT: 'Reisepass', DRIVERS_LICENSE: 'Führerschein', OTHER: 'Sonstiges' };

  const addr = contract.address;
  // Wenn keine separate Rechnungsadresse hinterlegt ist, fällt der Wert auf
  // die Lieferadresse zurück – konsistent mit der Kundenakte-Logik
  // (Contract.billingAddressId NULL = "Wie Lieferadresse"). Damit füllen
  // Auftragsformulare die Rechnungs-Felder nicht mehr leer aus, wenn der
  // Anbieter eine identische Adresse erwartet.
  const bAddr = contract.billingAddress ?? contract.address;

  const dataContext: Record<string, string> = {
    // Kunde
    'customer.salutation': contract.customer?.salutation || '',
    'customer.firstName': contract.customer?.firstName || '',
    'customer.lastName': contract.customer?.lastName || '',
    'customer.fullName': `${contract.customer?.firstName || ''} ${contract.customer?.lastName || ''}`.trim(),
    'customer.customerNumber': contract.customer?.customerNumber || '',
    'customer.email': contract.customer?.email || '',
    'customer.phone': contract.customer?.phone || '',
    'customer.mobile': contract.customer?.mobile || '',
    'customer.birthDate': formatDate(contract.customer?.birthDate),
    'customer.birthPlace': contract.customer?.birthPlace || '',
    'customer.companyName': contract.customer?.companyName || '',
    'customer.type': contract.customer?.type === 'BUSINESS' ? 'Geschäftskunde' : 'Privatkunde',
    'customer.taxNumber': contract.customer?.taxNumber || '',
    // Adresse
    'address.street': addr?.street || '',
    'address.houseNumber': addr?.houseNumber || '',
    'address.streetFull': addr ? `${addr.street} ${addr.houseNumber}` : '',
    'address.postalCode': addr?.postalCode || '',
    'address.city': addr?.city || '',
    'address.postalCodeCity': addr ? `${addr.postalCode} ${addr.city}` : '',
    'address.country': addr?.country || '',
    'address.full': addr ? `${addr.street} ${addr.houseNumber}, ${addr.postalCode} ${addr.city}` : '',
    // Eigentümer (aus Lieferadresse, Fallback: Kundendaten)
    'owner.company': addr?.ownerCompany || contract.customer?.companyName || '',
    'owner.firstName': addr?.ownerFirstName || contract.customer?.firstName || '',
    'owner.lastName': addr?.ownerLastName || contract.customer?.lastName || '',
    'owner.fullName': addr?.ownerFirstName
      ? `${addr.ownerFirstName} ${addr.ownerLastName || ''}`.trim()
      : `${contract.customer?.firstName || ''} ${contract.customer?.lastName || ''}`.trim(),
    'owner.companyFirstNameLastName': [addr?.ownerCompany || contract.customer?.companyName, addr?.ownerFirstName || contract.customer?.firstName, addr?.ownerLastName || contract.customer?.lastName].filter(Boolean).join(' '),
    'owner.companyLastNameFirstName': [addr?.ownerCompany || contract.customer?.companyName, addr?.ownerLastName || contract.customer?.lastName, addr?.ownerFirstName || contract.customer?.firstName].filter(Boolean).join(' '),
    'owner.companyFirstName': [addr?.ownerCompany || contract.customer?.companyName, addr?.ownerFirstName || contract.customer?.firstName].filter(Boolean).join(' '),
    'owner.companyLastName': [addr?.ownerCompany || contract.customer?.companyName, addr?.ownerLastName || contract.customer?.lastName].filter(Boolean).join(' '),
    'owner.street': addr?.ownerStreet || addr?.street || '',
    'owner.houseNumber': addr?.ownerHouseNumber || addr?.houseNumber || '',
    'owner.postalCode': addr?.ownerPostalCode || addr?.postalCode || '',
    'owner.city': addr?.ownerCity || addr?.city || '',
    'owner.phone': addr?.ownerPhone || contract.customer?.phone || '',
    'owner.mobile': addr?.ownerMobile || contract.customer?.mobile || '',
    'owner.email': addr?.ownerEmail || contract.customer?.email || '',
    // Rechnungsadresse
    'billingAddress.street': bAddr?.street || '',
    'billingAddress.houseNumber': bAddr?.houseNumber || '',
    'billingAddress.streetFull': bAddr ? `${bAddr.street} ${bAddr.houseNumber}` : '',
    'billingAddress.postalCode': bAddr?.postalCode || '',
    'billingAddress.city': bAddr?.city || '',
    'billingAddress.postalCodeCity': bAddr ? `${bAddr.postalCode} ${bAddr.city}` : '',
    'billingAddress.country': bAddr?.country || '',
    'billingAddress.full': bAddr ? `${bAddr.street} ${bAddr.houseNumber}, ${bAddr.postalCode} ${bAddr.city}` : '',
    // Bank
    'bankCard.iban': bankCard?.iban || '',
    'bankCard.bic': bankCard?.bic || '',
    'bankCard.bankName': bankCard?.bankName || '',
    'bankCard.accountHolder': bankCard?.accountHolder || '',
    // Vertrag
    'contract.contractNumber': contract.contractNumber || '',
    'contract.type': typeLabels[contract.type] || contract.type,
    'contract.status': contract.status || '',
    'contract.startDate': formatDate(contract.startDate),
    'contract.endDate': formatDate(contract.endDate),
    'contract.providerName': contract.providerName || contract.provider?.name || '',
    'contract.tariffName': contract.tariffName || contract.tariff?.name || '',
    'contract.customerNumberAtProvider': contract.customerNumberAtProvider || '',
    'contract.cancellationDate': formatDate((contract as any).cancellationDate),
    'contract.commission': contract.commission?.toString() || '',
    'contract.platformName': contract.salesPlatform?.name || '',
    'contract.notes': contract.notes || '',
    'contract.previousProviderName': contract.energyDetails?.previousProviderName || '',
    'contract.previousCustomerNumber': contract.energyDetails?.previousCustomerNumber || '',
    // Ausweis
    'identityDocument.type': docTypeLabels[contract.identityDocument?.type || ''] || '',
    'identityDocument.documentNumber': contract.identityDocument?.documentNumber || '',
    'identityDocument.issuingAuthority': contract.identityDocument?.issuingAuthority || '',
    'identityDocument.issueDate': formatDate(contract.identityDocument?.issueDate),
    'identityDocument.expiryDate': formatDate(contract.identityDocument?.expiryDate),
    // Energie
    'meter.meterNumber': contract.energyDetails?.meter?.meterNumber || '',
    'meter.type': contract.energyDetails?.meter?.type === 'ELECTRICITY' ? 'Strom' : contract.energyDetails?.meter?.type === 'GAS' ? 'Gas' : '',
    'energyDetails.maloId': contract.energyDetails?.maloId || '',
    'energyDetails.annualConsumption': contract.energyDetails?.annualConsumption?.toString() || '',
    'energyDetails.basePrice': contract.energyDetails?.basePrice?.toString() || '',
    'energyDetails.unitPrice': contract.energyDetails?.unitPrice?.toString() || '',
    'energyDetails.unitPriceNt': contract.energyDetails?.unitPriceNt?.toString() || '',
    'energyDetails.instantBonus': contract.energyDetails?.instantBonus?.toString() || '',
    'energyDetails.newCustomerBonus': contract.energyDetails?.newCustomerBonus?.toString() || '',
    'energyDetails.totalBonus': (
      ((contract.energyDetails?.instantBonus ?? 0) + (contract.energyDetails?.newCustomerBonus ?? 0)) || ''
    ).toString(),
    // Internet
    'internetDetails.downloadSpeed': contract.internetDetails?.downloadSpeed?.toString() || '',
    'internetDetails.uploadSpeed': contract.internetDetails?.uploadSpeed?.toString() || '',
    'internetDetails.routerModel': contract.internetDetails?.routerModel || '',
    'internetDetails.routerSerialNumber': contract.internetDetails?.routerSerialNumber || '',
    'internetDetails.installationDate': formatDate(contract.internetDetails?.installationDate),
    'internetDetails.internetUsername': contract.internetDetails?.internetUsername || '',
    'internetDetails.homeId': contract.internetDetails?.homeId || '',
    'internetDetails.activationCode': contract.internetDetails?.activationCode || '',
    'internetDetails.propertyType': contract.internetDetails?.propertyType || '',
    'internetDetails.propertyLocation': contract.internetDetails?.propertyLocation || '',
    'internetDetails.connectionLocation': contract.internetDetails?.connectionLocation || '',
    // Mobilfunk
    'mobileDetails.phoneNumber': contract.mobileDetails?.phoneNumber || '',
    'mobileDetails.simCardNumber': contract.mobileDetails?.simCardNumber || '',
    'mobileDetails.dataVolume': contract.mobileDetails?.dataVolume?.toString() || '',
    'mobileDetails.deviceName': contract.mobileDetails?.deviceImei || '',
    'mobileDetails.deviceImei': contract.mobileDetails?.deviceImei || '',
    // Sonstiges
    'today': new Date().toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' }),
    // Statische Werte (für Checkboxen/Radio/individuelle Felder)
    'static:true': 'true',
    'static:false': 'false',
    'static:X': 'X',
    'static:Ja': 'Ja',
    'static:Nein': 'Nein',
    'static:SEPA': 'SEPA-Lastschrift',
    'static:Privatkunde': 'Privatkunde',
    'static:Geschäftskunde': 'Geschäftskunde',
    'static:Herr': 'Herr',
    'static:Frau': 'Frau',
    'static:Anbieterwechsel': 'Anbieterwechsel',
    'static:Neuanschluss': 'Neuanschluss',
    'static:Umzug': 'Umzug',
  };

  // Stressfrei-E-Mail
  if (extras?.stressfreiEmailId) {
    const sfEmail = await prisma.stressfreiEmail.findUnique({
      where: { id: extras.stressfreiEmailId },
      select: { email: true, emailPasswordEncrypted: true },
    });
    if (sfEmail) {
      dataContext['stressfreiEmail'] = sfEmail.email;
      if (sfEmail.emailPasswordEncrypted) {
        try {
          const { decrypt } = await import('../utils/encryption.js');
          dataContext['stressfreiEmail.password'] = decrypt(sfEmail.emailPasswordEncrypted);
        } catch { /* ignore */ }
      }
    }
  } else if (contract.stressfreiEmail) {
    dataContext['stressfreiEmail'] = contract.stressfreiEmail.email;
  }

  // Freitext-Felder (manuell beim Generieren eingegeben)
  if (extras?.manualValues) {
    for (const [key, value] of Object.entries(extras.manualValues)) {
      dataContext[key] = value;
    }
  }

  // Rufnummern + Vorwahl-Extraktion
  const phoneNumbers = contract.internetDetails?.phoneNumbers || [];

  /**
   * Extrahiert Vorwahl und Rufnummer aus einer deutschen Festnetznummer.
   * z.B. "04941 123456" → { areaCode: "04941", local: "123456" }
   * z.B. "04941/123456" → { areaCode: "04941", local: "123456" }
   * z.B. "04941-123456" → { areaCode: "04941", local: "123456" }
   */
  const splitPhoneNumber = (phone: string): { areaCode: string; local: string } => {
    if (!phone) return { areaCode: '', local: '' };
    const cleaned = phone.replace(/[()]/g, '').trim();
    // Trennzeichen: Leerzeichen, /, -
    const match = cleaned.match(/^(\d{2,5})[\/\s\-](.+)$/);
    if (match) return { areaCode: match[1], local: match[2].replace(/[\s\-\/]/g, '') };
    // Kein Trennzeichen: Versuche deutsche Vorwahl-Muster (2-5 Stellen nach 0)
    const numMatch = cleaned.match(/^(0\d{1,4})(\d{3,})$/);
    if (numMatch) return { areaCode: numMatch[1], local: numMatch[2] };
    return { areaCode: '', local: cleaned };
  };

  const maxFields = template.maxPhoneFields || 8;
  for (let i = 0; i < Math.max(maxFields, phoneNumbers.length); i++) {
    const entry = phoneNumbers[i];
    const fullNumber = entry?.phoneNumber || '';
    // Bevorzugt den explizit gepflegten areaCode aus der DB (verlässlich),
    // fällt sonst auf die Heuristik zurück (Altbestand ohne separates
    // Vorwahl-Feld). `phoneLocal` analog: aus phoneNumber abgeleitet,
    // wenn areaCode da → den Vorwahl-Prefix abschneiden, sonst Heuristik.
    let areaCode = '';
    let local = '';
    if (entry?.areaCode) {
      areaCode = entry.areaCode;
      const split = splitPhoneNumber(fullNumber);
      // Wenn der heuristische areaCode mit dem DB-Wert übereinstimmt,
      // ist der heuristische local-Anteil korrekt – sonst pragmatisch:
      // alles nach dem areaCode-Prefix bis zum Ende
      if (split.areaCode === entry.areaCode) {
        local = split.local;
      } else {
        const stripped = fullNumber.replace(new RegExp('^' + entry.areaCode.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '[\\s\\-/]*'), '').trim();
        local = stripped || split.local;
      }
    } else {
      const split = splitPhoneNumber(fullNumber);
      areaCode = split.areaCode;
      local = split.local;
    }
    dataContext[`phoneNumbers[${i}]`] = fullNumber;
    dataContext[`phoneAreaCode[${i}]`] = areaCode;
    dataContext[`phoneLocal[${i}]`] = local;
  }

  // PDF laden und befüllen
  const pdfBytes = fs.readFileSync(path.join(process.cwd(), template.templatePath));
  const pdfDoc = await PDFDocument.load(pdfBytes);
  const form = pdfDoc.getForm();

  // Feld-Mapping anwenden
  const mapping: Record<string, string> = JSON.parse(template.fieldMapping || '{}');

  for (const [pdfFieldName, crmFieldPath] of Object.entries(mapping)) {
    const value = dataContext[crmFieldPath] || '';
    try {
      const field = form.getField(pdfFieldName);
      if (field instanceof PDFTextField) {
        field.setText(value);
      } else if (field instanceof PDFCheckBox) {
        if (value === 'true' || value === 'Ja' || value === '1') {
          field.check();
        }
      } else if (field instanceof PDFDropdown) {
        if (value) field.select(value);
      }
    } catch {
      // Feld nicht gefunden - überspringen
    }
  }

  // Rufnummern-Overflow: Extra-Seite wenn nötig
  const maxPhoneFields = template.maxPhoneFields || 8;
  const overflowNumbers = phoneNumbers.slice(maxPhoneFields);

  if (overflowNumbers.length > 0) {
    // Neue Seite für zusätzliche Rufnummern
    const page = pdfDoc.addPage();
    const { width, height } = page.getSize();
    const font = await pdfDoc.embedFont('Helvetica' as any);
    const boldFont = await pdfDoc.embedFont('Helvetica-Bold' as any);

    let y = height - 50;
    page.drawText('Weitere Rufnummern zur Portierung', { x: 50, y, size: 14, font: boldFont });
    y -= 25;
    page.drawText(`Kunde: ${contract.customer?.firstName} ${contract.customer?.lastName} (${contract.customer?.customerNumber})`, { x: 50, y, size: 10, font });
    y -= 15;
    page.drawText(`Vertrag: ${contract.contractNumber}`, { x: 50, y, size: 10, font });
    y -= 30;

    for (let i = 0; i < overflowNumbers.length; i++) {
      page.drawText(`${maxPhoneFields + i + 1}. ${overflowNumbers[i].phoneNumber}`, { x: 50, y, size: 11, font });
      y -= 20;
      if (y < 50) {
        // Nächste Seite
        const newPage = pdfDoc.addPage();
        y = newPage.getSize().height - 50;
      }
    }
  }

  // Nur zugeordnete Felder flatten (nicht editierbar machen)
  // Nicht zugeordnete Felder bleiben editierbar zum manuellen Ausfüllen
  for (const pdfFieldName of Object.keys(mapping)) {
    try {
      const field = form.getField(pdfFieldName);
      if (field instanceof PDFTextField) {
        field.enableReadOnly();
      }
    } catch { /* Feld nicht gefunden */ }
  }

  return Buffer.from(await pdfDoc.save());
}
