<?php


namespace app\controller\manager;

use app\model\Log;
use app\service\AliOss;
use think\facade\Config;
use Exception;
use app\model\Attachment as AttachmentModel;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\exception\ValidateException;
use think\response\Json;
use think\response\View;

/**
 * 附件管理 - 素材管理
 * Class Attachment
 * @package app\controller\manager
 */
class Attachment extends Base
{
    protected $noNeedLogin = ['file', 'getSize', 'md5List', 'pathDirHandle', 'toOss', 'delLostFile', 'test'];
    protected $DIRECTORY_SEPARATOR = "/";
    protected function initialize()
    {
        parent::initialize(); // TODO: Change the autogenerated stub
        $this->DIRECTORY_SEPARATOR = DIRECTORY_SEPARATOR == "\\" ? "/" : DIRECTORY_SEPARATOR;
    }

    /**
     * 删除
     *
     * @return Json
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function del(): Json
    {
        if ($this->request->isPost()) {
            $ids = input('post.ids/a', []);
            if (empty($ids)) {
                $ids[] = input('post.id/d');
            }

            $items = AttachmentModel::whereIn('id', $ids)->where('is_dir', AttachmentModel::COMMON_ON)->select();
            if ($items->where('is_dir', AttachmentModel::COMMON_ON)->count()) {
                $dirPaths = [];
                foreach ($items->toArray() as $item) {
                    $dirPaths[] = $item['path'].$item['name']. $this->DIRECTORY_SEPARATOR ;
                }
                if (AttachmentModel::where('path', 'in', $dirPaths)->count()) {
                    return $this->json(4001, '待删除目录下存在内容!');
                }
            }
            AttachmentModel::deleteByIds($ids);

            Log::write(get_class().'Del', 'del', '涉及到的ID为:'.implode(',', $ids));
            return $this->json();
        }
        return $this->json(4001, '非法请求!');
    }

    /**
     * 单个字段编辑
     *
     * @return Json
     * @throws Exception
     */
    public function modify(): Json
    {
        if ($this->request->isPost()) {
            $item     = input('post.');
            $validate = $this->validateByApi($item, [
                'field' => 'require',
                'value' => 'require',
            ]);

            if ($validate !== true) {
                return $validate;
            }

            if (!$info = AttachmentModel::findById($item['id'])) {
                return $this->json(4001, '记录不存在');
            }

            if ($item['field'] == 'name' && $info['is_dir'] == AttachmentModel::COMMON_ON) {
                return $this->json(4002, '目录名称不能修改');

            }

            $update = [$item['field'] => $item['value']];

            try {
                $info->save($update);
                return $this->json();
            } catch (ValidateException $e) {
                return $this->json(4001, $e->getError());
            }
        }
        return $this->json(4000, '非法请求');
    }

    /**
     * 添加
     *
     * @return Json|View
     * @throws Exception
     */
    public function add()
    {
        if ($this->request->isPost()) {
            $item = input('post.');

            $validate = $this->validateByApi($item, [
                'title|合集标题'     => 'require',
                'user|虚拟用户'      => 'require',
                'headimg|虚拟用户头像' => 'require',
            ]);

            if ($validate !== true) {
                return $validate;
            }

            try {
                $now                = date('Y-m-d H:i:s');
                $item['created_at'] = $now;
                $item['created_by'] = $this->auth['user_id'];
                AttachmentModel::create($item);
                return $this->json();
            } catch (ValidateException $e) {
                return $this->json(4001, $e->getError());
            }
        }

        return $this->view();
    }

    /**
     * 添加文件夹
     *
     * @return Json|View
     * @throws Exception
     */
    public function addFolder()
    {
        if ($this->request->isPost()) {
            $item = input('post.');

            $validate = $this->validateByApi($item, [
                'name|文件夹名称' => 'require|alphaDash|max:20',
                'path|文件路径'  => 'require',
            ]);

            // 例 name=dir4 path=/storage/dir1/dir2/dir3

            // 去首尾/
            $path = trim($item['path'],  $this->DIRECTORY_SEPARATOR );
            // 全路径 如 /storage/dir1/dir2/dir3/dir4/  注意前后都有/
            $fullPath =  $this->DIRECTORY_SEPARATOR .$path. $this->DIRECTORY_SEPARATOR .$item['name']. $this->DIRECTORY_SEPARATOR ;

            if ($validate !== true) {
                return $validate;
            }

            AttachmentModel::pathDirHandle($fullPath);
            return $this->json();
        }

        $path = input('path/s', AttachmentModel::ROOT_PATH);

        $this->data['path'] = $path;
        return $this->view();
    }

    /**
     * 图片列表
     *
     * @return View|Json
     * @throws Exception
     */
    public function image()
    {
        $path = input('post.path', AttachmentModel::ROOT_PATH);
        $path =  $this->DIRECTORY_SEPARATOR . trim($path,  $this->DIRECTORY_SEPARATOR). $this->DIRECTORY_SEPARATOR;
        $path = str_replace("\\","/",$path);
        $type     = input('type/s', 'image');
        $selected = input('selected', false);
        $multiple = input('multiple', false);

        Config::load('extra/alioss', 'alioss');
        $config = config('alioss');
        $oss    = $config['customDomain'];
        if ($this->request->isPost()) {
            $items = $this->list($path, ['image']);

            return $this->json(0, '操作成功', $items);
        }

        $this->data['path']     = $path;
        $this->data['oss']      = $oss;
        $this->data['type']     = $type;
        $this->data['multiple'] = $multiple;
        $this->data['selected'] = $selected;
        return $this->view();
    }

    /**
     * 视频列表
     *
     * @return View|Json
     * @throws Exception
     */
    public function video()
    {
        $path = input('post.path', AttachmentModel::ROOT_PATH);
        $path =  $this->DIRECTORY_SEPARATOR . trim($path,  $this->DIRECTORY_SEPARATOR ) .  $this->DIRECTORY_SEPARATOR ;
        $path = str_replace("\\","/",$path);
        $type     = input('type/s', 'video');
        $selected = input('selected', false);
        $multiple = input('multiple', false);

        Config::load('extra/alioss', 'alioss');
        $config = config('alioss');
        $oss    = $config['customDomain'];
        if ($this->request->isPost()) {
            $items = $this->list($path, ['video']);

            return $this->json(0, '操作成功', $items);
        }

        $this->data['path']     = $path;
        $this->data['oss']      = $oss;
        $this->data['type']     = $type;
        $this->data['multiple'] = $multiple;
        $this->data['selected'] = $selected;
        return $this->view();
    }

    /**
     * 文件列表
     *
     * @return View|Json
     * @throws Exception
     */
    public function file()
    {
        Config::load('extra/alioss', 'alioss');
        $config   = config('alioss');
        $oss      = $config['customDomain'];
        $type     = input('type/s', 'all');
        $selected = input('selected', false);
        $multiple = input('multiple', false);

        if ($this->request->isPost()) {
            $page         = input('post.page', 1);
            $size         = input('post.size', 20);
            $searchParams = input('searchParams');
            $where        = [];

            if ($searchParams) {
                foreach ($searchParams as $key => $param) {
                    if (!empty($param)) {
                        if (is_string($param)) {
                            if ($key == 'is_oss') {
                                if ($param >= 0) {
                                    $where[] = ['is_oss', '=', $param];
                                }
                            } else {
                                $where[] = [$key, 'like', '%'.$param.'%'];
                            }
                        } elseif (is_array($param)) {
                            //数组空元素去除
                            foreach ($param as $k => $val) {
                                if (empty($val)) {
                                    unset($param[$k]);
                                }
                            }
                            if (!empty($param)) {
                                $where[] = [$key, 'in', $param];
                            }
                        }
                    }
                }
            }


            if ($type !== 'all') {
                $where[] = ['type', '=', $type];
            }

            $items = AttachmentModel::findList($where, [], $page, $size, function ($q) {
                return $q->where('type', '<>', 'dir')->order('updated_at', 'desc');
            });

            $items['list']->each(function ($item) {
                $item->size_text = getFilesize($item['size'] ?? 0);
            });

            return $this->json(0, '操作成功', $items);
        }

        $this->data['oss']      = $oss;
        $this->data['type']     = $type;
        $this->data['multiple'] = $multiple;
        $this->data['selected'] = $selected;
        return $this->view();
    }

    /**
     * 一键删除失效记录 即oss不存在&&本地不存在
     *
     * @return Json
     */
    public function delLostFile(): Json
    {
        if ($this->request->isPost()) {
            $total = AttachmentModel::where('type', '<>', AttachmentModel::TYPE_DIR)
                ->where('is_dir', AttachmentModel::COMMON_OFF)
                ->where('is_oss', AttachmentModel::COMMON_OFF)
                ->where('has_local', AttachmentModel::COMMON_OFF)
                ->count();
            if ($total === 0) {
                return $this->json(0, 'success', ['total' => $total]);
            }

            if (AttachmentModel::where('type', '<>', AttachmentModel::TYPE_DIR)
                ->where('is_dir', AttachmentModel::COMMON_OFF)
                ->where('is_oss', AttachmentModel::COMMON_OFF)
                ->where('has_local', AttachmentModel::COMMON_OFF)
                ->delete()) {
                return $this->json(0, 'success', ['total' => $total]);
            }
            return $this->json(4004, '删除失败');
        }
        return $this->json(4000, '请求错误');
    }

    /**
     * 一键上传本地文件到OSS
     *
     * @return Json
     */
    public function toOss(): Json
    {
        if ($this->request->isPost()) {
            Config::load('extra/alioss', 'alioss');
            $config = config('alioss');

            $ossObject = AliOss::instance();
            $bucket    = $config['bucket'];

            $total = AttachmentModel::where('type', '<>', AttachmentModel::TYPE_DIR)
                ->where('is_dir', AttachmentModel::COMMON_OFF)
                ->where('is_oss', AttachmentModel::COMMON_OFF)
                ->field('id')
                ->count();
            $done  = 0;
            $none  = 0;
            if ($total === 0) {
                return $this->json(0, 'success', ['total' => $total, 'done' => $done, 'none' => $none]);
            }
            try {
                AttachmentModel::where('type', '<>', AttachmentModel::TYPE_DIR)
                    ->where('is_dir', AttachmentModel::COMMON_OFF)
                    ->where('is_oss', AttachmentModel::COMMON_OFF)
                    ->field('id,src')
                    ->chunk(3, function ($items) use ($ossObject, $bucket, &$done, &$none) {
                        $doneIds = [];
                        $noneIds = [];
                        foreach ($items as $item) {
                            if ($item['src']) {
                                $realPath = public_path().ltrim($item['src'],  $this->DIRECTORY_SEPARATOR );
                                if (!file_exists($realPath)) {
                                    $none++;
                                    $noneIds[] = $item['id'];
                                    continue;
                                }
                                $pathInfo = pathinfo($item['src']);
                                $object   = ltrim($item['src'],  $this->DIRECTORY_SEPARATOR );
                                //是否存在
                                if (!$ossObject->doesObjectExist($bucket, $object)) {
                                    //创建目录
                                    $ossObject->createObjectDir($bucket, ltrim($pathInfo['dirname'],  $this->DIRECTORY_SEPARATOR ));

                                    $ossObject->uploadFile($bucket, $object, $realPath);
                                }
                                $doneIds[] = $item['id'];
                                $done++;
                            }
                        }

                        // 失效标记
                        if ($noneIds) {
                            $update = ['is_oss' => AttachmentModel::COMMON_OFF, 'has_local' => AttachmentModel::COMMON_OFF];
                            (new AttachmentModel())->where('id', 'in', $noneIds)->update($update);
                        }

                        // 完成标记
                        if ($doneIds) {
                            $update = ['is_oss' => AttachmentModel::COMMON_ON];
                            (new AttachmentModel())->where('id', 'in', $doneIds)->update($update);
                        }
                    });

                return $this->json(0, 'success', ['total' => $total, 'done' => $done, 'none' => $none]);
            } catch (Exception $e) {
                \think\facade\Log::error('本地文件一键上传OSS失败 '.$e->getMessage());
                return $this->json(0, 'success', ['total' => $total, 'done' => $done, 'none' => $none]);
            }
        }
    }

    /**
     * 指定类型附件列表
     *
     * @param  array  $type
     * @param  string  $path
     * @return array
     * @throws Exception
     */
    protected function list(string $path, array $type): array
    {
        $type[]  = 'dir';
        $where[] = ['path', '=', $path];
        $where[] = ['type', 'in', $type];
        $items   = AttachmentModel::findList($where, [], 1, 0, function ($q) {
            return $q->order('is_dir', 'desc')->order('updated_at', 'desc');
        });
        $items['list']->each(function ($item) {
            $item->size_text = getFilesize($item['size'] ?? 0);
        });
        $items['path'] = $path;

        return $items;
    }

    /**
     * 获取文件大小
     *
     * @return Json
     */
    public function getSize(): Json
    {
        $path  = input('post.path', '');
        $types = input('post.type/a', []);

        $size = '';
        if (empty($path)) {
            return $this->json(0, '操作成功', $size);
        }
        $path = str_replace("\\","/",$path);
        $total = AttachmentModel::where('path', 'like', $path.'%')
            ->when(!empty($types), function ($q) use ($types) {
                $q->where('type', 'in', $types);
            })
            ->sum('size');

        return $this->json(0, '操作成功', getFilesize($total));
    }

    // 将没有md5的文件 更新md5  仅针对本地文件
    public function md5List()
    {
        $noMd5List = AttachmentModel::whereNull('md5')->select();
        $update    = [];
        foreach ($noMd5List as $item) {
            try {
                if (!empty($item['src'])) {
                    $arr        = [];
                    $path       = public_path().ltrim($item['src'],  $this->DIRECTORY_SEPARATOR );
                    $file       = new \think\File($path);
                    $arr['md5'] = $file->md5();
                    $arr['id']  = $item['id'];

                    $update[] = $arr;
                }
            } catch (Exception $e) {
                continue;
            }

        }
        (new AttachmentModel())->saveAll($update);
    }
}