<?php

namespace App\Domains\Unterschrift\Actions\SignDocument;

use Imagick;
use ImagickPixel;
use RuntimeException;

/**
 * imagick-Portierung des falsisign-Ablaufs (sauberer Scan, Modus -c):
 * PDF -> Seiten rastern -> Unterschrift auf Zielseite compositen -> zurueck zu PDF.
 * Kein shell_exec; nutzt die imagick-Extension (+ ghostscript-Delegate).
 */
class SignDocumentCommand
{
    /** Raster-Aufloesung fuer die Ausgabe (analog falsisign density 150). */
    private const OUTPUT_DPI = 150;

    /** Maximale Seitenzahl, die verarbeitet wird. */
    public const MAX_PAGES = 30;

    public function __construct(private readonly SignDocumentRequest $request) {}

    public function execute(): SignDocumentResponse
    {
        $signature = $this->prepareOverlay($this->request->signaturPath, 'Unterschrift-Bild');

        $stamp = $this->request->stempelPath !== null
            ? $this->prepareOverlay($this->request->stempelPath, 'Stempel-Bild')
            : null;

        $doc = new Imagick();
        $doc->setResolution(self::OUTPUT_DPI, self::OUTPUT_DPI);

        try {
            $doc->readImage($this->request->pdfPath);
        } catch (\ImagickException $e) {
            $signature->clear();
            $stamp?->clear();
            throw new RuntimeException('Das PDF konnte nicht gelesen werden.', 0, $e);
        }

        $pageCount = $doc->getNumberImages();

        if ($pageCount < 1) {
            throw new RuntimeException('Das PDF enthält keine Seiten.');
        }

        if ($pageCount > self::MAX_PAGES) {
            throw new RuntimeException('Das PDF hat zu viele Seiten (max. ' . self::MAX_PAGES . ').');
        }

        $targetIndex = min($this->request->page, $pageCount) - 1;

        for ($i = 0; $i < $pageCount; $i++) {
            $doc->setIteratorIndex($i);

            // Transparenten PDF-Hintergrund gegen Weiss aufloesen (sonst schwarz).
            $doc->setImageBackgroundColor(new ImagickPixel('white'));
            if ($doc->getImageAlphaChannel()) {
                $doc->setImageAlphaChannel(Imagick::ALPHACHANNEL_REMOVE);
            }

            if ($i === $targetIndex) {
                // Stempel zuerst, damit die Unterschrift ggf. darueber liegt.
                if ($stamp !== null) {
                    $this->composeOverlay(
                        $doc,
                        $stamp,
                        $this->request->stempelXFrac,
                        $this->request->stempelYFrac,
                        $this->request->stempelWidthFrac
                    );
                }

                $this->composeOverlay(
                    $doc,
                    $signature,
                    $this->request->xFrac,
                    $this->request->yFrac,
                    $this->request->widthFrac
                );
            }

            // Sauberer Scan-Look: Graustufen, aber kein Rauschen/keine Rotation.
            if ($this->request->graustufen) {
                $doc->transformImageColorspace(Imagick::COLORSPACE_GRAY);
            }

            // Format pro Seite setzen, damit getImagesBlob() ein mehrseitiges PDF liefert.
            $doc->setImageFormat('pdf');
        }

        $signature->clear();
        $stamp?->clear();

        $doc->resetIterator();
        $pdfContent = $doc->getImagesBlob();
        $doc->clear();

        return new SignDocumentResponse(
            pdfContent: $pdfContent,
            filename:   $this->request->filename,
        );
    }

    /** Overlay (Unterschrift/Stempel) laden, Weiss transparent machen und auf den Inhalt zuschneiden. */
    private function prepareOverlay(string $path, string $label): Imagick
    {
        $overlay = new Imagick();

        try {
            $overlay->readImage($path);
        } catch (\ImagickException $e) {
            throw new RuntimeException('Das ' . $label . ' konnte nicht gelesen werden.', 0, $e);
        }

        $overlay->setImageFormat('png');
        $overlay->setImageAlphaChannel(Imagick::ALPHACHANNEL_ACTIVATE);

        $range = $overlay->getQuantumRange()['quantumRangeLong'];

        // Weiss (inkl. leicht abweichender Scan-/Foto-Toene) transparent setzen.
        $overlay->transparentPaintImage(new ImagickPixel('white'), 0.0, 0.15 * $range, false);

        // Ueberfluessigen Rand entfernen, damit die Positionierung exakt ist.
        $overlay->trimImage(0.1 * $range);
        $overlay->setImagePage(0, 0, 0, 0);

        return $overlay;
    }

    /** Overlay skaliert an der gewuenschten Position auf die aktuelle Seite legen. */
    private function composeOverlay(Imagick $doc, Imagick $overlay, float $xFrac, float $yFrac, float $widthFrac): void
    {
        $pageWidth  = $doc->getImageWidth();
        $pageHeight = $doc->getImageHeight();

        $targetWidth = max(1, (int) round($widthFrac * $pageWidth));

        $placed = clone $overlay;
        $placed->resizeImage($targetWidth, 0, Imagick::FILTER_LANCZOS, 1);

        $x = (int) round($xFrac * $pageWidth);
        $y = (int) round($yFrac * $pageHeight);

        $doc->compositeImage($placed, Imagick::COMPOSITE_OVER, $x, $y);
        $placed->clear();
    }
}
