<?php

namespace Ignite\Core\Files\Encryption;

use Ignite\Core\Files\File;
use Illuminate\Contracts\Filesystem\FileNotFoundException;

class PGPFileEncrypter extends BaseFileEncrypter
{
    /**
     * {@inheritdoc}
     */
    public function encrypt(File $file, ?File $saveTo = null): File
    {
        $this->assertExists($file);
        $localFile = $this->localizeFile($file);
        $localSaveTo = $this->localizeFile($saveTo ?? $this->makeTemporaryFile());
        if ($saveTo && $file->is($saveTo)) {
            // Overwriting the original file. First save encrypted version to a temporary place.
            $localSaveTo = $this->makeTemporaryFile();
        }

        $command = "cat {$localFile->path()} | gpg --pinentry-mode loopback --batch --yes --quiet --encrypt ".
            "--recipient=".$this->getConfig('encryption.recipient')." > {$localSaveTo->path()}";

        passthru($command);

        if ($saveTo && ! $localSaveTo->is($saveTo)) {
            // We needed to work with a temporary file. Let's copy the result from the temporary file to the requested
            // save-to file.

            if ($file->is($saveTo)) {
                // If asked to overwrite the original file, we'll delete the file and copy the contents from the temporary
                // encrypted file. Otherwise, if save-to exists, we'll throw an exception instead of overwriting.
                $saveTo->delete();
            }

            $localSaveTo->copyToFile($saveTo);
        }

        return $saveTo ?? $localSaveTo;
    }

    /**
     * {@inheritdoc}
     */
    public function decrypt(File $file, ?File $saveTo = null): File
    {
        $this->assertExists($file);
        $localFile = $this->localizeFile($file);
        $localSaveTo = $this->localizeFile($saveTo ?? $this->makeTemporaryFile());
        if ($saveTo && $file->is($saveTo)) {
            // Overwriting the original file. First save decrypted version to a temporary place.
            $localSaveTo = $this->makeTemporaryFile();
        }

        $command = "gpg --passphrase ".$this->getConfig('decryption.passphrase')." --pinentry-mode loopback --batch ".
            "--yes --quiet --decrypt {$localFile->path()} > {$localSaveTo->path()} 2>/dev/null";

        passthru($command);

        if ($saveTo && ! $localSaveTo->is($saveTo)) {
            // We needed to work with a temporary file. Let's copy the result from the temporary file to the
            // requested save-to file.

            if ($file->is($saveTo)) {
                // If asked to overwrite the same file, we'll delete the file and copy the contents from the temporary
                // decrypted file. Otherwise, if save-to exists, we'll throw an exception instead of overwriting.
                $saveTo->delete();
            }

            $localSaveTo->copyToFile($saveTo);
        }

        return $saveTo ?? $localSaveTo;
    }

    /**
     * Copy the file to a local disk if not there already.
     * We want one on the local disk so that the gpg binary can get at it.
     *
     * @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;
    }
}
