/**
 * MetLAB Certificate Generator
 * Creates .docx calibration certificates matching the GSA template exactly.
 * 
 * Usage: node generate_certificate.js <data.json>
 * 
 * The data.json is written by PHP (CertificateController) before calling this script.
 * Output: certificate.docx in the certificates/ directory.
 */

'use strict';

const {
    Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell,
    ImageRun, Header, Footer, AlignmentType, BorderStyle, WidthType,
    ShadingType, VerticalAlign, PageNumber, PageBreak, TabStopType,
    TabStopPosition, HeadingLevel, UnderlineType
} = require('docx');

const fs   = require('fs');
const path = require('path');

// ── Paths ────────────────────────────────────────────────────
const ASSETS_DIR = path.join(__dirname, 'labs', 'shared');
const CERT_DIR   = path.join(__dirname, 'certificates');

// ── Load data from PHP-written JSON ─────────────────────────
const dataFile = process.argv[2];
if (!dataFile || !fs.existsSync(dataFile)) {
    console.error('Usage: node generate_certificate.js <data.json>');
    process.exit(1);
}
const DATA = JSON.parse(fs.readFileSync(dataFile, 'utf8'));

// ── Load logo images ─────────────────────────────────────────
function loadImage(filename, fallbackSize = [100, 50]) {
    const p = path.join(ASSETS_DIR, filename);
    if (fs.existsSync(p)) return fs.readFileSync(p);
    // Return a minimal valid JPEG placeholder if logo missing
    return null;
}

const logoGSA      = loadImage('logo_humidity.jpeg');  // GSA logo (top-left)
const logoDAkkS    = loadImage('logo_dakks.png') || loadImage('logo_dakks.jpeg'); // DAkkS / accreditation logo
const logoGSALarge = loadImage('logo_gsa_p1.jpeg');    // Large GSA logo for subsequent pages

// ── Shared style helpers ─────────────────────────────────────
const FONT     = 'Calibri';
const FONT_SM  = 18;   // 9pt
const FONT_REG = 20;   // 10pt
const FONT_MED = 22;   // 11pt
const FONT_LG  = 28;   // 14pt
const FONT_XL  = 36;   // 18pt

// Page dims: A4 = 11906 x 16838 DXA. Margins from original: L=1191, R=992, T=624, B=669
const PAGE_W    = 11906;
const PAGE_H    = 16838;
const MARGIN_L  = 1191;
const MARGIN_R  = 992;
const MARGIN_T  = 624;
const MARGIN_B  = 669;
const CONTENT_W = PAGE_W - MARGIN_L - MARGIN_R; // 9723 DXA

const border = (color = 'AAAAAA') => ({ style: BorderStyle.SINGLE, size: 4, color });
const borders = (color = 'AAAAAA') => ({
    top: border(color), bottom: border(color),
    left: border(color), right: border(color)
});
const noBorder = () => ({
    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 cell = (children, opts = {}) => new TableCell({
    borders: opts.border ? borders(opts.borderColor || 'AAAAAA') : noBorder(),
    width:   opts.width ? { size: opts.width, type: WidthType.DXA } : undefined,
    shading: opts.shade ? { fill: opts.shade, type: ShadingType.CLEAR } : undefined,
    verticalAlign: opts.vAlign || VerticalAlign.TOP,
    columnSpan: opts.colSpan,
    rowSpan:    opts.rowSpan,
    margins:    { top: 60, bottom: 60, left: 100, right: 100 },
    children:   Array.isArray(children) ? children : [children],
});

const para = (text, opts = {}) => new Paragraph({
    alignment:  opts.align || AlignmentType.LEFT,
    spacing:    { before: opts.before || 0, after: opts.after || 40 },
    children: [new TextRun({
        text:       text || '',
        font:       FONT,
        size:       opts.size   || FONT_REG,
        bold:       opts.bold   || false,
        italics:    opts.italic || false,
        color:      opts.color  || '000000',
        underline:  opts.underline ? { type: UnderlineType.SINGLE } : undefined,
    })],
});

const multiRun = (runs, opts = {}) => new Paragraph({
    alignment: opts.align || AlignmentType.LEFT,
    spacing:   { before: opts.before || 0, after: opts.after || 40 },
    children:  runs.map(r => new TextRun({
        text:    r.text || '',
        font:    FONT,
        size:    r.size    || opts.size || FONT_REG,
        bold:    r.bold    || opts.bold || false,
        italics: r.italic  || opts.italic || false,
        color:   r.color   || '000000',
        break:   r.break   || 0,
    })),
});

const hLine = (color = '003366') => new Paragraph({
    border: { bottom: { style: BorderStyle.SINGLE, size: 8, color, space: 1 } },
    spacing: { before: 80, after: 80 },
    children: [],
});

const empty = (spacing = 40) => new Paragraph({ spacing: { after: spacing }, children: [] });

// ── Logo image paragraph ─────────────────────────────────────
function logoPara(imgData, w, h, align = AlignmentType.LEFT) {
    if (!imgData) {
        return para('[Logo]', { size: FONT_SM, color: '888888', align });
    }
    const ext = imgData[0] === 0xFF ? 'jpg' : 'png';
    return new Paragraph({
        alignment: align,
        spacing:   { after: 0 },
        children: [new ImageRun({
            type:           ext,
            data:           imgData,
            transformation: { width: w, height: h },
            altText:        { title: 'Logo', description: 'Laboratory Logo', name: 'logo' },
        })],
    });
}

// ── Header (page 2+): cert number box top-right ───────────────
function makeHeader(certNum, labCode, issueYM) {
    const certBoxRows = [
        ['', certNum],
        ['Calibration mark', 'D-K-15209-01-00'],
        ['', issueYM],
    ].map(([label, value]) => new TableRow({ children: [
        cell(para(label, { size: FONT_SM, italic: true }), { width: 2800 }),
        cell(new Paragraph({
            spacing: { after: 0 },
            children: [new TextRun({ text: value, font: FONT, size: FONT_SM, bold: true })],
        }), { width: 2800, border: true, borderColor: 'AAAAAA' }),
    ]}));

    return new Header({ children: [
        new Table({
            width: { size: CONTENT_W, type: WidthType.DXA },
            columnWidths: [CONTENT_W - 5600, 2800, 2800],
            rows: [new TableRow({ children: [
                cell(logoGSA
                    ? logoPara(logoGSA, 120, 55)
                    : para('Ghana Standards Authority', { bold: true, size: FONT_MED }),
                    { width: CONTENT_W - 5600 }),
                ...certBoxRows.map((_, i) => cell(para(''), { width: 0 })),
            ]})],
        }),
        new Table({
            width: { size: CONTENT_W, type: WidthType.DXA },
            columnWidths: [CONTENT_W - 5600, 5600],
            rows: [new TableRow({ children: [
                cell(empty(0), { width: CONTENT_W - 5600 }),
                cell(new Table({
                    width:        { size: 5600, type: WidthType.DXA },
                    columnWidths: [2800, 2800],
                    rows:         certBoxRows,
                }), { width: 5600 }),
            ]})],
        }),
        hLine(),
    ]});
}

// ════════════════════════════════════════════════════════════════
// BUILD THE CERTIFICATE
// ════════════════════════════════════════════════════════════════

function buildCertificate(data) {
    const {
        lab_type,         // 'humidity' | 'balance' | 'generic'
        cert_number,
        issue_date,
        issue_ym,         // 'YYYY-MM'
        lab_code,
        lab_name,
        // From request (read-only fields)
        equipment_name,
        manufacturer,
        model,
        serial_number,
        company_name,
        request_number,
        // Form fields
        form,
        // Results array
        results,
        // Officer
        officer_name,
        signature_path,   // absolute path on server
    } = data;

    // Load signature image
    let sigImg = null;
    if (signature_path && fs.existsSync(signature_path)) {
        sigImg = fs.readFileSync(signature_path);
    }

    const issueYM = issue_ym || issue_date.substring(0, 7);

    // ── Page margins ─────────────────────────────────────────
    const pageProps = {
        page: {
            size:   { width: PAGE_W, height: PAGE_H },
            margin: { top: MARGIN_T, bottom: MARGIN_B, left: MARGIN_L, right: MARGIN_R, header: 0, footer: 0 },
        },
    };
    const pageProps2 = {
        page: {
            size:   { width: PAGE_W, height: PAGE_H },
            margin: { top: 776, bottom: MARGIN_B, left: MARGIN_L, right: 851, header: 720, footer: 0 },
        },
    };

    // ── Cert number box (page 1 top-right) ────────────────────
    const certBoxTable = new Table({
        width: { size: 4200, type: WidthType.DXA },
        columnWidths: [2000, 2200],
        rows: [
            ['', cert_number],
            ['Calibration mark', 'D-K-15209-01-00'],
            ['', issueYM],
        ].map(([label, value]) => new TableRow({ children: [
            cell(para(label, { size: FONT_SM, italic: true }), { width: 2000 }),
            cell(para(value, { size: FONT_SM, bold: true }), { width: 2200, border: true }),
        ]})),
    });

    // ── PAGE 1 CONTENT ───────────────────────────────────────
    const page1Children = [

        // ─ Top: logos + cert-number box
        new Table({
            width: { size: CONTENT_W, type: WidthType.DXA },
            columnWidths: [CONTENT_W - 4200, 4200],
            rows: [new TableRow({ children: [
                // Left: GSA logo + org name
                cell([
                    logoGSA
                        ? logoPara(logoGSA, 150, 68)
                        : para('Ghana Standards Authority', { bold: true, size: FONT_LG }),
                    empty(20),
                    para('P. O. Box MB 245, Accra, Ghana', { size: FONT_SM }),
                    para('TEL: +233-302-500065/6, 500086, Fax: +233-302-500092', { size: FONT_SM }),
                    para('E-Mail: scientificmetrology@gsa.gov.gh', { size: FONT_SM }),
                ], { width: CONTENT_W - 4200 }),
                // Right: cert number box
                cell(certBoxTable, { width: 4200, vAlign: VerticalAlign.TOP }),
            ]})],
        }),

        hLine('003366'),

        // ─ Title
        multiRun([
            { text: 'Calibration Certificate / ', size: FONT_XL, bold: true },
            { text: 'Certificat d\'étalonnage', size: FONT_LG, italic: true },
        ], { before: 80, after: 40 }),
        para('issued by the calibration laboratory  délivré par le laboratoire d\'étalonnage', { size: FONT_SM, italic: true, after: 60 }),
        hLine('003366'),

        // ─ Object info table + legal text side-by-side
        new Table({
            width: { size: CONTENT_W, type: WidthType.DXA },
            columnWidths: [Math.round(CONTENT_W * 0.42), Math.round(CONTENT_W * 0.58)],
            rows: [new TableRow({ children: [
                // Left: object details
                cell([
                    ...[
                        ['Object / Objet',                    equipment_name,  true ],
                        ['Manufacturer / Fabricant',          manufacturer,    true ],
                        ['Type / Type',                       model,           true ],
                        ['Serial number / Numéro de série',   serial_number,   true ],
                        ['Customer / Client',                 company_name,    true ],
                        ['Order number / Numéro de commande', request_number,  true ],
                        ['Number of pages',                   '3',             true ],
                        ['Date of calibration',               form.calibration_date || '',  true ],
                        ['Date of issue',                     issue_date,      true ],
                        ['Due date',                          form.due_date    || '', true ],
                    ].map(([label, value, bold]) => new Table({
                        width: { size: Math.round(CONTENT_W * 0.42), type: WidthType.DXA },
                        columnWidths: [Math.round(CONTENT_W * 0.20), Math.round(CONTENT_W * 0.22)],
                        rows: [new TableRow({ children: [
                            cell(para(label,  { size: FONT_SM, italic: true }),  { width: Math.round(CONTENT_W * 0.20) }),
                            cell(para(value || '', { size: FONT_SM, bold }), { width: Math.round(CONTENT_W * 0.22) }),
                        ]})],
                    })),
                ], { width: Math.round(CONTENT_W * 0.42) }),

                // Right: legal text + DAkkS logo
                cell([
                    logoDAkkS
                        ? logoPara(logoDAkkS, 100, 50, AlignmentType.RIGHT)
                        : para('D-K-15209-01-00\nAccredited Calibration Laboratory', { size: FONT_SM, align: AlignmentType.RIGHT }),
                    empty(60),
                    para(
                        'This calibration certificate documents the metrological traceability to national standards, which realize the units of measurement according to the International System of Units (SI).',
                        { size: FONT_SM, after: 60 }),
                    para(
                        'The DAkkS is signatory to the multilateral agreements of the European co-operation for Accreditation (EA) and of the International Laboratory Accreditation Cooperation (ILAC) for the mutual recognition of calibration certificates.',
                        { size: FONT_SM, italic: true, after: 60 }),
                    para(
                        'The user is obliged to have the object recalibrated at appropriate intervals.',
                        { size: FONT_SM, italic: true, after: 60 }),
                    empty(30),
                    para(
                        'Ce certificat d\'étalonnage documente la traçabilité métrologique des grandeurs mesurées par raccordement aux étalons nationaux en conformité avec le Système international d\'unités (SI).',
                        { size: FONT_SM, italic: true, after: 40 }),
                    para(
                        'L\'utilisateur est tenu de faire étalonner le matériel référencé ci-dessus à des intervalles appropriés.',
                        { size: FONT_SM, italic: true }),
                ], { width: Math.round(CONTENT_W * 0.58) }),
            ]})],
        }),

        hLine('AAAAAA'),

        // ─ Reproduction disclaimer
        para(
            'This calibration certificate may not be reproduced other than in full except with the permission of the issuing laboratory. Calibration certificates with the full name of the approval responsible person are valid without signature.',
            { size: FONT_SM, after: 40 }),
        para(
            'Ce certificat d\'étalonnage ne peut être reproduit que dans son intégralité, sauf avec l\'autorisation du laboratoire émetteur. Les certificats d\'étalonnage avec le nom complet de la personne responsable de l\'approbation sont valides sans signature.',
            { size: FONT_SM, italic: true, after: 60 }),

        hLine('003366'),

        // ─ Date of issue + Signature
        new Table({
            width: { size: CONTENT_W, type: WidthType.DXA },
            columnWidths: [Math.round(CONTENT_W * 0.4), Math.round(CONTENT_W * 0.6)],
            rows: [new TableRow({ children: [
                cell([
                    para('Date of issue / Date d\'Émission', { size: FONT_SM, italic: true }),
                    para(issue_date, { size: FONT_REG, bold: true }),
                ], { width: Math.round(CONTENT_W * 0.4) }),
                cell([
                    para('Approval of the certificate of calibration by', { size: FONT_SM }),
                    para('Approbation du certificat d\'étalonnage par', { size: FONT_SM, italic: true }),
                    empty(80),
                    sigImg
                        ? logoPara(sigImg, 160, 65)
                        : empty(60),
                    new Paragraph({
                        border: { top: { style: BorderStyle.SINGLE, size: 6, color: '000000', space: 1 } },
                        spacing: { before: 40, after: 20 },
                        children: [new TextRun({ text: officer_name || form.checked_by || '', font: FONT, size: FONT_REG, bold: true })],
                    }),
                    para('Laboratory Officer — ' + lab_name, { size: FONT_SM, italic: true }),
                ], { width: Math.round(CONTENT_W * 0.6) }),
            ]})],
        }),

        hLine('AAAAAA'),

        // ─ Footer strip
        para('Ghana Standards Authority, Metrology Directorate, Scientific Metrology Department', {
            size: FONT_SM, bold: true, align: AlignmentType.CENTER, before: 60, after: 20,
        }),
        para('Accredited Calibration Laboratory for Mass, Balance, Humidity, Pressure, Temperature and Volume', {
            size: FONT_SM, bold: true, align: AlignmentType.CENTER,
        }),
    ];

    // ════════════════════════════════════════════════════════
    // PAGE 2+ content: differs by lab type
    // ════════════════════════════════════════════════════════
    const page2Children = buildPage2(data, lab_type);
    const page3Children = buildPage3(data, lab_type);

    // ── Document assembly ─────────────────────────────────
    const doc = new Document({
        sections: [
            // Page 1 — cover (no header)
            {
                properties: pageProps,
                children:   page1Children,
            },
            // Page 2 — technical details
            {
                properties: pageProps2,
                headers:    { default: makeHeader(cert_number, lab_code, issueYM) },
                children:   page2Children,
            },
            // Page 3 — results
            {
                properties: pageProps2,
                headers:    { default: makeHeader(cert_number, lab_code, issueYM) },
                children:   page3Children,
            },
        ],
    });

    return doc;
}

// ════════════════════════════════════════════════════════════
// PAGE 2 — Technical details (lab-specific)
// ════════════════════════════════════════════════════════════
function buildPage2(data, labType) {
    const { form, equipment_name, manufacturer, model, serial_number } = data;
    const children = [];

    children.push(
        para('The French version of the calibration certificate is not a binding translation. If any matter gives rise to controversy, the English original text must be used.', { size: FONT_SM, italic: true, after: 60 }),
        hLine('AAAAAA'),
    );

    // ── Calibration Object
    children.push(
        multiRun([{ text: 'Calibration object   ', italic: true }, { text: equipment_name || '', bold: true }], { size: FONT_REG, after: 80 }),
    );

    if (labType === 'humidity') {
        children.push(
            new Table({
                width: { size: CONTENT_W, type: WidthType.DXA },
                columnWidths: [2400, 2000, 2400, 2923],
                rows: [
                    new TableRow({ children: [
                        cell(multiRun([{text:'Manufacturer / Fabricant  ', italic:true}, {text: data.manufacturer, bold:true}], {size: FONT_SM}), { width: 2400 }),
                        cell(multiRun([{text:'Type  ', italic:true}, {text: data.model, bold:true}], {size: FONT_SM}), { width: 2000 }),
                        cell(multiRun([{text:'Serial No.:  ', italic:true}, {text: data.serial_number, bold:true}], {size: FONT_SM}), { width: 2400 }),
                        cell(multiRun([{text:'ID No.:  ', italic:true}, {text: form.id_no||'Nil', bold:true}], {size: FONT_SM}), { width: 2923 }),
                    ]}),
                    new TableRow({ children: [
                        cell(multiRun([{text:'Measuring range:  ', italic:true}, {text: form.measuring_range||'Nil', bold:true}], {size: FONT_SM}), { width: 2400, colSpan: 2 }),
                        cell(empty(0), {width: 0}),
                        cell(multiRun([{text:'Calibration point(s):  ', italic:true}, {text: form.calibration_points||'(40/60/80)% at 25°C / (15/25/35)°C at 50%', bold:true}], {size: FONT_SM}), { width: 2400, colSpan: 2 }),
                        cell(empty(0), {width: 0}),
                    ]}),
                    new TableRow({ children: [
                        cell(multiRun([{text:'Resolution:  ', italic:true}, {text: (form.res_temp_eut||'0.1')+'°C / '+(form.res_rh_eut||'1')+'%', bold:true}], {size: FONT_SM}), { width: 2400, colSpan: 4 }),
                        cell(empty(0), {width: 0}), cell(empty(0), {width: 0}), cell(empty(0), {width: 0}),
                    ]}),
                ],
            }),
            empty(60),
            para('Place of Calibration: ' + (form.place_of_test || 'GSA Humidity Calibrations Laboratory'), { size: FONT_SM, italic: true, after: 80, bold: false }),
        );
    } else if (labType === 'balance') {
        children.push(
            new Table({
                width: { size: CONTENT_W, type: WidthType.DXA },
                columnWidths: [Math.round(CONTENT_W/2), Math.round(CONTENT_W/2)],
                rows: [new TableRow({ children: [
                    cell([
                        multiRun([{text:'Type: ', italic:true}, {text: model, bold:true}], {size:FONT_SM}),
                        multiRun([{text:'Serial No.: ', italic:true}, {text: serial_number, bold:true}], {size:FONT_SM}),
                        multiRun([{text:'Accuracy Class: ', italic:true}, {text:'II', bold:true}], {size:FONT_SM}),
                    ], {width: Math.round(CONTENT_W/2)}),
                    cell([
                        multiRun([{text:'Max. Capacity: ', italic:true}, {text: (form.max_capacity||'')+' '+(form.unit||''), bold:true}], {size:FONT_SM}),
                        multiRun([{text:'Scale Division (d): ', italic:true}, {text:'5 '+(form.unitsec||'g'), bold:true}], {size:FONT_SM}),
                        multiRun([{text:'Min. Capacity: ', italic:true}, {text: (form.min_capacity||'')+' '+(form.unitsec||'g'), bold:true}], {size:FONT_SM}),
                    ], {width: Math.round(CONTENT_W/2)}),
                ]})],
            }),
            empty(60),
        );
    }

    // ── Calibration Procedure
    children.push(
        hLine('AAAAAA'),
        para('Calibration procedure', { size: FONT_MED, bold: true, after: 40 }),
    );

    if (labType === 'humidity') {
        children.push(
            para(form.procedure || 'The calibration was done in accordance with DKD guideline, DKD-R 5-8:2019 according to B2 sequence (upwards) and DKD R-5-1:2018 for humidity and temperature parameters respectively.', { size: FONT_SM, italic: true, after: 60 }),
            para('The calibration was carried out by direct comparison of the EUT and the following Reference Standards:', { size: FONT_SM, after: 40 }),
            para('Humidity sensors with serial numbers 61277752 and 20185365', { size: FONT_SM, after: 20 }),
            para('Pt100 temperature sensors with serial numbers 16/07/14-1 and 16/07/14-2.', { size: FONT_SM, after: 20 }),
            para('A climatic chamber was used as the calibration medium.', { size: FONT_SM, after: 60 }),
        );
    } else if (labType === 'balance') {
        children.push(
            para('The calibration was done according to guideline EURAMET/cg-18.', { size: FONT_SM, italic: true, after: 40 }),
            para('The calibration comprised:', { size: FONT_SM, after: 20 }),
            para('  • Repeatability', { size: FONT_SM, after: 10 }),
            para('  • Test of errors of indication', { size: FONT_SM, after: 10 }),
            para('  • Eccentricity test', { size: FONT_SM, after: 60 }),
        );
    } else {
        children.push(
            para(form.procedure || '', { size: FONT_SM, italic: true, after: 60 }),
        );
    }

    // ── Traceability
    children.push(
        hLine('AAAAAA'),
        para('Traceability', { size: FONT_MED, bold: true, after: 40 }),
        para('The Reference standards are traceable to D-K-15209-01-00, Ghana and D-K-15186-01-00, Germany through calibration certificates.', { size: FONT_SM, italic: true, after: 80 }),
    );

    // ── Measuring Conditions
    children.push(
        hLine('AAAAAA'),
        para('Measuring conditions', { size: FONT_MED, bold: true, after: 40 }),
    );

    if (labType === 'humidity') {
        children.push(
            para('EUT and standards were placed in direct air stream in the climatic chamber.', { size: FONT_SM, after: 30 }),
            para('The measurement was taken after a stable condition in temperature and humidity were attained.', { size: FONT_SM, after: 30 }),
            para('Stable conditions are considered to be achieved when there is no more systematic change of the indication of both EUT and standards. The conditioning time (stability time) for each set point was over three hours.', { size: FONT_SM, after: 60 }),
        );
    } else if (labType === 'balance') {
        children.push(
            para('Place of test: ' + (form.place_of_test || ''), { size: FONT_SM, after: 30 }),
            para('Temperature during calibration — Start: ' + (form.temp_during_cal_start || '') + ' °C    End: ' + (form.temp_during_cal_end || '') + ' °C', { size: FONT_SM, after: 30 }),
            para('High air circulation: Yes ( ' + (form.high_air_circulation === 'Yes' ? '✓' : ' ') + ' )  No ( ' + (form.high_air_circulation === 'No' ? '✓' : ' ') + ' )', { size: FONT_SM, after: 20 }),
            para('High vibration: Yes ( ' + (form.high_vibration === 'Yes' ? '✓' : ' ') + ' )  No ( ' + (form.high_vibration === 'No' ? '✓' : ' ') + ' )', { size: FONT_SM, after: 60 }),
        );
    }

    // ── Environmental Conditions
    if (labType === 'humidity') {
        children.push(
            hLine('AAAAAA'),
            new Table({
                width: { size: CONTENT_W, type: WidthType.DXA },
                columnWidths: [Math.round(CONTENT_W * 0.5), Math.round(CONTENT_W * 0.5)],
                rows: [new TableRow({ children: [
                    cell([
                        multiRun([{text:'Environmental conditions', bold:true, size: FONT_REG}]),
                        multiRun([{text:'Temperature: ', italic:true}, {text:'(25±5) °C', bold:true}], {size:FONT_SM}),
                        multiRun([{text:'Relative Humidity: ', italic:true}, {text:'(55±20) %', bold:true}], {size:FONT_SM}),
                    ], {width: Math.round(CONTENT_W * 0.5)}),
                    cell(empty(0), {width: Math.round(CONTENT_W * 0.5)}),
                ]})],
            }),
            empty(60),
        );
    }

    return children;
}

// ════════════════════════════════════════════════════════════
// PAGE 3 — Measurement Results (lab-specific)
// ════════════════════════════════════════════════════════════
function buildPage3(data, labType) {
    const { form, results } = data;
    const children = [];
    const unit = form.unitsec || 'g';

    children.push(
        para('Measuring Results', { size: FONT_LG, bold: true, after: 80 }),
    );

    if (labType === 'humidity') {
        // ── RH Results Table
        children.push(
            para('Relative Humidity', { size: FONT_MED, bold: true, after: 40 }),
            new Table({
                width: { size: CONTENT_W, type: WidthType.DXA },
                columnWidths: [Math.round(CONTENT_W/5), Math.round(CONTENT_W/5), Math.round(CONTENT_W/5), Math.round(CONTENT_W/5), Math.round(CONTENT_W/5)],
                rows: [
                    new TableRow({ children: [
                        cell(para('Temperature of Humid Air\n°C', { size: FONT_SM, bold: true, align: AlignmentType.CENTER }), { width: Math.round(CONTENT_W/5), border: true, shade: 'E8F0F8' }),
                        cell(para('Mean Reference Reading\n%', { size: FONT_SM, bold: true, align: AlignmentType.CENTER }), { width: Math.round(CONTENT_W/5), border: true, shade: 'E8F0F8' }),
                        cell(para('Mean EUT Reading\n%', { size: FONT_SM, bold: true, align: AlignmentType.CENTER }), { width: Math.round(CONTENT_W/5), border: true, shade: 'E8F0F8' }),
                        cell(para('Mean Deviation\n%', { size: FONT_SM, bold: true, align: AlignmentType.CENTER }), { width: Math.round(CONTENT_W/5), border: true, shade: 'E8F0F8' }),
                        cell(para('Uncertainty\n%', { size: FONT_SM, bold: true, align: AlignmentType.CENTER }), { width: Math.round(CONTENT_W/5), border: true, shade: 'E8F0F8' }),
                    ]}),
                    ...(results || []).map(r => new TableRow({ children: [
                        cell(para(fmtNum(r.corrected_ref_temp), { size: FONT_SM, align: AlignmentType.CENTER }), { width: Math.round(CONTENT_W/5), border: true }),
                        cell(para(fmtNum(r.corrected_ref_rh), { size: FONT_SM, align: AlignmentType.CENTER }), { width: Math.round(CONTENT_W/5), border: true }),
                        cell(para(fmtNum(r.avg_eut_rh), { size: FONT_SM, align: AlignmentType.CENTER }), { width: Math.round(CONTENT_W/5), border: true }),
                        cell(para(fmtNum(r.rh_deviation, 4), { size: FONT_SM, align: AlignmentType.CENTER }), { width: Math.round(CONTENT_W/5), border: true }),
                        cell(para(fmtNum(r.rh_uncertainty, 3), { size: FONT_SM, align: AlignmentType.CENTER }), { width: Math.round(CONTENT_W/5), border: true }),
                    ]})),
                ],
            }),
            empty(80),
        );

        // ── Temperature Results (only where include_temp)
        const tempResults = (results || []).filter(r => r.include_temp);
        children.push(
            para('Temperature', { size: FONT_MED, bold: true, after: 40 }),
            new Table({
                width: { size: CONTENT_W, type: WidthType.DXA },
                columnWidths: [Math.round(CONTENT_W/4), Math.round(CONTENT_W/4), Math.round(CONTENT_W/4), Math.round(CONTENT_W/4)],
                rows: [
                    new TableRow({ children: [
                        cell(para('Mean Reference Reading\n°C', { size: FONT_SM, bold: true, align: AlignmentType.CENTER }), { width: Math.round(CONTENT_W/4), border: true, shade: 'E8F0F8' }),
                        cell(para('Mean EUT Reading\n°C', { size: FONT_SM, bold: true, align: AlignmentType.CENTER }), { width: Math.round(CONTENT_W/4), border: true, shade: 'E8F0F8' }),
                        cell(para('Mean Deviation\n°C', { size: FONT_SM, bold: true, align: AlignmentType.CENTER }), { width: Math.round(CONTENT_W/4), border: true, shade: 'E8F0F8' }),
                        cell(para('Uncertainty\nK', { size: FONT_SM, bold: true, align: AlignmentType.CENTER }), { width: Math.round(CONTENT_W/4), border: true, shade: 'E8F0F8' }),
                    ]}),
                    ...(tempResults.length ? tempResults : (results||[]).slice(0,3)).map(r => new TableRow({ children: [
                        cell(para(fmtNum(r.corrected_ref_temp), { size: FONT_SM, align: AlignmentType.CENTER }), { width: Math.round(CONTENT_W/4), border: true }),
                        cell(para(fmtNum(r.avg_eut_temp), { size: FONT_SM, align: AlignmentType.CENTER }), { width: Math.round(CONTENT_W/4), border: true }),
                        cell(para(fmtNum(r.temp_deviation, 4), { size: FONT_SM, align: AlignmentType.CENTER }), { width: Math.round(CONTENT_W/4), border: true }),
                        cell(para(fmtNum(r.temp_uncertainty, 3), { size: FONT_SM, align: AlignmentType.CENTER }), { width: Math.round(CONTENT_W/4), border: true }),
                    ]})),
                ],
            }),
            empty(40),
            para('EUT = Equipment Under Test', { size: FONT_SM, italic: true, after: 10 }),
            para('Deviation = Mean EUT Reading - Mean Reference Reading', { size: FONT_SM, italic: true, after: 80 }),
        );

    } else if (labType === 'balance') {
        // ── A. Repeatability
        children.push(
            para('A. REPEATABILITY TEST', { size: FONT_REG, bold: true, after: 30 }),
            multiRun([
                {text: 'Applied Load (L): ', italic:true}, {text: (form.applied_load_rt||'') + ' ' + unit, bold:true},
                {text: '      Standard deviation: ', italic:true}, {text: (form.standard_deviation||'') + ' ' + unit, bold:true},
            ], {size: FONT_SM, after: 40}),
            new Table({
                width: { size: CONTENT_W, type: WidthType.DXA },
                columnWidths: [Math.round(CONTENT_W/6), Math.round(CONTENT_W/6), Math.round(CONTENT_W/6), Math.round(CONTENT_W/6), Math.round(CONTENT_W/6), Math.round(CONTENT_W/6)],
                rows: [
                    new TableRow({ children: ['No.','1','2','3','4','5'].map((h,i) => cell(para(h, {size:FONT_SM, bold:true, align:AlignmentType.CENTER}), {width:Math.round(CONTENT_W/6), border:true, shade:'E8F0F8'})) }),
                    new TableRow({ children: [
                        cell(para('Indication / '+unit, {size:FONT_SM, align:AlignmentType.CENTER}), {width:Math.round(CONTENT_W/6), border:true}),
                        ...[1,2,3,4,5].map(i => cell(para(form['repeatability'+i]||'', {size:FONT_SM, align:AlignmentType.CENTER}), {width:Math.round(CONTENT_W/6), border:true})),
                    ]}),
                ],
            }),
            empty(80),
        );

        // ── B. Test of Errors of Indication
        children.push(
            para('B. TEST OF ERRORS OF INDICATION', { size: FONT_REG, bold: true, after: 30 }),
            new Table({
                width: { size: CONTENT_W, type: WidthType.DXA },
                columnWidths: [Math.round(CONTENT_W/6), Math.round(CONTENT_W/6), Math.round(CONTENT_W/6), Math.round(CONTENT_W/6), Math.round(CONTENT_W/6), Math.round(CONTENT_W/6)],
                rows: [
                    new TableRow({ children: [
                        'Applied Load /%',
                        'Nominal Mass / '+unit,
                        'Mean Scale Reading / '+unit,
                        'Mean Deviation / '+unit,
                        'Coverage Factor (k)',
                        'Expanded Uncertainty / '+unit,
                    ].map(h => cell(para(h, {size:FONT_SM, bold:true, align:AlignmentType.CENTER}), {width:Math.round(CONTENT_W/6), border:true, shade:'E8F0F8'})) }),
                    ...([['0','0/min'],['25','25'],['50','50'],['75','75'],['100','100']].map(([pct,label],idx) =>
                        new TableRow({ children: [
                            cell(para(label, {size:FONT_SM, align:AlignmentType.CENTER}), {width:Math.round(CONTENT_W/6), border:true}),
                            cell(para(form['nominal_value_'+pct]||'', {size:FONT_SM, align:AlignmentType.CENTER}), {width:Math.round(CONTENT_W/6), border:true}),
                            cell(para(form['indication_'+pct]||'', {size:FONT_SM, align:AlignmentType.CENTER}), {width:Math.round(CONTENT_W/6), border:true}),
                            cell(para(form['deviation_of_indication_'+pct]||'', {size:FONT_SM, align:AlignmentType.CENTER}), {width:Math.round(CONTENT_W/6), border:true}),
                            cell(para(form['K_value_'+(idx+1)]||'', {size:FONT_SM, align:AlignmentType.CENTER}), {width:Math.round(CONTENT_W/6), border:true}),
                            cell(para(form['U_value_'+(idx+1)]||'', {size:FONT_SM, align:AlignmentType.CENTER}), {width:Math.round(CONTENT_W/6), border:true}),
                        ]})
                    )),
                ],
            }),
            empty(80),
        );

        // ── C. Eccentricity
        children.push(
            para('C. ECCENTRICITY TEST', { size: FONT_REG, bold: true, after: 30 }),
            new Table({
                width: { size: CONTENT_W, type: WidthType.DXA },
                columnWidths: [Math.round(CONTENT_W/3), Math.round(CONTENT_W/3), Math.round(CONTENT_W/3)],
                rows: [
                    new TableRow({ children: ['Load Position', 'Scale Reading / '+unit, 'Deviation referring to centre / '+unit].map(h =>
                        cell(para(h, {size:FONT_SM, bold:true, align:AlignmentType.CENTER}), {width:Math.round(CONTENT_W/3), border:true, shade:'E8F0F8'})
                    )}),
                    ...([
                        ['CENTRE',      'EC_Indication1_Centre', 'EC_Deviation_Centre'],
                        ['FRONT LEFT',  'EC_Indication_FL',      'EC_Deviation_FL'],
                        ['BACK LEFT',   'EC_Indication_BL',      'EC_Deviation_BL'],
                        ['BACK RIGHT',  'EC_Indication_BR',      'EC_Deviation_BR'],
                        ['FRONT RIGHT', 'EC_Indication_FR',      'EC_Deviation_FR'],
                    ].map(([pos, indKey, devKey]) => new TableRow({ children: [
                        cell(para(pos,            {size:FONT_SM}), {width:Math.round(CONTENT_W/3), border:true}),
                        cell(para(form[indKey]||'',{size:FONT_SM, align:AlignmentType.CENTER}), {width:Math.round(CONTENT_W/3), border:true}),
                        cell(para(form[devKey]||'',{size:FONT_SM, align:AlignmentType.CENTER}), {width:Math.round(CONTENT_W/3), border:true}),
                    ]}))),
                ],
            }),
            empty(80),
        );
    }

    // ── Uncertainty Statement (all labs)
    children.push(
        hLine('AAAAAA'),
        para('Uncertainty of measurement:', { size: FONT_REG, bold: true, after: 40 }),
        para('The uncertainty stated is the expanded uncertainty obtained by multiplying the combined standard uncertainty by the coverage factor k = 2. It has been determined in accordance with EA-4/02 M:2022. The value of the measurand lies within the assigned range of values with a probability of approximately 95%.', { size: FONT_SM, after: 60 }),
        hLine('AAAAAA'),
        para('Additional information:', { size: FONT_REG, bold: true, after: 40 }),
        para('The DAkkS is signatory to the multilateral agreements of the European co-operation for Accreditation (EA) and of the International Laboratory Accreditation Cooperation (ILAC) for the mutual recognition of calibration certificates.', { size: FONT_SM, italic: true, after: 30 }),
        para('The given measuring results are only valid for the condition of the above-mentioned calibration object at the time of calibration.', { size: FONT_SM, italic: true, after: 30 }),
        para('A calibration mark is affixed on the calibration object.', { size: FONT_SM, italic: true, after: 80 }),
        para('End of calibration certificate.', { size: FONT_REG, bold: true, italic: true }),
    );

    return children;
}

// ── Number formatter ──────────────────────────────────────────
function fmtNum(v, decimals = 2) {
    if (v === null || v === undefined || v === '') return '';
    return parseFloat(v).toFixed(decimals);
}

// ── Generate & save ───────────────────────────────────────────
(async () => {
    try {
        const doc      = buildCertificate(DATA);
        const buffer   = await Packer.toBuffer(doc);
        const outFile  = DATA.output_path || path.join(CERT_DIR, 'certificate.docx');
        fs.mkdirSync(path.dirname(outFile), { recursive: true });
        fs.writeFileSync(outFile, buffer);
        console.log('OK:' + outFile);
    } catch (err) {
        console.error('ERROR:' + err.message);
        process.exit(1);
    }
})();
