<?php
error_reporting(E_ALL & ~E_DEPRECATED);

require('../config.php');
require('../EpiApi.php');
require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../vendor/tecnickcom/tcpdf/tcpdf.php';
date_default_timezone_set('Europe/Berlin');
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
$deviceNumber = (!empty($_GET['nr'])) ? '--' . trim($_GET['nr']) : '';
 
$Epi = new Epirent();
$product =json_decode($Epi->requestEpiApi('/v1/product/'.$id.'?cl=' . Epirent_Mandant))->payload[0];
$productImage =json_decode($Epi->requestEpiApi('/v1/product/image/'.$id.'?cl=' . Epirent_Mandant))->payload[0]->image_data;


function s(?string $v): string { return trim((string)$v); }
function fnum(?float $v, int $dec = 2): string {
  if ($v === null) return '';
  $n = number_format((float)$v, $dec, ',', '.');
  return rtrim(rtrim($n, '0'), ',');
}

//Set Variables
$productName   = $product->name;
$productNumber = $product->product_no; 

$bruttoWeightSum = $product->tech_data->weight_gro;

$bundle = buildBundleForProduct($Epi, $product);

/** Test-Daten 
*/
$dimsBHT     =$product->tech_data->width_gro." x ".$product->tech_data->depth_gro." x ".$product->tech_data->height_gro;
$volume    = ((float)$product->tech_data->height_gro * (float)$product->tech_data->width_gro * (float)$product->tech_data->depth_gro)* 0.000001;
$storageLoc  = $product->product_data->storage_location_nearby;

/** Layout */
$PAGE_SIZE      = 'A4';
$MARGIN_LEFT    = 8;
$MARGIN_TOP     = 8;
$MARGIN_RIGHT   = 8;
$MARGIN_BOTTOM  = 10;

$CELL_PAD       = 1.6;
$LINE_GRAY      = '#efefef';

$LEVEL_MIN = 1; $LEVEL_MAX = 5;
$INDENT_PX_PER_LEVEL = 10;
$BASE_FONT_PX        = 10;
$FONT_STEP_PX        = 1.5;



/** TCPDF */
$pdf = new TCPDF('P', 'mm', $PAGE_SIZE, true, 'UTF-8', false);

// 1) Pfade sauber
$fontDir = realpath(__DIR__ . '/../src/assets/font') . '/';
$tcpdfFontsDir = defined('K_PATH_FONTS') ? K_PATH_FONTS : (dirname((new \ReflectionClass('TCPDF'))->getFileName()) . '/fonts/');

// 2) Rechte (einmalig sicherstellen)
// -> hast du schon gefixt

// 3) TTFs registrieren und Rückgabewerte merken
$fontReg  = file_exists($fontDir.'Hurme3/HurmeGeometricSans3-Regular.ttf')
          ? TCPDF_FONTS::addTTFfont($fontDir.'Hurme3/HurmeGeometricSans3-Regular.ttf', 'TrueTypeUnicode', '', 96)
          : false;
$fontBold = file_exists($fontDir.'Hurme1/HurmeGeometricSans1-Bold.ttf')
          ? TCPDF_FONTS::addTTFfont($fontDir.'Hurme1/HurmeGeometricSans1-Bold.ttf', 'TrueTypeUnicode', '', 96)
          : false;
$fontItal = file_exists($fontDir.'Hurme3/HurmeGeometricSans3-Italic.ttf')
          ? TCPDF_FONTS::addTTFfont($fontDir.'Hurme3/HurmeGeometricSans3-Italic.ttf', 'TrueTypeUnicode', '', 96)
          : false;
$fontBI   = file_exists($fontDir.'Hurme1/HurmeGeometricSans1-BoldItalic.ttf')
          ? TCPDF_FONTS::addTTFfont($fontDir.'Hurme1/HurmeGeometricSans1-BoldItalic.ttf', 'TrueTypeUnicode', '', 96)
          : false;

// 4) Fallback setzen
$baseFont = $fontReg ?: 'dejavusans';

// 5) Header/Footer und Defaults
$pdf->setHeaderFont([$baseFont, '', 10]);
$pdf->setFooterFont([$baseFont, '', 9]);
$pdf->SetDefaultMonospacedFont('courier');

// 6) *** AB JETZT KEIN dejavusans MEHR SETZEN! ***
$pdf->SetFont($baseFont, '', 10.5);



$pdf->SetCreator('VT-Media EpiWebview');
$pdf->SetAuthor('VT-Media');
$pdf->SetTitle('Kistenetikett small'.$productName);
$pdf->SetSubject('Kistenetikett small');

$pdf->setPrintHeader(false);
$pdf->setPrintFooter(false);
$pdf->SetMargins($MARGIN_LEFT, $MARGIN_TOP, $MARGIN_RIGHT);
$pdf->SetAutoPageBreak(true, $MARGIN_BOTTOM);
$pdf->setImageScale(1.0);
$pdf->setFontSubsetting(true);
$pdf->setCellPadding($CELL_PAD);

$pdf->AddPage();




// --- LOGO in fester Box mit Rahmen, zentriert & verzerrungsfrei ---
$logoPath = __DIR__ . Labelprint_Logopath;

$startX = $pdf->GetX();
$startY = $pdf->GetY();

// feste Rahmen-Größe (in mm)
$frameW = 20.0;  // Gesamtbreite des Rahmens
$frameH = 9.8;  // Gesamthöhe des Rahmens
$frameOffsetX = 1.4; // Abstand vom linken Margin (wie vorher genutzt)
$frameOffsetY = 1.5; // Abstand von oben (wie vorher genutzt)

// Rahmen-Position
$frameX = $startX + $frameOffsetX;
$frameY = $startY + $frameOffsetY;

// Rahmen zeichnen
$pdf->SetLineWidth(0.3);
$pdf->Rect($frameX, $frameY, $frameW, $frameH, 'D');

// Logo innerhalb des Rahmens mit Innenabstand
$innerPad = 1.2; // mm Luft im Rahmen
$maxW = $frameW - 2 * $innerPad;
$maxH = $frameH - 2 * $innerPad;

if (is_file($logoPath)) {
    // Seitenverhältnis ermitteln
    $imgWpx = $imgHpx = 0;
    if (@list($imgWpx, $imgHpx) = @getimagesize($logoPath)) {
        $ratio = ($imgHpx > 0) ? ($imgWpx / $imgHpx) : 1.0;

        // proportional in den Rahmen einpassen
        if ($maxW / $maxH > $ratio) {
            $drawH = $maxH;
            $drawW = $maxH * $ratio;
        } else {
            $drawW = $maxW;
            $drawH = $maxW / $ratio;
        }

        // zentrieren
        $imgX = $frameX + ($frameW - $drawW) / 2;
        $imgY = $frameY + ($frameH - $drawH) / 2;

        // ausgeben
        $pdf->Image($logoPath, $imgX, $imgY, $drawW, $drawH, '', '', '', false, 300);
    }
}





$afterLogoX = $frameX + $frameW - 1.4;        // gleicher Abstand wie zuvor, jetzt basierend auf Rahmenbreite
$afterLogoBottomY = $frameY + $frameH;        // falls du unten bündig etwas brauchst
$pdf->SetXY($afterLogoX, $startY);            // Cursor rechts neben dem Logo positionieren




// Feste Box rechts neben dem Logo (direkt bündig)
$addrBoxW = 0.0;   // feste Breite (mm)
$addrBoxH = 0.0;   // feste Höhe  (mm)
$addrPad  = 0.5;    // Innenabstand Inhalt → Rahmen (mm)

// bündig rechts neben dem Logo-Rahmen starten
$addrX = $frameX + $frameW;     // kein zusätzlicher Abstand
$addrY = $startY+1.5;               // gleiche Oberkante wie Logo




$pdf->StopTransform();

// Cursor steht bereits auf: $pdf->SetXY($addrX + $addrBoxW, $addrY);

// 1) HTML OHNE border (sonst Doppelrahmen)
$productionHtml = '
<div style="line-height:1.3; font-size:4px; margin:0; padding:0;">
  <b>Produktion</b><br/><br><br><br>
</div>';

// 2) Feste Box rechts neben der Adress-Box
$prodX = $addrX + $addrBoxW;                 // bündig rechts neben Adresse
$prodY = $addrY;                             // gleiche Oberkante
// Breite: restliche Seitenbreite bis zum rechten Rand
$prodW = 27;
// Höhe: fix (anpassen nach Wunsch)
$prodH = 10.0;                               // mm
$prodPad = 0;                              // Innenabstand

// 3) EIN sichtbarer Rahmen zeichnen
$pdf->SetLineWidth(0.3);
$pdf->Rect($prodX, $prodY, $prodW, $prodH, 'D');

// 4) Inhalt in die Box schreiben und clippen (Überhang abgeschnitten)
$pdf->StartTransform();
$pdf->Rect($prodX, $prodY, $prodW, $prodH, 'CNZ'); // Clip auf Box setzen

$pdf->writeHTMLCell(
    $prodW - 2*$prodPad,  // feste Breite des Inhalts
    $prodH - 2*$prodPad,  // feste Höhe des Inhalts
    $prodX + $prodPad,    // x mit Innenabstand
    $prodY + $prodPad,    // y mit Innenabstand
    $productionHtml,      // Inhalt (ohne border!)
    0,                    // kein Border (Rahmen haben wir schon)
    0,                    // ln
    false,                // fill
    true,                 // reseth
    'L',                  // align
    true                  // autopadding
);

$pdf->StopTransform();

// 5) (Optional) Cursor direkt rechts neben der Box positionieren
$pdf->SetXY($prodX + $prodW, $prodY);

// === Name-Box: feste Größe, Text bricht, kein Doppelrahmen ===

// Breite: gesamte Restbreite bis zum rechten Rand (unter Adresse + Produktion)
$nameX    = $frameX; // beginnt rechts neben dem Logo/Adressbereich
$nameW    = 67.1;

// Höhe & Padding der Box (anpassen nach Wunsch)
$nameBoxH = 8;  // mm - feste Höhe
$namePad  = 0.4;   // mm - Innenabstand

// Y: direkt unter die höhere der beiden Boxen (Adresse vs. Produktion)
$yAfterRow = max($addrY + $addrBoxH, $prodY + $prodH) + 0.0; // +2 mm Abstand
$nameY     = $yAfterRow;

// Inhalt OHNE border (sonst Doppelrahmen)
$nameHtml = '
  <div style="font-size:8px; font-weight:700; line-height:1.3; margin:0; padding:0;">' . htmlspecialchars($productName) . '
  </div>';

// 1) Sichtbaren Rahmen zeichnen (feste Größe)
$pdf->SetLineWidth(0.3);
$pdf->Rect($nameX, $nameY, $nameW, $nameBoxH, 'D');

// 2) Inhalt in die Box schreiben und clippen
$pdf->StartTransform();
$pdf->Rect($nameX, $nameY, $nameW, $nameBoxH, 'CNZ'); // Clip auf Box setzen

$pdf->writeHTMLCell(
    $nameW - 2*$namePad,     // feste Breite für den Text
    $nameBoxH - 2*$namePad,  // feste Höhe für den Text
    $nameX + $namePad,       // x mit Innenabstand
    $nameY + $namePad,       // y mit Innenabstand
    $nameHtml,
    0,                       // kein HTML-Border
    0,                       // ln
    false,                   // fill
    true,                    // reseth
    'L',                     // align
    true                     // autopadding
);

$pdf->StopTransform();

// Optional: Cursor unter die Box setzen (falls du danach im Fluss weitermachen willst)
$pdf->SetXY($nameX, $nameY + $nameBoxH);

// 1) Box-Geometrie (immer gleich groß)
$pnBoxW   = 20.0;   // mm – Gesamtbreite der Box
$pnBoxH   = 10.0;   // mm – Gesamthöhe der Box
$pnBoxPad = 0.2;    // mm – Innenabstand innerhalb der Box

// Position der Box (aktuell am rechten Rand oben; passe frei an)
$pnBoxX = 56.5; // bündig am rechten Rand
$pnBoxY = $startY+1.5;                                        // gleiche Oberkante wie Kopf

// 2) Sichtbaren Rahmen zeichnen (einmalig)
$pdf->SetLineWidth(0.3);
$pdf->Rect($pnBoxX, $pnBoxY, $pnBoxW, $pnBoxH, 'D');

// 3) Clipping aktivieren, damit Inhalt in der Box bleibt
$pdf->StartTransform();
$pdf->Rect($pnBoxX, $pnBoxY, $pnBoxW, $pnBoxH, 'CNZ'); // Clip auf die Box

// 4) Produktnummer: frei verschiebbar innerhalb der Box
$pnCombined = trim($productNumber . $deviceNumber);
$pnText = htmlspecialchars($pnCombined !== '' ? $pnCombined : 'N/A');

// Offsets innerhalb der Box – hier stellst du die Position ein
$pnTextOffsetX = -2.9;   // mm von linker Innenkante
$pnTextOffsetY = 2;   // mm von oberer Innenkante
$pnTextW       = 0;   // nutzbare Breite
$pnTextH       = 8.0;    // feste Höhe für den PN-Textbereich (mm)

$pnHtml = '
  <div style="font-size:8px; line-height:1.25; margin:0; padding:0;">
    '.$pnText.'
  </div>';

// PN-Text rendern (ohne HTML-Border!)
$pdf->writeHTMLCell(
    $pnTextW,                                // Breite Textbereich
    $pnTextH,                                // Höhe Textbereich
    $pnBoxX + $pnBoxPad + $pnTextOffsetX,    // X in der Box
    $pnBoxY + $pnBoxPad + $pnTextOffsetY,    // Y in der Box
    $pnHtml,
    0, 0, false, true, 'L', true
);

// 5) QR-Code: frei verschiebbar innerhalb der Box, verzerrungsfrei per size
$qrData = trim((string)($productNumber . $deviceNumber));
if ($qrData === '') {
    $qrData = 'N/A';
}

$qrSize      = 7.8;  // mm – Kantenlänge des QR
$qrOffsetX   = 11;  // mm von linker Innenkante
$qrOffsetY   = 1.2;  // mm von oberer Innenkante

$qrX = $pnBoxX + $qrOffsetX;
$qrY = $pnBoxY + $qrOffsetY;

$qrStyle = [
  'border'   => 0,
  'vpadding' => 0,
  'hpadding' => 0,
  'fgcolor'  => [0,0,0],
  'bgcolor'  => false,
];

// QR rendern (Warnings/Deprecated für diesen Block stumm schalten)
$_old_reporting = error_reporting();
error_reporting($_old_reporting & ~(E_WARNING | E_DEPRECATED | E_NOTICE));
$pdf->write2DBarcode($qrData, 'QRCODE,H', $qrX, $qrY, $qrSize, $qrSize, $qrStyle, 'N');
error_reporting($_old_reporting);

// 6) Clipping beenden
$pdf->StopTransform();

// 7) Optional: Cursor unter die PN-Box setzen, falls du im Fluss weiter willst
$pdf->SetXY($pnBoxX, $pnBoxY + $pnBoxH);

/** Bundle */

$bundleHeader = '<div style=" background-color: black; color: white; font-size:7px; font-weight:700;line-height:1.4; margin:4px 0 3px 0;">&nbsp;Bundleinhalt</div>';
$pdf->writeHTMLCell(40, 50, 7.65, 26, $bundleHeader, 0, 0, 0, true, 'L', true);
$bundleHeaderFillable = '<div style="border: 1px solid black; line-heigt:0.6"></div>';
//$pdf->writeHTMLCell(130.8, 39.8, 74.5, 46, $bundleHeaderFillable, 0, 0, 0, false, 'L', true);
drawFixedBox($pdf, 46.0, 27.5, 30.5, 3.45);


$bundleRows = '';

$BASE_FONT_PX        = 7.0;  // Level 1
$FONT_STEP_PX        = 1;   // je Ebene kleiner
$MIN_FONT_PX         = 3.0;
$LINE_HEIGHT_BASE    = 0.35;  // Basis Zeilenhöhe
$LINE_HEIGHT_STEP    = 0.05;  // je Ebene etwas kompakter
$ROW_PAD_V_MM        = 0.15;   // vertikales Padding je Zeile
$ROW_PAD_H_MM        = 0.0;   // horizontales Padding

$WEIGHT_PAD_RIGHT_MM = 1.6;   // mehr Abstand zum rechten Rand
$INDENT_MM_PER_LVL   = 1.8;   // Einrückung je Ebene in mm (stabil!)

foreach ($bundle as $item) {
    $qty = (int)($item['qty'] ?? 1);
    $text = htmlspecialchars($item['text'] ?? '');
    $lvl = max($LEVEL_MIN, min($LEVEL_MAX, (int)($item['level'] ?? 1)));
    $wkg = isset($item['weight_kg']) ? (float)$item['weight_kg'] : null;

    $textFontPx   = max($MIN_FONT_PX, $BASE_FONT_PX - ($lvl - 1) * $FONT_STEP_PX);
    $weightFontPx = max($MIN_FONT_PX, $BASE_FONT_PX - ($lvl - 1) * $FONT_STEP_PX);
    $lineHeight   = max(1.0, $LINE_HEIGHT_BASE - ($lvl - 1) * $LINE_HEIGHT_STEP);

// Einrückung robust über mm – aber nur Tabelle nutzen, wenn > 0 mm
$indentMm = max(0, ($lvl - 1) * $INDENT_MM_PER_LVL);

// Name-Spalte: ohne Spacer-Tabelle bei Level 1 (indent=0), sonst mit
if ($indentMm > 0) {
    /*$nameCellHtml = '
      <table cellspacing="0" cellpadding="0" width="100%">
        <tr>
          <td style="width:'.$indentMm.'mm; padding:0; margin:0;"></td>
          <td style="font-size:'.$textFontPx.'px; line-height:'.$lineHeight.'; padding:0; margin:0;">'.$text.'</td>
        </tr>
      </table>';*/

$indentMm = max(0, ($lvl - 1) * $INDENT_MM_PER_LVL);
$indentSpaces = str_repeat('&nbsp;', max(0, ($lvl - 1) * 4));

$nameCellHtml = '
  <span style="
    font-size:'.$textFontPx.'px;
    line-height:'.$lineHeight.';
  ">'.$indentSpaces.$text.'</span>';

  //Ende ersetzter Teil

} else {
    // Level 1: direkt rendern
    $nameCellHtml = '<span style="font-size:'.$textFontPx.'px; line-height:'.$lineHeight.';">'.$text.'</span>';
}


    $bundleRows .= '
      <tr>
        <!-- Anzahl -->
        <td style="
          line-height: 10px;
          width:8%;
          text-align:right;
          vertical-align:middle;
          font-size:'.$textFontPx.'px;
          
          padding:'.$ROW_PAD_V_MM.'mm '.$ROW_PAD_H_MM.'mm;">
          '.$qty.'
        </td>

        <!-- Name mit Einrückung -->
        <td style="
        line-height: 8px;
          width:88%;
          vertical-align:middle;
          padding:'.$ROW_PAD_V_MM.'mm '.$ROW_PAD_H_MM.'mm;">
          '.$nameCellHtml.'
        </td>

       
      </tr>';
}

$bundleHtml = '
<table cellspacing="0" cellpadding="0" style="border:1px solid #000; border-collapse:collapse;" width="100%">
  <tbody>'.$bundleRows.'</tbody>
</table>';

$pdf->writeHTMLCell(
    73.1,
    0,
    7.8,
    29.3,
    $bundleHtml,
    0,
    0,
    0,
    true,
    'L',
    true
);



/** ---------- Meta-Block dynamisch unterhalb der Bundle-Tabelle ---------- */

// Höhe der zuletzt gerenderten Bundle-Tabelle holen
$bundleH = $pdf->getLastH();

// Y-Position direkt nach der Bundle-Tabelle (mit 3 mm Abstand)
$metaY = 26.2+ $bundleH;  // 50 = dein bisheriges Y der Bundle-Tabelle
$metaX = $MARGIN_LEFT;


// Falls Meta-Block zu nah am Seitenende wäre → neue Seite
$usableBottom = $pdf->getPageHeight() - $MARGIN_BOTTOM;
if ($metaY > $usableBottom - 40) { // 40 mm Puffer für Meta
    $pdf->AddPage();
    $metaY = $MARGIN_TOP;
}

// --- Inhalt wie gehabt ---
$leftMeta = '
<table cellspacing="0" cellpadding="'.$CELL_PAD.'" width="100%">
  <tbody>

 
   <!-- Lagerplatz mit innerer Tabelle (optisch perfekt ausgerichtet) -->
<tr>
  <td width="45%" border="1" style="padding-left:20px;">
    <table cellspacing="0" cellpadding="0" border="0" width="100%">
      <tr>
        <td style="font-size: 8px" width="100%">Lagerplatz</td>
      </tr>
    </table>
  </td>
  <td width="55%" border="1" style="font-size: 8px"><b>  '.htmlspecialchars($storageLoc).'</b></td>
</tr>

  </tbody>
</table>';

// ---- Koordinaten der Meta-Zeile bestimmen (nimm deine Werte) ----
// Falls du sie schon berechnet hast, nutze deine $metaX, $metaY, $metaW.
// Beispiel, wie du sie meist setzt:
$metaX = $MARGIN_LEFT;
$metaW = 111.2;
// $metaY hast du zuvor als Ziel-Y für den Meta-Block berechnet:
/// $metaY = ... (z.B. 50 + $bundleH + 3);

// ---- Spaltenbreiten wie in deinem metaWrap: 62% | 2% | 36% ----
$leftW  = $metaW * 0.62;
$gapW   = $metaW * 0.02;
$rightW = $metaW * 0.36;

// Obere linke Ecke der rechten Spalte:
$rightX = $metaX + $leftW + $gapW;
$rightY = $metaY;

// ---- Fester Rahmen in der rechten Spalte ----

$metaWrap = '
<table cellspacing="0" cellpadding="0" border="0" width="100%" style="margin-top:2px;">
  <tr>
    <td width="62%">'.$leftMeta.'</td>
    <td width="2%"></td>
    <td width="36%"></td>
  </tr>
</table>';

// Meta-Block exakt an berechneter Position ausgeben
$pdf->writeHTMLCell($metaW, 0, $metaX, $metaY, $metaWrap, 0, 0, 0, true, 'L', true);
/** Output */
// Falls trotz allem vorher etwas ausgegeben wurde (Warnings), Buffer leeren:
if (ob_get_length()) { ob_end_clean(); }



// Äußerer Rahmen, Größenberechnung

// 1) Höhe der META-Zelle (zuletzt geschrieben) holen:
$metaH = $pdf->getLastH();

// 2) Y der Bundle-Tabelle kennst du (50.8 in deinem Code) + deren Höhe:
$bundleY = 33.7;           // dein fixer Y für die Bundle-Tabelle
// $bundleH hast du bereits oben: $bundleH = $pdf->getLastH();

// 3) Bottom-Kanten aller Boxen berechnen:
$bottoms = [
    $frameY + $frameH,       // Logo-Box
    $addrY  + $addrBoxH,     // Adress-Box
    $prodY  + $prodH,        // Produktion-Box
    $nameY  + $nameBoxH,     // Name-Box
    $pnBoxY + $pnBoxH,       // PN/QR-Box
    $bundleY + $bundleH,     // Bundle-Tabelle
    $metaY   + $metaH,       // Meta-Block (linke Tabelle + rechter Bildrahmen)
];

// 4) Start-Y des Contentbereichs: oben an deinem Kopf beginnen
$outerY = min($frameY, $addrY, $prodY, $nameY, $pnBoxY, 46.0 /*Bundle-Header-Y*/, $metaY);

// 5) Untere Kante ist die größte Bottom-Kante:
$outerBottom = max($bottoms);

// 6) Außenrahmen-Geometrie:
$outerX = $MARGIN_LEFT+1.2;
$outerW = 67.5;
$outerH = max(2, $outerBottom - $outerY)-1.2; // Mindeshöhe absichern

// 7) Dicke Linie zeichnen (z.B. 1.2 mm)
drawFixedBox($pdf, $outerX, $outerY, $outerW, $outerH, 1, '', 0, false);



$filename = 'Kistenetikett_'.preg_replace('/\s+/', '_', trim($productName)).'_'.$productNumber.$deviceNumber.'_'.date('Ymd_His').'.pdf';
$pdf->Output($filename, 'I');


/**
 * Zeichnet eine flexible, leere oder beschriftete Box mit TCPDF.
 *
 * @param TCPDF $pdf         Referenz auf dein TCPDF-Objekt
 * @param float $x           Linke obere Ecke (mm)
 * @param float $y           Obere Position (mm)
 * @param float $w           Breite (mm)
 * @param float $h           Höhe (mm)
 * @param float $border      Linienstärke in mm (z. B. 0.3)
 * @param string $text       (Optional) Inhalt oder Beschriftung
 * @param float $pad         Innenabstand in mm
 * @param bool $fill         true = grau hinterlegt, false = leer
 * @param string|null $style (Optional) Linienstil – 'solid', 'dashed', 'dotted'
 */
function drawFixedBox(
    TCPDF $pdf,
    float $x,
    float $y,
    float $w,
    float $h,
    float $border = 0.3,
    string $text = '',
    float $pad = 1.5,
    bool $fill = false,
    ?string $style = 'solid'
): void {
    // Farbe für Füllung (hellgrau, wenn aktiviert)
    $fillColor = [240, 240, 240];

    // Linienstil (falls angegeben)
    switch ($style) {
        case 'dashed':
            $pdf->SetLineStyle(['width' => $border, 'dash' => '3,2']);
            break;
        case 'dotted':
            $pdf->SetLineStyle(['width' => $border, 'dash' => '1,2']);
            break;
        default:
            $pdf->SetLineWidth($border);
            $pdf->SetLineStyle(['width' => $border]);
            break;
    }

    // 1) Rahmen (und ggf. Füllung)
    if ($fill) {
        $pdf->SetFillColorArray($fillColor);
        $pdf->Rect($x, $y, $w, $h, 'DF'); // Draw + Fill
    } else {
        $pdf->Rect($x, $y, $w, $h, 'D');  // nur Rahmen
    }

    // 2) Optionaler Textinhalt – sauber eingepasst
    if (trim($text) !== '') {
        $pdf->StartTransform();
        $pdf->Rect($x, $y, $w, $h, 'CNZ'); // Clip auf Box setzen

        $pdf->writeHTMLCell(
            $w - 2*$pad, $h - 2*$pad,
            $x + $pad, $y + $pad,
            '<div style="font-size:9px; line-height:1.2;">'.htmlspecialchars($text).'</div>',
            0, 0, false, true, 'L', true
        );

        $pdf->StopTransform();
    }

    // Linienstil zurücksetzen
    $pdf->SetLineStyle(['width' => 0.3, 'dash' => 0]);
}




/**
 * Produkt-Details mit Cache holen (vermeidet doppelte API-Calls).
 *
 * @param Epirent $Epi
 * @param int|string $productPk
 * @param array $cache  Referenz-Array für Produkt-Cache
 * @return object|null
 */
function fetchProductDetailCached($Epi, $productPk, array &$cache) {
    $key = (string)$productPk;
    if (isset($cache[$key])) {
        return $cache[$key];
    }
    try {
        $res = $Epi->requestEpiApi('/v1/product/' . $key . '?cl=' . Epirent_Mandant);
        $payload = json_decode($res, false);
        $prod = $payload->payload[0] ?? null;
        if ($prod) {
            $cache[$key] = $prod;
            return $prod;
        }
    } catch (\Throwable $e) {
        // optional: loggen
    }
    $cache[$key] = null;
    return null;
}
/**
 * Rekursive Hilfsfunktion: fügt Materialien (und deren Submaterialien) zu $bundle hinzu
 * und summiert alle Bruttogewichte in $bruttoWeightSum.
 *
 * @param Epirent $Epi
 * @param array   $materials       Materialliste (vom Produkt)
 * @param int     $level           aktuelle Ebene (1..MAX_BUNDLE_LEVEL)
 * @param array  &$bundle          Ergebnisliste (wird befüllt)
 * @param array  &$cache           Produkt-Cache
 * @param array  &$seenStack       Schutz gegen Zyklen (Stack von Produkt-Keys)
 * @param float   $qtyMultiplier   Multiplikator für Mengen aus übergeordneten Ebenen
 * @param float  &$bruttoWeightSum Summiert alle weight_gro-Werte × Menge
 */
function addMaterialsRecursive($Epi, array $materials, int $level, array &$bundle, array &$cache, array &$seenStack, float $qtyMultiplier = 1.0): void
{
    global $bruttoWeightSum;

    $MAX_BUNDLE_LEVEL = 5;
    if ($level > $MAX_BUNDLE_LEVEL || empty($materials)) {
        return;
    }
     // Nach "position" aufsteigend sortieren (pro Level)
    usort($materials, function ($a, $b) {
        $posA = isset($a->position) ? (int)$a->position : PHP_INT_MAX;
        $posB = isset($b->position) ? (int)$b->position : PHP_INT_MAX;
        return $posA <=> $posB;
    });

    foreach ($materials as $mat) {
        $isFree   = !empty($mat->is_free_material);
        $amount   = isset($mat->amount) ? (float)$mat->amount : 1.0;
        $effQty   = $amount * $qtyMultiplier;

        // 🟢 1. FREIES MATERIAL → nur Name + Menge, kein weiterer Drilldown
        if ($isFree) {
            $name = isset($mat->name) ? (string)$mat->name : '(Unbenanntes freies Material)';
            $bundle[] = [
                'qty'       => $effQty,
                'text'      => $name,
                'level'     => $level,
                'weight_kg' => null,
            ];
            continue; // ⬅️ ganz wichtig: hier abbrechen
        }

        // 🟡 2. "Normales" Produkt-Material
        $childPk = $mat->mat_product_pk ?? null;
        if (!$childPk) {
            $bundle[] = [
                'qty'       => $effQty,
                'text'      => '(Unbekanntes Material)',
                'level'     => $level,
                'weight_kg' => null,
            ];
            continue;
        }

        // Zyklen-Schutz
        $childKey = (string)$childPk;
        if (in_array($childKey, $seenStack, true)) {
            $bundle[] = [
                'qty'       => $effQty,
                'text'      => '[Zyklus erkannt bei Produkt ' . $childKey . ']',
                'level'     => $level,
                'weight_kg' => null,
            ];
            continue;
        }

        // Produktdetail abrufen (Cache-basiert)
        $child = fetchProductDetailCached($Epi, $childPk, $cache);

        // Basisinfos
        $childName        = $child->name ?? ('Produkt ' . $childKey);
        $childWeightNet   = $child->tech_data->weight_net ?? null;
        $childWeightGross = $child->tech_data->weight_gro ?? null;
        $childNumber      = $child->product_no ?? null;

        // 🧮 Bruttogewicht zur globalen Summe addieren
        if (!empty($childWeightGross)) {
            $bruttoWeightSum += $effQty * (float)$childWeightGross;
        }

        // 📦 Anzeige: Artikelnummer in Klammern voranstellen
        $displayName = $childNumber ? '(' . $childNumber . ') ' . $childName : $childName;

        $bundle[] = [
            'qty'       => $effQty,
            'text'      => (string)$displayName,
            'level'     => $level,
            'weight_kg' => $childWeightNet ? (float)$childWeightNet : null,
        ];

        // 🔁 Rekursion für Sub-Materialien
        if ($child && !empty($child->materials) && $level < $MAX_BUNDLE_LEVEL) {
            $seenStack[] = $childKey;
            addMaterialsRecursive($Epi, (array)$child->materials, $level + 1, $bundle, $cache, $seenStack, $effQty);
            array_pop($seenStack);
        }
    }
}



/**
 * Einstieg: Baut das Bundle eines Produkts bis Tiefe MAX_BUNDLE_LEVEL
 * und liefert zusätzlich die Bruttogewichtsumme zurück.
 *
 * @param Epirent $Epi
 * @param object  $product
 * @param float  &$bruttoWeightSum  Rückgabe der aufsummierten weight_gro
 * @return array
 */
function buildBundleForProduct($Epi, $product, float &$bruttoWeightSum = 0.0): array
{
    $bundle = [];
    $cache  = [];
    $seen   = [];
    $materials = isset($product->materials) ? (array)$product->materials : [];

    addMaterialsRecursive($Epi, $materials, 1, $bundle, $cache, $seen, 1.0, $bruttoWeightSum);
    return $bundle;
}
