import { Injectable, Logger } from '@nestjs/common';
import { ExpertProfileService } from './expert-profile.service';
import PDFDocument from 'pdfkit';
import * as fs from 'fs';
import * as path from 'path';
import { PNG } from 'pngjs';
import sharp from 'sharp';
import {
  Document,
  Packer,
  Paragraph,
  TextRun,
  Table,
  TableRow,
  TableCell,
  WidthType,
  BorderStyle,
  AlignmentType,
  ImageRun,
  HeadingLevel,
  ShadingType,
} from 'docx';

// ============================================================
// Typen für Export-Daten
// ============================================================
interface ExportProject {
  fromMonth: number;
  fromYear: number;
  toMonth: number | null;
  toYear: number | null;
  isCurrent: boolean;
  role: string;
  tasks: string | null;
  company: string | null;
  companySize: string | null;
  industry: string | null;
}

interface ExportCertification {
  title: string;
  issuingBody: string;
  website: string | null;
  issueYear: number;
}

interface ExportExperience {
  area: string;
  years: number;
  level: string | null;
}

interface ExportLanguage {
  language: string;
  level: string;
}

interface ExportAttachment {
  id: string;
  filename: string;
  mimetype: string;
  size: number;
  data: string;
}

interface ExportData {
  firstName: string;
  lastName: string;
  email: string;
  phone: string | null;
  mobile: string | null;
  street: string | null;
  postalCode: string | null;
  city: string | null;
  avatar: string | null;
  expertProfile: {
    skills: string[];
    experiences: ExportExperience[];
    languages: ExportLanguage[];
    projects: ExportProject[];
    certifications: ExportCertification[];
    attachments: ExportAttachment[];
  } | null;
}

// ============================================================
// Farb-Hilfsfunktionen
// ============================================================
function hexToRgb(hex: string): { r: number; g: number; b: number } {
  const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
  return result
    ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) }
    : { r: 0, g: 150, b: 136 };
}

function lightenColor(hex: string, factor: number): string {
  const { r, g, b } = hexToRgb(hex);
  const lr = Math.round(r + (255 - r) * factor);
  const lg = Math.round(g + (255 - g) * factor);
  const lb = Math.round(b + (255 - b) * factor);
  return `#${lr.toString(16).padStart(2, '0')}${lg.toString(16).padStart(2, '0')}${lb.toString(16).padStart(2, '0')}`;
}

// ============================================================
// ProfileExportService
// ============================================================
@Injectable()
export class ProfileExportService {
  private readonly logger = new Logger(ProfileExportService.name);

  // Pfad zu den Icon-Assets (relativ zum Working Directory /app im Container)
  private readonly iconsDir = path.resolve(process.cwd(), 'assets', 'icons');

  constructor(private readonly expertProfileService: ExpertProfileService) {}

  private loadIcon(name: string, color?: string): Buffer | null {
    try {
      const iconPath = path.join(this.iconsDir, name);
      if (fs.existsSync(iconPath)) {
        const raw = fs.readFileSync(iconPath);
        if (color) {
          return this.recolorPng(raw, color);
        }
        return raw;
      }
      this.logger.warn(`Icon nicht gefunden: ${iconPath}`);
    } catch (err) {
      this.logger.warn(`Icon konnte nicht geladen werden: ${name}`, err);
    }
    return null;
  }

  /** Ersetzt alle sichtbaren Pixel eines PNG durch die angegebene Farbe (Alpha bleibt erhalten) */
  private recolorPng(pngBuffer: Buffer, hexColor: string): Buffer {
    const { r, g, b } = hexToRgb(hexColor);
    const png = PNG.sync.read(pngBuffer);

    for (let i = 0; i < png.data.length; i += 4) {
      const alpha = png.data[i + 3];
      if (alpha > 0) {
        png.data[i] = r;
        png.data[i + 1] = g;
        png.data[i + 2] = b;
        // Alpha bleibt unverändert
      }
    }

    return PNG.sync.write(png);
  }

  /** Erzeugt ein rundes PNG aus einem beliebigen Bild-Buffer (JPEG, PNG, etc.) */
  private async makeCircularAvatar(imageBuffer: Buffer, size: number): Promise<Buffer> {
    const circleSvg = Buffer.from(
      `<svg width="${size}" height="${size}"><circle cx="${size / 2}" cy="${size / 2}" r="${size / 2}" fill="white"/></svg>`,
    );

    return sharp(imageBuffer)
      .resize(size, size, { fit: 'cover' })
      .composite([{ input: circleSvg, blend: 'dest-in' }])
      .png()
      .toBuffer();
  }

  // ============================================================
  // Hilfsfunktionen: Text-Bereinigung
  // ============================================================

  /** Entfernt Markdown-Marker (**bold**, *italic*, __underline__) aus Text */
  private stripMarkdown(text: string): string {
    return text
      .replace(/\*\*([^*\n]+)\*\*/g, '$1')  // **fett** → Text
      .replace(/\*([^*\n]+)\*/g, '$1')       // *kursiv* → Text
      .replace(/__([^_\n]+)__/g, '$1');      // __unterstrichen__ → Text
  }

  /** Normalisiert eine Aufgaben-Zeile: entfernt bestehende Bullet/Nummer-Präfixe */
  private normalizeTaskLine(line: string): string {
    return this.stripMarkdown(line.trim())
      .replace(/^[•\u2022]\s*/, '')           // vorhandenes Bullet entfernen
      .replace(/^\d+\.\s+/, '')               // vorhandene Nummerierung entfernen
      .trim();
  }

  // ============================================================
  // PDF Export
  // ============================================================
  async generatePdf(userId: string, accentColor = '#009688'): Promise<{ buffer: Buffer; firstName: string; lastName: string }> {
    const data = await this.expertProfileService.getExportData(userId) as ExportData;
    const profile = data.expertProfile;
    const fullName = `${data.firstName} ${data.lastName}`;
    const { firstName, lastName } = data;

    return new Promise<{ buffer: Buffer; firstName: string; lastName: string }>((resolve, reject) => {
      const chunks: Buffer[] = [];
      const doc = new PDFDocument({
        size: 'A4',
        margins: { top: 40, bottom: 40, left: 40, right: 40 },
        bufferPages: true,
      });

      doc.on('data', (chunk: Buffer) => chunks.push(chunk));
      doc.on('end', () => resolve({ buffer: Buffer.concat(chunks), firstName, lastName }));
      doc.on('error', reject);

      // --- Konstanten ---
      const pageWidth = 595.28;
      const leftColWidth = 180;
      const leftColX = 40;
      const rightColX = leftColX + leftColWidth + 20;
      const rightColWidth = pageWidth - rightColX - 40;
      const pageBottom = 800;

      let yLeft = 40;
      let yRight = 40;

      // --- SEITE 1: Linke Spalte ---

      // Avatar (rundes Bild)
      if (data.avatar) {
        try {
          const avatarBuffer = this.base64ToBuffer(data.avatar);
          const avatarSize = 110;
          const centerX = leftColX + leftColWidth / 2;
          const centerY = yLeft + avatarSize / 2;

          doc.save();
          doc.circle(centerX, centerY, avatarSize / 2).clip();
          doc.image(avatarBuffer, centerX - avatarSize / 2, yLeft, {
            width: avatarSize,
            height: avatarSize,
          });
          doc.restore();
          yLeft += avatarSize + 15;
        } catch (err) {
          this.logger.warn('Avatar konnte nicht geladen werden', err);
          yLeft += 10;
        }
      }

      // Name
      doc.font('Helvetica-Bold').fontSize(16).fillColor('#333333');
      doc.text(fullName, leftColX, yLeft, { width: leftColWidth, align: 'center' });
      yLeft += doc.heightOfString(fullName, { width: leftColWidth }) + 5;

      // Rolle (erste Erfahrung als Titel)
      if (profile && profile.experiences.length > 0) {
        const mainRole = profile.experiences[0].area;
        doc.font('Helvetica').fontSize(10).fillColor(accentColor);
        doc.text(mainRole, leftColX, yLeft, { width: leftColWidth, align: 'center' });
        yLeft += doc.heightOfString(mainRole, { width: leftColWidth }) + 8;
      }

      // Akzentlinie
      doc.moveTo(leftColX + 20, yLeft).lineTo(leftColX + leftColWidth - 20, yLeft)
        .strokeColor(accentColor).lineWidth(2).stroke();
      yLeft += 15;

      // --- KONTAKT ---
      yLeft = this.pdfSectionTitle(doc, 'KONTAKT', leftColX, yLeft, leftColWidth, accentColor);

      // Icons laden (eingefärbt in Akzentfarbe)
      const phoneIcon = this.loadIcon('Phone.png', accentColor);
      const mobileIcon = this.loadIcon('Mobile.png', accentColor);
      const mailIcon = this.loadIcon('Mail.png', accentColor);
      const addressIcon = this.loadIcon('Address.png', accentColor);
      const iconSize = 9;
      const iconTextOffset = 14; // Abstand Icon → Text

      if (data.phone) {
        if (phoneIcon) doc.image(phoneIcon, leftColX, yLeft + 1, { width: iconSize, height: iconSize });
        yLeft = this.pdfContactText(doc, data.phone, leftColX + iconTextOffset, yLeft, leftColWidth - iconTextOffset);
      }
      if (data.mobile) {
        if (mobileIcon) doc.image(mobileIcon, leftColX, yLeft + 1, { width: iconSize, height: iconSize });
        yLeft = this.pdfContactText(doc, data.mobile, leftColX + iconTextOffset, yLeft, leftColWidth - iconTextOffset);
      }
      if (data.email) {
        if (mailIcon) doc.image(mailIcon, leftColX, yLeft + 1, { width: iconSize, height: iconSize });
        yLeft = this.pdfContactText(doc, data.email, leftColX + iconTextOffset, yLeft, leftColWidth - iconTextOffset);
      }
      if (data.street || data.city) {
        if (addressIcon) doc.image(addressIcon, leftColX, yLeft + 1, { width: iconSize, height: iconSize });
        const line1 = data.street || '';
        const line2 = [data.postalCode, data.city].filter(Boolean).join(' ');
        const addressText = [line1, line2].filter(Boolean).join('\n');
        yLeft = this.pdfContactText(doc, addressText, leftColX + iconTextOffset, yLeft, leftColWidth - iconTextOffset);
      }
      yLeft += 10;

      // --- SPRACHEN ---
      if (profile && profile.languages.length > 0) {
        yLeft = this.pdfSectionTitle(doc, 'SPRACHEN', leftColX, yLeft, leftColWidth, accentColor);
        for (const lang of profile.languages) {
          doc.font('Helvetica').fontSize(9).fillColor('#333333');
          doc.text(lang.language, leftColX, yLeft, { width: leftColWidth * 0.55, continued: false });
          doc.font('Helvetica').fontSize(8).fillColor('#777777');
          doc.text(lang.level, leftColX + leftColWidth * 0.6, yLeft, { width: leftColWidth * 0.4 });
          yLeft += 11;
        }
        yLeft += 4;
      }

      // --- ERFAHRUNG (Expertise-Bereiche) ---
      if (profile && profile.experiences.length > 0) {
        yLeft = this.pdfSectionTitle(doc, 'ERFAHRUNG', leftColX, yLeft, leftColWidth, accentColor);
        for (const exp of profile.experiences) {
          doc.font('Helvetica').fontSize(9).fillColor('#333333');
          doc.text(exp.area, leftColX, yLeft, { width: leftColWidth * 0.65 });
          doc.font('Helvetica').fontSize(8).fillColor('#777777');
          const expDetail = `${exp.years}J.${exp.level ? ' · ' + exp.level : ''}`;
          doc.text(expDetail, leftColX + leftColWidth * 0.65, yLeft, { width: leftColWidth * 0.35 });
          yLeft += 11;
        }
      }

      // --- ZERTIFIZIERUNGEN (linke Spalte, nach Erfahrungen) ---
      if (profile && profile.certifications.length > 0) {
        yLeft += 6;
        yLeft = this.pdfSectionTitle(doc, 'ZERTIFIZIERUNGEN', leftColX, yLeft, leftColWidth, accentColor);

        const certTimelineX = leftColX + 6;
        const certContentX = leftColX + 14;
        const certContentWidth = leftColWidth - 14;

        for (let i = 0; i < profile.certifications.length; i++) {
          const cert = profile.certifications[i];
          const certEntryStartY = yLeft;

          // Timeline-Punkt
          doc.circle(certTimelineX, certEntryStartY + 4, 3.5).fill(accentColor);

          // Jahr (fett)
          doc.font('Helvetica-Bold').fontSize(8).fillColor('#555555');
          doc.text(String(cert.issueYear), certContentX, yLeft, { width: certContentWidth });
          yLeft += 11;

          // Titel
          doc.font('Helvetica-Bold').fontSize(9).fillColor(accentColor);
          doc.text(cert.title, certContentX, yLeft, { width: certContentWidth });
          yLeft += doc.heightOfString(cert.title, { width: certContentWidth }) + 2;

          // Zertifizierungsstelle
          doc.font('Helvetica').fontSize(8).fillColor('#555555');
          doc.text(cert.issuingBody, certContentX, yLeft, { width: certContentWidth });
          yLeft += 11;

          yLeft += 4;

          // Timeline-Linie (korrekte Länge)
          if (i < profile.certifications.length - 1) {
            doc.moveTo(certTimelineX, certEntryStartY + 8).lineTo(certTimelineX, yLeft - 4)
              .strokeColor(accentColor).lineWidth(1).stroke();
          }
        }
      }

      // --- FÄHIGKEITEN (linke Spalte, nach Zertifizierungen) ---
      if (profile && profile.skills.length > 0) {
        yLeft += 6;
        yLeft = this.pdfSectionTitle(doc, 'FÄHIGKEITEN', leftColX, yLeft, leftColWidth, accentColor);
        const skillStartX = leftColX;
        const skillMaxX = leftColX + leftColWidth;
        let skillX = skillStartX;
        let skillY = yLeft;
        const skillChipH = 16;
        const skillChipPad = 5;
        const skillChipGap = 4;
        const skillBg = lightenColor(accentColor, 0.85);
        for (const skill of profile.skills) {
          doc.font('Helvetica').fontSize(7);
          const tw = doc.widthOfString(skill);
          const cw = tw + skillChipPad * 2;
          if (skillX + cw > skillMaxX) {
            skillX = skillStartX;
            skillY += skillChipH + skillChipGap;
          }
          doc.roundedRect(skillX, skillY, cw, skillChipH, 7).fill(skillBg);
          doc.font('Helvetica').fontSize(7).fillColor(accentColor);
          doc.text(skill, skillX + skillChipPad, skillY + 4, { width: tw, lineBreak: false });
          skillX += cw + skillChipGap;
        }
        yLeft = skillY + skillChipH + 4;
      }

      // --- SEITE 1: Rechte Spalte — BERUFSERFAHRUNG ---
      if (profile && profile.projects.length > 0) {
        yRight = this.pdfSectionTitle(doc, 'BERUFSERFAHRUNG / PROJEKTE', rightColX, yRight, rightColWidth, accentColor);

        const timelineX = rightColX + 6;
        const contentX = rightColX + 18;
        const contentWidth = rightColWidth - 18;

        for (let i = 0; i < profile.projects.length; i++) {
          const proj = profile.projects[i];

          // Seitenumbruch prüfen
          if (yRight > pageBottom) {
            doc.addPage();
            yRight = 40;
            yRight = this.pdfSectionTitle(doc, 'BERUFSERFAHRUNG / PROJEKTE (Forts.)', rightColX, yRight, rightColWidth, accentColor);
          }

          let pageBreakOccurred = false;
          const entryStartY = yRight;

          // Timeline-Punkt
          doc.circle(timelineX, entryStartY + 4, 3.5).fill(accentColor);

          // Zeitraum (fett, größer)
          const dateRange = this.formatDateRange(proj.fromMonth, proj.fromYear, proj.toMonth, proj.toYear, proj.isCurrent);
          doc.font('Helvetica-Bold').fontSize(10).fillColor('#555555');
          doc.text(dateRange, contentX, yRight, { width: contentWidth });
          yRight += 14;

          // Rolle
          doc.font('Helvetica-Bold').fontSize(10).fillColor(accentColor);
          doc.text(proj.role, contentX, yRight, { width: contentWidth });
          yRight += doc.heightOfString(proj.role, { width: contentWidth }) + 2;

          // Firma + Branche (fett)
          if (proj.company) {
            const companyLine = [proj.company, proj.industry].filter(Boolean).join(' · ');
            doc.font('Helvetica-Bold').fontSize(9).fillColor('#333333');
            doc.text(companyLine, contentX, yRight, { width: contentWidth });
            yRight += doc.heightOfString(companyLine, { width: contentWidth }) + 2;
          }

          // Aufgaben (Bullet nur wenn Original-Zeile ein Aufzählungszeichen hat)
          if (proj.tasks) {
            const taskLines = proj.tasks.split('\n').filter((l: string) => l.trim());
            doc.font('Helvetica').fontSize(8).fillColor('#444444');
            for (const task of taskLines) {
              if (yRight > pageBottom) {
                doc.addPage();
                yRight = 40;
                pageBreakOccurred = true;
              }
              const raw = task.trim();
              const hasBullet = /^[•\u2022]\s/.test(raw) || /^\d+\.\s/.test(raw);
              const cleaned = this.normalizeTaskLine(raw);
              if (!cleaned) continue;
              const displayText = hasBullet ? `\u2022 ${cleaned}` : cleaned;
              doc.text(displayText, contentX + 4, yRight, { width: contentWidth - 8 });
              yRight += doc.heightOfString(displayText, { width: contentWidth - 8 }) + 1;
            }
          }

          yRight += 12;

          // Timeline-Linie (korrekte Länge basiert auf tatsächlicher Höhe)
          if (i < profile.projects.length - 1 && !pageBreakOccurred) {
            doc.moveTo(timelineX, entryStartY + 8).lineTo(timelineX, yRight - 4)
              .strokeColor(accentColor).lineWidth(1).stroke();
          }
        }
      }

      // --- ANHÄNGE (Bild-Anhänge als zusätzliche Seiten) ---
      const attachments = profile?.attachments ?? [];
      for (const att of attachments) {
        if (!att.mimetype.startsWith('image/')) continue;
        doc.addPage();
        let yAtt = 40;
        yAtt = this.pdfSectionTitle(doc, `ANLAGE: ${att.filename}`, 40, yAtt, pageWidth - 80, accentColor);
        try {
          const imgBuffer = this.base64ToBuffer(att.data);
          const maxW = pageWidth - 80;
          const maxH = 680;
          doc.image(imgBuffer, 40, yAtt, { fit: [maxW, maxH], align: 'center' });
        } catch (err) {
          this.logger.warn(`Anhang-Bild konnte nicht gerendert werden: ${att.filename}`, err);
          doc.font('Helvetica').fontSize(9).fillColor('#777777');
          doc.text(`(Bild konnte nicht dargestellt werden: ${att.filename})`, 40, yAtt, { width: pageWidth - 80 });
        }
      }

      doc.end();
    });
  }

  // ============================================================
  // DOCX Export
  // ============================================================
  async generateDocx(userId: string, accentColor = '#009688'): Promise<{ buffer: Buffer; firstName: string; lastName: string }> {
    const data = await this.expertProfileService.getExportData(userId) as ExportData;
    const profile = data.expertProfile;
    const fullName = `${data.firstName} ${data.lastName}`;
    const accentHex = accentColor.replace('#', '');
    const lightAccent = lightenColor(accentColor, 0.85).replace('#', '');

    const sections: Paragraph[] = [];

    // --- Kontakt-Infos für linke Spalte ---
    const leftParagraphs: Paragraph[] = [];

    // Avatar (rund zugeschnitten)
    let avatarImageRun: ImageRun | null = null;
    if (data.avatar) {
      try {
        const avatarRaw = this.base64ToBuffer(data.avatar);
        const avatarBuffer = await this.makeCircularAvatar(avatarRaw, 240);
        avatarImageRun = new ImageRun({
          data: avatarBuffer,
          transformation: { width: 120, height: 120 },
          type: 'png',
        });
      } catch (err) {
        this.logger.warn('Avatar für DOCX konnte nicht geladen werden', err);
      }
    }

    if (avatarImageRun) {
      leftParagraphs.push(
        new Paragraph({
          children: [avatarImageRun],
          alignment: AlignmentType.CENTER,
          spacing: { after: 200 },
        }),
      );
    }

    // Name
    leftParagraphs.push(
      new Paragraph({
        children: [
          new TextRun({
            text: fullName,
            bold: true,
            size: 28,
            color: '333333',
          }),
        ],
        alignment: AlignmentType.CENTER,
        spacing: { after: 100 },
      }),
    );

    // Rolle
    if (profile && profile.experiences.length > 0) {
      leftParagraphs.push(
        new Paragraph({
          children: [
            new TextRun({
              text: profile.experiences[0].area,
              size: 20,
              color: accentHex,
            }),
          ],
          alignment: AlignmentType.CENTER,
          spacing: { after: 200 },
        }),
      );
    }

    // Kontakt-Sektion
    leftParagraphs.push(this.docxSectionHeading('KONTAKT', accentHex));

    // Icons für DOCX laden (eingefärbt in Akzentfarbe)
    const docxPhoneIcon = this.loadIcon('Phone.png', accentColor);
    const docxMobileIcon = this.loadIcon('Mobile.png', accentColor);
    const docxMailIcon = this.loadIcon('Mail.png', accentColor);
    const docxAddressIcon = this.loadIcon('Address.png', accentColor);
    const docxIconSize = 12;

    if (data.phone) {
      const children: (ImageRun | TextRun)[] = [];
      if (docxPhoneIcon) {
        children.push(new ImageRun({ data: docxPhoneIcon, transformation: { width: docxIconSize, height: docxIconSize }, type: 'png' }));
      }
      children.push(new TextRun({ text: '  ' + data.phone, size: 16, color: '555555' }));
      leftParagraphs.push(new Paragraph({ children, spacing: { after: 60 } }));
    }
    if (data.mobile) {
      const children: (ImageRun | TextRun)[] = [];
      if (docxMobileIcon) {
        children.push(new ImageRun({ data: docxMobileIcon, transformation: { width: docxIconSize, height: docxIconSize }, type: 'png' }));
      }
      children.push(new TextRun({ text: '  ' + data.mobile, size: 16, color: '555555' }));
      leftParagraphs.push(new Paragraph({ children, spacing: { after: 60 } }));
    }
    if (data.email) {
      const children: (ImageRun | TextRun)[] = [];
      if (docxMailIcon) {
        children.push(new ImageRun({ data: docxMailIcon, transformation: { width: docxIconSize, height: docxIconSize }, type: 'png' }));
      }
      children.push(new TextRun({ text: '  ' + data.email, size: 16, color: '555555' }));
      leftParagraphs.push(new Paragraph({ children, spacing: { after: 60 } }));
    }
    if (data.street || data.city) {
      const children: (ImageRun | TextRun)[] = [];
      if (docxAddressIcon) {
        children.push(new ImageRun({ data: docxAddressIcon, transformation: { width: docxIconSize, height: docxIconSize + 2 }, type: 'png' }));
      }
      if (data.street) {
        children.push(new TextRun({ text: '  ' + data.street, size: 16, color: '555555' }));
      }
      const cityLine = [data.postalCode, data.city].filter(Boolean).join(' ');
      if (cityLine) {
        children.push(new TextRun({ text: '      ' + cityLine, size: 16, color: '555555', break: 1 }));
      }
      leftParagraphs.push(new Paragraph({ children, spacing: { after: 60 } }));
    }

    // Sprachen
    if (profile && profile.languages.length > 0) {
      leftParagraphs.push(this.docxSectionHeading('SPRACHEN', accentHex));
      for (const lang of profile.languages) {
        leftParagraphs.push(
          new Paragraph({
            children: [
              new TextRun({ text: lang.language, size: 18, color: '333333' }),
              new TextRun({ text: `  ${lang.level}`, size: 16, color: '777777' }),
            ],
            spacing: { after: 40 },
          }),
        );
      }
    }

    // Erfahrung (Expertise-Bereiche)
    if (profile && profile.experiences.length > 0) {
      leftParagraphs.push(this.docxSectionHeading('ERFAHRUNG', accentHex));
      for (const exp of profile.experiences) {
        const detail = `${exp.years} J.${exp.level ? ' · ' + exp.level : ''}`;
        leftParagraphs.push(
          new Paragraph({
            children: [
              new TextRun({ text: exp.area, size: 18, color: '333333' }),
              new TextRun({ text: `  ${detail}`, size: 16, color: '777777' }),
            ],
            spacing: { after: 40 },
          }),
        );
      }
    }

    // --- Rechte Spalte: Berufserfahrung ---
    const rightParagraphs: Paragraph[] = [];

    if (profile && profile.projects.length > 0) {
      rightParagraphs.push(this.docxSectionHeading('BERUFSERFAHRUNG', accentHex));

      for (const proj of profile.projects) {
        const dateRange = this.formatDateRange(proj.fromMonth, proj.fromYear, proj.toMonth, proj.toYear, proj.isCurrent);

        rightParagraphs.push(
          new Paragraph({
            children: [
              new TextRun({ text: dateRange, size: 16, color: '555555', bold: true }),
            ],
            spacing: { before: 160, after: 40 },
          }),
        );

        rightParagraphs.push(
          new Paragraph({
            children: [
              new TextRun({ text: proj.role, bold: true, size: 20, color: accentHex }),
            ],
            spacing: { after: 30 },
          }),
        );

        if (proj.company) {
          const companyLine = [proj.company, proj.industry].filter(Boolean).join(' · ');
          rightParagraphs.push(
            new Paragraph({
              children: [
                new TextRun({ text: companyLine, size: 18, color: '333333', bold: true }),
              ],
              spacing: { after: 40 },
            }),
          );
        }

        if (proj.tasks) {
          const taskLines = proj.tasks.split('\n').filter((l: string) => l.trim());
          for (const task of taskLines) {
            const raw = task.trim();
            const hasBullet = /^[•\u2022]\s/.test(raw) || /^\d+\.\s/.test(raw);
            const cleaned = this.normalizeTaskLine(raw);
            if (!cleaned) continue;
            const displayText = hasBullet ? `\u2022 ${cleaned}` : cleaned;
            rightParagraphs.push(
              new Paragraph({
                children: [
                  new TextRun({ text: displayText, size: 16, color: '444444' }),
                ],
                spacing: { after: 20 },
              }),
            );
          }
        }
      }
    }

    // --- Zertifizierungen (rechte Spalte) ---
    if (profile && profile.certifications.length > 0) {
      rightParagraphs.push(this.docxSectionHeading('ZERTIFIZIERUNGEN', accentHex));

      for (const cert of profile.certifications) {
        rightParagraphs.push(
          new Paragraph({
            children: [
              new TextRun({ text: String(cert.issueYear), size: 16, color: '555555', bold: true }),
            ],
            spacing: { before: 120, after: 30 },
          }),
        );

        rightParagraphs.push(
          new Paragraph({
            children: [
              new TextRun({ text: cert.title, bold: true, size: 20, color: accentHex }),
            ],
            spacing: { after: 20 },
          }),
        );

        rightParagraphs.push(
          new Paragraph({
            children: [
              new TextRun({ text: cert.issuingBody, size: 18, color: '555555' }),
            ],
            spacing: { after: 60 },
          }),
        );
      }
    }

    // --- Tabelle (Zwei-Spalten-Layout) ---
    const noBorders = {
      top: { style: BorderStyle.NONE, size: 0, color: 'FFFFFF' },
      bottom: { style: BorderStyle.NONE, size: 0, color: 'FFFFFF' },
      left: { style: BorderStyle.NONE, size: 0, color: 'FFFFFF' },
      right: { style: BorderStyle.NONE, size: 0, color: 'FFFFFF' },
    };

    const layoutTable = new Table({
      rows: [
        new TableRow({
          children: [
            new TableCell({
              children: leftParagraphs,
              width: { size: 30, type: WidthType.PERCENTAGE },
              borders: noBorders,
            }),
            new TableCell({
              children: rightParagraphs.length > 0 ? rightParagraphs : [new Paragraph('')],
              width: { size: 70, type: WidthType.PERCENTAGE },
              borders: noBorders,
            }),
          ],
        }),
      ],
      width: { size: 100, type: WidthType.PERCENTAGE },
    });

    // --- Volle Breite: Fähigkeiten (Skills als Chips) ---
    if (profile && profile.skills.length > 0) {
      sections.push(this.docxSectionHeading('FÄHIGKEITEN', accentHex));

      // Skills als inline TextRuns mit Shading
      const skillRuns: TextRun[] = [];
      for (let i = 0; i < profile.skills.length; i++) {
        skillRuns.push(
          new TextRun({
            text: ` ${profile.skills[i]} `,
            size: 16,
            color: accentHex,
            shading: { type: ShadingType.CLEAR, color: 'auto', fill: lightAccent },
          }),
        );
        if (i < profile.skills.length - 1) {
          skillRuns.push(new TextRun({ text: '  ', size: 16 }));
        }
      }

      sections.push(
        new Paragraph({
          children: skillRuns,
          spacing: { before: 100, after: 100 },
        }),
      );
    }

    // --- Dokument zusammenstellen ---
    const document = new Document({
      styles: {
        default: {
          document: {
            run: {
              font: 'Arial',
            },
          },
        },
      },
      sections: [
        {
          properties: {
            page: {
              margin: { top: 720, bottom: 720, left: 720, right: 720 },
            },
          },
          children: [layoutTable, ...sections],
        },
      ],
    });

    const buffer = await Packer.toBuffer(document);
    this.logger.log(`DOCX generiert für User ${userId}: ${buffer.length} Bytes`);
    return { buffer, firstName: data.firstName, lastName: data.lastName };
  }

  // ============================================================
  // PDF-Hilfsfunktionen
  // ============================================================

  private pdfSectionTitle(
    doc: PDFKit.PDFDocument,
    title: string,
    x: number,
    y: number,
    width: number,
    accentColor: string,
  ): number {
    doc.font('Helvetica-Bold').fontSize(10).fillColor('#333333');
    doc.text(title, x, y);
    y += 14;
    doc.moveTo(x, y).lineTo(x + width, y)
      .strokeColor(accentColor).lineWidth(2).stroke();
    y += 8;
    return y;
  }

  private pdfContactText(
    doc: PDFKit.PDFDocument,
    text: string,
    x: number,
    y: number,
    width: number,
  ): number {
    doc.font('Helvetica').fontSize(8).fillColor('#555555');
    doc.text(text, x, y, { width });
    const textHeight = doc.heightOfString(text, { width });
    return y + Math.max(textHeight, 10) + 3;
  }

  // ============================================================
  // DOCX-Hilfsfunktionen
  // ============================================================

  private docxSectionHeading(title: string, accentHex: string): Paragraph {
    return new Paragraph({
      children: [
        new TextRun({
          text: title,
          bold: true,
          size: 22,
          color: '333333',
        }),
      ],
      heading: HeadingLevel.HEADING_2,
      spacing: { before: 300, after: 80 },
      border: {
        bottom: { style: BorderStyle.SINGLE, size: 3, color: accentHex, space: 4 },
      },
    });
  }

  // ============================================================
  // Utility
  // ============================================================

  private formatDateRange(
    fromMonth: number,
    fromYear: number,
    toMonth: number | null,
    toYear: number | null,
    isCurrent: boolean,
  ): string {
    const from = `${String(fromMonth).padStart(2, '0')}/${fromYear}`;
    if (isCurrent) return `${from} - heute`;
    if (toMonth && toYear) return `${from} - ${String(toMonth).padStart(2, '0')}/${toYear}`;
    return from;
  }

  private base64ToBuffer(dataUrl: string): Buffer {
    // Entferne Data-URL-Prefix (z.B. "data:image/png;base64,")
    const base64Data = dataUrl.includes(',') ? dataUrl.split(',')[1] : dataUrl;
    return Buffer.from(base64Data, 'base64');
  }
}
