<?php

namespace Ignite\Core\Files\Encryption;

use Ignite\Core\Contracts\Files\FileEncrypter;
use Ignite\Core\Facades\FileEncryption;
use Ignite\Core\Files\File;
use Ignite\Core\Files\TemporaryFileBuilder;
use Illuminate\Contracts\Filesystem\FileNotFoundException;

abstract class BaseFileEncrypter implements FileEncrypter
{
    /**
     * @var string
     */
    protected $configKey;

    public function __construct(?string $configKey = null)
    {
        $configKey = $configKey ?? config('config.file_encryption.default');
        FileEncryption::assertProviderConfigExists($configKey);

        $this->configKey = $configKey;
    }

    /**
     * Get the value of a config key for this config set.
     *
     * @param string $key
     * @return mixed
     */
    protected function getConfig(string $key)
    {
        return config("core.file_encryption.providers.$this->configKey.$key");
    }

    /**
     * Is the given file on a local disk?
     *
     * @param File $file
     * @return bool
     */
    protected function isOnLocalDisk(File $file): bool
    {
        return config("filesystems.disks.{$file->getDisk()}.driver") === 'local';
    }

    /**
     * Copy the file to a local disk if not there already.
     * Helpful for cases where the encrypter can only work with files on the local disk.
     *
     * @param File $file
     * @return void
     * @throws FileNotFoundException
     */
    protected function localizeFile(File $file): File
    {
        if ($this->isOnLocalDisk($file)) {
            return $file;
        }

        $file->copyToFile($temporaryLocalFile = $this->makeTemporaryFile());

        return $temporaryLocalFile;
    }

    /**
     * Assert that that given file exists.
     * Using this over File::assertExists() (\Illuminate\Filesystem\FilesystemAdapter::assertExists()), because those
     * counterparts are specific to working with PHPUnit.
     *
     * @param File $file
     * @return void
     * @throws FileNotFoundException
     */
    protected function assertExists(File $file)
    {
        if (! $file->exists()) {
            throw new FileNotFoundException(
                "Could not find file {$file->getRelativePath()} on  {$file->getDisk()}"
            );
        }
    }

    /**
     * Make a temporary file.
     *
     * @param string|null $contents
     * @return File
     */
    protected function makeTemporaryFile(?string $contents = null): File
    {
        return TemporaryFileBuilder::start()
            ->disk('local')
            ->prefix($this)
            ->make($contents);
    }
}
