<?php

/*
 * This file is part of the Serenity 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 Serenity\MediaBundle\Decorator;

use League\Flysystem\DirectoryListing;
use League\Flysystem\FilesystemOperator;
use Serenity\CoreBundle\Saas\SerenitySaasContext;
use Serenity\MediaBundle\Enum\PathStorage;

class FilesystemDecorator implements FilesystemOperator
{
    private bool $disableApplyPrefix;

    public function __construct(
        private readonly FilesystemOperator $innerFilesystem,
    ) {
        $this->disableApplyPrefix = false;
    }

    private function applyPrefix(string $location, ?string $serenitySaasId = null): string
    {
        $location = '/'.mb_ltrim($location, '/');

        $serenitySaasId = $serenitySaasId ?? SerenitySaasContext::$identifierSerenityId;

        if (!str_starts_with($location, PathStorage::TEMP->value)) {
            if ($serenitySaasId) {
                $location = '/'.$serenitySaasId.'/'.mb_ltrim($location, '/');
            }
        }

        return $location;
    }

    /**
     * @param array<string, mixed> $config
     */
    public function write(string $location, string $contents, array $config = []): void
    {
        if ($this->disableApplyPrefix) {
            $this->innerFilesystem->write($location, $contents, $config);

            return;
        }

        $serenitySaasId = $config['serenitySaasId'] ?? null;
        $this->innerFilesystem->write($this->applyPrefix($location, $serenitySaasId), $contents, $config);
    }

    /**
     * @param array<string, mixed> $config
     */
    public function writeStream(string $location, mixed $contents, array $config = []): void
    {
        if ($this->disableApplyPrefix) {
            $this->innerFilesystem->writeStream($location, $contents, $config);

            return;
        }

        $serenitySaasId = $config['serenitySaasId'] ?? null;
        $this->innerFilesystem->writeStream($this->applyPrefix($location, $serenitySaasId), $contents, $config);
    }

    public function read(string $location): string
    {
        if ($this->disableApplyPrefix) {
            return $this->innerFilesystem->read($location);
        }

        return $this->innerFilesystem->read($this->applyPrefix($location));
    }

    public function readStream(string $location): mixed
    {
        if ($this->disableApplyPrefix) {
            return $this->innerFilesystem->readStream($location);
        }

        return $this->innerFilesystem->readStream($this->applyPrefix($location));
    }

    public function delete(string $location): void
    {
        if ($this->disableApplyPrefix) {
            $this->innerFilesystem->delete($location);
        }

        $this->innerFilesystem->delete($this->applyPrefix($location));
    }

    public function deleteDirectory(string $location): void
    {
        if ($this->disableApplyPrefix) {
            $this->innerFilesystem->deleteDirectory($location);
        }
        $this->innerFilesystem->deleteDirectory($this->applyPrefix($location));
    }

    /**
     * @param array<string, mixed> $config
     */
    public function createDirectory(string $location, array $config = []): void
    {
        if ($this->disableApplyPrefix) {
            $this->innerFilesystem->createDirectory($location, $config);

            return;
        }
        $serenitySaasId = $config['serenitySaasId'] ?? null;
        $this->innerFilesystem->createDirectory($this->applyPrefix($location, $serenitySaasId), $config);
    }

    public function listContents(string $location, bool $deep = false): DirectoryListing
    {
        if ($this->disableApplyPrefix) {
            return $this->innerFilesystem->listContents($location, $deep);
        }

        return $this->innerFilesystem->listContents($this->applyPrefix($location), $deep);
    }

    public function fileExists(string $location): bool
    {
        $this->disableApplyPrefix = false;
        if (!$this->innerFilesystem->fileExists($this->applyPrefix($location))) {
            $this->disableApplyPrefix = true;

            return $this->innerFilesystem->fileExists($location);
        }

        return true;
    }

    public function directoryExists(string $location): bool
    {
        if ($this->disableApplyPrefix) {
            $this->innerFilesystem->directoryExists($location);
        }

        return $this->innerFilesystem->directoryExists($this->applyPrefix($location));
    }

    public function visibility(string $path): string
    {
        if ($this->disableApplyPrefix) {
            return $this->innerFilesystem->visibility($path);
        }

        return $this->innerFilesystem->visibility($this->applyPrefix($path));
    }

    public function mimeType(string $path): string
    {
        if ($this->disableApplyPrefix) {
            return $this->innerFilesystem->mimeType($path);
        }

        return $this->innerFilesystem->mimeType($this->applyPrefix($path));
    }

    public function setVisibility(string $path, string $visibility): void
    {
        if ($this->disableApplyPrefix) {
            $this->innerFilesystem->setVisibility($path, $visibility);
        }
        $this->innerFilesystem->setVisibility($this->applyPrefix($path), $visibility);
    }

    public function fileSize(string $path): int
    {
        if ($this->disableApplyPrefix) {
            return $this->innerFilesystem->fileSize($path);
        }

        return $this->innerFilesystem->fileSize($this->applyPrefix($path));
    }

    public function lastModified(string $path): int
    {
        if ($this->disableApplyPrefix) {
            return $this->innerFilesystem->lastModified($path);
        }

        return $this->innerFilesystem->lastModified($this->applyPrefix($path));
    }

    /**
     * @param array<string, mixed> $config
     */
    public function publicUrl(string $path, array $config = []): string
    {
        if ($this->disableApplyPrefix) {
            return $this->innerFilesystem->publicUrl($path, $config);
        }

        $serenitySaasId = $config['serenitySaasId'] ?? null;

        return $this->innerFilesystem->publicUrl($this->applyPrefix($path, $serenitySaasId), $config);
    }

    /**
     * @param array<string, mixed> $config
     */
    public function temporaryUrl(string $path, \DateTimeInterface $expiresAt, array $config = []): string
    {
        if ($this->disableApplyPrefix) {
            return $this->innerFilesystem->temporaryUrl($path, $expiresAt, $config);
        }

        $serenitySaasId = $config['serenitySaasId'] ?? null;

        return $this->innerFilesystem->temporaryUrl($this->applyPrefix($path, $serenitySaasId), $expiresAt, $config);
    }

    /**
     * @param array<string, mixed> $config
     */
    public function checksum(string $path, array $config = []): string
    {
        if ($this->disableApplyPrefix) {
            return $this->innerFilesystem->checksum($path, $config);
        }

        $serenitySaasId = $config['serenitySaasId'] ?? null;

        return $this->innerFilesystem->checksum($this->applyPrefix($path, $serenitySaasId), $config);
    }

    public function has(string $location): bool
    {
        $this->disableApplyPrefix = false;
        if (!$this->innerFilesystem->fileExists($this->applyPrefix($location))) {
            $this->disableApplyPrefix = true;

            return $this->innerFilesystem->fileExists($location);
        }

        return true;
    }

    /**
     * @param array<string, mixed> $config
     */
    public function copy(string $source, string $destination, array $config = []): void
    {
        $serenitySaasId = $config['serenitySaasId'] ?? null;
        $this->innerFilesystem->copy(
            $this->applyPrefix($source, $serenitySaasId),
            $this->applyPrefix($destination, $serenitySaasId),
            $config
        );
    }

    /**
     * @param array<string, mixed> $config
     */
    public function move(string $source, string $destination, array $config = []): void
    {
        $serenitySaasId = $config['serenitySaasId'] ?? null;
        $this->innerFilesystem->move(
            $this->applyPrefix($source, $serenitySaasId),
            $this->applyPrefix($destination, $serenitySaasId),
            $config
        );
    }

    /**
     * @param array<mixed> $arguments
     */
    public function __call(string $method, array $arguments): mixed
    {
        return $this->innerFilesystem->$method(...$arguments);
    }
}
