<?php

namespace Ignite\Core\Files;

use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;

class TemporaryFileBuilder
{
    /**
     * @var \Illuminate\Contracts\Filesystem\Filesystem|\Illuminate\Filesystem\FilesystemAdapter
     */
    private $filesystem;

    /**
     * @var string
     */
    private $disk;

    /**
     * @var string string
     */
    private $prefix = 'tmp_';

    /**
     * @var string string
     */
    private $extension = '';

    /**
     * @var string string
     */
    private $directory = '';

    public function __construct()
    {
        $this->disk(config('filesystems.default'));
    }

    public static function start(): self
    {
        return new static();
    }

    /**
     * Set a prefix for the temporary file.
     *
     * @param string|object $prefix If object, we'll use the class's name as the prefix
     *                              (\Path\To\SomeClass => some_class)
     * @return $this
     */
    public function prefix($prefix): self
    {
        if (is_object($prefix)) {
            $prefix = snake_case(class_basename($prefix));
        }

        $this->prefix = $prefix;
        return $this;
    }

    /**
     * Give an extension to use for this file.
     *
     * @param string $extension
     * @return $this
     */
    public function extension(string $extension): self
    {
        $this->extension = $extension;
        return $this;
    }

    /**
     * Set the disk where to put the temporary file.
     *
     * @param string $disk
     * @return $this
     */
    public function disk(string $disk): self
    {
        $this->disk = $disk;
        $this->filesystem = Storage::disk($disk);
        return $this;
    }

    /**
     * Set a directory path on the disk where the temporary file should exist.
     *
     * @param string $path
     * @return $this
     */
    public function directory(string $path): self
    {
        $this->directory = $path;
        return $this;
    }

    /**
     * Generates a unique path for the temporary file, taking into account the builder's settings.
     *
     * @return string
     */
    protected function generatePath(): string
    {
        do {
            $tempPath = trim($this->directory, '\\/').'/'.
                $this->prefix.Str::random().($this->extension ? '.'.$this->extension : '');
        } while ($this->filesystem->exists($tempPath));

        return $tempPath;
    }

    /**
     * Make a real (one that exists on the disk) or theoretical temporary file (one that has a unique path & disk, but
     * does not actually exist yet).
     *
     * @param string|null $contents If given a string, we'll save the file to disk.
     * @return TemporaryFile
     */
    public function make(?string $contents = null): TemporaryFile
    {
        // TODO Might be best to save this to disk when we do this so that subsequent generatePath calls can know about
        //  this temporary file. The likelihood of this being an issue is very low because generating the same random
        //  string twice is very low and making a temporary file and then not putting anything in it probably won't
        //  happen much.
        $file = new TemporaryFile($this->generatePath(), $this->disk);

        if (! is_null($contents)) {
            $file->put($contents);
        }

        return $file;
    }
}
