<?php

namespace app\common\arw\adjfut\src;

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 ChunkUpload
{
    /**
     * 磁盘
     *
     * @var string
     */
    private static $disk = null;

    /**
     * id
     *
     * @var string
     */
    private $id = null;
    /**
     * 上传文件
     *
     * @var UploadedFile
     */
    private $file = null;
    /**
     * 合并文件
     *
     * @var UploadedFile
     */
    private $mergeFile = null;

    /**
     * 初始化
     *
     * @param string $id 上传id
     * @param string $name 上传id
     * @param UploadedFile|string|array $file 文件
     * @date 2022-08-21
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function __construct(string $id, $file = 'file')
    {
        $this->id = $id;
        if (is_string($file)) {
            $file = Request::file($file);
        }
        if (is_array($file)) {
            $file = new UploadedFile(...$file);
        }
        if (!($file instanceof UploadedFile)) {
            throw new ErrorMsg("非法文件", 1);
        }
        $this->file = $file;
    }

    /**
     * 获取存储配置
     *
     * @return Driver
     * @date 2022-08-21
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function getDisk(): Driver
    {
        return Filesystem::disk(self::$disk);
    }

    /**
     * 分片是否上传完成
     *
     * @return boolean
     * @date 2022-08-22
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function isDone(): bool
    {
        return $this->isDone;
    }

    /**
     * 合并文件
     *
     * @return self
     * @date 2022-08-21
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function merge(string $name): self
    {
        if (!$this->isDone()) {
            throw new ErrorMsg("文件未上传完成 禁止合并", 1);
        }

        $id = $this->id;

        $root = self::getDiskConfig('root');

        $path = join('', [
            $root,
            $id,
            '.' . pathinfo($name, PATHINFO_EXTENSION)
        ]);

        for ($i = 0; $i <= $this->total; $i++) {
            $_path = $root . $id . DIRECTORY_SEPARATOR . $i;
            file_put_contents(
                $path,
                file_get_contents($_path),
                FILE_APPEND
            );
            unlink($_path);
        }
        rmdir($root . $id);
        $this->mergeFile = new UploadedFile($path, $name);
        return $this;
    }

    /**
     * 获取合并文件
     *
     * @return UploadedFile
     * @date 2022-08-22
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function getMergeFile(): UploadedFile
    {
        if (!$this->mergeFile) {
            throw new ErrorMsg("未合并文件", 1);
        }
        return $this->mergeFile;
    }

    /**
     * 获取上传文件
     *
     * @param string $diskName
     * @param string $validate
     * @return UploadFile
     * @date 2022-08-22
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function getUploadFile(string $diskName, string $validate = ''): UploadFile
    {
        return new UploadFile(
            $diskName,
            $validate,
            $this->getMergeFile()
        );
    }

    /**
     * 保存文件
     *
     * @param integer $index
     * @param integer $total
     * @return self
     * @date 2022-08-21
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function put(int $index, int $total): self
    {
        $this->total = $total;
        $this->getDisk()->putFileAs(
            $this->id,
            $this->file,
            $index
        );
        $this->isDone = $index === $total;
        return $this;
    }

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

    /**
     * 初始化配置
     *
     * @param array $config
     * @return void
     * @date 2022-08-21
     * @example
     * @author arw
     * @since 1.0.0
     */
    public static function init(array $config): void
    {
        self::$disk = Arr::get($config, 'disk', '');
        $type = self::getDiskConfig('type');
        if ($type != 'local') {
            throw new ErrorMsg("分片上传仅支持 local类型磁盘", 1);
        }
    }

    /**
     * 获取磁盘配置
     *
     * @param string $name
     * @param mixed $default
     * @return mixed
     * @date 2022-08-22
     * @example
     * @author arw
     * @since 1.0.0
     */
    public static function getDiskConfig($name = null, $default = null)
    {
        return Filesystem::getDiskConfig(self::$disk, $name, $default);
    }

    /**
     * 生成上传唯一id
     *
     * @return string
     * @date 2022-08-21
     * @example
     * @author arw
     * @since 1.0.0
     */
    public static function generateUploadId(): string
    {
        return md5(join('-', [
            Request::ip(),
            time()
        ]));
    }
}
