<?php

namespace AutoAdmin\Bundle\Media\Upload;

use AutoAdmin\Bundle\Core\Upload\FileVerifier;
use AutoAdmin\Bundle\Media\Helper\FileHelper;
use AutoAdmin\Bundle\Media\Mapper\MapperManager;
use AutoAdmin\Bundle\Media\Model\FileInterface;
use AutoAdmin\Bundle\Media\Model\ImageInterface;
use AutoAdmin\Bundle\Media\Namer\NamerInterface;
use Exception;
use ReflectionClass;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\UploadedFile;

class UploadHandler implements UploadHandlerInterface
{
    private MapperManager $mapperManager;

    private NamerInterface $namer;

    private array $config;

    private Filesystem $fileSystem;

    private FileVerifier $fileVerifier;

    protected array $alreadyHandled = [];

    public function __construct(
        MapperManager $mapperManager,
        NamerInterface $namer,
        FileSystem $fileSystem,
        array $config,
        FileVerifier $fileVerifier,
    ) {
        $this->mapperManager = $mapperManager;
        $this->namer = $namer;
        $this->fileSystem = $fileSystem;
        $this->config = $config;
        $this->fileVerifier = $fileVerifier;
    }

    public function handle(FileInterface $media)
    {
        $mapper = $this->mapperManager->getMapper($media);
        $mediaFile = $media->getFile();

        if (in_array($mediaFile, $this->alreadyHandled)) {
            return;
        }

        $this->fileVerifier->verify($mediaFile);

        if (null === $mediaFile) {
            return null;
        } elseif ($mediaFile instanceof UploadedFile) {
            $refl = new ReflectionClass($media);


            if (
                $media->getId() &&
                $mediaFile->getClientOriginalName() !== $media->getOriginalName() &&
                $this->config['delete_old_on_replace'] === true
            ) {
                $this->handleRemove($media, true);
            }

            $media->setOriginalName($mediaFile->getClientOriginalName());
            $media->setMimeType($mediaFile->getMimeType());
            $media->setSize($mediaFile->getSize());

            if ($refl->implementsInterface(ImageInterface::class)) {
                try {
                    $size = getimagesize($mediaFile->getPathname());
                    if (!$size) {
                        $size = [0, 0];
                    }
                } catch (Exception $e) {
                    $size = [0, 0];
                }

                $media->setWidth($size[0]);
                $media->setHeight($size[1]);
            }

            $media->setFileName($this->namer->generateName($media->getOriginalName()));
            $media->setExtension(FileHelper::extractExtension($media->getOriginalName()));

            $mediaFile->move($mapper->getUploadPath($media), $media->getFileName());
        } elseif ($mediaFile instanceof File) {
            // used to programmatically create media (without real uploads)
            $src = $mediaFile->getPathname();
            $dest = $mapper->getUploadPath($media).DIRECTORY_SEPARATOR.$mediaFile->getFilename();
            $this->fileSystem->copy($src, $dest);

            $media->setTitle($mediaFile->getFilename());
            $media->setFileName($mediaFile->getFilename());
            $media->setOriginalName($mediaFile->getFilename());
            $media->setMimeType($mediaFile->getMimeType());
            $media->setSize($mediaFile->getSize());
            $media->setFile(null);
        }

        $this->alreadyHandled[] = $mediaFile;
    }

    public function handleRemove(FileInterface $media, $force = false)
    {
        if (empty($media->getFileName())) {
            return;
        }

        $mapper = $this->mapperManager->getMapper($media);
        $filePath = $mapper->getUploadPath($media).DIRECTORY_SEPARATOR.$media->getFileName();
        if ($this->config['delete_old_on_remove'] === true || $force === true) {
            if (file_exists($filePath)) {
                unlink($filePath);
            }
        }
    }
}
