package com.yonge.toolset.thirdparty.storage.provider;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.ksyun.ks3.dto.PostObjectFormFields;
import com.ksyun.ks3.service.request.PutObjectACLRequest;
import com.yonge.toolset.thirdparty.entity.UploadSign;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.util.IOUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.ksyun.ks3.dto.CannedAccessControlList;
import com.ksyun.ks3.dto.GetObjectResult;
import com.ksyun.ks3.dto.Ks3Object;
import com.ksyun.ks3.http.HttpClientConfig;
import com.ksyun.ks3.service.Ks3;
import com.ksyun.ks3.service.Ks3Client;
import com.ksyun.ks3.service.Ks3ClientConfig;
import com.ksyun.ks3.service.Ks3ClientConfig.PROTOCOL;
import com.ksyun.ks3.service.request.GetObjectRequest;
import com.ksyun.ks3.service.request.PutObjectRequest;
import com.yonge.toolset.base.exception.ThirdpartyException;
import com.yonge.toolset.thirdparty.storage.StoragePlugin;
import com.yonge.toolset.thirdparty.storage.StoragePluginContext;

@Component
public class KS3StoragePlugin implements StoragePlugin, InitializingBean, DisposableBean {

    public final static String PLUGIN_NAME = "Ksyun";

    @Value("${storage.oss.endpoint:ks3-cn-beijing.ksyun.com}")
    private String endpoint;

    @Value("${storage.oss.accessKeyId:AKLTtTeIbadpRG-pil4S0Q4m-Q}")
    private String accessKeyId;

    @Value("${storage.oss.accessKeySecret:OB1HmNOfDNW95wHoxMkP6IPFZXormk2ngA800TkvKAw7ozhiJGRqrMnnV8ZrAU3WRQ==}")
    private String accessKeySecret;

    @Value("${storage.oss.bucketName:daya}")
    private String bucketName;

    private Ks3 client;

    public String getName() {
        return PLUGIN_NAME;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Ks3ClientConfig config = new Ks3ClientConfig();
        config.setEndpoint(endpoint);// 如果使用自定义域名，设置endpoint为自定义域名，同时设置domainMode为true
        /**
         * true：表示以自定义域名访问
         * false：表示以KS3的外网域名或内网域名访问
         * 默认为false
         */
        config.setDomainMode(false);
        config.setProtocol(PROTOCOL.http);

        /**
         * true表示以   endpoint/{bucket}/{key}的方式访问
         * false表示以  {bucket}.endpoint/{key}的方式访问
         * 如果domainMode设置为true，pathStyleAccess可忽略设置
         */
        config.setPathStyleAccess(false);
        HttpClientConfig hconfig = new HttpClientConfig();
        // 在HttpClientConfig中可以设置httpclient的相关属性，比如代理，超时，重试等。
        config.setHttpClientConfig(hconfig);
        client = new Ks3Client(accessKeyId, accessKeySecret, config);

        StoragePluginContext.addStoragePlugin(this);
    }

    @Override
    public String uploadFile(String folderName, File file) {
        if (!file.exists()) {
            throw new ThirdpartyException("需要上传的文件[{}]不存在", file.getAbsolutePath());
        }

        if (folderName.endsWith("/")) {
            folderName = folderName.substring(0, folderName.length() - 1);
        }

        PutObjectRequest request = new PutObjectRequest(bucketName, folderName + "/" + file.getName(), file);

        // 上传一个公开文件
        request.setCannedAcl(CannedAccessControlList.PublicRead);

        client.putObject(request);

        return "https://" + bucketName + "." + endpoint + "/" + folderName + "/" + file.getName();
    }

    @Override
    public String asyncUploadFile(String folderName, File file, boolean delLocalFile) {
        if (!file.exists()) {
            throw new ThirdpartyException("需要上传的文件[{}]不存在", file.getAbsolutePath());
        }

        if (folderName.endsWith("/")) {
            folderName = folderName.substring(0, folderName.length() - 1);
        }

        PutObjectRequest request = new PutObjectRequest(bucketName, folderName + "/" + file.getName(), file);

        // 上传一个公开文件
        request.setCannedAcl(CannedAccessControlList.PublicRead);

        Thread thread = new Thread(new Runnable() {

            @Override
            public void run() {
                client.putObject(request);
                if (delLocalFile) {
                    FileUtils.deleteQuietly(file);
                }
            }
        });
        thread.start();

        return "https://" + bucketName + "." + endpoint + "/" + folderName + "/" + file.getName();
    }

    @Override
    public byte[] getFile(String folderName, String fileName) throws IOException {
        GetObjectRequest request = new GetObjectRequest(bucketName, folderName + "/" + fileName);
        GetObjectResult result = client.getObject(request);

        Ks3Object object = result.getObject();
        try {
            return IOUtils.toByteArray(object.getObjectContent());
        } finally {
            if (object != null) {
                object.close();
            }
        }
    }

    @Override
    public PostObjectFormFields getUploadSign(UploadSign uploadSign) {
        String bucketName = uploadSign.getBucketName();
        if (bucketName == null) {
            bucketName = this.bucketName;
        }
        //要上传的文件名称,不包含路径信息
        String fileName = uploadSign.getFilename();
        //可以确定值得表单项
        Map<String, String> postData = uploadSign.getPostData();
        //无法确定值得表单项
        List<String> unknowValueField = uploadSign.getUnknowValueField();
        if (unknowValueField == null) {
            unknowValueField = new ArrayList<>();
        }

        Ks3ClientConfig config = new Ks3ClientConfig();
        // 设置域名
        config.setEndpoint(endpoint);
        // 创建Ks3Client实例
        Ks3 client = new Ks3Client(accessKeyId, accessKeySecret, config);

        /**
         * 需要用户在postData和unknowValueField中提供所有的除KSSAccessKeyId, signature, file, policy外的所有表单项。否则用生成的签名上传会返回403</br>
         * 对于用户可以确定表单值的放在 postData中，对于用户无法确定表单值的放在unknownValueField中(比如有的上传控件会添加一些表单项,但表单项的值可能是随机的)</br>
         */
        //Map<String, String> postData = new HashMap<String, String>();
        // 如果使用js sdk上传的时候设置了ACL，请提供以下一行，且值要与SDK中一致，否则删除下面一行代码
        //postData.put("acl","public-read");
        // 提供js sdk中的key值
        //postData.put("key","20150115/中文/${filename}");
        // 设置无法确定的表单值
        //List<String> unknowValueField = new ArrayList<String>();

        // js sdk上传的时候会自动加上一个name的表单项，所以下面需要加上这样的代码。
        unknowValueField.add("name");

        // 如果计算签名时提供的key里不包含${filename}占位符，第二个参数传一个空字符串。
        String postDataKey = postData.get("key");
        if (StringUtils.isNotBlank(postDataKey)) {
            if (postDataKey.indexOf("${filename}") == -1) {
                return client.postObject(bucketName, "", postData, unknowValueField);
            }
        }
        return client.postObject(bucketName, fileName, postData, unknowValueField);
    }

    @Override
    public void setFileAcl(String fileName, Boolean isPublic,String bucketName) {
        PutObjectACLRequest request = new PutObjectACLRequest(bucketName,fileName);
        if (isPublic){
            //设为公开读
            request.setCannedAcl(CannedAccessControlList.PublicRead);
        }else {
            //设为私有
            request.setCannedAcl(CannedAccessControlList.Private);
        }
        client.putObjectACL(request);
    }

    @Override
    public void destroy() throws Exception {
    }

    public void setEndpoint(String endpoint) {
        this.endpoint = endpoint;
    }

    public void setAccessKeyId(String accessKeyId) {
        this.accessKeyId = accessKeyId;
    }

    public void setAccessKeySecret(String accessKeySecret) {
        this.accessKeySecret = accessKeySecret;
    }

    public void setBucketName(String bucketName) {
        this.bucketName = bucketName;
    }

    public static void main(String[] args) throws Exception {
        KS3StoragePlugin aliyunOssStorageService = new KS3StoragePlugin();
        aliyunOssStorageService.setAccessKeyId("LTAIwZW9XqrfsZ4r");
        aliyunOssStorageService.setAccessKeySecret("5uDsNZmHMxcnxav8w9byII4zcPpu5G");
        aliyunOssStorageService.setBucketName("yooma-test");
        aliyunOssStorageService.setEndpoint("oss-cn-beijing.aliyuncs.com");
        aliyunOssStorageService.afterPropertiesSet();

        File file = new File("e:/var/2.jpg");
        System.out.println(aliyunOssStorageService.uploadFile("aaa", file));

        System.err.println("***********" + aliyunOssStorageService.getFile("aaa", file.getName()).length);

        aliyunOssStorageService.destroy();
    }
}
