<?php
declare(strict_types=1);

namespace App\Domain\Media;

/**
 * GD-basierter Bildprozessor als Fallback
 */
final readonly class GdImageProcessor 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 GD
     *
     * @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
    {
        $imageInfo = getimagesize($sourcePath);
        if ($imageInfo === false) {
            throw new \RuntimeException('Could not get image info');
        }

        $source = $this->createImageFromFile($sourcePath, $imageInfo[2]);

        $originalWidth = imagesx($source);
        $originalHeight = imagesy($source);

        // Nur die Breite begrenzen, Höhe proportional berechnen
        $scale = $maxWidth / $originalWidth;

        // Nur skalieren wenn nötig
        if ($originalWidth > $maxWidth) {
            $newWidth = $maxWidth;
            $newHeight = (int)round($originalHeight * $scale);
        } else {
            $newWidth = $originalWidth;
            $newHeight = $originalHeight;
            // Kopie für konsistente Verarbeitung
            $dst = imagecreatetruecolor($newWidth, $newHeight);
            imagecopy($dst, $source, 0, 0, 0, 0, $newWidth, $newHeight);
            imagedestroy($source);
            $this->saveImageInFormat($dst, $destination, $format);
            imagedestroy($dst);
            return ['width' => $newWidth, 'height' => $newHeight];
        }

        $dst = imagecreatetruecolor($newWidth, $newHeight);

        // Transparenz erhalten
        if ($format === ImageFormat::WEBP || $imageInfo[2] === IMAGETYPE_PNG) {
            imagealphablending($dst, false);
            imagesavealpha($dst, true);
            $transparent = imagecolorallocatealpha($dst, 0, 0, 0, 127);
            imagefilledrectangle($dst, 0, 0, $newWidth, $newHeight, $transparent);
        }

        // Bessere Qualität mit IMAGICK_FILTER_LANCZOS Equivalent
        imagecopyresampled($dst, $source, 0, 0, 0, 0, $newWidth, $newHeight, $originalWidth, $originalHeight);

        // Einfache Schärfung bei Verkleinerung
        if ($scale < 0.8) {
            $this->sharpenImage($dst);
        }

        $this->saveImageInFormat($dst, $destination, $format);

        imagedestroy($source);
        imagedestroy($dst);

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

    /**
     * Erstellt ein Bild aus einer Datei
     */
    private function createImageFromFile(string $sourcePath, int $imageType): \GdImage
    {
        return match ($imageType) {
            IMAGETYPE_PNG => imagecreatefrompng($sourcePath),
            IMAGETYPE_JPEG => imagecreatefromjpeg($sourcePath),
            IMAGETYPE_GIF => imagecreatefromgif($sourcePath),
            IMAGETYPE_WEBP => imagecreatefromwebp($sourcePath),
            default => throw new \RuntimeException('Unsupported image type'),
        };
    }

    /**
     * Speichert ein Bild im angegebenen Format
     */
    private function saveImageInFormat(\GdImage $image, string $destination, ImageFormat $format): void
    {
        match ($format) {
            ImageFormat::JPEG => imagejpeg($image, $destination, $format->getQuality()),
            ImageFormat::WEBP => imagewebp($image, $destination, $format->getQuality()),
            ImageFormat::AVIF => function_exists('imageavif')
                ? imageavif($image, $destination, $format->getQuality())
                : imagewebp($image, $destination, $format->getQuality()), // Fallback auf WebP
        };
    }

    /**
     * Einfache Schärfung mit Konvolution
     */
    private function sharpenImage(\GdImage $image): void
    {
        // Einfacher Sharpen-Kernel
        $sharpenMatrix = [
            [-1, -1, -1],
            [-1, 16, -1],
            [-1, -1, -1]
        ];
        $divisor = 8;
        $offset = 0;

        if (function_exists('imageconvolution')) {
            imageconvolution($image, $sharpenMatrix, $divisor, $offset);
        }
    }
}
