<?php
// generate_certificate_pdf.php
// Liftek Iraq — Server-side PDF generation endpoint (Dompdf v3)
//
// HOW IT WORKS (no HTTP loopback):
//   1. Validates the job exists in the DB.
//   2. Captures the PDF-ready HTML from generate_certificate.php using PHP
//      output buffering (ob_start / ob_get_clean) — zero HTTP loopback
//      requests, so it works on XAMPP where loopback causes deadlocks.
//   3. Passes the self-contained HTML to Dompdf and streams the PDF.
//
// ACCESS CONTROL:
//   ?token=XXXX  — public path (no login required).
//   ?id=X        — internal staff path. Login required.
//
// SECURITY CONTROLS:
//   - enable_remote = false  → Dompdf never makes outbound HTTP requests (SSRF).
//   - isPhpEnabled  = false  → PHP never executes inside Dompdf templates.
//   - chroot = project root  → Dompdf cannot read files outside the project.
//   - All images are base64 data URIs (produced by generate_certificate.php
//     in pdf mode) — no file-system paths ever reach Dompdf's renderer.
//   - Job ID is cast to (int); token compared as string against DB value.
//   - Filename sanitised with preg_replace before it reaches the header.

declare(strict_types=1);

ini_set('display_errors', '0');
error_reporting(0);

require_once __DIR__ . '/db.php';

// ---------------------------------------------------------------------------
// DOMPDF BOOTSTRAP
// ---------------------------------------------------------------------------
if (!file_exists(__DIR__ . '/vendor/autoload.php')) {
    http_response_code(500);
    exit('PDF generation is not available: run "composer install" in the project root first.');
}
require_once __DIR__ . '/vendor/autoload.php';

if (!class_exists(\Dompdf\Dompdf::class)) {
    http_response_code(500);
    exit('Dompdf is not installed. Run: composer require dompdf/dompdf');
}

use Dompdf\Dompdf;
use Dompdf\Options;

// ---------------------------------------------------------------------------
// 1. PARSE & VALIDATE INPUT PARAMETERS
// ---------------------------------------------------------------------------
$cert_token = isset($_GET['token']) ? trim($_GET['token']) : null;
$job_id     = isset($_GET['id'])    ? (int) $_GET['id']   : 0;

if (empty($cert_token) && $job_id <= 0) {
    http_response_code(400);
    exit('Bad request: supply either ?token= or ?id=');
}

// ---------------------------------------------------------------------------
// 2. ACCESS CONTROL — same rules as generate_certificate.php
//    ?id= path requires login; ?token= path is public.
// ---------------------------------------------------------------------------
if (empty($cert_token) && $job_id > 0) {
    require_once __DIR__ . '/includes/auth.php';
    require_login();
}

// ---------------------------------------------------------------------------
// 3. PRE-VALIDATE JOB IN DATABASE
// ---------------------------------------------------------------------------
try {
    if (!empty($cert_token)) {
        $stmt = $pdo->prepare("SELECT ID, Job_No FROM tbl_jobs WHERE CertToken = ? LIMIT 1");
        $stmt->execute([$cert_token]);
    } else {
        $stmt = $pdo->prepare("SELECT ID, Job_No FROM tbl_jobs WHERE ID = ? LIMIT 1");
        $stmt->execute([$job_id]);
    }
    $jobRecord = $stmt->fetch(PDO::FETCH_ASSOC);
} catch (\PDOException $e) {
    error_log('[generate_certificate_pdf] DB error: ' . $e->getMessage());
    http_response_code(500);
    exit('A server error occurred. Please try again later.');
}

if (!$jobRecord) {
    http_response_code(404);
    exit('Certificate not found. It may have been removed or the link is invalid.');
}

$job_pk_id = (int) $jobRecord['ID'];
$job_no    = preg_replace('/[^A-Za-z0-9_\-]/', '_', $jobRecord['Job_No'] ?? 'Certificate');

// ---------------------------------------------------------------------------
// 4. GENERATE HTML VIA OUTPUT BUFFERING — zero HTTP loopback
//    Set $_GET so generate_certificate.php picks up the right parameters,
//    then capture its entire HTML output with ob_start / ob_get_clean.
// ---------------------------------------------------------------------------

// Save the original $_GET so we can restore it after capture (defensive).
$_orig_GET = $_GET;

if (!empty($cert_token)) {
    $_GET = ['token' => $cert_token, 'mode' => 'pdf'];
} else {
    $_GET = ['id' => (string) $job_pk_id, 'mode' => 'pdf'];
}

// generate_certificate.php checks require_login() for ?id= paths.
// We already verified auth above, so we force-load auth.php so that
// current_user() / require_login() are available (idempotent: require_once).
if (!function_exists('require_login')) {
    require_once __DIR__ . '/includes/auth.php';
}

// Capture HTML output.
ob_start();
// Use include (not require_once) so the template body re-runs each call.
// Function declarations inside generate_certificate.php are guarded by
// defined('CERT_RENDER_HELPERS_LOADED') so they are declared only once.
include __DIR__ . '/generate_certificate.php';
$html = ob_get_clean();

// Restore $_GET.
$_GET = $_orig_GET;

if ($html === false || trim($html) === '') {
    http_response_code(500);
    exit('Could not render certificate HTML. Please check server logs.');
}

// ---------------------------------------------------------------------------
// 5. RENDER HTML → PDF WITH DOMPDF
// ---------------------------------------------------------------------------
$options = new Options();
$options->set('isRemoteEnabled',      false); // SSRF prevention.
$options->set('isPhpEnabled',         false); // No PHP inside Dompdf content.
$options->set('isHtml5ParserEnabled', true);
$options->set('chroot',               realpath(__DIR__)); // File-system sandboxing.
$options->set('defaultFont',          'DejaVu Sans');
// BUG FIX: generate_certificate.php already ships a correct `@media print`
// block that zeroes out .cert-paper's box-shadow/border/20mm bottom margin
// (exactly what a print layout needs) — but Dompdf's default media type is
// "screen", so it was silently ignoring that block. Every cert-paper section
// was therefore ~20mm taller than one true A4 page, and Dompdf auto-paginated
// the overflow onto extra near-blank pages (the "missing content / extra
// pages" bug). Telling Dompdf to render as print picks up the existing CSS
// with zero HTML changes needed.
$options->set('defaultMediaType',     'print');
// DEFENSE-IN-DEPTH for the Arabic font: Dompdf's @font-face handling still
// caches computed font metrics on disk (font family cache), even when the
// font itself is supplied as a data URI. Its default cache directory lives
// inside vendor/dompdf/dompdf/lib/fonts, which is not always writable by
// the web-server user after a fresh deploy/composer install. Point it at a
// project-local folder we create and can guarantee is writable; if that
// fails for any reason, we simply skip the option and Dompdf falls back to
// its own default (identical to previous behaviour — never a fatal risk).
$fontCacheDir = __DIR__ . '/storage/dompdf_font_cache';
if (!is_dir($fontCacheDir)) {
    @mkdir($fontCacheDir, 0775, true);
}
if (is_dir($fontCacheDir) && is_writable($fontCacheDir)) {
    $options->set('fontDir',   $fontCacheDir);
    $options->set('fontCache', $fontCacheDir);
}

try {
    $dompdf = new Dompdf($options);
    $dompdf->loadHtml($html, 'UTF-8');
    $dompdf->setPaper('A4', 'portrait');
    $dompdf->render();
} catch (\Throwable $e) {
    // Most common cause: the GD PHP extension is disabled, so Dompdf cannot
    // decode the embedded PNG/JPEG images (certificate logo, photos, QR
    // code). error_log() keeps the raw message out of the HTTP response for
    // regular/public visitors; a logged-in admin gets a short, actionable
    // hint (never the raw exception text) to avoid leaking server internals.
    error_log('[generate_certificate_pdf] Dompdf render failed for job ' . $job_pk_id . ': ' . $e->getMessage());
    http_response_code(500);

    $isAdmin = false;
    if (function_exists('current_user')) {
        $__me = current_user();
        $isAdmin = $__me && ($__me['role'] ?? '') === 'admin';
    }

    if ($isAdmin) {
        exit('PDF generation failed. This is almost always caused by the PHP "gd" extension being disabled in php.ini — Dompdf needs it to embed images (logo, photos, QR code). Enable "extension=gd" in php.ini, restart Apache, and try again. (Admin-only diagnostic message — regular users see a generic error.)');
    }
    exit('Could not generate the PDF. Please try again later or contact support.');
}

// ---------------------------------------------------------------------------
// 6. STREAM THE PDF TO THE BROWSER
// ---------------------------------------------------------------------------
$safeFilename = 'Certificate_' . $job_no . '.pdf';

if (ob_get_level() > 0) {
    ob_end_clean();
}

header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="' . $safeFilename . '"');
header('Cache-Control: private, max-age=0, must-revalidate');
header('Pragma: public');

echo $dompdf->output();
exit;
