<?php

namespace Ignite\Core\Models\Import;

use Ignite\Core\Contracts\Repositories\ImportRepository;
use Illuminate\Support\Facades\Storage;

class File
{
    /**
     * @var string
     */
    protected $type;

    /**
     * @var string
     */
    protected $label;

    /**
     * @var string
     */
    protected $file;

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

    /**
     * @var string
     */
    protected $log;

    /**
     * Create a new instance from the provided request.
     *
     * @param \Illuminate\Http\Request $request
     * @param array $allowedTypes
     * @param string $disk
     * @return static
     */
    public static function fromRequest($request, $allowedTypes = [], $disk = 'local')
    {
        $type = $request->get('type');
        $label = $allowedTypes[$type];
        $folder = 'imports';
        $filename = self::makeFilename($label);

        // Seems like this should use $request instead of request().
        $file = request()->file('file')->storeAs($folder, $filename.'.csv', [
            'disk' => $disk,
            'visibility' => 'public',
        ]);

        $log = $folder.DIRECTORY_SEPARATOR.$filename.'.log';

        return new static($type, $label, $file, $log, $disk);
    }

    /**
     * Create an instance based on a file on a filesystem.
     *
     * @param string $fromPath The path to the file.
     * @param string $type The type of import. See \Ignite\Core\Contracts\Repositories\ImportRepository::getAllowedTypes()
     * @param string|null $fromDisk The filesystem the file is on (defaults to config('filesystems.default')).
     * @return static
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
     * @throws \Illuminate\Contracts\Filesystem\FileExistsException
     */
    public static function fromPath(string $fromPath, string $type, ?string $fromDisk = null): File
    {
        /** @var ImportRepository $importRepository */
        $importRepository = app(ImportRepository::class);
        $allowedTypes = $importRepository->getAllowedTypes();

        if (! array_key_exists($type, $allowedTypes)) {
            throw new \InvalidArgumentException("Cannot make import file. Given type does not exist: $type");
        }

        $disk = 'local'; // We could make this a config value (core.import.disk).
        $folder = 'imports'; // We could make this a config value (core.import.folder).
        $label = $allowedTypes[$type];
        $filename = self::makeFilename($label);
        $file = $folder.DIRECTORY_SEPARATOR.$filename.'.csv';
        $log = $folder.DIRECTORY_SEPARATOR.$filename.'.log';

        $fromStream = Storage::disk($fromDisk)->readStream($fromPath);
        Storage::disk($disk)->writeStream($file, $fromStream, [
            'visibility' => 'public',
        ]);

        return new static($type, $label, $file, $log, $disk);
    }

    /**
     * Makes a filename for a file import of the given type (without the extension).
     *
     * @param string $typeLabel
     * @return string
     */
    private static function makeFilename(string $typeLabel): string
    {
        return sprintf('%s-%s', snake_case($typeLabel), now()->format('YmdHis'));
    }

    /**
     * ImportFile constructor.
     *
     * @param string $type
     * @param $label
     * @param string $file
     * @param string $disk
     */
    public function __construct($type, $label, $file, $log, $disk)
    {
        $this->type = $type;
        $this->file = $file;
        $this->log = $log;
        $this->disk = $disk;
        $this->label = $label;
    }

    /**
     * Cast the object to an array.
     *
     * @return array
     */
    public function toArray()
    {
        return [
            'type' => $this->type,
            'label' => $this->label,
            'file' => $this->file,
            'log' => $this->log,
            'disk' => $this->disk,
        ];
    }

    /**
     * Get the import type.
     *
     * @return string
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * Get the import type label.
     *
     * @return string
     */
    public function getLabel()
    {
        return $this->label;
    }

    /**
     * Get the import file path.
     *
     * @return string
     */
    public function getFile()
    {
        return $this->file;
    }

    /**
     * Get the import log path.
     *
     * @return string
     */
    public function getLog()
    {
        return $this->log;
    }

    /**
     * Get the import file path.
     *
     * @return string
     */
    public function getDisk()
    {
        return $this->disk;
    }
}
