<?php

declare(strict_types=1);

namespace Drupal\native_observability_report\Service;

use Dompdf\Dompdf;
use Dompdf\Options;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Process\Process;

/**
 * Exports printable HTML documents as PDF when a supported renderer is present.
 */
final class ObservabilityReportPdfExporter {

  /**
   * Builds a PDF response using a browser renderer when available.
   */
  public function buildPdfResponse(string $html, string $filename): Response {
    $chromiumPdf = $this->buildChromiumPdf($html);
    if ($chromiumPdf !== NULL) {
      return new Response($chromiumPdf, 200, [
        'Content-Type' => 'application/pdf',
        'Content-Disposition' => 'inline; filename="' . $filename . '"',
      ]);
    }

    if (class_exists('Dompdf\\Dompdf')) {
      $options = new Options();
      $options->set('isRemoteEnabled', FALSE);
      $dompdf = new Dompdf($options);
      $dompdf->loadHtml($html, 'UTF-8');
      $dompdf->setPaper('A4');
      $dompdf->render();

      return new Response($dompdf->output(), 200, [
        'Content-Type' => 'application/pdf',
        'Content-Disposition' => 'inline; filename="' . $filename . '"',
      ]);
    }

    return new Response($html, 200, [
      'Content-Type' => 'text/html; charset=UTF-8',
    ]);
  }

  /**
   * Builds a PDF using headless Chromium when available.
   */
  private function buildChromiumPdf(string $html): ?string {
    $binary = $this->resolveChromiumBinary();
    if ($binary === NULL) {
      return NULL;
    }

    $directory = sys_get_temp_dir() . '/native-observability-report-' . uniqid('', TRUE);
    if (!@mkdir($directory, 0700, TRUE) && !is_dir($directory)) {
      return NULL;
    }

    $htmlPath = $directory . '/report.html';
    $pdfPath = $directory . '/report.pdf';
    file_put_contents($htmlPath, $html);

    $process = new Process([
      $binary,
      '--headless=new',
      '--disable-gpu',
      '--disable-dev-shm-usage',
      '--no-sandbox',
      '--allow-file-access-from-files',
      '--run-all-compositor-stages-before-draw',
      '--virtual-time-budget=12000',
      '--print-to-pdf=' . $pdfPath,
      '--print-to-pdf-no-header',
      'file://' . $htmlPath,
    ]);
    $process->setTimeout(45);

    try {
      $process->mustRun();
      if (is_readable($pdfPath)) {
        $pdf = (string) file_get_contents($pdfPath);
        $this->cleanupTemporaryDirectory($directory);
        return $pdf !== '' ? $pdf : NULL;
      }
    }
    catch (\Throwable) {
      // Fall back to the next available renderer.
    }

    $this->cleanupTemporaryDirectory($directory);
    return NULL;
  }

  /**
   * Resolves the Chromium executable path.
   */
  private function resolveChromiumBinary(): ?string {
    foreach (['/usr/bin/chromium', '/usr/bin/chromium-browser', '/usr/bin/google-chrome'] as $candidate) {
      if (is_executable($candidate)) {
        return $candidate;
      }
    }

    return NULL;
  }

  /**
   * Removes a temporary export directory.
   */
  private function cleanupTemporaryDirectory(string $directory): void {
    if (!is_dir($directory)) {
      return;
    }

    $paths = new \RecursiveIteratorIterator(
      new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS),
      \RecursiveIteratorIterator::CHILD_FIRST,
    );

    foreach ($paths as $path) {
      $path->isDir() ? @rmdir($path->getPathname()) : @unlink($path->getPathname());
    }

    @rmdir($directory);
  }

}
