<?php

declare(strict_types=1);

/*
 * This file is part of the Ferienpass package.
 *
 * (c) Richard Henkenjohann <richard@ferienpass.online>
 *
 * For more information visit the project website <https://ferienpass.online>
 * or the documentation under <https://docs.ferienpass.online>.
 */

namespace Ferienpass\CoreBundle\Export\Offer\PrintSheet;

use Contao\CoreBundle\Filesystem\VirtualFilesystemInterface;
use Ferienpass\CoreBundle\Export\Offer\OffersExportInterface;
use Ferienpass\CoreBundle\Export\PrintSheetPreview;
use Mpdf\Mpdf;
use Mpdf\Output\Destination as MpdfDestination;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\DependencyInjection\Attribute\Exclude;
use Twig\Environment;

#[Exclude]
class PdfExport implements OffersExportInterface
{
    public function __construct(private readonly array $config, private readonly string $template, private readonly VirtualFilesystemInterface $exportStorage, private readonly Environment $twig, #[Autowire(param: 'kernel.project_dir')] private readonly string $projectDir, #[Autowire(param: 'kernel.cache_dir')] private string $cacheDir)
    {
    }

    public static function getName(): string
    {
        // Managed via DI extension
        return '';
    }

    public function generate(iterable $offers, ?string $destination = null, bool $stream = false): mixed
    {
        ini_set('pcre.backtrack_limit', '100000000');

        $html = $this->render($offers);
        $hash = md5($html);

        $destination ??= \sprintf('pdf-%s.pdf', $hash);
        $path = 'pdf/'.$destination;

        $content = $this->createPdf($html, $this->config);
        if ($stream) {
            $stream = fopen('php://temp', 'r+');
            fwrite($stream, $content);
            rewind($stream);

            return $stream;
        }

        $this->exportStorage->write($path, $content);

        return $path;
    }

    /**
     * Render the print-sheet HTML (the markup that is later fed to mPDF).
     *
     * Exposed so the admin live preview can show the real template output
     * without paying the cost of generating an actual PDF on every keystroke.
     */
    public function html(iterable $offers): string
    {
        return $this->render($offers);
    }

    /**
     * Browser-friendly variant of the print-sheet markup for the admin live
     * preview: resolves images, converts mPDF-only units/colours/tags and
     * positions the page header/footer (see PrintSheetPreview).
     */
    public function previewHtml(iterable $offers): string
    {
        return new PrintSheetPreview($this->projectDir)->transform($this->render($offers));
    }

    private function render(iterable $items): string
    {
        return $this->twig->render($this->template, [
            'items' => $items,
        ]);
    }

    private function createPdf(string $html, array $mPdfConfig): string
    {
        $mPdfConfig = array_merge(['tempDir' => $this->cacheDir], $mPdfConfig);

        // Forbid mPDF from fetching remote resources (<img>, url()) to prevent SSRF.
        // Only the local "file" stream wrapper is allowed; "http"/"https" are dropped.
        $mPdfConfig['whitelistStreamWrappers'] = ['file'];

        // disable errors for now (undefined array index inside mpdf)
        @$mPdf = new Mpdf($mPdfConfig);
        @$mPdf->SetBasePath($this->projectDir);
        @$mPdf->WriteHTML($html);

        return $mPdf->Output(dest: MpdfDestination::STRING_RETURN);
    }
}
