<?php
defined('ABSPATH') or die('Direct access not allowed');

/**
 * TCPDF subclass with custom footer.
 * Footer: left = date/time + staff, center = document code, right = page X/Y.
 */
class VollstartRD_TCPDF extends \TCPDF {

    public $footerDocCode = '';
    public $footerStaffName = '';
    public $footerFontName = 'helvetica';
    public $footerFontSize = 7;

    public function Footer() {
        $m = $this->original_lMargin;
        $w = $this->getPageWidth() - $m - $this->original_rMargin;

        $this->SetFont($this->footerFontName, '', $this->footerFontSize);
        $this->SetTextColor(150, 150, 150);

        // Line 1: date/time (left), doc code (center), page (right)
        $this->SetY(-15);
        $this->SetX($m);
        $this->Cell($w, 4, date_i18n(get_option('date_format') . ' ' . get_option('time_format')), 0, 0, 'L');
        $this->SetX($m);
        $this->Cell($w, 4, $this->footerDocCode, 0, 0, 'C');
        $this->SetX($m);
        $this->Cell($w, 4, $this->getAliasNumPage() . '/' . $this->getAliasNbPages(), 0, 0, 'R');

        // Line 2: staff name (left)
        if ($this->footerStaffName) {
            $this->SetY(-11);
            $this->SetX($m);
            $this->Cell($w, 4, $this->footerStaffName, 0, 0, 'L');
        }
    }
}

/**
 * VollstartRD_PDF — PDF generation for invoices.
 *
 * SRP: Generates PDF documents using TCPDF.
 */
class VollstartRD_PDF {

    private $MAIN;

    public function __construct($MAIN) {
        $this->MAIN = $MAIN;
    }

    /**
     * Public so a registered add-on can compose its own PDFs using the same building blocks.
     * Create a configured TCPDF instance with footer.
     */
    public function createPdf(string $paperSize, array $business, string $docCode, string $fontName, float $fontSize, float $margin, string $direction = 'L'): VollstartRD_TCPDF {
        $pdf = new VollstartRD_TCPDF('P', 'mm', $paperSize, true, 'UTF-8', false);
        $pdf->SetCreator('Vollstart Repair Desk');
        $pdf->SetAuthor($business['name']);

        if ($direction === 'R') {
            $pdf->setRTL(true);
        }

        $pdf->setPrintHeader(false);
        $pdf->setPrintFooter(true);
        $pdf->SetMargins($margin, $margin, $margin);
        $pdf->SetAutoPageBreak(true, 20);

        // Footer config
        $pdf->footerDocCode = $docCode;
        $pdf->footerFontName = $fontName;
        $pdf->footerFontSize = $fontSize - 3;

        // Staff name: WP user if logged in, empty otherwise
        $user = wp_get_current_user();
        $pdf->footerStaffName = ($user && $user->ID) ? $user->display_name : '';

        return $pdf;
    }

    /**
     * Generate a repair receipt PDF (Annahme-Quittung).
     * Compact document with QR code for customer handout at intake.
     *
     * @param int $repairId The repair ID.
     * @return string PDF content (binary string), empty on failure.
     */
    public function generateReceipt(int $repairId): string {
        if (!class_exists('TCPDF')) {
            require_once VOLLSTART_RD_PLUGIN_DIR_PATH . 'vendors/TCPDF/tcpdf.php';
        }

        $repair = $this->MAIN->getRepairs()->getById($repairId);
        if (!$repair) return '';

        $customer = !empty($repair['customer_id']) ? $this->MAIN->getCustomers()->getById((int) $repair['customer_id']) : null;
        $product = !empty($repair['product_id']) ? $this->MAIN->getProducts()->getById((int) $repair['product_id']) : null;
        $opts = $this->MAIN->getOptions();

        $business = [
            'name'    => $opts->getOptionValue('businessName') ?: get_bloginfo('name'),
            'email'   => $opts->getOptionValue('businessEmail') ?: get_option('admin_email'),
            'phone'   => $opts->getOptionValue('businessPhone') ?: '',
            'address' => $opts->getOptionValue('businessAddress') ?: '',
        ];

        $paperSize = $opts->getOptionValue('pdfPaperSize') ?: 'A4';
        $fontName = $opts->getOptionValue('pdfFont') ?: 'helvetica';
        $fontSize = max(7, min(14, (int) ($opts->getOptionValue('pdfFontSize') ?: 10)));
        $margin = max(5, min(30, (int) ($opts->getOptionValue('pdfMargin') ?: 15)));
        $direction = $opts->getOptionValue('pdfDirection') ?: 'L';
        $isRtl = $direction === 'R';

        // Receipt display options
        $showCustomer = $opts->isCheckboxActive('receiptShowCustomer');
        $showDevice = $opts->isCheckboxActive('receiptShowDevice');
        $showDefect = $opts->isCheckboxActive('receiptShowDefect');
        $showUndoneSteps = $opts->isCheckboxActive('receiptShowUndoneSteps');
        $showSteps = $opts->isCheckboxActive('receiptShowSteps');
        $showQr = $opts->isCheckboxActive('receiptShowQr') && $opts->isCheckboxActive('statusPageEnabled');
        $showEstimate = $opts->isCheckboxActive('receiptShowEstimate');
        $receiptFooter = trim((string) $opts->getOptionValue('receiptFooterText'));

        $repairNum = $repair['repair_number'] ?? $repairId;
        $pdf = $this->createPdf($paperSize, $business, $repairNum, $fontName, $fontSize, $margin, $direction);
        $pdf->SetTitle('Receipt ' . $repairNum);
        $pdf->AddPage();

        $sizeXL = $fontSize + 14;
        $sizeLG = $fontSize + 8;
        $sizeMD = $fontSize;
        $sizeSM = $fontSize - 1;
        $sizeXS = $fontSize - 2;
        $sizeXXS = $fontSize - 3;

        $pageW = $pdf->getPageWidth() - (2 * $margin);

        $this->renderPdfHeader($pdf, $business, __('REPAIR RECEIPT', 'vollstart-repair-desk'), $margin, $pageW, $fontName, ['xs' => $sizeXS, 'sm' => $sizeSM, 'lg' => $sizeLG, 'xl' => $sizeXL]);

        $pdf->SetFont($fontName, '', $sizeSM);
        $pdf->SetTextColor(100, 100, 100);
        $pdf->Cell(0, 5, $this->formatDateTime(null, false), 0, 1, 'R');

        $pdf->Ln(8);

        // =====================================================================
        // REPAIR INFO (left) + CUSTOMER (right) — same row
        // =====================================================================
        $statusPageEnabled = $opts->isCheckboxActive('statusPageEnabled');
        $colW = ($pageW - 10) / 2; // two columns with 10mm gap
        $y = $pdf->GetY();

        // --- Left column: Repair details ---
        $pdf->SetFont($fontName, 'B', $sizeLG);
        $pdf->SetTextColor(0, 0, 0);
        $pdf->Cell($colW, 8, '#' . ($repair['repair_number'] ?? ''), 0, 1, 'L');

        $pdf->SetFont($fontName, '', $sizeSM);
        $pdf->SetTextColor(80, 80, 80);

        $statusLabel = $this->MAIN->getRepairs()->getStatusLabel($repair['status'] ?? '');
        /* translators: %s is the repair status label */
        $pdf->Cell($colW, 5, sprintf(__('Status: %s', 'vollstart-repair-desk'), $statusLabel), 0, 1, 'L');

        if (!empty($repair['created_at'])) {
            /* translators: %s is the date the repair was received */
            $pdf->Cell($colW, 5, sprintf(__('Received: %s', 'vollstart-repair-desk'), $this->formatDateTime(strtotime($repair['created_at']))), 0, 1, 'L');
        }
        if ($showEstimate && !empty($repair['estimated_date']) && $repair['estimated_date'] !== '0000-00-00' && $repair['estimated_date'] !== '0000-00-00 00:00:00') {
            /* translators: %s is the estimated completion date */
            $pdf->Cell($colW, 5, sprintf(__('Estimated: %s', 'vollstart-repair-desk'), $this->formatDateTime(strtotime($repair['estimated_date']), false)), 0, 1, 'L');
        }
        if ($showEstimate && !empty($repair["estimated_price"]) && (float) $repair['estimated_price'] > 0) {
            $currency = $this->MAIN->getCurrency();
            /* translators: %s is the estimated price with currency */
            $pdf->Cell($colW, 5, sprintf(__('Est. Price: %s', 'vollstart-repair-desk'), number_format((float) $repair['estimated_price'], 2) . ' ' . $currency), 0, 1, 'L');
        }
        $leftBottomY = $pdf->GetY();

        $sizes = ['xs' => $sizeXS, 'sm' => $sizeSM, 'md' => $sizeMD, 'lg' => $sizeLG];

        if ($showCustomer) {
            $rightX = $margin + $colW + 10;
            $pdf->SetY($y);
            $this->renderCustomerBlock($pdf, $customer, $colW, $fontName, $sizes, __('CUSTOMER', 'vollstart-repair-desk'), 'R', $rightX);
            $rightBottomY = $pdf->GetY();
        } else {
            $rightBottomY = $y;
        }

        // Move below whichever column is taller
        $pdf->SetY(max($leftBottomY, $rightBottomY));
        $pdf->SetX($margin);

        // QR code (full width, below both columns — only if status page enabled)
        $statusUrl = '';
        if ($showQr) {
            $qrSize = 30;
            $qrY = $pdf->GetY() + 4;
            $statusPageSlug = $opts->getOptionValue('statusPageSlug') ?: 'repair-status';
            $statusUrl = home_url($statusPageSlug . '/' . ($repair['repair_number'] ?? '') . '/' . ($repair['access_code'] ?? ''));

            $pdf->write2DBarcode($statusUrl, 'QRCODE,M', $margin, $qrY, $qrSize, $qrSize, [
                'border' => false, 'padding' => 1,
                'fgcolor' => [0, 0, 0], 'bgcolor' => [255, 255, 255],
            ]);

            $pdf->SetY($qrY + 2);
            $pdf->SetX($margin + $qrSize + 5);
            $pdf->SetFont($fontName, '', $sizeSM);
            $pdf->SetTextColor(80, 80, 80);
            $pdf->Cell(0, 5, __('Scan to track your repair status', 'vollstart-repair-desk'), 0, 1, 'L');
            $pdf->SetX($margin + $qrSize + 5);
            $pdf->SetFont($fontName, '', $sizeXS);
            $pdf->SetTextColor(100, 100, 100);
            $pdf->Cell(0, 4, $statusUrl, 0, 1, 'L');
            $pdf->SetX($margin + $qrSize + 5);
            $pdf->Cell(0, 4, __('Access code:', 'vollstart-repair-desk') . ' ' . ($repair['access_code'] ?? ''), 0, 1, 'L');

            $pdf->SetY(max($pdf->GetY(), $qrY + $qrSize + 2));
        }

        $pdf->Ln(4);

        if ($showDevice) {
            $deviceData = $product ?: $repair;
            $this->renderDeviceBlock($pdf, $deviceData, $pageW, $fontName, $sizes);
            $pdf->Ln(4);
        }

        // =====================================================================
        // DEFECT DESCRIPTION
        // =====================================================================
        if ($showDefect) {
            $pdf->SetFont($fontName, 'B', $sizeSM);
            $pdf->SetTextColor(100, 100, 100);
            $pdf->Cell(0, 5, __('DEFECT DESCRIPTION', 'vollstart-repair-desk'), 0, 1, 'L');
            $pdf->Ln(2);
            if (!empty($repair['defect_description'])) {
                $pdf->SetFont($fontName, '', $sizeSM);
                $pdf->SetTextColor(60, 60, 60);
                $pdf->MultiCell(0, 4, $repair['defect_description'], 0, 'L');
            } else {
                $pdf->SetFont($fontName, '', $sizeSM);
                $pdf->SetTextColor(150, 150, 150);
                $pdf->Cell(0, 5, '-', 0, 1, 'L');
            }
            $pdf->Ln(4);
        }

        // =====================================================================
        // REPAIR STEPS (checklist)
        // =====================================================================
        if ($showSteps) {
            $steps = $this->MAIN->getChecklists()->getRepairStepsFlat($repairId);
            if (!empty($steps)) {
                $pdf->SetFont($fontName, '', $sizeXS);
                $pdf->SetTextColor(100, 100, 100);
                $pdf->Cell(0, 5, __('REPAIR STEPS', 'vollstart-repair-desk'), 0, 1, 'L');
                $pdf->SetFont($fontName, '', $sizeSM);
                $pdf->SetTextColor(60, 60, 60);
                foreach ($steps as $step) {
                    $done = !empty($step['checked_at']);
                    if (!$done && !$showUndoneSteps) continue;
                    $name = $step['title'] ?? '';
                    $pdf->SetFont($fontName, '', $sizeSM);
                    $pdf->SetTextColor($done ? 34 : 100, $done ? 197 : 100, $done ? 94 : 100);
                    $pdf->Cell(8, 5, $done ? '[x]' : '[ ]', 0, 0, 'L');
                    $pdf->SetTextColor(60, 60, 60);
                    $pdf->Cell(0, 5, $name, 0, 1, 'L');
                }
                $pdf->Ln(4);
            }
        }

        // =====================================================================
        // RECEIPT FOOTER
        // =====================================================================
        if (!empty($receiptFooter)) {
            $pdf->SetDrawColor(200, 200, 200);
            $pdf->Line($margin, $pdf->GetY(), $margin + $pageW, $pdf->GetY());
            $pdf->Ln(4);
            $pdf->SetFont($fontName, '', $sizeSM);
            $pdf->SetTextColor(80, 80, 80);
            $pdf->MultiCell(0, 4, $receiptFooter, 0, 'C');
            $pdf->Ln(2);
        }

        $this->renderPdfBusinessCredit($pdf, $fontName, $sizeSM);

        return $pdf->Output('', 'S');
    }

    /**
     * Generate a device report PDF.
     *
     * @param int  $productId The product/device ID.
     * @param bool $full      If true, include all repairs + change history.
     * @return string PDF content (binary string), empty on failure.
     */
    public function generateCustomerPdf(int $customerId, bool $full = false): string {
        if (!class_exists('TCPDF')) {
            require_once VOLLSTART_RD_PLUGIN_DIR_PATH . 'vendors/TCPDF/tcpdf.php';
        }

        $customer = $this->MAIN->getCustomers()->getById($customerId);
        if (!$customer) return '';

        $opts = $this->MAIN->getOptions();

        $business = [
            'name'    => $opts->getOptionValue('businessName') ?: get_bloginfo('name'),
            'email'   => $opts->getOptionValue('businessEmail') ?: get_option('admin_email'),
            'phone'   => $opts->getOptionValue('businessPhone') ?: '',
            'address' => $opts->getOptionValue('businessAddress') ?: '',
            'taxId'   => (string) $opts->getOptionValue('businessTaxId'),
        ];

        $taxLabel = trim((string) $opts->getOptionValue('businessTaxLabel')) ?: 'VAT';

        $paperSize = $opts->getOptionValue('pdfPaperSize') ?: 'A4';
        $fontName = $opts->getOptionValue('pdfFont') ?: 'helvetica';
        $fontSize = max(7, min(14, (int) ($opts->getOptionValue('pdfFontSize') ?: 10)));
        $margin = max(5, min(30, (int) ($opts->getOptionValue('pdfMargin') ?: 15)));
        $direction = $opts->getOptionValue('pdfDirection') ?: 'L';
        $isRtl = $direction === 'R';

        $docCode = $customer['customer_number'] ?? '';
        $pdf = $this->createPdf($paperSize, $business, $docCode, $fontName, $fontSize, $margin, $direction);
        $title = $full ? __('Full Customer Report', 'vollstart-repair-desk') : __('Customer Report', 'vollstart-repair-desk');
        $pdf->SetTitle($title);
        $pdf->AddPage();

        $sizeXL = $fontSize + 14;
        $sizeLG = $fontSize + 8;
        $sizeMD = $fontSize;
        $sizeSM = $fontSize - 1;
        $sizeXS = $fontSize - 2;
        $sizeXXS = $fontSize - 3;

        $pageW = $pdf->getPageWidth() - (2 * $margin);

        $reportTitle = $full ? __('FULL CUSTOMER REPORT', 'vollstart-repair-desk') : __('CUSTOMER REPORT', 'vollstart-repair-desk');
        $this->renderPdfHeader($pdf, $business, $reportTitle, $margin, $pageW, $fontName, ['xs' => $sizeXS, 'sm' => $sizeSM, 'lg' => $sizeLG, 'xl' => $sizeXL], $taxLabel);

        $pdf->SetFont($fontName, '', $sizeSM);
        $pdf->SetTextColor(100, 100, 100);
        $pdf->Cell(0, 5, $this->formatDateTime(null, false), 0, 1, 'R');

        $pdf->Ln(8);

        $sizes = ['xs' => $sizeXS, 'sm' => $sizeSM, 'md' => $sizeMD];
        $this->renderCustomerBlock($pdf, $customer, 0, $fontName, $sizes, __('CUSTOMER DETAILS', 'vollstart-repair-desk'), 'L', 0, $taxLabel);
        $pdf->Ln(8);

        // =====================================================================
        // NOTES
        // =====================================================================
        $pdf->SetFont($fontName, 'B', $sizeSM);
        $pdf->SetTextColor(100, 100, 100);
        $pdf->Cell(0, 5, __('NOTES', 'vollstart-repair-desk'), 0, 1, 'L');
        $pdf->Ln(2);

        $pdf->SetFont($fontName, '', $sizeSM);
        if (!empty($customer['notes'])) {
            $pdf->SetTextColor(80, 80, 80);
            $pdf->MultiCell(0, 4, $customer['notes'], 0, 'L');
        } else {
            $pdf->SetTextColor(150, 150, 150);
            $pdf->Cell(0, 5, '-', 0, 1, 'L');
        }

        $pdf->Ln(8);

        // =====================================================================
        // DEVICES
        // =====================================================================
        $db = $this->MAIN->getDB();
        $productsTbl = $db->getTabelle('products');
        $products = $db->getResults("SELECT * FROM %i WHERE customer_id = %d ORDER BY created_at DESC", $productsTbl, $customerId);

        $pdf->SetFont($fontName, 'B', $sizeSM);
        $pdf->SetTextColor(100, 100, 100);
        $pdf->Cell(0, 5, __('DEVICES', 'vollstart-repair-desk'), 0, 1, 'L');
        $pdf->Ln(2);

        if (empty($products)) {
            $pdf->SetFont($fontName, '', $sizeSM);
            $pdf->SetTextColor(150, 150, 150);
            $pdf->Cell(0, 5, __('No devices found.', 'vollstart-repair-desk'), 0, 1, 'L');
        } else {
            $repairsTbl = $db->getTabelle('repairs');

            $thtml = '<table cellpadding="3" cellspacing="0" style="font-size:' . $sizeXS . 'pt">';
            $thtml .= '<thead><tr style="background-color:#f1f5f9;font-weight:bold">';
            $thtml .= '<td width="8%">' . esc_html__('ID', 'vollstart-repair-desk') . '</td><td width="22%">' . esc_html__('Name / Type', 'vollstart-repair-desk') . '</td><td width="22%">' . esc_html__('Brand / Model', 'vollstart-repair-desk') . '</td><td width="20%">' . esc_html__('Serial', 'vollstart-repair-desk') . '</td><td width="12%" align="right">' . esc_html__('Repairs', 'vollstart-repair-desk') . '</td><td width="16%">' . esc_html__('Added', 'vollstart-repair-desk') . '</td>';
            $thtml .= '</tr></thead><tbody>';

            foreach ($products as $p) {
                $brandModel = trim(($p['brand'] ?? '') . ' ' . ($p['model'] ?? ''));
                $repairCount = $db->getVar("SELECT COUNT(*) FROM %i WHERE product_id = %d", $repairsTbl, (int) $p['id']);
                $date = !empty($p['created_at']) ? esc_html($this->formatDateTime(strtotime($p['created_at']), false)) : '';

                $bd = 'border-bottom:0.2mm solid #e2e8f0';
                $thtml .= '<tr style="color:#3c3c3c">';
                $thtml .= '<td width="8%" style="' . $bd . '">' . (int) $p['id'] . '</td>';
                $thtml .= '<td width="22%" style="' . $bd . '">' . esc_html($p['name'] ?? '') . '</td>';
                $thtml .= '<td width="22%" style="' . $bd . '">' . esc_html($brandModel) . '</td>';
                $thtml .= '<td width="20%" style="' . $bd . '">' . esc_html($p['serial_number'] ?? '') . '</td>';
                $thtml .= '<td width="12%" style="' . $bd . '" align="right">' . (int) $repairCount . '</td>';
                $thtml .= '<td width="16%" style="' . $bd . '">' . $date . '</td>';
                $thtml .= '</tr>';
            }
            $thtml .= '</tbody></table>';
            $pdf->writeHTML($thtml, true, false, true, false, '');
        }

        $pdf->Ln(6);

        // =====================================================================
        // REPAIR HISTORY
        // =====================================================================
        $repairsTbl = $db->getTabelle('repairs');
        $query = "SELECT * FROM %i WHERE customer_id = %d ORDER BY created_at DESC";
        if (!$full) {
            $query .= ' LIMIT 10';
        }
        $repairs = $db->getResults($query, $repairsTbl, $customerId);

        $pdf->SetFont($fontName, 'B', $sizeSM);
        $pdf->SetTextColor(100, 100, 100);
        $sectionTitle = $full ? __('REPAIR HISTORY (ALL)', 'vollstart-repair-desk') : __('REPAIR HISTORY (LAST 10)', 'vollstart-repair-desk');
        $pdf->Cell(0, 5, $sectionTitle, 0, 1, 'L');
        $pdf->Ln(2);

        $this->renderRepairHistoryTable($pdf, $repairs, $fontName, ['xs' => $sizeXS, 'sm' => $sizeSM], true);

        // =====================================================================
        // CHANGE HISTORY (full only)
        // =====================================================================
        if ($full) {
            $pdf->Ln(6);

            $history = $this->MAIN->getCustomers()->getHistory($customerId);

            $pdf->SetFont($fontName, 'B', $sizeSM);
            $pdf->SetTextColor(100, 100, 100);
            $pdf->Cell(0, 5, __('CHANGE HISTORY', 'vollstart-repair-desk'), 0, 1, 'L');
            $pdf->Ln(2);

            if (empty($history)) {
                $pdf->SetFont($fontName, '', $sizeSM);
                $pdf->SetTextColor(150, 150, 150);
                $pdf->Cell(0, 5, __('No changes recorded.', 'vollstart-repair-desk'), 0, 1, 'L');
            } else {
                $thtml = '<table cellpadding="3" cellspacing="0" style="font-size:' . $sizeXS . 'pt">';
                $thtml .= '<thead><tr style="background-color:#f1f5f9;font-weight:bold">';
                $thtml .= '<td width="25%">' . esc_html__('Date', 'vollstart-repair-desk') . '</td><td width="20%">' . esc_html__('Field', 'vollstart-repair-desk') . '</td><td width="27%">' . esc_html__('Old Value', 'vollstart-repair-desk') . '</td><td width="28%">' . esc_html__('New Value', 'vollstart-repair-desk') . '</td>';
                $thtml .= '</tr></thead><tbody>';

                foreach ($history as $h) {
                    $date = !empty($h['changed_at']) ? esc_html($this->formatDateTime(strtotime($h['changed_at']))) : '';
                    $changedBy = !empty($h['changed_by_name']) ? ' (' . esc_html($h['changed_by_name']) . ')' : '';

                    $bd = 'border-bottom:0.2mm solid #e2e8f0';
                    $thtml .= '<tr style="color:#3c3c3c">';
                    $thtml .= '<td style="' . $bd . '">' . $date . $changedBy . '</td>';
                    $thtml .= '<td style="' . $bd . '">' . esc_html($h['field_name'] ?? '') . '</td>';
                    $thtml .= '<td style="' . $bd . '">' . esc_html($h['old_value'] ?? '') . '</td>';
                    $thtml .= '<td style="' . $bd . '">' . esc_html($h['new_value'] ?? '') . '</td>';
                    $thtml .= '</tr>';
                }
                $thtml .= '</tbody></table>';
                $pdf->writeHTML($thtml, true, false, true, false, '');
            }
        }

        $this->renderPdfBusinessCredit($pdf, $fontName, $sizeSM);

        return $pdf->Output('', 'S');
    }

    public function generateDevicePdf(int $productId, bool $full = false): string {
        if (!class_exists('TCPDF')) {
            require_once VOLLSTART_RD_PLUGIN_DIR_PATH . 'vendors/TCPDF/tcpdf.php';
        }

        $product = $this->MAIN->getProducts()->getById($productId);
        if (!$product) return '';

        $customer = !empty($product['customer_id']) ? $this->MAIN->getCustomers()->getById((int) $product['customer_id']) : null;
        $opts = $this->MAIN->getOptions();

        $business = [
            'name'    => $opts->getOptionValue('businessName') ?: get_bloginfo('name'),
            'email'   => $opts->getOptionValue('businessEmail') ?: get_option('admin_email'),
            'phone'   => $opts->getOptionValue('businessPhone') ?: '',
            'address' => $opts->getOptionValue('businessAddress') ?: '',
            'taxId'   => (string) $opts->getOptionValue('businessTaxId'),
        ];

        $taxLabel = trim((string) $opts->getOptionValue('businessTaxLabel')) ?: 'VAT';

        // PDF settings
        $paperSize = $opts->getOptionValue('pdfPaperSize') ?: 'A4';
        $fontName = $opts->getOptionValue('pdfFont') ?: 'helvetica';
        $fontSize = max(7, min(14, (int) ($opts->getOptionValue('pdfFontSize') ?: 10)));
        $margin = max(5, min(30, (int) ($opts->getOptionValue('pdfMargin') ?: 15)));
        $direction = $opts->getOptionValue('pdfDirection') ?: 'L';
        $isRtl = $direction === 'R';

        $docCode = 'DEV-' . ($product['id'] ?? '');
        $pdf = $this->createPdf($paperSize, $business, $docCode, $fontName, $fontSize, $margin, $direction);
        $title = $full ? __('Full Device Report', 'vollstart-repair-desk') : __('Device Report', 'vollstart-repair-desk');
        $pdf->SetTitle($title);
        $pdf->AddPage();

        // Relative font sizes
        $sizeXL = $fontSize + 14;
        $sizeLG = $fontSize + 8;
        $sizeMD = $fontSize;
        $sizeSM = $fontSize - 1;
        $sizeXS = $fontSize - 2;
        $sizeXXS = $fontSize - 3;

        // Available width
        $pageW = $pdf->getPageWidth() - (2 * $margin);

        $reportTitle = $full ? __('FULL DEVICE REPORT', 'vollstart-repair-desk') : __('DEVICE REPORT', 'vollstart-repair-desk');
        $this->renderPdfHeader($pdf, $business, $reportTitle, $margin, $pageW, $fontName, ['xs' => $sizeXS, 'sm' => $sizeSM, 'lg' => $sizeLG, 'xl' => $sizeXL], $taxLabel);

        $pdf->SetFont($fontName, '', $sizeSM);
        $pdf->SetTextColor(100, 100, 100);
        $pdf->Cell(0, 5, $this->formatDateTime(null, false), 0, 1, 'R');

        $pdf->Ln(8);

        // =====================================================================
        // DEVICE INFO (left) + OWNER INFO (right)
        // =====================================================================
        $halfW = $pageW / 2;
        $y = $pdf->GetY();

        $sizes = ['xs' => $sizeXS, 'sm' => $sizeSM, 'md' => $sizeMD, 'lg' => $sizeLG];

        // Left: Device
        $this->renderDeviceBlock($pdf, $product, $halfW, $fontName, $sizes);
        $leftBottomY = $pdf->GetY();

        // Right: Owner
        $rightX = $margin + $halfW;
        $pdf->SetY($y);
        $this->renderCustomerBlock($pdf, $customer, $halfW, $fontName, $sizes, __('OWNER', 'vollstart-repair-desk'), 'R', $rightX);
        $rightBottomY = $pdf->GetY();

        $pdf->SetY(max($leftBottomY, $rightBottomY));

        $pdf->Ln(8);

        // =====================================================================
        // DEVICE PHOTOS
        // =====================================================================
        $fileUpload = $this->MAIN->getFileUpload();
        $files = $fileUpload->getByEntity('product', $productId);
        $imageFiles = [];
        foreach ($files as $file) {
            $path = $fileUpload->getFilePath($file);
            if ($path && file_exists($path) && in_array($file['mime_type'], ['image/jpeg', 'image/png', 'image/gif', 'image/webp'])) {
                $imageFiles[] = $path;
            }
        }

        if (!empty($imageFiles)) {
            $pdf->SetFont($fontName, 'B', $sizeSM);
            $pdf->SetTextColor(100, 100, 100);
            $pdf->Cell(0, 5, __('PHOTOS', 'vollstart-repair-desk'), 0, 1, 'L');
            $pdf->Ln(2);

            $imgWidth = 40; // mm
            $imgGap = 5;    // mm gap between images
            $imgsPerRow = 3;
            $xStart = $margin;
            $col = 0;
            $rowY = $pdf->GetY();

            foreach ($imageFiles as $imgPath) {
                $x = $xStart + $col * ($imgWidth + $imgGap);

                // Check if we need a new page
                if ($pdf->GetY() + 40 > $pdf->getPageHeight() - $margin) {
                    $pdf->AddPage();
                    $rowY = $pdf->GetY();
                }

                $pdf->Image($imgPath, $x, $rowY, $imgWidth, 0, '', '', '', false, 300, '', false, false, 0, 'CT');
                $col++;

                if ($col >= $imgsPerRow) {
                    $col = 0;
                    // Move Y down past the images (estimate 30mm height)
                    $rowY = $pdf->GetY();
                    if ($rowY < $rowY + 30) {
                        $pdf->SetY($rowY + 30);
                        $rowY = $pdf->GetY();
                    }
                }
            }

            // If last row was not full, still advance Y
            if ($col > 0) {
                $pdf->SetY($rowY + 30);
            }

            $pdf->Ln(4);
        }

        // =====================================================================
        // REPAIR HISTORY
        // =====================================================================
        $db = $this->MAIN->getDB();
        $repairsTbl = $db->getTabelle('repairs');
        $query = "SELECT * FROM %i WHERE product_id = %d ORDER BY created_at DESC";
        if (!$full) {
            $query .= ' LIMIT 5';
        }
        $repairs = $db->getResults($query, $repairsTbl, $productId);

        $pdf->SetFont($fontName, 'B', $sizeSM);
        $pdf->SetTextColor(100, 100, 100);
        $sectionTitle = $full ? __('REPAIR HISTORY (ALL)', 'vollstart-repair-desk') : __('REPAIR HISTORY (LAST 5)', 'vollstart-repair-desk');
        $pdf->Cell(0, 5, $sectionTitle, 0, 1, 'L');
        $pdf->Ln(2);

        $this->renderRepairHistoryTable($pdf, $repairs, $fontName, ['xs' => $sizeXS, 'sm' => $sizeSM], false);

        // =====================================================================
        // CHANGE HISTORY (full only)
        // =====================================================================
        if ($full) {
            $pdf->Ln(6);

            $history = $this->MAIN->getProducts()->getHistory($productId);

            $pdf->SetFont($fontName, 'B', $sizeSM);
            $pdf->SetTextColor(100, 100, 100);
            $pdf->Cell(0, 5, __('CHANGE HISTORY', 'vollstart-repair-desk'), 0, 1, 'L');
            $pdf->Ln(2);

            if (empty($history)) {
                $pdf->SetFont($fontName, '', $sizeSM);
                $pdf->SetTextColor(150, 150, 150);
                $pdf->Cell(0, 5, __('No changes recorded.', 'vollstart-repair-desk'), 0, 1, 'L');
            } else {
                $thtml = '<table cellpadding="3" cellspacing="0" style="font-size:' . $sizeXS . 'pt">';
                $thtml .= '<thead><tr style="background-color:#f1f5f9;font-weight:bold">';
                $thtml .= '<td width="25%">' . esc_html__('Date', 'vollstart-repair-desk') . '</td><td width="20%">' . esc_html__('Field', 'vollstart-repair-desk') . '</td><td width="27%">' . esc_html__('Old Value', 'vollstart-repair-desk') . '</td><td width="28%">' . esc_html__('New Value', 'vollstart-repair-desk') . '</td>';
                $thtml .= '</tr></thead><tbody>';

                foreach ($history as $h) {
                    $date = !empty($h['changed_at']) ? esc_html($this->formatDateTime(strtotime($h['changed_at']))) : '';
                    $changedBy = !empty($h['changed_by_name']) ? ' (' . esc_html($h['changed_by_name']) . ')' : '';

                    $bd = 'border-bottom:0.2mm solid #e2e8f0';
                    $thtml .= '<tr style="color:#3c3c3c">';
                    $thtml .= '<td style="' . $bd . '">' . $date . $changedBy . '</td>';
                    $thtml .= '<td style="' . $bd . '">' . esc_html($h['field_name'] ?? '') . '</td>';
                    $thtml .= '<td style="' . $bd . '">' . esc_html($h['old_value'] ?? '') . '</td>';
                    $thtml .= '<td style="' . $bd . '">' . esc_html($h['new_value'] ?? '') . '</td>';
                    $thtml .= '</tr>';
                }
                $thtml .= '</tbody></table>';
                $pdf->writeHTML($thtml, true, false, true, false, '');
            }
        }

        $this->renderPdfBusinessCredit($pdf, $fontName, $sizeSM);

        return $pdf->Output('', 'S');
    }

    // =====================================================================
    // SHARED PDF BLOCKS — used by receipt, invoice, device, customer PDFs
    // =====================================================================

    /**
     * Public so a registered add-on can compose its own PDFs using the same building blocks.
     * Render PDF header: business info (left) + title (right) + date.
     */
    public function renderPdfHeader(\TCPDF $pdf, array $business, string $title, float $margin, float $pageW, string $fontName, array $sizes, string $taxLabel = ''): void {
        $pdf->SetFont($fontName, 'B', $sizes['lg']);
        $pdf->Cell(0, 10, $business['name'], 0, 1, 'L');
        $pdf->SetFont($fontName, '', $sizes['xs']);
        $pdf->SetTextColor(100, 100, 100);
        if ($business['address']) {
            $pdf->MultiCell($pageW / 2, 4, $business['address'], 0, 'L');
        }
        $contactParts = array_filter([$business['phone'], $business['email']]);
        if ($contactParts) {
            $pdf->Cell($pageW / 2, 4, implode(' · ', $contactParts), 0, 1, 'L');
        }
        if ($taxLabel && $business['taxId']) {
            /* translators: 1: tax label (e.g. VAT), 2: tax ID number */
            $pdf->Cell($pageW / 2, 4, sprintf(__('%1$s ID: %2$s', 'vollstart-repair-desk'), $taxLabel, $business['taxId']), 0, 1, 'L');
        }

        $pdf->SetTextColor(0, 0, 0);
        $pdf->SetFont($fontName, 'B', $sizes['xl']);
        $pdf->SetY($margin);
        $pdf->Cell(0, 10, $title, 0, 1, 'R');
    }

    /**
     * Render closing business credit (last lines on the receipt/PDF).
     * Shows the site title and URL — fully built-in Free feature so the
     * workshop is identifiable on every handout. If the site title or URL
     * is empty, the entire credit is suppressed (no stray blank line).
     *
     * @param \TCPDF $pdf
     * @param string $fontName
     * @param float  $fontSize
     */
    public function renderPdfBusinessCredit(\TCPDF $pdf, string $fontName, float $fontSize): void {
        $name = trim((string) get_bloginfo('name'));
        $url  = trim((string) site_url());

        if ($name === '' && $url === '') return;

        $pdf->Ln(8);
        if ($name !== '') {
            $pdf->SetFont($fontName, 'B', $fontSize);
            $pdf->SetTextColor(80, 80, 80);
            $pdf->Cell(0, 5, $name, 0, 1, 'C');
        }
        if ($url !== '') {
            $pdf->Ln(1);
            $pdf->SetFont($fontName, '', $fontSize - 1);
            $pdf->SetTextColor(120, 120, 120);
            $pdf->Cell(0, 4, $url, 0, 1, 'C');
        }
    }

    /**
     * Public so a registered add-on can compose its own PDFs using the same building blocks.
     * Format a date/time using WordPress settings.
     *
     * @param int|null $timestamp  Unix timestamp, or null for now
     * @param bool     $withTime   Include time format
     */
    public function formatDateTime(?int $timestamp = null, bool $withTime = true): string {
        $fmt = get_option('date_format');
        if ($withTime) {
            $fmt .= ' ' . get_option('time_format');
        }
        return $timestamp ? date_i18n($fmt, $timestamp) : date_i18n($fmt);
    }

    /**
     * Public so a registered add-on can compose its own PDFs using the same building blocks.
     * Render a repair history HTML table via writeHTML().
     * Used by both Device PDF and Customer PDF.
     *
     * @param \TCPDF $pdf
     * @param array  $repairs       Array of repair rows
     * @param string $fontName
     * @param array  $sizes         Font sizes array (needs 'xs', 'sm')
     * @param bool   $showDevice    Whether to include a Device column
     */
    public function renderRepairHistoryTable(\TCPDF $pdf, array $repairs, string $fontName, array $sizes, bool $showDevice = false): void {
        if (empty($repairs)) {
            $pdf->SetFont($fontName, '', $sizes['sm']);
            $pdf->SetTextColor(150, 150, 150);
            $pdf->Cell(0, 5, __('No repairs found.', 'vollstart-repair-desk'), 0, 1, 'L');
            return;
        }

        if ($showDevice) {
            $wDate = '12%'; $wNum = '18%'; $wDev = '14%'; $wPrice = '10%'; $wStatus = '12%'; $wDefect = '34%';
        } else {
            $wDate = '15%'; $wNum = '15%'; $wPrice = '10%'; $wStatus = '10%'; $wDefect = '50%';
        }

        $thtml = '<table width="100%" cellpadding="3" cellspacing="0" style="font-size:' . $sizes['xs'] . 'pt">';
        $thtml .= '<thead><tr style="background-color:#f1f5f9;font-weight:bold">';
        $thtml .= '<th width="' . $wDate . '">' . esc_html__('Date', 'vollstart-repair-desk') . '</th><th width="' . $wNum . '">' . esc_html__('Repair #', 'vollstart-repair-desk') . '</th>';
        if ($showDevice) $thtml .= '<th width="' . $wDev . '">' . esc_html__('Device', 'vollstart-repair-desk') . '</th>';
        $thtml .= '<th width="' . $wPrice . '" align="right">' . esc_html__('Est. Price', 'vollstart-repair-desk') . '</th><th width="' . $wStatus . '">' . esc_html__('Status', 'vollstart-repair-desk') . '</th><th width="' . $wDefect . '">' . esc_html__('Defect', 'vollstart-repair-desk') . '</th>';
        $thtml .= '</tr></thead><tbody>';

        $bd = 'border-bottom:0.2mm solid #e2e8f0';
        foreach ($repairs as $r) {
            $statusLabel = esc_html($this->MAIN->getRepairs()->getStatusLabel($r['status'] ?? ''));
            $date = !empty($r['created_at']) ? esc_html($this->formatDateTime(strtotime($r['created_at']), false)) : '';
            $price = !empty($r['estimated_price']) ? esc_html(number_format((float) $r['estimated_price'], 2)) : '';
            $defect = esc_html($r['defect_description'] ?? '');

            $thtml .= '<tr style="color:#3c3c3c">';
            $thtml .= '<td width="' . $wDate . '" style="' . $bd . '">' . $date . '</td>';
            $thtml .= '<td width="' . $wNum . '" style="' . $bd . '">' . esc_html($r['repair_number'] ?? '') . '</td>';
            if ($showDevice) {
                $device = trim(($r['device_brand'] ?? '') . ' ' . ($r['device_model'] ?? ''));
                if (empty($device)) $device = $r['device_type'] ?? '';
                $thtml .= '<td width="' . $wDev . '" style="' . $bd . '">' . esc_html($device) . '</td>';
            }
            $thtml .= '<td width="' . $wPrice . '" style="' . $bd . '" align="right">' . $price . '</td>';
            $thtml .= '<td width="' . $wStatus . '" style="' . $bd . '">' . $statusLabel . '</td>';
            $thtml .= '<td width="' . $wDefect . '" style="' . $bd . '">' . $defect . '</td>';
            $thtml .= '</tr>';
        }
        $thtml .= '</tbody></table>';
        $pdf->writeHTML($thtml, true, false, true, false, '');
    }

    /**
     * Public so a registered add-on can compose its own PDFs using the same building blocks.
     * Render a customer/owner block.
     *
     * @param \TCPDF  $pdf
     * @param ?array  $customer    Customer data array or null
     * @param float   $w           Cell width
     * @param string  $fontName
     * @param array   $sizes       Font sizes (xs, sm, md)
     * @param string  $label       Section label (e.g. 'CUSTOMER', 'OWNER')
     * @param string  $align       Text alignment: 'L' or 'R'
     * @param float   $x           X position (only needed for R-aligned columns)
     * @param string  $taxLabel    Tax label (e.g. 'VAT') for vat_number display
     */
    public function renderCustomerBlock(\TCPDF $pdf, ?array $customer, float $w, string $fontName, array $sizes, string $label = 'CUSTOMER', string $align = 'L', float $x = 0, string $taxLabel = ''): void {
        if ($align === 'R' && $x > 0) $pdf->SetX($x);
        $pdf->SetFont($fontName, '', $sizes['xs']);
        $pdf->SetTextColor(100, 100, 100);
        $pdf->Cell($w, 5, $label, 0, 1, $align);

        if (!$customer) {
            if ($align === 'R' && $x > 0) $pdf->SetX($x);
            $pdf->SetTextColor(150, 150, 150);
            $pdf->Cell($w, 5, '---', 0, 1, $align);
            return;
        }

        $pdf->SetFont($fontName, 'B', $sizes['md']);
        $pdf->SetTextColor(0, 0, 0);
        if ($align === 'R' && $x > 0) $pdf->SetX($x);
        $pdf->Cell($w, 5, $customer['name'] ?? '', 0, 1, $align);

        $pdf->SetFont($fontName, '', $sizes['sm']);
        $pdf->SetTextColor(80, 80, 80);

        if (!empty($customer['customer_number'])) {
            if ($align === 'R' && $x > 0) $pdf->SetX($x);
            $pdf->Cell($w, 5, '#' . $customer['customer_number'], 0, 1, $align);
        }
        if (!empty($customer['email'])) {
            if ($align === 'R' && $x > 0) $pdf->SetX($x);
            $pdf->Cell($w, 4, $customer['email'], 0, 1, $align);
        }
        if (!empty($customer['phone'])) {
            if ($align === 'R' && $x > 0) $pdf->SetX($x);
            $pdf->Cell($w, 4, $customer['phone'], 0, 1, $align);
        }
        if (!empty($customer['address'])) {
            if ($align === 'R' && $x > 0) $pdf->SetX($x);
            $pdf->MultiCell($w, 4, $customer['address'], 0, $align);
        }
        if (!empty($customer['vat_number']) && $taxLabel) {
            if ($align === 'R' && $x > 0) $pdf->SetX($x);
            $pdf->Cell($w, 5, $taxLabel . ': ' . $customer['vat_number'], 0, 1, $align);
        }
    }

    /**
     * Public so a registered add-on can compose its own PDFs using the same building blocks.
     * Render a DEVICE block (left-aligned).
     * Shows: label, name (bold), brand, model, serial, notes.
     * $device can be a product array OR a repair array with device_* fields.
     */
    public function renderDeviceBlock(\TCPDF $pdf, array $device, float $w, string $fontName, array $sizes): void {
        $pdf->SetFont($fontName, '', $sizes['xs']);
        $pdf->SetTextColor(100, 100, 100);
        $pdf->Cell($w, 5, __('DEVICE', 'vollstart-repair-desk'), 0, 1, 'L');

        $deviceId = $device['id'] ?? '';
        $name   = $device['name'] ?? '';
        $brand  = $device['brand'] ?? ($device['device_brand'] ?? '');
        $model  = $device['model'] ?? ($device['device_model'] ?? '');
        $serial = $device['serial_number'] ?? ($device['device_serial'] ?? '');
        $type   = $device['device_type'] ?? '';

        if (empty($name) && (!empty($brand) || !empty($model))) {
            $name = trim($brand . ' ' . $model);
        }
        if (empty($name) && !empty($type)) {
            $name = $type;
        }

        if (!empty($name)) {
            $pdf->SetFont($fontName, 'B', $sizes['md']);
            $pdf->SetTextColor(0, 0, 0);
            $pdf->Cell($w, 5, $name, 0, 1, 'L');
            $pdf->SetFont($fontName, '', $sizes['sm']);
            $pdf->SetTextColor(80, 80, 80);
        }
        if (!empty($deviceId)) {
            /* translators: %s is the device ID */
            $pdf->Cell($w, 5, sprintf(__('ID: %s', 'vollstart-repair-desk'), $deviceId), 0, 1, 'L');
        }
        if (!empty($brand) && $name !== $brand) {
            /* translators: %s is the device brand */
            $pdf->Cell($w, 5, sprintf(__('Brand: %s', 'vollstart-repair-desk'), $brand), 0, 1, 'L');
        }
        if (!empty($model) && $name !== $model && $name !== trim($brand . ' ' . $model)) {
            /* translators: %s is the device model */
            $pdf->Cell($w, 5, sprintf(__('Model: %s', 'vollstart-repair-desk'), $model), 0, 1, 'L');
        }
        if (!empty($serial)) {
            /* translators: %s is the device serial number */
            $pdf->Cell($w, 5, sprintf(__('SN: %s', 'vollstart-repair-desk'), $serial), 0, 1, 'L');
        }
    }
}
