<?php
declare(strict_types=1);
/**
 * modules/laporan/export_word.php
 * Export laporan bulanan ke Word (.docx) via docx-js (Node.js)
 * Dipanggil via index.php?module=laporan_bulanan&page=export_word&report_id=X
 *
 * Karena dipanggil oleh handler khusus di index.php (sebelum HTML render),
 * $conn dan session sudah tersedia.
 */

if (!isset($conn)) { exit('Koneksi DB tidak tersedia.'); }
if (!function_exists('e')) { function e($v): string { return htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8'); } }

$repId = (int)($_GET['report_id'] ?? 0);
if ($repId < 1) { exit('Report ID tidak valid.'); }

/* ── DB helpers ── */
function ew_q(mysqli $c, string $sql, array $p=[], string $t=''): array {
    $s=$c->prepare($sql); if(!$s) return [];
    if($p) $s->bind_param($t,...$p);
    $s->execute(); $r=$s->get_result(); $rows=[];
    if($r) while($row=$r->fetch_assoc()) $rows[]=$row;
    $s->close(); return $rows;
}
function ew_one(mysqli $c, string $sql, array $p=[], string $t=''): ?array {
    $rows=ew_q($c,$sql,$p,$t); return $rows[0]??null;
}

/* ── Load data ── */
$report = ew_one($conn,"SELECT h.*, u.name unit_name FROM monthly_report_headers h LEFT JOIN units u ON u.id=h.unit_id WHERE h.id=?",[$repId],'i');
if (!$report) { exit('Laporan tidak ditemukan.'); }

$sections = ew_q($conn,"SELECT * FROM monthly_report_sections WHERE report_id=? ORDER BY urut",[$repId],'i');
$sectionRows = [];
foreach($sections as $sec) {
    $sectionRows[(int)$sec['id']] = ew_q($conn,"SELECT * FROM monthly_report_rows WHERE section_id=? ORDER BY urut",[(int)$sec['id']],'i');
}

/* section cols config */
function ew_col_count(string $code, string $level): int {
    $map = [
        'unit'     => ['ketenagaan'=>5,'jumlah_pasien'=>7,'keterlambatan_staf'=>4,'capaian_rka'=>5,'makp'=>6,'permasalahan'=>7,'mutu'=>4,'ikp'=>6],
        'kasubdiv' => ['ketenagaan'=>5,'jumlah_pasien'=>7,'capaian_rka'=>6,'makp'=>5,'permasalahan'=>5,'mutu'=>5,'ikp'=>5,'analisis'=>4],
        'kadiv'    => ['ketenagaan'=>5,'jumlah_pasien'=>6,'capaian_rka'=>5,'makp'=>5,'permasalahan'=>5,'mutu'=>5,'ikp'=>7,'analisis'=>4],
    ];
    return $map[$level][$code] ?? 5;
}

$bulanNama = ['','Januari','Februari','Maret','April','Mei','Juni','Juli','Agustus','September','Oktober','November','Desember'][(int)$report['bulan']] ?? '';
$period    = $bulanNama.' '.(int)$report['tahun'];
$rLevel    = (string)$report['report_level'];
$levelLabel = ['unit'=>'KEPALA UNIT','kasubdiv'=>'KEPALA SUB DIVISI','kadiv'=>'KEPALA DIVISI'][$rLevel] ?? strtoupper($rLevel);

/* ── Build Node.js script ── */
$docxScript = tempnam(sys_get_temp_dir(), 'docx_') . '.js';
$outFile    = tempnam(sys_get_temp_dir(), 'report_') . '.docx';

/* Helper: escape JS string */
function jsStr(string $s): string {
    return json_encode($s);
}

/* Build section content JS */
$sectionsJs = '';
foreach($sections as $sec) {
    $code  = (string)$sec['section_code'];
    $title = (string)$sec['section_title'];
    $intro = (string)($sec['intro_text']??'');
    $rows  = $sectionRows[(int)$sec['id']] ?? [];
    $ncols = ew_col_count($code, $rLevel);

    $sectionsJs .= "
    // Section: {$title}
    children.push(new Paragraph({
        heading: HeadingLevel.HEADING_2,
        children:[new TextRun({text: ".jsStr($title).", bold:true, size:24, font:'Arial'})]
    }));
    ";

    if ($intro !== '') {
        $sectionsJs .= "
    children.push(new Paragraph({
        spacing:{before:80,after:80},
        children:[new TextRun({text:".jsStr($intro).", size:22, font:'Arial'})]
    }));
    ";
    }

    if ($rows) {
        /* Nama kolom asli dari config section */
        $colNames    = ew_section_cols($code, $rLevel); // ['No','Nama Unit',...]
        $dataColCount = count($colNames) - 1;           // exclude 'No'

        $sectionsJs .= "\n    (function(){\n    var tRows=[];\n    ";

        /* Header row dengan nama kolom asli */
        $sectionsJs .= "tRows.push(new TableRow({children:[";
        foreach($colNames as $colName) {
            $sectionsJs .= "new TableCell({shading:{fill:'1D4ED8',type:ShadingType.CLEAR},margins:{top:80,bottom:80,left:120,right:120},children:[new Paragraph({children:[new TextRun({text:".json_encode($colName).",bold:true,color:'FFFFFF',size:20,font:'Arial'})]})]}),";
        }
        $sectionsJs .= "]});\n";

        foreach($rows as $ri => $row) {
            $sectionsJs .= "tRows.push(new TableRow({children:[";
            $fill = $ri%2===0 ? 'FFFFFF' : 'F8FAFC';
            $sectionsJs .= "new TableCell({shading:{fill:'".$fill."',type:ShadingType.CLEAR},margins:{top:60,bottom:60,left:100,right:100},children:[new Paragraph({children:[new TextRun({text:".jsStr((string)($ri+1)).",size:20,font:'Arial'})]})]}),";
            for($ci=1;$ci<=$dataColCount;$ci++) {
                $val = (string)($row['col'.$ci]??'');
                $sectionsJs .= "new TableCell({shading:{fill:'".$fill."',type:ShadingType.CLEAR},margins:{top:60,bottom:60,left:100,right:100},children:[new Paragraph({children:[new TextRun({text:".jsStr($val).",size:20,font:'Arial'})]})]}),";
            }
            $sectionsJs .= "]});\n";
        }

        $colWidths = [500];
        $baseW = max(500, intval((9026 - 500) / max(1,$dataColCount)));
        for($ci=0;$ci<$dataColCount;$ci++) $colWidths[] = $baseW;
        $cwJs = '['.implode(',',$colWidths).']';

        $sectionsJs .= "
    children.push(new Table({
        width:{size:9026,type:WidthType.DXA},
        columnWidths:$cwJs,
        rows:tRows
    }));
    children.push(new Paragraph({spacing:{before:80,after:80},children:[new TextRun({text:'',size:20})]}));
    })();
    ";
    } else {
        $sectionsJs .= "
    children.push(new Paragraph({
        spacing:{before:60,after:80},
        children:[new TextRun({text:'(Belum ada data)',color:'999999',italics:true,size:20,font:'Arial'})]
    }));
    ";
    }
}

$script = <<<JS
const { Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell,
        HeadingLevel, AlignmentType, WidthType, ShadingType, PageNumber,
        Header, Footer, PageBreak, BorderStyle, VerticalAlign } = require('docx');
const fs = require('fs');

const title     = {$this->jsE($report['title'])};
const period    = {$this->jsE($period)};
const unitName  = {$this->jsE((string)($report['unit_name']??''))};
const levelLbl  = {$this->jsE($levelLabel)};
const signedName= {$this->jsE((string)($report['signed_name']??''))};
const signedPos = {$this->jsE((string)($report['signed_position']??''))};
const pendah    = {$this->jsE((string)($report['pendahuluan']??''))};
const simpulan  = {$this->jsE((string)($report['simpulan']??''))};
const penutup   = {$this->jsE((string)($report['penutup']??''))};

const border = {style:BorderStyle.SINGLE,size:1,color:'CCCCCC'};
const borders = {top:border,bottom:border,left:border,right:border};

var children = [];

// Cover / Judul
children.push(new Paragraph({
    spacing:{before:2880,after:360},
    alignment:AlignmentType.CENTER,
    children:[new TextRun({text:'LAPORAN BULANAN KEPERAWATAN',bold:true,size:32,font:'Arial',color:'1E3A5F'})]
}));
children.push(new Paragraph({
    alignment:AlignmentType.CENTER,
    spacing:{before:0,after:240},
    children:[new TextRun({text:levelLbl+(unitName?(' — '+unitName):''),bold:true,size:24,font:'Arial',color:'1E3A5F'})]
}));
children.push(new Paragraph({
    alignment:AlignmentType.CENTER,
    spacing:{before:0,after:600},
    children:[new TextRun({text:period.toUpperCase(),bold:true,size:28,font:'Arial',color:'374151'})]
}));
children.push(new Paragraph({border:{bottom:{style:BorderStyle.SINGLE,size:6,color:'1D4ED8',space:1}},children:[]}));

// BAB I
children.push(new Paragraph({
    heading:HeadingLevel.HEADING_1,
    spacing:{before:360,after:120},
    children:[new TextRun({text:'BAB I — PENDAHULUAN',bold:true,size:28,font:'Arial',color:'1E3A5F'})]
}));
children.push(new Paragraph({
    spacing:{before:60,after:200},
    alignment:AlignmentType.JUSTIFIED,
    children:[new TextRun({text:pendah||'Laporan keperawatan adalah gambaran terhadap pelaksanaan kegiatan keperawatan yang mencakup ketenagaan, jumlah pasien, keberhasilan Management Asuhan Keperawatan Professional, pencapaian pelaksanaan RKA serta permasalahan terkait pelayanan keperawatan.',size:22,font:'Arial'})]
}));

// BAB II
children.push(new Paragraph({
    heading:HeadingLevel.HEADING_1,
    spacing:{before:360,after:120},
    children:[new TextRun({text:'BAB II — PEMBAHASAN',bold:true,size:28,font:'Arial',color:'1E3A5F'})]
}));

// Sections
{$sectionsJs}

// BAB III
children.push(new Paragraph({
    heading:HeadingLevel.HEADING_1,
    spacing:{before:360,after:120},
    children:[new TextRun({text:'BAB III — SIMPULAN DAN PENUTUP',bold:true,size:28,font:'Arial',color:'1E3A5F'})]
}));
if(simpulan){
    children.push(new Paragraph({
        heading:HeadingLevel.HEADING_2,
        spacing:{before:120,after:60},
        children:[new TextRun({text:'A. Simpulan',bold:true,size:24,font:'Arial'})]
    }));
    children.push(new Paragraph({
        alignment:AlignmentType.JUSTIFIED,
        spacing:{before:60,after:120},
        children:[new TextRun({text:simpulan,size:22,font:'Arial'})]
    }));
}
if(penutup){
    children.push(new Paragraph({
        heading:HeadingLevel.HEADING_2,
        spacing:{before:120,after:60},
        children:[new TextRun({text:'B. Penutup',bold:true,size:24,font:'Arial'})]
    }));
    children.push(new Paragraph({
        alignment:AlignmentType.JUSTIFIED,
        spacing:{before:60,after:120},
        children:[new TextRun({text:penutup,size:22,font:'Arial'})]
    }));
}

// Tanda tangan
children.push(new Paragraph({spacing:{before:600,after:0},alignment:AlignmentType.RIGHT,
    children:[new TextRun({text:'Denpasar, '+new Date().toLocaleDateString('id-ID',{day:'numeric',month:'long',year:'numeric'}),size:22,font:'Arial'})]}));
children.push(new Paragraph({spacing:{before:80,after:0},alignment:AlignmentType.RIGHT,
    children:[new TextRun({text:levelLbl,size:22,font:'Arial'})]}));
children.push(new Paragraph({spacing:{before:800,after:0},alignment:AlignmentType.RIGHT,
    children:[new TextRun({text:signedName,bold:true,size:22,font:'Arial',underline:{}})]}));
if(signedPos){
    children.push(new Paragraph({spacing:{before:60,after:0},alignment:AlignmentType.RIGHT,
        children:[new TextRun({text:signedPos,size:22,font:'Arial'})]}));
}

// Buat document
const doc = new Document({
    styles:{
        default:{document:{run:{font:'Arial',size:22}}},
        paragraphStyles:[
            {id:'Heading1',name:'Heading 1',basedOn:'Normal',next:'Normal',quickFormat:true,
             run:{size:28,bold:true,font:'Arial',color:'1E3A5F'},
             paragraph:{spacing:{before:300,after:120},outlineLevel:0}},
            {id:'Heading2',name:'Heading 2',basedOn:'Normal',next:'Normal',quickFormat:true,
             run:{size:24,bold:true,font:'Arial'},
             paragraph:{spacing:{before:200,after:80},outlineLevel:1}},
        ]
    },
    sections:[{
        properties:{page:{size:{width:11906,height:16838},
            margin:{top:1440,right:1134,bottom:1440,left:1701}}},
        headers:{default:new Header({children:[
            new Paragraph({border:{bottom:{style:BorderStyle.SINGLE,size:4,color:'1D4ED8',space:6}},
                children:[
                    new TextRun({text:'RS Balimed — Sistem Keperawatan ITC Program',size:18,font:'Arial',color:'1D4ED8',bold:true}),
                    new TextRun({text:'     Laporan Bulanan '+period,size:18,font:'Arial',color:'666666',children:[]}),
                ]})
        ]})},
        footers:{default:new Footer({children:[
            new Paragraph({border:{top:{style:BorderStyle.SINGLE,size:4,color:'E2E8F0',space:6}},
                children:[
                    new TextRun({text:title,size:18,font:'Arial',color:'666666'}),
                    new TextRun({text:'  |  Hal. ',size:18,font:'Arial',color:'666666'}),
                    new TextRun({children:[new PageNumber()],size:18,font:'Arial',color:'1D4ED8',bold:true}),
                ]})
        ]})},
        children
    }]
});

Packer.toBuffer(doc).then(buf => {
    fs.writeFileSync({$this->jsE($outFile)}, buf);
    process.exit(0);
}).catch(e => { console.error(e); process.exit(1); });
JS;

/* PHP can't call $this->jsE, fix: */
$script = str_replace('{{JS_TITLE}}',    json_encode($report['title']), $script);

/* Re-build cleanly without $this */
$jsTitle    = json_encode($report['title']);
$jsPeriod   = json_encode($period);
$jsUnit     = json_encode((string)($report['unit_name']??''));
$jsLevel    = json_encode($levelLabel);
$jsSigned   = json_encode((string)($report['signed_name']??''));
$jsSignedP  = json_encode((string)($report['signed_position']??''));
$jsPend     = json_encode((string)($report['pendahuluan']??''));
$jsSimpul   = json_encode((string)($report['simpulan']??''));
$jsPenutup  = json_encode((string)($report['penutup']??''));
$jsOut      = json_encode($outFile);

$script2 = <<<JS
const { Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell,
        HeadingLevel, AlignmentType, WidthType, ShadingType, PageNumber,
        Header, Footer, BorderStyle } = require('docx');
const fs = require('fs');

const title      = $jsTitle;
const period     = $jsPeriod;
const unitName   = $jsUnit;
const levelLbl   = $jsLevel;
const signedName = $jsSigned;
const signedPos  = $jsSignedP;
const pendah     = $jsPend;
const simpulan   = $jsSimpul;
const penutup    = $jsPenutup;

var children = [];

children.push(new Paragraph({
    spacing:{before:2880,after:360},alignment:AlignmentType.CENTER,
    children:[new TextRun({text:'LAPORAN BULANAN KEPERAWATAN',bold:true,size:36,font:'Arial',color:'1E3A5F'})]
}));
children.push(new Paragraph({
    alignment:AlignmentType.CENTER,spacing:{before:0,after:240},
    children:[new TextRun({text:levelLbl+(unitName?' — '+unitName:''),bold:true,size:26,font:'Arial',color:'1E3A5F'})]
}));
children.push(new Paragraph({
    alignment:AlignmentType.CENTER,spacing:{before:0,after:600},
    children:[new TextRun({text:period.toUpperCase(),bold:true,size:30,font:'Arial',color:'374151'})]
}));
children.push(new Paragraph({
    border:{bottom:{style:BorderStyle.SINGLE,size:8,color:'1D4ED8',space:1}},children:[]
}));
children.push(new Paragraph({
    heading:HeadingLevel.HEADING_1,spacing:{before:360,after:120},
    children:[new TextRun({text:'BAB I — PENDAHULUAN',bold:true,size:28,font:'Arial',color:'1E3A5F'})]
}));
children.push(new Paragraph({
    spacing:{before:60,after:200},alignment:AlignmentType.JUSTIFIED,
    children:[new TextRun({text:pendah||'Laporan keperawatan adalah gambaran terhadap pelaksanaan kegiatan keperawatan yang mencakup ketenagaan, jumlah pasien, keberhasilan Management Asuhan Keperawatan Professional, pencapaian pelaksanaan RKA serta permasalahan terkait pelayanan keperawatan.',size:22,font:'Arial'})]
}));
children.push(new Paragraph({
    heading:HeadingLevel.HEADING_1,spacing:{before:360,after:120},
    children:[new TextRun({text:'BAB II — PEMBAHASAN',bold:true,size:28,font:'Arial',color:'1E3A5F'})]
}));

{$sectionsJs}

children.push(new Paragraph({
    heading:HeadingLevel.HEADING_1,spacing:{before:360,after:120},
    children:[new TextRun({text:'BAB III — SIMPULAN DAN PENUTUP',bold:true,size:28,font:'Arial',color:'1E3A5F'})]
}));
if(simpulan){children.push(new Paragraph({
    heading:HeadingLevel.HEADING_2,spacing:{before:120,after:60},
    children:[new TextRun({text:'A. Simpulan',bold:true,size:24,font:'Arial'})]
}));children.push(new Paragraph({
    alignment:AlignmentType.JUSTIFIED,spacing:{before:60,after:120},
    children:[new TextRun({text:simpulan,size:22,font:'Arial'})]
}));}
if(penutup){children.push(new Paragraph({
    heading:HeadingLevel.HEADING_2,spacing:{before:120,after:60},
    children:[new TextRun({text:'B. Penutup',bold:true,size:24,font:'Arial'})]
}));children.push(new Paragraph({
    alignment:AlignmentType.JUSTIFIED,spacing:{before:60,after:120},
    children:[new TextRun({text:penutup,size:22,font:'Arial'})]
}));}

children.push(new Paragraph({spacing:{before:600,after:0},alignment:AlignmentType.RIGHT,
    children:[new TextRun({text:'Denpasar, '+new Date().toLocaleDateString('id-ID',{day:'numeric',month:'long',year:'numeric'}),size:22,font:'Arial'})]}));
children.push(new Paragraph({spacing:{before:80,after:0},alignment:AlignmentType.RIGHT,
    children:[new TextRun({text:levelLbl,size:22,font:'Arial'})]}));
children.push(new Paragraph({spacing:{before:800,after:0},alignment:AlignmentType.RIGHT,
    children:[new TextRun({text:signedName,bold:true,size:22,font:'Arial',underline:{}})]}));
if(signedPos){children.push(new Paragraph({spacing:{before:60,after:0},alignment:AlignmentType.RIGHT,
    children:[new TextRun({text:signedPos,size:22,font:'Arial'})]}));}

const doc = new Document({
    styles:{
        default:{document:{run:{font:'Arial',size:22}}},
        paragraphStyles:[
            {id:'Heading1',name:'Heading 1',basedOn:'Normal',next:'Normal',quickFormat:true,
             run:{size:28,bold:true,font:'Arial',color:'1E3A5F'},
             paragraph:{spacing:{before:300,after:120},outlineLevel:0}},
            {id:'Heading2',name:'Heading 2',basedOn:'Normal',next:'Normal',quickFormat:true,
             run:{size:24,bold:true,font:'Arial'},
             paragraph:{spacing:{before:200,after:80},outlineLevel:1}},
        ]
    },
    sections:[{
        properties:{page:{size:{width:11906,height:16838},
            margin:{top:1440,right:1134,bottom:1440,left:1701}}},
        headers:{default:new Header({children:[
            new Paragraph({
                border:{bottom:{style:BorderStyle.SINGLE,size:4,color:'1D4ED8',space:6}},
                children:[
                    new TextRun({text:'RS Balimed \u2014 Sistem Keperawatan ITC Program',size:18,font:'Arial',color:'1D4ED8',bold:true}),
                    new TextRun({text:'     Laporan Bulanan '+period,size:18,font:'Arial',color:'666666'}),
                ]})
        ]})},
        footers:{default:new Footer({children:[
            new Paragraph({
                border:{top:{style:BorderStyle.SINGLE,size:4,color:'E2E8F0',space:6}},
                children:[
                    new TextRun({text:title+' \u2014 ',size:18,font:'Arial',color:'999999'}),
                    new TextRun({children:[new PageNumber()],size:18,font:'Arial',color:'1D4ED8',bold:true}),
                ]})
        ]})},
        children
    }]
});

Packer.toBuffer(doc).then(buf=>{
    fs.writeFileSync($jsOut, buf);
    process.exit(0);
}).catch(e=>{console.error(e.message);process.exit(1);});
JS;

file_put_contents($docxScript, $script2);

/* ── Run Node.js ── */
$nodeCmd = 'node ' . escapeshellarg($docxScript) . ' 2>&1';
exec($nodeCmd, $nodeOut, $nodeRet);

if ($nodeRet !== 0 || !file_exists($outFile) || filesize($outFile) < 100) {
    /* Fallback: HTML download jika node tidak tersedia */
    header('Content-Type: text/html; charset=utf-8');
    echo '<!DOCTYPE html><html lang="id"><head><meta charset="UTF-8"><title>'.e($report['title']).'</title>';
    echo '<style>body{font-family:Arial,sans-serif;margin:2cm 2.5cm;font-size:12pt;line-height:1.5}
    h1{color:#1E3A5F;font-size:14pt;margin-top:24pt}
    h2{font-size:12pt;margin-top:16pt;color:#1e293b}
    table{width:100%;border-collapse:collapse;margin:8pt 0;font-size:11pt}
    th{background:#1D4ED8;color:#fff;padding:6pt 8pt;text-align:left}
    td{border:1pt solid #ccc;padding:5pt 8pt;vertical-align:top}
    tr:nth-child(even) td{background:#f8fafc}
    .cover{text-align:center;padding-top:4cm}
    .sign{text-align:right;margin-top:2cm}
    @media print{body{margin:1.5cm 2cm}}</style>';
    echo '<body><div class="cover">';
    echo '<h1 style="font-size:18pt">LAPORAN BULANAN KEPERAWATAN</h1>';
    echo '<p style="font-size:14pt;font-weight:bold">'.e($levelLabel.($report['unit_name']?' — '.$report['unit_name']:'')).'</p>';
    echo '<p style="font-size:16pt;font-weight:bold">'.e($period).'</p>';
    echo '<hr style="border:2px solid #1D4ED8;margin:1cm 0"></div>';
    echo '<h1>BAB I — PENDAHULUAN</h1>';
    echo '<p style="text-align:justify">'.nl2br(e((string)($report['pendahuluan']??''))).'</p>';
    echo '<h1>BAB II — PEMBAHASAN</h1>';
    foreach($sections as $sec) {
        $rows = $sectionRows[(int)$sec['id']] ?? [];
        $ncols = ew_col_count((string)$sec['section_code'], $rLevel);
        echo '<h2>'.e((string)$sec['section_title']).'</h2>';
        if($sec['intro_text']) echo '<p style="text-align:justify">'.nl2br(e((string)$sec['intro_text'])).'</p>';
        if($rows) {
            $fbCols = ew_section_cols((string)$sec['section_code'],$rLevel);
            $fbDataCols = count($fbCols)-1;
            echo '<table><thead><tr>';
            foreach($fbCols as $fc) echo '<th>'.e($fc).'</th>';
            echo '</tr></thead><tbody>';
            foreach($rows as $ri=>$row) {
                echo '<tr><td>'.($ri+1).'</td>';
                for($c=1;$c<=$fbDataCols;$c++) echo '<td>'.nl2br(e((string)($row['col'.$c]??''))).'</td>';
                echo '</tr>';
            }
            echo '</tbody></table>';
        } else {
            echo '<p style="color:#999;font-style:italic">(Belum ada data)</p>';
        }
    }
    echo '<h1>BAB III — SIMPULAN DAN PENUTUP</h1>';
    if($report['simpulan']) { echo '<h2>A. Simpulan</h2><p style="text-align:justify">'.nl2br(e((string)$report['simpulan'])).'</p>'; }
    if($report['penutup'])  { echo '<h2>B. Penutup</h2><p style="text-align:justify">'.nl2br(e((string)$report['penutup'])).'</p>'; }
    echo '<div class="sign">';
    echo '<p>Denpasar, '.date('d F Y').'</p>';
    echo '<p>'.e($levelLabel).'</p>';
    echo '<br><br><br>';
    echo '<p><strong><u>'.e((string)$report['signed_name']).'</u></strong></p>';
    if($report['signed_position']) echo '<p>'.e((string)$report['signed_position']).'</p>';
    echo '</div>';
    echo '<script>window.onload=function(){window.print();}</script>';
    echo '</body></html>';
    @unlink($docxScript);
    exit;
}

/* ── Serve file ── */
$filename = preg_replace('/[^A-Za-z0-9\-_]/','_', strtolower((string)$report['title'])) . '.docx';
while(ob_get_level()>0) ob_end_clean();
header('Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Content-Length: ' . filesize($outFile));
header('Cache-Control: max-age=0');
readfile($outFile);
@unlink($outFile);
@unlink($docxScript);
exit;