<?php

namespace Server\storage;

use App\Services\FileService;
use App\Services\MicroService;
use Aws\Sts\StsClient;
use Illuminate\Support\Facades\Storage;

class StorageService extends MicroService {

    public $uniacid = 0;
    public $attach_types = array("文档","图片","音频","视频");
    public $accept_type = array("file","image","audio","video");
    public $defaultImg = '//dl.gxswa.com/res/images/nopic.jpg';
    public $settings = [
        "upload"=>array(
            'image'=>[
                "extentions"=>["gif","jpg","png","jpeg"],
                "limit"=>5000,
                "zip_percentage"=>0
            ],
            'media'=>array(
                "extentions"=>["mp3","amr","wav","rm","rmvb","wmv","avi","mpg","mpeg","mp4"],
                "limit"=>50000
            ),
            'file'=>[
                "extentions"=>["zip","pdf","doc","docx","xls","xlsx","ppt","psd","rar","txt"],
                "limit"=>10000,
            ]
        ),
        "remote"=>["type"=>0]
    ];
    public $tables = array(
        "attach"=>"core_attachment",
        "group"=>"attachment_group"
    );

    function __construct($uniacid=null){
        parent::__construct('storage');
        if ($uniacid===null){
            $this->uniacid = $GLOBALS['_W']['uniacid'];
        }else{
            $this->uniacid = intval($uniacid);
        }
        $settings = $this->SettingLoad(array('remote'), $this->uniacid);
        $storageSet = $this->SettingLoad('storage');
        if (empty($storageSet['storage'])){
            $settings['storage'] = $this->settings['upload'];
            if (!empty($settings['upload'])){
                foreach ($settings['upload'] as $key=>$value){
                    if($key=='audio'){
                        $key = 'media';
                    }
                    $settings['storage'][$key] = $value;
                }
            }
            $this->SettingSave("storage", $settings['storage']);
        }else{
            $settings['storage'] = $storageSet['storage'];
        }
        $this->settings['upload'] = $settings['storage'];
        $this->settings['remote']['isglobal'] = !$this->uniacid;
        if (!empty($settings['remote']) || $this->uniacid){
            $this->settings['remote'] = $settings['remote'];
            $this->settings['remote']['isglobal'] = false;
        }
    }

    /**
     * 保存文件
     * @param  string $name 上传文件的文本域，即$_FILES对象的键名
     * @param  string|null $type 文件类型，支持file(文档)、image(图片，默认)、audio(音频)、video(视频)
     * @param  string|null $path 保存路径
     * @return  array 保存结果，失败时返回error结构的数组
    */
    public function saveFile($name, $type="image", $path = ''){
        if (!defined('QingFrame')){
            $fileobj = $_FILES[$name];
            if(!function_exists('file_upload')){
                load()->func('file');
            }
            $result = file_upload($fileobj,$type);
            if (is_error($result)) return $result;
            $fileParts = pathinfo($fileobj['name']);
            $result['name'] = $fileParts['basename'];
            $result['size'] = filesize($fileobj['tmp_name']);
        }else{
            $request = request();
            if (!$request->hasFile($name)) return error(-1,'没有上传内容');
            if (!in_array($type, $this->accept_type)) {
                return error(-2, '未知的上传类型');
            }
            $harmType = array('asp', 'php', 'jsp', 'js', 'css', 'php3', 'php4', 'php5', 'ashx', 'aspx', 'exe', 'cgi');
            $Upload = $request->file($name);
            $ext = $Upload->getClientOriginalExtension();
            $size = $Upload->getSize();
            $type_setting = in_array($type, array('audio', 'video')) ? 'media' : $type;
            $setting = $this->settings['upload'][$type_setting];
            if (in_array(strtolower($ext), $harmType) || !in_array(strtolower($ext), $setting['extentions'])){
                return error(-3, "不允许上传此类文件({$type_setting}:{$ext})");
            }
            if (!empty($setting['limit']) && $setting['limit'] * 1024 < $size) {
                return error(-4, "上传的文件超过大小限制({$size}byte)");
            }
            $this->Composer();
            if(empty($path)){
                $path = "{$type}s/".$this->uniacid."/".date('Y/m');
            }
            $filePath = $Upload->storeAs($path, random(32) . ".$ext");
            if (!$filePath) return error(-1,'上传失败，请重试');
            //图片压缩
            if ($type=='image'){
                $quality = intval($setting['upload']['image']['zip_percentage']);
                if ($quality>0 && $quality<100){
                    $savePath = ATTACHMENT_ROOT . $filePath;
                    FileService::file_image_quality($savePath, $savePath, $ext);
                }
            }
            $result = array(
                "path"=>$filePath,
                "name"=>$Upload->getClientOriginalName(),
                "size"=>$size,
                "success"=>true
            );
        }
        return $result;
    }

    /**
     * 上传文件
     * @param  string $name 上传文件的文本域，即$_FILES对象的键名
     * @param string|null $type 上传文件类型image、video、audio、file
     * @param boolean|null $disRemote 本地保存
     * @return  array 上传结果，失败时返回error结构的数组
    */
    public function putFile($name, $type="image", $disRemote=false){
        //上传到本地
        $result = $this->saveFile($name, $type);
        if (is_error($result)) return $result;
        $result['remote'] = false;
        $result['message'] = "OK";
        if (!empty($this->settings['remote']['type']) && !$disRemote) {
            $remoteStatus = $this->remoteUpload($result['path']);
            if (is_error($remoteStatus)) {
                $result['message'] = '远程附件上传失败，请检查配置并重新上传';
            }else{
                $result['remote'] = true;
                if (file_exists(ATTACHMENT_ROOT . $result['path'])) {
                    @unlink(ATTACHMENT_ROOT . $result['path']);
                }
            }
        }
        $result['url'] = tomedia($result['path']);
        $this->Event("storage.upload", $result, $this->uniacid);
        return $result;
    }

    public function getSessionToken(){
        $Sts = new StsClient([
            'credentials'=>false,
            'region'=>env('AWS_DEFAULT_REGION'),
            'version'=>'lasted',
            'accessKeyId'=>env('AWS_ACCESS_KEY_ID'),
            'secretAccessKey'=>env('AWS_SECRET_ACCESS_KEY')
        ]);
        return $Sts->getSessionToken();
    }

    public function moveFile($filename, $dest){
        global $_W;
        AttachService::mkdirs(dirname($dest));
        if (is_uploaded_file($filename)) {
            move_uploaded_file($filename, $dest);
        } else {
            rename($filename, $dest);
        }
        @chmod($filename, $_W['config']['setting']['filemode']);

        return is_file($dest);
    }

    public function removeFile($file){
        $remoteRm = false;
        if (!empty($this->settings['remote']['type'])) {
            $remoteRm = $this->remoteRemove($file);
        }
        if (empty($file)) return $remoteRm;
        if (file_exists($file)) {
            @unlink($file);
        }elseif (file_exists(ATTACHMENT_ROOT . $file)){
            @unlink(ATTACHMENT_ROOT . $file);
        }
        pdo_delete("core_attachment", array("attachment"=>$file));
        $this->Event("storage.remove", array('path'=>$file,'uniacid'=>$this->uniacid), $this->uniacid);
        return true;
    }

    public function remoteUpload($file){
        $this->Composer();
        if (empty($this->settings['remote']['type'])) return error(-1, "未配置云存储");
        $filePath = ATTACHMENT_ROOT . $file;
        if (!file_exists($filePath)){
            return error(-1, "File($file) dose not exists.");
        }
        if ($this->settings['remote']['type']==2){
            $complete = RemoteService::alioss_upload($file, $filePath, $this->settings['remote']['alioss']);
        }elseif ($this->settings['remote']['type']==4){
            $complete = RemoteService::cos_upload($file, $this->settings['remote']['cos']);
        }elseif ($this->settings['remote']['type']==5){
            try {
                if(Storage::disk('s3')->exists($file)){
                    return true;
                }
                if (!Storage::disk('s3')->put($file, file_get_contents($filePath))){
                    return error(-1, "上传S3失败，请重试");
                }
            }catch (Exception $exception){
                return error(-1, $exception->getMessage());
            }
        }
        if (is_error($complete)) return $complete;
        return true;
    }

    public function remoteRemove($file){
        $this->Composer();
        if (empty($this->settings['remote']['type'])) return true;
        if (!class_exists('\Storage\RemoteService')){
            include_once MICRO_SERVER . "storage/RemoteService.php";
        }
        if ($this->settings['remote']['type']==2){
            $complete = RemoteService::alioss_delete($file, $this->settings['remote']['alioss']);
        }elseif ($this->settings['remote']['type']==4){
            $complete = RemoteService::cos_delete($file, $this->settings['remote']['cos']);
        }elseif ($this->settings['remote']['type']==5){
            try {
                $complete = Storage::disk('s3')->delete($file);
            }catch (Exception $exception){
                return false;
            }
        }
        if (is_error($complete)) return false;
        return true;
    }

    public function getGroups($pid=0, $type=false){
        global $_W;
        $condition = array(
            'uniacid'=>$this->uniacid,
            'pid'=>intval($pid)
        );
        if (empty($pid)){
            $condition['uid'] = $_W['uid'];
        }
        if ($type!==false){
            $condition['type'] = intval($type);
        }
        $groups = pdo_getall($this->tables['group'], $condition);
        if (empty($groups)) return array();
        if (empty($pid)){
            foreach ($groups as &$value){
                $value['sub_group'] = $this->getGroups($value['id']);
                $value['total'] = $this->getList(array('group_id'=>$value['id']), -1, 'total');
            }
        }
        return $groups;
    }

    public function getCover($file, $type=1){
        if($type==1) return tomedia($file);
        $types = array("file","image","audio","video");
        return "//dl.gxswa.com/res/icon/$types[$type].svg";
    }

    public function detectText($text, $BizType='', $showDetail=false){
        $cos = $this->settings['remote']['cos'];
        $result = array(
            'type'=>'text',
            'input'=>$text,
            'Result'=>0,
            'Score'=>0,
            'filtration'=>$text,
            'Keywords'=>"",
            'Label'=>'',
            'remark'=>'未审核'
        );
        if (empty($cos)) return $result;
        $this->Composer();
        try {
            $cosClient = RemoteService::InitCos($cos);
            $Conf = [];
            if (!empty($BizType)){
                //审核策略
                $Conf['BizType'] = $BizType;
            }
            $res = $cosClient->detectText(array(
                'Bucket' => $cos['bucket'],
                'Input' => array(
                    'Content' => base64_encode($text)
                ),
                'Conf' => $Conf
            ));
            $result['remark'] = '已审核';
            $result['Result'] = intval($res['JobsDetail']['Result']);
            if ($showDetail){
                $result['JobsDetail'] = $res['JobsDetail'] ?? [];
            }
            if ($result['Result']!=0){
                $result['Label'] = $res['JobsDetail']['Label'];
                $Label = $result['Label'] . "Info";
                $Section = !empty($res['JobsDetail']['Section'][0][$Label]) ? $res['JobsDetail']['Section'][0][$Label] : [];
                if (!empty($Section)){
                    $result['Score'] = $Section['Score'];
                    $result['Keywords'] = $Section['Keywords'];
                }
                $result['Keywords'] = $result['Keywords']?:$text;
                $Keywords = explode(',', $result['Keywords']);
                $result['filtration'] = str_replace($Keywords, '**', $text);
                if (strpos($result['Keywords'], '&')!==false){
                    $result['filtration'] = '**';
                    $result['remark'] .= "，存在违规：{$result['Keywords']}";
                }else{
                    $result['remark'] .= "，存在违禁词：{$result['Keywords']}";
                }
            }
        }catch (\Exception $exception){
            $result['Result'] = 2;
            $result['remark'] = $exception->getMessage();
        }
        return $result;
    }

    public function tpl_form_image($name,$value="",$options=array(), $bootstrap=false){
        if ($bootstrap && function_exists('tpl_form_field_image')){
            return tpl_form_field_image($name,$value);
        }
        $val = $this->defaultImg;
        if (!empty($value)) {
            $val = $this->uniacid ? tomedia($value) : globalMedia($value);
        }
        $options['class_extra'] = trim($options['class_extra']);
        $inputattr = ' name="'.$name.'" value="'.$value.'"';
        if (!empty($options['placeholder'])){
            $inputattr .= ' placeholder="'.$options['placeholder'].'"';
        }
        if (!empty($options['required'])){
            $inputattr .= ' required lay-verify="required"';
        }
        $params = array("type"=>1,"uniacid"=>$this->uniacid);
        if (isset($options['disremote'])){
            $params['disremote'] = intval($options['disremote']);
        }
        $pickerurl = $this->url("picker", $params);
        if(!empty($options['client'])){
            $pickerurl = $this->api("picker", array("type"=>1,"uniacid"=>$this->uniacid,"r"=>$options['client']), 'app');
        }
        return '
            <div class="layui-input-block'.$options['class_extra'].'">
                <div class="layui-input-inline" style="width:70%;">
                    <input type="text"'.$inputattr.($options['disabled']?" readonly":"").' class="layui-input" autocomplete="off">
                </div>
                <button data-url="'.$pickerurl.'" data-title="图片选择器" class="layui-btn layui-btn-default" type="button" onclick="Core.StoragePicker(this);">选择图片</button>
            </div>
            <div class="layui-input-block input-group" style="margin-top:.5em;">
                <img src="'.$val.'" alt="'.$options['placeholder'].'" data-val="'.$this->defaultImg.'" class="img-responsive img-thumbnail'.(empty($value)?' nopic':'').'" width="150" />
                <em class="close" onclick="Core.StorageRmImg(this)" style="position:absolute;" title="删除这张图片">×</em>
            </div>';
    }

    public function tpl_form_file($name,$value="",$options=array()){
        $options['class_extra'] = trim($options['class_extra']);
        $inputAttr = ' name="'.$name.'" value="'.$value.'"';
        if (!empty($options['attrExtra'])){
            $inputAttr .= " ".$options['attrExtra'];
        }
        if (!empty($options['placeholder'])){
            $inputAttr .= ' placeholder="'.$options['placeholder'].'"';
        }
        if (!empty($options['required'])){
            $inputAttr .= ' required lay-verify="required"';
        }
        $params = array("type"=>0,"uniacid"=>$this->uniacid);
        if (isset($options['fileType'])){
            $params['type'] = intval($options['fileType']);
        }
        if (isset($options['disremote'])){
            $params['disremote'] = intval($options['disremote']);
        }
        $pickerUrl = $this->url("picker", $params);
        $titles = ['文件选择器', '图片选择器', '音频选择器', '视频选择器'];
        return '
            <div class="layui-input-block'.$options['class_extra'].'">
                <div class="layui-input-inline'.(empty($options['addon'])?'':' input-group').'" style="width:70%;">
                    '.(empty($options['addon'])?'':'<span class="input-group-addon">'.$options['addon'].'</span>').'
                    <input type="text"'.$inputAttr.($options['disabled']?" readonly":"").' class="layui-input" autocomplete="off">
                </div>
                <button data-url="'.$pickerUrl.'" data-title="'.$titles[$params['type']].'" class="layui-btn layui-btn-default" type="button" onclick="Core.StoragePicker(this);">选择'.$this->attach_types[$params['type']].'</button>
            </div>';
    }

    public function tpl_form_images($name,$value=array(),$options=array(), $bootstrap=false){
        if ($bootstrap && function_exists('tpl_form_field_multi_image')){
            return tpl_form_field_multi_image($name,$value, $options);
        }
        $valueStr = empty($value)?'':implode(',', $value);
        $name = preg_replace('/\[\]$/',"",$name);
        $pickerurl = !empty($options['client'])?$this->urlApp("picker", array("type"=>1,"uniacid"=>$this->uniacid,"r"=>$options['client'])):$this->url("picker", array("type"=>1,"uniacid"=>$this->uniacid));
        $html = '<div class="layui-input-block'.$options['class_extra'].'">
            <div class="layui-input-inline" style="width:70%;">
                <input type="text" class="layui-input" name="'.$name.'" readonly="readonly" value="'.$valueStr.'" placeholder="'.$options['placeholder'].'" autocomplete="off">
            </div>
            <button data-url="'.$pickerurl.'" data-title="图片选择器" class="layui-btn layui-btn-default" type="button" onclick="Core.StoragePicker(this, true);" multiple>选择图片</button>
            <input type="hidden" value="'.$name.'" />
        </div>';
        $html .= '<div class="layui-input-block multi-img-details" style="overflow: hidden; min-height: unset">';
        if (!empty($value)){
            foreach ($value as $pic){
                $src = $this->uniacid ? tomedia($pic) : globalMedia($pic);
                $html .= '<div class="multi-item">
                    <input type="hidden" name="'.$name.'[]" value="'.$pic.'" >
                    <img src="'.$src.'" class="img-responsive img-thumbnail">
                    <em class="close" title="删除这张图片" onclick="Core.StorageRmImg(this, true)">×</em>
                </div>';
            }
        }
        $html .= '</div>';
        return $html;
    }

    public function getList($condition=array(),$page=1,$fetch="all"){
        global $_W;
        $where = "a.uniacid=".$this->uniacid;
        $params = array();
        if (isset($condition['type'])){
            $where .= " and a.type=".intval($condition['type']);
        }
        if (isset($condition['group_id'])){
            $gid = intval($condition['group_id']);
            if ($gid>0){
                $where .= " and (a.group_id=$gid or g.pid=$gid)";
            }else{
                $where .= " and a.group_id=".$gid;
            }
        }
        if (isset($condition['uid'])){
            $where .= " and a.uid=".intval($condition['uid']);
        }
        if (isset($condition['module_name'])){
            $where .= " and a.module_name='{$condition['module_name']}'";
        }
        $order = " order by a.displayorder desc,a.createtime desc";
        $pagesize = empty($condition['pagesize']) ? 18 : intval($condition['pagesize']);
        $joinsql = "";
        $list = [];
        if ($fetch=='list' || $fetch=='all'){
            $limit = "";
            if ($page!=-1){
                $page = max(1,intval($page));
                $limit = " limit ".($page-1)*$pagesize .",$pagesize";
            }
            $select = empty($condition['select']) ? "a.*,g.name" : trim($condition['select']);
            $list = pdo_fetchall("select $select from ".tablename($this->tables['attach'])." as a left join ".tablename($this->tables['group'])." as g on g.id=a.group_id$joinsql where ".$where.$order.$limit,$params);
            if (!empty($list)){
                foreach ($list as &$value){
                    $value['cover'] = $this->getCover($value['attachment'], $value['type']);
                    $value['url'] = tomedia($value['attachment']);
                    $value['datetime'] = date('Y-m-d H:i', $value['createtime']);
                    $localPath = ATTACHMENT_ROOT . $value['attachment'];
                    $value['isLocal'] = file_exists($localPath);
                }
            }
            if($fetch=='list') return $list;
        }
        if ($fetch=='total' || $fetch=='all'){
            $total = (int)pdo_fetchcolumn("select count(*) from ".tablename($this->tables['attach'])." as a left join ".tablename($this->tables['group'])." as g on g.id=a.group_id$joinsql where ".$where,$params);
            if($fetch=='total') return $total;
        }
        $pager = pagination($total, $page, $pagesize, '', ['ajaxcallback'=>$_W['isajax']]);
        return array($list, $total, $pager);
    }

}
