package org.springblade.modules.app.manager;


import com.aliyun.oss.ClientConfiguration;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.common.comm.Protocol;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;
import com.aliyun.oss.model.PutObjectResult;
import com.wn.common.validator.Validator;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springblade.modules.app.constant.ErrorCode;
import org.springblade.modules.app.dto.PostPolicyDTO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.UUID;

@Slf4j
@Component
@RefreshScope
public class OssManager {

    private final static String HTTPS_PREFIX = "https://";
    private final static String HTTP_PREFIX = "http://";

    @Value("${aliyunoss.endpoint}")
    private String endpoint;
    @Value("${aliyunoss.accessKeyId}")
    private String accessKeyId;
    @Value("${aliyunoss.accessKeySecret}")
    private String accessKeySecret;
    @Value("${aliyunoss.bucketName}")
    private String bucketName;
    @Value("${aliyunoss.writeExpireTime:120}")
    private int writeExpireTime = 120;
    @Value("${aliyunoss.readExpireTime:600}")
    private int readExpireTime = 600;
    @Value("${aliyunoss.https:true}")
    private boolean https = true;
    @Value("${aliyunoss.base.dir.path}")
    private String baseDirPath;

    private OSSClient client;
    /**
     * //当前host地址
     */
    private String host;
    private String curPrefix;

    @PostConstruct
    private void initOssClient() {
        if (https) {
            ClientConfiguration clientConfiguration = new ClientConfiguration();
            clientConfiguration.setProtocol(Protocol.HTTPS);
            client = new OSSClient(endpoint, accessKeyId, accessKeySecret, clientConfiguration);
            curPrefix = HTTPS_PREFIX;
        } else {
            client = new OSSClient(endpoint, accessKeyId, accessKeySecret);
            curPrefix = HTTP_PREFIX;
        }
        host = curPrefix + bucketName + "." + endpoint;
    }

    public PostPolicyDTO getPostPolicy(String dir) {
        PostPolicyDTO postPolicyOutDTO = null;
        try {
            long expireEndTime = System.currentTimeMillis() + writeExpireTime * 1000;
            Date expiration = new Date(expireEndTime);
            PolicyConditions policyConds = new PolicyConditions();
            policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
            policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);

            String postPolicy = client.generatePostPolicy(expiration, policyConds);
            byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
            String encodedPolicy = BinaryUtil.toBase64String(binaryData);
            String postSignature = client.calculatePostSignature(postPolicy);

            postPolicyOutDTO = new PostPolicyDTO();
            postPolicyOutDTO.setAccessid(accessKeyId);
            postPolicyOutDTO.setPolicy(encodedPolicy);
            postPolicyOutDTO.setSignature(postSignature);
            postPolicyOutDTO.setDir(dir);
            postPolicyOutDTO.setHost(host);
            postPolicyOutDTO.setExpire(String.valueOf(expireEndTime / 1000));

        } catch (Exception e) {
            log.error("Generate policy fail.", e);
        }

        return postPolicyOutDTO;
    }

    public String upLoadImage(HttpServletRequest request, String subForder) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
            String sdate = sdf.format(new Date()) + "/";
            // 创建一个通用的多部分解析器
            CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(
                    request.getSession().getServletContext());
            // 判断 request 是否有文件上传,即多部分请求
            if (multipartResolver.isMultipart(request)) {
                // 转换成多部分request
                MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
                // 取得request中的所有文件名
                Iterator<String> iter = multiRequest.getFileNames();
                while (iter.hasNext()) {
                    // 取得上传文件
                    MultipartFile file = multiRequest.getFile(iter.next());
                    if (file != null) {
                        // 取得当前上传文件的文件名称
                        String myFileName = file.getOriginalFilename();
                        // 如果名称不为"",说明该文件存在，否则说明该文件不存在
                        if (myFileName.trim() != "") {
                            String fileName = UUID.randomUUID().toString() + getExtName(myFileName);
                            Validator.isTrue(putObject(file.getInputStream(), subForder + sdate + fileName), ErrorCode.FILE_UPLOAD_FAIL);
                            String baseDir = StringUtils.isEmpty(baseDirPath) ? host : baseDirPath;
                            return baseDir + "/" + subForder + sdate + fileName;
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error("文件上传失败", e);
        }
        return null;
    }

    /**
     * 获取文件的后缀名
     *
     * @return
     */
    public String getExtName(String fileName) {
        String prefix = fileName.substring(fileName.lastIndexOf(".") + 1);
        return "." + prefix;
    }

    public boolean putObject(String localFilePath, String localFile) {
        try {
            return putObject(new FileInputStream(localFilePath), localFile);
        } catch (FileNotFoundException e) {
            log.error("文件上传异常", e);
            return false;
        }
    }

    /**
     * 图片上传
     *
     * @param is
     * @param localFile
     * @return
     */
    public boolean putObject(InputStream is, String localFile) {
        OSSClient client = null;
        try {
            client = new OSSClient(endpoint, accessKeyId, accessKeySecret);
            PutObjectResult res = client.putObject(bucketName, localFile, is);
            if (res.getETag() != null) {
                return true;
            }
        } catch (OSSException oe) {
            log.info("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            log.info("Error Message: " + oe.getErrorCode());
            log.info("Error Code:       " + oe.getErrorCode());
            log.info("Request ID:      " + oe.getRequestId());
            log.info("Host ID:           " + oe.getHostId());
        } catch (ClientException ce) {
            log.info("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            log.info("Error Message: " + ce.getMessage());
        } finally {
            if (client != null) {
                client.shutdown();
            }
        }
        return false;
    }
}

