<?php

namespace Ignite\Core\Files;

use Ignite\Core\Contracts\Files\FileGenerator as FileGeneratorContract;
use Ignite\Core\Contracts\Files\FileProcessor;
use Ignite\Core\Files\Processors\AdhocFileProcessor;
use Ignite\Core\Files\Processors\EncryptFileProcessor;

abstract class FileGenerator implements FileGeneratorContract
{
    /**
     * The processors to run against the file once it's generated.
     *
     * @var array<FileProcessor>
     */
    protected $fileProcessors = [];

    /**
     * Where to save the generated file.
     *
     * @var ?File
     */
    protected $destination = null;

    /**
     * Generate the file.
     *
     * @return TemporaryFile The generated file.
     */
    abstract protected function generateFile(): TemporaryFile;

    /**
     * {@inheritdoc}
     */
    public function destination(File $file): FileGeneratorContract
    {
        $this->destination = $file;
        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function addEncryption($fileEncrypter = null): FileGeneratorContract
    {
        return $this->addProcessor(new EncryptFileProcessor($fileEncrypter));
    }

    /**
     * {@inheritdoc}
     */
    public function addProcessor($fileProcessor): FileGeneratorContract
    {
        $this->fileProcessors[] = $fileProcessor instanceof FileProcessor
            ? $fileProcessor
            : new AdhocFileProcessor($fileProcessor);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function generate()
    {
        $tempFile = $this->generateFile();
        $tempFile = $this->runProcessors($tempFile);
        $resultFile = $this->destination ?? $this->temporaryFileBuilder()->make();

        if (! $tempFile->copyToFile($resultFile)) {
            throw new \RuntimeException('Unable to save to destination filesystem.');
        }

        return $resultFile;
    }

    /**
     * Run the file processors against the given file.
     *
     * @param $file
     * @return File
     */
    protected function runProcessors($file): File
    {
        foreach ($this->fileProcessors as $fileProcessor) {
            $file = $fileProcessor->run($file);
        }

        return $file;
    }

    /**
     * Get an instance of the temporary file builder for this generator.
     *
     * @return TemporaryFileBuilder
     */
    protected function temporaryFileBuilder(): TemporaryFileBuilder
    {
        return TemporaryFileBuilder::start()->prefix($this);
    }
}
