<?php

namespace Velis\Filesystem\Adapter;

use Aws\S3\S3Client;
use Gaufrette\Adapter\AwsS3 as AwsS3Adapter;
use Gaufrette\Adapter\Local as LocalAdapter;
use Gaufrette\Filesystem;
use RuntimeException;
use Velis\App;
use Velis\App\Config;
use Velis\Cache\CacheInterface;
use Velis\Filesystem\Adapter\AwsS3 as AwsS3Filesystem;
use Velis\Filesystem\Adapter\Local as LocalFilesystem;
use Velis\Filesystem\Av\Contract\AvClientInterface;
use Velis\Filesystem\Filesystem as VelisFilesystem;
use Velis\Filesystem\FilesystemInterface as VelisFilesystemInterface;

/**
 * @author Szymon Janaczek <szymon.janaczek@velistech.com>
 */
final readonly class UploadFilesystemFactory
{
    public function __construct(
        private Config $config,
        private ?CacheInterface $cache,
        private ?AvClientInterface $avClient = null,
    ) {
    }

    public function get(string $uploadPath): VelisFilesystemInterface
    {
        // We are in BPM, so use Filesystem from lib/Velis.
        if (!class_exists(Filesystem::class)) {
            return $this->getVelisFilesystem($uploadPath);
        }

        // We are not in BPM, but filesystem is not S3, so use LocalAdapter.
        if ('s3' !== $this->config->upload->filesystem) {
            return $this->getLocalAdapter($uploadPath);
        }

        // We are not in BPM, and filesystem is S3, so use AwsS3Adapter.
        return $this->getS3();
    }

    public function getVelisFilesystem(string $uploadPath): VelisFilesystem
    {
        return new VelisFilesystem($uploadPath);
    }

    public function getLocalAdapter(string $uploadPath): LocalFilesystem
    {
        $adapter = new LocalAdapter($uploadPath);
        $filesystem = new Filesystem($adapter);

        return new LocalFilesystem($filesystem, $uploadPath);
    }

    /**
     * @throws RuntimeException
     */
    public function getS3(): AwsS3Filesystem
    {
        if (!$this->cache instanceof CacheInterface) {
            throw new RuntimeException('S3 filesystem requires cache. Register `cache` service.');
        }

        $s3Config = $this->config->upload->s3;
        $client = App::$di->get(S3Client::class, [$s3Config]);

        $adapter = new AwsS3Adapter(
            service: $client,
            bucket: $s3Config->bucketName,
        );
        $filesystem = new Filesystem($adapter);

        $ttl = $s3Config->cacheTtl;
        if (!$ttl) {
            $ttl = $this->config->cache->options->ttl;
        }

        return new AwsS3Filesystem(
            filesystem: $filesystem,
            client: $client,
            bucketName: $s3Config->bucketName,
            cache: $this->cache,
            ttl: $ttl,
            avClient: $this->avClient,
        );
    }
}
