<?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\Payments;

use Ferienpass\CoreBundle\Entity\Payment;
use Ferienpass\CoreBundle\Payments\PaymentMethodResolver;
use Mpdf\Mpdf;
use Mpdf\Output\Destination as MpdfDestination;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\Filesystem\Filesystem;
use Twig\Environment as TwigEnvironment;

/**
 * "Kassenbericht": a cash report summing the given (paid) payments per
 * payment method, with the active filters printed at the top so the document
 * is self-documenting.
 */
final readonly class CashReportPdfExport
{
    public function __construct(
        private Filesystem $filesystem,
        private TwigEnvironment $twig,
        private PaymentMethodResolver $methodResolver,
        #[Autowire(param: 'kernel.cache_dir')]
        private string $cacheDir,
    ) {
    }

    /**
     * @param iterable<Payment>     $payments   payments to summarise (expected: status PAID)
     * @param array<string, string> $filterInfo ordered label => value lines describing the active filters
     */
    public function generate(iterable $payments, array $filterInfo = [], ?string $destination = null): string
    {
        $groups = [];
        $total = 0;
        $count = 0;

        foreach ($payments as $payment) {
            $method = $this->methodResolver->resolve($payment);
            $groups[$method] ??= ['count' => 0, 'sum' => 0];
            ++$groups[$method]['count'];
            $groups[$method]['sum'] += $payment->getTotalAmount();

            $total += $payment->getTotalAmount();
            ++$count;
        }

        ksort($groups);

        $html = $this->twig->render('@FerienpassCore/Payments/Pdf/CashReport.html.twig', [
            'groups' => $groups,
            'total' => $total,
            'count' => $count,
            'filterInfo' => $filterInfo,
        ]);

        $pdfPath = \sprintf('%s/kassenbericht-%s.pdf', sys_get_temp_dir(), md5($html));

        $this->createPdf($pdfPath, $html);

        if (null !== $destination) {
            $this->filesystem->copy($pdfPath, $destination, true);
        }

        return $destination ?? $pdfPath;
    }

    private function createPdf(string $path, string $html, array $mPdfConfig = []): void
    {
        if (file_exists($path)) {
            return;
        }

        $mPdfConfig = array_merge([
            'tempDir' => $this->cacheDir,
            'default_font_size' => 12,
            'default_font' => 'roboto',
            'fontDir' => \dirname(__DIR__).'/../../fonts/Roboto',
            'fontdata' => [
                'roboto' => [
                    'R' => 'Roboto-Regular.ttf',
                    'B' => 'Roboto-Bold.ttf',
                    'I' => 'Roboto-Italic.ttf',
                ],
                'roboto-light' => [
                    'R' => 'Roboto-Light.ttf',
                    'I' => 'Roboto-LightItalic.ttf',
                ],
                'roboto-thin' => [
                    'R' => 'Roboto-Thin.ttf',
                    'I' => 'Roboto-ThinItalic.ttf',
                ],
            ],
        ], $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'];

        $mPdf = new Mpdf($mPdfConfig);

        $mPdf->WriteHTML($html);

        $mPdf->Output($path, MpdfDestination::FILE);
    }
}
