<?php

namespace Tricks\MediaLibraryOverrides\Conversions;

use Exception;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Spatie\MediaLibrary\Conversions\Conversion;
use Spatie\MediaLibrary\Conversions\ConversionCollection;
use Spatie\MediaLibrary\MediaCollections\Filesystem;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
use Spatie\MediaLibrary\Support\TemporaryDirectory;
use Throwable;
use Tricks\MediaLibraryOverrides\Actions\PerformConversionAction;

class FileManipulator extends \Spatie\MediaLibrary\Conversions\FileManipulator
{
    /**
     * @param  ConversionCollection<int,Conversion>  $conversions
     * @throws Throwable
     */
    public function performConversions(
        ConversionCollection $conversions,
        Media $media,
        bool $onlyMissing = false
    ): self {
        if ($conversions->isEmpty()) {
            return $this;
        }

        throw_unless($media->disk, new Exception('Can not perform conversion: Media has no disk.'));

        $temporaryDirectory = TemporaryDirectory::create();

        $copiedOriginalFile = app(Filesystem::class)->copyFromMediaLibrary(
            $media,
            $temporaryDirectory->path(Str::random(32).'.'.$media->extension)
        );

        $conversions
            ->reject(function (Conversion $conversion) use ($onlyMissing, $media) {
                $relativePath = $media->getPath($conversion->getName());

                if ($rootPath = config("filesystems.disks.$media->disk.root")) {
                    throw_unless(is_string($rootPath), new Exception("Disk $media->disk has malformed root."));
                    $relativePath = str_replace($rootPath, '', $relativePath);
                }

                return $onlyMissing && Storage::disk($media->disk)->exists($relativePath);
            })
            ->each(function (Conversion $conversion) use ($media, $copiedOriginalFile) {
                (new PerformConversionAction())->execute($conversion, $media, $copiedOriginalFile);
            });

        $temporaryDirectory->delete();

        return $this;
    }
}
