<?php namespace App\Servers\Tencent;

use Overtrue\CosClient\BucketClient;
use Overtrue\CosClient\ObjectClient;

    /**
     * 腾讯云Cos
     * 
     */
	class Cos{

        /* 配置 */
        private $config     = [];

        /**
         * 初始化
         * 
         */
        public function __construct(){
            $this->config['app_id']                     = config('tencent.cos.app_id','');
            $this->config['secret_id']                  = config('tencent.secretId','');
            $this->config['secret_key']                 = config('tencent.secretKey','');
            $this->config['uin']                        = config('tencent.cos.uin','');
            $this->config['region']                     = config('tencent.cos.region','');
            $this->config['bucket']                     = config('tencent.cos.bucket','');
            $this->config['guzzle']                     = config('tencent.cos.guzzle',[]);
            $this->config['signature_expires']          = config('tencent.cos.signature_expires','');
            $this->config['base_uri']                   = sprintf('https://%s-%s.cos.%s.myqcloud.com/',$this->config['bucket'],$this->config['app_id'],$this->config['region']);
        }

        /**
         * 获取指定目录下的所有对象
         * @param   Array          $result      结果集
         * @param   String         $prefix      列出对象的前缀
         * @param   String         $marker      上次列出对象的断点
         * @param   Int            $maxkeys     设置单次查询打印的最大数量，最大为1000
         * @param   String         $filter_name 搜索文件
         */
        public function listObjectsAll($prefix='',$filter_name='',$marker='',$maxkeys=16,$delimiter='/',$result=[]){
            // 查询条件
            $query                  = [];
            // 组合条件
            if( $marker )           $query['marker']    = $marker;
            if( $maxkeys )          $query['max-keys']  = $maxkeys;
            if( $delimiter )        $query['delimiter'] = $delimiter;
            if( $prefix )           $query['prefix']    = rtrim($prefix,$delimiter).$delimiter;
            // 增加分隔符后缀，以实现列出当前目录下的对象内容，必须与分割符号配合使用
            // 实例化存储桶
            $obj                    = new BucketClient($this->config);
            // 尝试执行
            try {
                // 查询列表
                $objects		    = $obj->getObjects($query)->toArray();
                // 文件夹
                $prefixes           = isset($objects['ListBucketResult']['CommonPrefixes']) ? $objects['ListBucketResult']['CommonPrefixes'] : [];
                // 文件
                $contents           = isset($objects['ListBucketResult']['Contents']) ? $objects['ListBucketResult']['Contents'] : [];
                // 是否有下一页
                $isTruncated        = isset($objects['ListBucketResult']['IsTruncated']) ? $objects['ListBucketResult']['IsTruncated'] : 'false';
                // 断点
                $marker             = $isTruncated == 'true' ? $objects['ListBucketResult']['NextMarker'] : '';
                /* 是个大坑，如果当前列表只有一个对象时只会返回一维关联数组 */
                // 如果存在文件夹，但是文件夹数组中不是以索引数组出现的话
                if( $prefixes && empty($prefixes[0]['Prefix']) ) $prefixes = [$prefixes];
                // ，但是文件夹数组中不是以索引数组出现的话
                if( $contents && empty($contents[0]['Key']) ) $contents = [$contents];
                // 获取列表
                $objectList         = [];
                // 循环文件夹对象
                foreach ( $prefixes  as $value ) {
                    // 如果有搜索条件 并且不含有搜索词
                    if( $filter_name && !stripos( '$'.basename($value['Prefix']), $filter_name) )  continue;
                    // 获取文件key作为路径
                    array_unshift($objectList,$value['Prefix']);
                }
                // 循环文件对象
                foreach ( $contents as $value ) {
                    // 如果有搜索条件 并且不含有搜索词
                    if( $filter_name && !stripos( '$'.basename($value['Key']), $filter_name) )  continue;
                    // 获取文件key作为路径
                    $objectList[]                   = $this->config['base_uri'].$value['Key'];
                }
                // 合并结果集
                $result                             = array_merge($result,$objectList);
                // 删除变量，释放内存
                unset($objectList,$temp,$prefixes,$contents,$isTruncated);
                // 如果有断点，继续获取下一批
                if( $marker )                       $result = $this->listObjectsAll($prefix,$filter_name,$marker,$maxkeys,$delimiter,$result);
                // 如果有错误返回错误结果
                if( isset($result['error']))        return ['error'=>$result['error']];
                // 返回结果
                return                              $result;
            } catch (\Exception $e) {
                // 如果错误
                return                              ['error'=>$e->getMessage()];
            }
        }

        /**
         * 上传对象
         * 
         * 将本地的对象（Object）上传至指定存储桶中
         * @param   String         $key         对象键（Key）是对象在存储桶中的唯一标识,亦就是对象在存储桶中的以根起始的完整存储路径
         * @param   String         $body        上传的内容
         * @param   Array          $headers     选项
         * 
         * @return  String|Array   完整的url访问路径
         */
        public function upload($key,$body,$headers=[]){
            // 避免重复目录路径
            $key                    = ltrim($key,'/');
            // 实例化对象管理
            $obj                    = new ObjectClient($this->config);
            // 尝试执行
            try {
                // 调用上传
                $obj->putObject($key,$body,$headers);
                // 返回结果
                return              $this->config['base_uri'].$key;
            } catch (\Exception $e) {
                // 如果错误
                return      ['error'=>$e->getMessage()];
            }
        }

        /**
         * 删除单个对象
         * 在存储桶中删除指定 Object （文件/对象）
         * @param   String         $key         对象键（Key）是对象在存储桶中的唯一标识,亦就是对象在存储桶中的以根起始的完整存储路径
         * @param   String         $versionId   删除文件的版本号
         * 
         */
        public function deleteObject($key,$versionId=null){
            // 实例化对象管理
            $obj                    = new ObjectClient($this->config);
            // 尝试执行
            try {
                // 选择对象,替换掉文件路径
                $key                = str_ireplace($this->config['base_uri'],'',$key);
                // 调用上传
                $obj->deleteObject($key,$versionId);
                // 返回结果
                return              true;
            } catch (\Exception $e) {
                // 如果错误
                return              ['error'=>$e->getMessage()];
            }
        }




    }