<?php declare(strict_types=1);

namespace App\Engine\Thumbnail;

class Thumbnail
{

    protected const THUMBNAIL_EXTENSION = "webp";

    protected ?\Intervention\Image\ImageManager $imageManager = null;
    protected ?string $sourceAbsolutePath = null;
    protected ?string $sourceRelativePath = null;
    protected ?int $width = null;
    protected ?int $height = null;
    protected SizeOperation $sizeOperation = SizeOperation::SCALE_KEEP_ASCPECT_RATIO;
    protected ?string $alt = null;

    private readonly string $absolutePathToPublicDirectory;
    private readonly string $absoluteThumbsDirectory;
    private readonly string $relativeThumbsDirectory;

    protected function __construct()
    {
        /**
         * Hack kvůli statické třídě.
         *
         * @var \Symfony\Component\HttpKernel\KernelInterface $app
         */
        global $app;
        $this->absolutePathToPublicDirectory = $app->getProjectDir() . "/public";
        $this->relativeThumbsDirectory = "data/storage/thumbs";
        $this->absoluteThumbsDirectory = "$this->absolutePathToPublicDirectory/$this->relativeThumbsDirectory";
    }

    public static function create(): static
    {
        return new static();
    }

    public function sizeOperation(SizeOperation $sizeOperation): static
    {
        $this->sizeOperation = $sizeOperation;
        return $this;
    }

    public function fromRelativePath(string $path): static
    {
        $this->sourceRelativePath = $path;
        $this->sourceAbsolutePath = "$this->absolutePathToPublicDirectory/$this->sourceRelativePath";
        if (!is_file($this->sourceAbsolutePath)) { // Pokud není obrázek nalezen, pokusíme se nastavit no-image.
            $this->sourceRelativePath = "assets/images/frontend/no-image.png";
            $this->sourceAbsolutePath = "$this->absolutePathToPublicDirectory/$this->sourceRelativePath";
        }

        if (!is_file($this->sourceAbsolutePath)) {
            throw new ImageNotFoundException("Obrázek: '$this->sourceAbsolutePath' se nepodařilo najít!");
        }

        return $this;
    }

    public function getThumbnailPath(): string
    {
        $this->build();

        return $this->composeThumbnailUrl();
    }

    public function width(int $width): static
    {
        $this->width = $width;
        return $this;
    }

    public function height(int $height): static
    {
        $this->height = $height;
        return $this;
    }

    public function getPictureHtmlTag(): string
    {
        $this->build();

        $alt = htmlspecialchars((string)$this->alt);
        $webpThumbnailUrl = $this->composeWebpThumbnailUrl();
        $thumbnailUrl = $this->composeThumbnailUrl();
        $width = $this->width ?? 200;
        $height = $this->height ?? 200;

        return <<<HTML
<picture>
    <source srcset="$webpThumbnailUrl" type="image/webp">
    <img loading="lazy" src="$thumbnailUrl" width="$width" height="$height" alt="$alt">
</picture>
HTML;
    }

    public function alt(string $alt): static
    {
        $this->alt = $alt;
        return $this;
    }

    protected function build(): bool
    {
        if (!$this->sourceAbsolutePath) {
            throw new UnsetImagePathException("Pro vygenerování thumbnailu musí být nastavena cesta!");
        }

        $thumbnailAbsolutePath = $this->composeAbsoluteThumbnailFilePath();
        $webpThumbnailAbsolutePath = $this->composeAbsoluteWebpThumbnailFilePath();
        $thumbnailExists = is_file($thumbnailAbsolutePath);
        $webpThumbnailExists = is_file($webpThumbnailAbsolutePath);
        if ($thumbnailExists && $webpThumbnailExists) {
            return true;
        }

        $image = $this->getImageManager()->read($this->sourceAbsolutePath);
        $originalWidth = $image->width();
        $originalHeight = $image->height();

        $image = match ($this->sizeOperation) {
            SizeOperation::RESIZE => $image->resize($this->width, $this->height),
            SizeOperation::SCALE_KEEP_ASCPECT_RATIO => $image->scale($this->width, $this->height),
            SizeOperation::COVER => $image->cover(width: $this->width ?? $originalWidth, height: $this->height ?? $originalHeight),
            SizeOperation::CONTAIN => $image->contain(width: $this->width ?? $originalWidth, height: $this->height ?? $originalHeight),
        };

        if (!$thumbnailExists) {
            $this->saveImage(image: $image, path: $thumbnailAbsolutePath);
        }

        if (!$webpThumbnailExists) {
            $webp = $image->toWebp();
            $this->saveImage(image: $webp, path: $webpThumbnailAbsolutePath);
        }

        return true;
    }

    protected function composeAbsoluteThumbnailFilePath(): string
    {
        return "$this->absoluteThumbsDirectory/{$this->composeRelativeThumbnailFilePath()}";
    }

    protected function composeRelativeThumbnailFilePath(): string
    {
        $width = (string)($this->width ?? "default");
        $height = (string)($this->height ?? "default");

        return "{$this->sizeOperation->value}-$width-$height/$this->sourceRelativePath";
    }

    protected function composeAbsoluteWebpThumbnailFilePath(): string
    {
        return "{$this->composeAbsoluteThumbnailFilePath()}." . self::THUMBNAIL_EXTENSION;
    }

    protected function getImageManager(): \Intervention\Image\ImageManager
    {
        if ($this->imageManager === null) {
            $this->imageManager = new \Intervention\Image\ImageManager(new \Intervention\Image\Drivers\Gd\Driver());
        }

        return $this->imageManager;
    }

    protected function saveImage(\Intervention\Image\Interfaces\ImageInterface|\Intervention\Image\Interfaces\EncodedImageInterface $image, string $path): void
    {
        $directory = dirname($path);
        if (!is_dir($directory)) {
            $result = mkdir($directory, 0755, recursive: true);
            if (!$result) {
                throw new UnableToCreateThumbnailDirectoryException("Nepodařilo se vytvořit adresář: '$directory'!");
            }
        }

        $image->save($path);
    }

    protected function composeThumbnailUrl(): string
    {
        return "/$this->relativeThumbsDirectory/{$this->composeRelativeThumbnailFilePath()}";
    }

    protected function composeWebpThumbnailUrl(): string
    {
        return "{$this->composeThumbnailUrl()}." . self::THUMBNAIL_EXTENSION;
    }

}