/home/bdqbpbxa/api-uniferx.goodface.com.ua/vendor/oneduo/nova-file-manager/src/FileManager.php
<?php

declare(strict_types=1);

namespace Oneduo\NovaFileManager;

use Closure;
use JsonException;
use Laravel\Nova\Contracts\Cover;
use Laravel\Nova\Fields\Field;
use Laravel\Nova\Fields\HasThumbnail;
use Laravel\Nova\Fields\PresentsImages;
use Laravel\Nova\Fields\SupportsDependentFields;
use Laravel\Nova\Http\Requests\NovaRequest;
use Oneduo\NovaFileManager\Contracts\Services\FileManagerContract;
use Oneduo\NovaFileManager\Contracts\Support\InteractsWithFilesystem as InteractsWithFilesystemContract;
use Oneduo\NovaFileManager\Support\Asset;
use Oneduo\NovaFileManager\Traits\Support\InteractsWithFilesystem;
use stdClass;

class FileManager extends Field implements InteractsWithFilesystemContract, Cover
{
    use SupportsDependentFields;
    use InteractsWithFilesystem;
    use HasThumbnail;
    use PresentsImages;

    public $component = 'nova-file-manager-field';

    public bool $multiple = false;

    public ?int $limit = null;

    public bool $asHtml = false;

    public Closure $storageCallback;

    public static array $wrappers = [];

    public ?string $wrapper = null;

    public function __construct($name, $attribute = null, Closure $storageCallback = null)
    {
        parent::__construct($name, $attribute);

        $this->prepareStorageCallback($storageCallback);

        $this->thumbnail(function (array $assets, $resource) {
            foreach ($assets as $asset) {
                if (data_get($asset, 'type') === 'image' && $url = data_get($asset, 'url')) {
                    return $url;
                }
            }

            return null;
        });
    }

    public function multiple(bool $multiple = true): static
    {
        $this->multiple = $multiple;

        return $this;
    }

    public function limit(?int $limit = null): static
    {
        $this->limit = $limit;

        return $this;
    }

    public function asHtml(): static
    {
        $this->asHtml = true;

        return $this;
    }

    public function wrapper(string $name): static
    {
        $this->wrapper = $name;

        return $this;
    }

    public function resolveThumbnailUrl()
    {
        return is_callable($this->thumbnailUrlCallback) && !empty($this->value)
            ? call_user_func($this->thumbnailUrlCallback, $this->value, $this->resource)
            : null;

    }

    /**
     * @throws \JsonException
     */
    protected function fillAttribute(NovaRequest $request, $requestAttribute, $model, $attribute)
    {
        $this->applyWrapper();

        $result = call_user_func(
            $this->storageCallback,
            $request,
            $model,
            $attribute,
            $requestAttribute
        );

        if ($result === true) {
            return;
        }

        if ($result instanceof Closure) {
            return $result;
        }

        if (!is_array($result)) {
            return $model->{$attribute} = $result;
        }

        foreach ($result as $key => $value) {
            $model->{$key} = $value;
        }
    }

    protected function prepareStorageCallback(Closure $storageCallback = null): void
    {
        $this->storageCallback = $storageCallback ?? function (
            NovaRequest $request,
            $model,
            string $attribute,
            string $requestAttribute
        ) {
            $value = $request->input($requestAttribute);

            try {
                $payload = json_decode($value ?? '', true, 512, JSON_THROW_ON_ERROR);
            } catch (JsonException) {
                $payload = [];
            }

            $files = collect($payload);

            if ($this->multiple) {
                $value = collect($files)->map(fn(array $file) => new Asset(...$file));
            } else {
                $value = $files->isNotEmpty() ? new Asset(...$files->first()) : null;
            }

            return [$attribute => $value];
        };
    }

    protected function resolveAttribute($resource, $attribute = null): ?array
    {
        if (!$value = parent::resolveAttribute($resource, $attribute)) {
            return null;
        }

        if ($value instanceof Asset) {
            $value = collect([$value]);
        }

        if ($value instanceof stdClass) {
            $value = (array)$value;
        }

        if (is_array($value)) {
            if ($this->multiple) {
                $value = collect($value)->map(fn(array|object $asset) => new Asset(...(array)$asset));
            } else {
                $value = collect([new Asset(...$value)]);
            }
        }

        $this->applyWrapper();

        return $value
            ->map(function (Asset $asset) {
                $disk = $this->resolveFilesystem(app(NovaRequest::class)) ?? $asset->disk;

                $manager = app(FileManagerContract::class, ['disk' => $disk]);

                if ($this->hasUrlResolver()) {
                    $manager->resolveUrlUsing($this->getUrlResolver());
                }

                return $manager->makeEntity($asset->path, $asset->disk);
            })
            ->toArray();
    }

    public static function registerWrapper(string $name, Closure $callback): void
    {
        static::$wrappers[$name] = $callback;
    }

    public static function forWrapper(string $name): ?static
    {
        if (!$callback = (static::$wrappers[$name] ?? null)) {
            return null;
        }

        return $callback(static::make('wrapped'));
    }

    public function applyWrapper(): static
    {
        if (empty($this->wrapper)) {
            return $this;
        }

        if (!$wrapper = static::forWrapper($this->wrapper)) {
            return $this;
        }

        $this->prepareStorageCallback($wrapper->storageCallback);

        $this->multiple = $wrapper->multiple;
        $this->limit = $wrapper->limit;
        $this->asHtml = $wrapper->asHtml;

        $this->merge($wrapper);

        return $this;
    }

    public function jsonSerialize(): array
    {
        $this->applyWrapper();

        return array_merge(
            parent::jsonSerialize(),
            [
                'multiple' => $this->multiple,
                'limit' => $this->multiple ? $this->limit : 1,
                'asHtml' => $this->asHtml,
                'wrapper' => $this->wrapper,
            ],
            $this->options(),
        );
    }
}