<?php

/*
 * This file is part of the VITICORE2 package.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace CpCreation\VitiCore\Bridge\Doctrine\EventSubscriber;

use CpCreation\VitiCore\Behavior\HasImage;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Filesystem\Filesystem;
use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Service\Uploader\ImageUploader;
use CpCreation\VitiCore\Media\Model\Image;
use Doctrine\ORM\Events;
use Doctrine\Persistence\Event\LifecycleEventArgs;

#[AsDoctrineListener(event: Events::prePersist)]
#[AsDoctrineListener(event: Events::postUpdate)]
class ImageSubscriber
{
    private const FIXTURE_STUBS_DIR = __DIR__.'/../../fixtures/stubs';

    public function __construct(
        private ImageUploader $uploader,
        private Filesystem $filesystem,
    ) {
    }

    private function resolveImageAbsolutePath(string $path): ?string
    {
        $absolutePath = $this->filesystem->absolutePath('image', $path);
        if ($this->filesystem->exists($absolutePath)) {
            return $absolutePath;
        }

        $stubPath = self::FIXTURE_STUBS_DIR.\DIRECTORY_SEPARATOR.mb_ltrim($path, \DIRECTORY_SEPARATOR);
        if (!is_file($stubPath)) {
            return null;
        }

        $stream = fopen($stubPath, 'r');
        if (false !== $stream) {
            try {
                $imageFilesystem = $this->filesystem->fs('image');
                if (!$imageFilesystem->fileExists($path)) {
                    $imageFilesystem->writeStream($path, $stream);
                }
            } finally {
                fclose($stream);
            }
        }

        return $this->filesystem->exists($absolutePath) ? $absolutePath : $stubPath;
    }

    private function hydrateMetadataFromStoredPath(Image $image): void
    {
        $path = $image->getPath();
        if (!$path) {
            return;
        }

        $absolutePath = $this->resolveImageAbsolutePath($path);
        if (null === $absolutePath) {
            return;
        }

        if (!$image->getMimeType()) {
            $mimeType = mime_content_type($absolutePath);
            if (false !== $mimeType) {
                $image->setMimeType($mimeType);
            }
        }

        if (!$image->getOriginalName()) {
            $image->setOriginalName(pathinfo($path, \PATHINFO_BASENAME));
        }

        if (null === $image->getSize()) {
            $size = filesize($absolutePath);
            if (false !== $size) {
                $image->setSize($size);
            }
        }

        if (null === $image->getWidth() || null === $image->getHeight()) {
            $dimensions = getimagesize($absolutePath);
            if (false !== $dimensions) {
                $image->setWidth($dimensions[0] ?? null);
                $image->setHeight($dimensions[1] ?? null);
            }
        }
    }

    public function prePersist(LifecycleEventArgs $args): void
    {
        $entity = $args->getObject();

        if ($entity instanceof Image) {
            if ($entity->getFile()) {
                $image = $this->uploader->upload($entity->getFile());
                $entity->setPath($image->getPath());
                $entity->setSize($image->getSize());
                $entity->setMimeType($image->getMimeType());
                $entity->setOriginalName($image->getOriginalName());
                $entity->setWidth($image->getWidth());
                $entity->setHeight($image->getHeight());
            } else {
                $this->hydrateMetadataFromStoredPath($entity);
            }
        }
    }

    /**
     * @throws \Doctrine\ORM\ORMException
     * @throws \Doctrine\ORM\OptimisticLockException
     */
    public function postUpdate(LifecycleEventArgs $args): void
    {
        $entity = $args->getObject();
        if ($entity instanceof HasImage && $entity->getImage()) {
            if ($entity->getImage()->getFile()) {
                $image = $this->uploader->upload($entity->getImage()->getFile());
                $entityManager = $args->getObjectManager();
                $entityManager->remove($entity->getImage());
                $entity->setImage($image);
                $entityManager->persist($entity);
                $entityManager->flush();
            }
        }
    }
}
