<?php
declare(strict_types=1);

namespace App\Domain\Media;

use Imagick;
use ImagickException;
use ImagickPixel;

/**
 * ImageMagick-basierter Bildprozessor für optimale Bildqualität
 */
final readonly class ImagickImageProcessor implements ImageProcessorInterface
{
    /**
     * @inheritDoc
     */
    public function createAllVariants(Image $image): array
    {
        $variants = [];

        foreach (ImageVariantConfig::getAllVariants() as $variantConfig) {
            try {
                $variant = $this->createVariant($image, $variantConfig);
                $variants[] = $variant;
            } catch (\Exception $e) {
                error_log("Failed to create variant {$variantConfig['type']->value}_{$variantConfig['size']->value}_{$variantConfig['format']->value}: " . $e->getMessage());
            }
        }

        return $variants;
    }

    /**
     * @inheritDoc
     */
    public function createVariantsForType(Image $image, ImageVariantType $type): array
    {
        $variants = [];

        foreach (ImageVariantConfig::getVariantsForType($type) as $variantConfig) {
            try {
                $variant = $this->createVariant($image, $variantConfig);
                $variants[] = $variant;
            } catch (\Exception $e) {
                error_log("Failed to create variant {$variantConfig['type']->value}_{$variantConfig['size']->value}_{$variantConfig['format']->value}: " . $e->getMessage());
            }
        }

        return $variants;
    }

    /**
     * Erstellt eine einzelne Bildvariante
     *
     * @param Image $image
     * @param array $config
     * @return ImageVariant
     */
    private function createVariant(Image $image, array $config): ImageVariant
    {
        $sourcePath = $image->path . $image->filename;

        /** @var ImageVariantType $type */
        /** @var ImageSize $size */
        /** @var ImageFormat $format */
        $type = $config['type'];
        $size = $config['size'];
        $format = $config['format'];
        $width = $config['width'];

        $filename = $this->generateVariantFilename(
            $image->filename,
            $type->value,
            $size->value,
            $format->value
        );

        $destination = $image->path . $filename;

        $actualDimensions = $this->processImage($sourcePath, $destination, $width, $format);

        $fileSize = filesize($destination);

        return new ImageVariant(
            imageId: $image->ulid,
            variantType: $type->value,
            size: $size->value,
            format: $format->value,
            mimeType: $format->getMimeType(),
            fileSize: $fileSize,
            width: $actualDimensions['width'],
            height: $actualDimensions['height'],
            filename: $filename,
            path: $image->path,
        );
    }

    /**
     * Generiert einen Dateinamen für eine Bildvariante
     */
    private function generateVariantFilename(string $originalFilename, string $type, string $size, string $format): string
    {
        $pathInfo = pathinfo($originalFilename);
        return $pathInfo['filename'] . "_{$type}_{$size}.{$format}";
    }

    /**
     * Verarbeitet das Bild mit ImageMagick
     *
     * @param string $sourcePath
     * @param string $destination
     * @param int $maxWidth
     * @param ImageFormat $format
     * @return array{width: int, height: int}
     */
    private function processImage(string $sourcePath, string $destination, int $maxWidth, ImageFormat $format): array
    {
        try {
            $imagick = new Imagick($sourcePath);

            // Bildorientierung korrigieren (EXIF)
            $imagick->autoOrientImage();

            // Farbprofil entfernen für kleinere Dateien
            $imagick->stripImage();

            // Bessere Resampling-Methode
            $imagick->setImageResolution(72, 72);

            // Original-Dimensionen speichern
            $originalWidth = $imagick->getImageWidth();
            $originalHeight = $imagick->getImageHeight();

            // Skalierung nur wenn nötig
            if ($originalWidth > $maxWidth) {
                $scale = $maxWidth / $originalWidth;
                $newWidth = $maxWidth;
                $newHeight = (int)round($originalHeight * $scale);

                // Lanczos-Filter für beste Qualität
                $imagick->resizeImage($newWidth, $newHeight, Imagick::FILTER_LANCZOS, 1);

                // Schärfen bei Verkleinerung
                $this->applySharpeningAfterResize($imagick, $originalWidth, $newWidth);
            } else {
                $newWidth = $originalWidth;
                $newHeight = $originalHeight;
            }

            // Format-spezifische Optimierungen
            $this->optimizeForFormat($imagick, $format);

            // Speichern
            $imagick->writeImage($destination);

            $imagick->destroy();

            return ['width' => $newWidth, 'height' => $newHeight];

        } catch (ImagickException $e) {
            throw new \RuntimeException('ImageMagick error: ' . $e->getMessage(), 0, $e);
        }
    }

    /**
     * Wendet formatspezifische Optimierungen an
     */
    private function optimizeForFormat(Imagick $imagick, ImageFormat $format): void
    {
        match ($format) {
            ImageFormat::JPEG => $this->optimizeJpeg($imagick),
            ImageFormat::WEBP => $this->optimizeWebP($imagick),
            ImageFormat::AVIF => $this->optimizeAvif($imagick),
        };
    }

    /**
     * Optimiert JPEG-Einstellungen
     */
    private function optimizeJpeg(Imagick $imagick): void
    {
        $imagick->setImageFormat('jpeg');
        $imagick->setImageCompressionQuality(ImageFormat::JPEG->getQuality());
        $imagick->setImageCompression(Imagick::COMPRESSION_JPEG);

        // Progressive JPEG für bessere Ladezeiten
        $imagick->setInterlaceScheme(Imagick::INTERLACE_PLANE);

        // Chroma-Subsampling für kleinere Dateien
        $imagick->setSamplingFactors(['2x2', '1x1', '1x1']);
    }

    /**
     * Optimiert WebP-Einstellungen
     */
    private function optimizeWebP(Imagick $imagick): void
    {
        $imagick->setImageFormat('webp');
        $imagick->setImageCompressionQuality(ImageFormat::WEBP->getQuality());

        // WebP-spezifische Optionen
        $imagick->setOption('webp:lossless', 'false');
        $imagick->setOption('webp:alpha-quality', '85');
        $imagick->setOption('webp:method', '4'); // Balancierte Kompression
    }

    /**
     * Optimiert AVIF-Einstellungen
     */
    private function optimizeAvif(Imagick $imagick): void
    {
        // Wenn die ImageMagick-Version AVIF direkt unterstützt
        if (in_array('AVIF', Imagick::queryFormats())) {
            $imagick->setImageFormat('avif');
            $imagick->setImageCompressionQuality(ImageFormat::AVIF->getQuality());

            // AVIF-spezifische Optionen, falls unterstützt
            $imagick->setOption('avif:speed', '6'); // Balancierte Kompression
        } else {
            // Fallback: Speichern als WebP wenn AVIF nicht unterstützt wird
            $this->optimizeWebP($imagick);
        }
    }

    /**
     * Wendet intelligente Schärfung an, um Details zu verbessern
     */
    private function applySharpeningAfterResize(Imagick $imagick, int $originalWidth, int $newWidth): void
    {
        $scaleFactor = $newWidth / $originalWidth;

        // Nur bei signifikanter Verkleinerung schärfen
        if ($scaleFactor < 0.8) {
            // Stärke der Schärfung an die Verkleinerung anpassen
            $radius = 0.5;
            $sigma = 0.5;
            $amount = 0.8 + ((1 - $scaleFactor) * 0.3); // Mehr Schärfung bei größerer Verkleinerung
            $threshold = 0.05;

            $imagick->unsharpMaskImage($radius, $sigma, $amount, $threshold);
        }
    }
}
