<?php

namespace app\common\arw\adjfut\src;

use think\exception\ValidateException;
use app\common\arw\adjfut\src\Exception\ErrorMsg;
use think\facade\Filesystem;
use think\facade\Request;
use think\file\UploadedFile;
use think\filesystem\Driver;
use think\helper\Arr;

class UploadFile
{
    /**
     * 磁盘
     *
     * @var string
     */
    private $diskName = '';
    /**
     * 文件
     *
     * @var UploadedFile
     */
    private $file = null;
    /**
     * 文件路径
     *
     * @var string
     */
    private $path = '';

    /**
     * 初始化
     *
     * @param string $diskName 磁盘
     * @param string $validate 验证
     * @param UploadedFile|string|array $file 文件
     * @date 2022-04-15
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function __construct(
        string $diskName,
        string $validate = '',
        $file = 'file'
    ) {
        if (is_string($file)) {
            $file = Request::file($file);
        }
        if (is_array($file)) {
            $file = new UploadedFile(...$file);
        }
        if (!($file instanceof UploadedFile)) {
            throw new ErrorMsg("非法文件", 1);
        }
        if ($validate) {
            try {
                Validate::check([
                    'file' => $file
                ], [
                    'file|文件' => $validate
                ]);
            } catch (ValidateException $th) {
                $this->getDisk()->delete($file->getFilename());
                throw $th;
            }
        }

        $this->diskName = $diskName;
        $this->file = $file;
    }

    /**
     * 上传base64图片
     *
     * @param string $diskName 磁盘
     * @param string $base64 base64
     * @return self
     * @date 2023-01-09
     * @example
     * @author arw
     * @since 1.0.0
     */
    public static function uploadBase64Image(
        string $diskName,
        string $base64
    ): self {
        $Base64 = new Base64($base64);
        if (!$Base64->isImage()) {
            throw new ErrorMsg("非法图片base64", 1);
        }
        $path = tempnam(sys_get_temp_dir(), 'saveBase64Image');
        $Base64->saveImage($path);
        $name = md5(md5_file($path) . Tool::generateGuid() . time()) . '.' . $Base64->getFileExt();
        return new self($diskName, '', [
            $path,
            $name
        ]);
    }

    /**
     * 获取文件
     *
     * @return UploadedFile
     * @date 2022-07-05
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function getFile(): UploadedFile
    {
        return $this->file;
    }

    /**
     * 获取磁盘名称
     *
     * @return string
     * @date 2022-07-05
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function getDiskName(): string
    {
        return $this->diskName;
    }

    /**
     * 获取配置项
     *
     * @param string $key
     * @param mixed $default
     * @return mixed
     * @date 2022-04-15
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function getConfig(string $key, $default  = null)
    {
        return Arr::get(
            Filesystem::getDiskConfig($this->diskName),
            $key,
            $default
        );
    }

    /**
     * 获取文件相对路径
     *
     * @date 2022-04-15
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function getPath(): string
    {
        if (!$this->path) {
            throw new ErrorMsg("请保存文件后使用", 1);
        }
        return $this->path;
    }

    /**
     * 获取文件真实路径
     *
     * @return string
     * @date 2022-07-19
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function getRealPath(): string
    {
        $path = $this->getPath();
        if ($this->getConfig('type') != 'local') {
            throw new ErrorMsg("非本地文件类型 无法获取真实路径", 1);
        }
        return str_replace(['\\', '/'], [DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR], $this->getConfig('root') . DIRECTORY_SEPARATOR . $path);
    }

    /**
     * 获取文件绝对路径
     *
     * @date 2022-04-15
     * @deprecated 1.0.0
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function getFullPath(): string
    {
        return $this->getRealPath();
    }

    /**
     * 保存文件
     * @param string               $path    路径
     * @param null|string|\Closure $rule    文件名规则
     * @param array                $options 参数
     * @return string
     */
    public function putFile(
        string $path,
        $rule = null,
        array $options = []
    ): string {
        $this->path = $this->getDisk()->putFile(
            $path,
            $this->file,
            $rule,
            $options
        );
        if (!$this->path) {
            throw new ErrorMsg("文件保存失败", 1);
        }
        return $this->path;
    }

    /**
     * 指定文件名保存文件
     * @param string $path    路径
     * @param string $name    文件名
     * @param array  $options 参数
     * @return string
     */
    public function putFileAs(
        string $path,
        string $name = '',
        array $options = []
    ): string {
        $this->path = $this->getDisk()->putFileAs(
            $path,
            $this->file,
            $name,
            $options
        );
        if (!$this->path) {
            throw new ErrorMsg("文件保存失败", 1);
        }
        return $this->path;
    }

    /**
     * 删除文件
     *
     * @return void
     * @date 2022-08-22
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function delete(): void
    {
        $this->getDisk()->delete($this->file->getFilename());
    }

    /**
     * 移除文件
     *
     * @date 2022-04-15
     * @deprecated 1.0.0
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function unlink(): void
    {
        $this->delete();
    }

    /**
     * 获取存储配置
     *
     * @return Driver
     * @date 2022-04-15
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function getDisk(): Driver
    {
        return Filesystem::disk($this->diskName);
    }
}
