package com.philisense.www.cloud;

import cn.hutool.http.HttpUtil;
import com.philisense.www.config.OssStorageProperties;
import com.philisense.www.exception.StorageException;
import com.philisense.www.rule.OssRule;
import com.philisense.www.utils.FileUtils;
import com.philisense.www.utils.SpringContextUtils;
import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Region;
import com.qiniu.storage.UploadManager;
import com.qiniu.util.Auth;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

/**
 * 七牛云存储
 *
 * @author smalljop
 */
public class QiniuStorageService extends OssStorageService {

    private static Logger log = LogManager.getLogger(QiniuStorageService.class);

    private OssRule ossRule;

    private OssStorageProperties.QiniuConfig qiniuConfig;
    private String diskName;

    private UploadManager uploadManager;
    private BucketManager bucketManager;
    private Auth auth;

    public QiniuStorageService(OssStorageProperties.QiniuConfig qiniuConfig, String diskName) {
        this.qiniuConfig = qiniuConfig;
        this.diskName = diskName;

        Configuration cfg = new Configuration(Region.autoRegion());
        uploadManager = new UploadManager(cfg);
        auth = Auth.create(qiniuConfig.getAccessKey(), qiniuConfig.getAccessSecret());
        bucketManager = new BucketManager(auth, cfg);
        ossRule = SpringContextUtils.getBean(OssRule.class);
    }

    @Override
    public Map<String, Object> upload(MultipartFile file, String path) {
        try {
            return put(qiniuConfig.getBucketName(), file.getInputStream(), path, false);
        } catch (Exception e) {
            log.error("上传文件失败：{} | {}",e.getMessage(), e);
            throw new StorageException("上传文件失败", e);
        }
    }

    @Override
    public Map<String, Object> upload(byte[] data, String path) {
        try {
            return put(qiniuConfig.getBucketName(), new ByteArrayInputStream(data), path, false);
        } catch (Exception e) {
            log.error("上传文件失败：{} | {}",e.getMessage(), e);
            throw new StorageException("上传文件失败", e);
        }
    }

    private Map<String, Object> put(String bucketName, InputStream stream, String path, boolean cover) {
        try {
            path = ossRule.filePath(diskName + FileUtils.SLASH + path);
            // 覆盖上传
            if (cover) {
                uploadManager.put(stream, path, getUploadToken(bucketName, path), null, null);
            } else {
                Response response = uploadManager.put(stream, path, getUploadToken(bucketName), null, null);
                int retry = 0;
                int retryCount = 5;
                while (response.needRetry() && retry < retryCount) {
                    response = uploadManager.put(stream, path, getUploadToken(bucketName), null, null);
                    retry++;
                }
            }

            Map<String, Object> fileMap = new HashMap<>();
            fileMap.put("path", path);
            fileMap.put("url", getUrl(path));
            return fileMap;
        } catch (Exception e) {
            log.error("上传文件失败：{} | {}",e.getMessage(), e);
            throw new StorageException("上传文件失败", e);
        }
    }

    @Override
    public InputStream download(String path) {
        try {
            final ByteArrayOutputStream output = new ByteArrayOutputStream();
            HttpUtil.download(getUrl(path), output, false);
            return new ByteArrayInputStream(output.toByteArray());
        } catch (Exception e) {
            log.error("下载文件失败：{} | {}",e.getMessage(), e);
            throw new StorageException("下载文件失败", e);
        }
    }

    @Override
    public void delete(String path) {
        try {
            Response res = bucketManager.delete(qiniuConfig.getBucketName(), path);
            if (!res.isOK()) {
                throw new StorageException("删除文件失败：" + res);
            }
        } catch (QiniuException e) {
            log.error("删除文件失败：{} | {}",e.getMessage(), e);
            throw new StorageException("删除文件失败", e);
        }
    }

    @Override
    public String getUrl(String path) {
        return qiniuConfig.getEndpoint().concat(String.valueOf(FileUtils.SLASH)).concat(path);
    }

    @Override
    public Map<String, Object> chunksCheck(String path, String name, String hash, Integer fileSize, Integer total) {
        throw new StorageException("暂未实现该功能");
    }

    @Override
    public Map<String, Object> chunksUpload(MultipartFile file, String path, String hash, String uploadId, Integer index, String fileSize) {
        throw new StorageException("暂未实现该功能");
    }

    @Override
    public Map<String, Object> chunksMerge(String path, String name, String hash, String uploadId, String fileSize) {
        throw new StorageException("暂未实现该功能");
    }

    /**
     * 获取上传凭证，普通上传
     *
     * @param bucketName 存储桶名称
     * @return string
     */
    private String getUploadToken(String bucketName) {
        return auth.uploadToken(bucketName);
    }

    /**
     * 获取上传凭证，覆盖上传
     *
     * @param bucketName 存储桶名称
     * @param key        key
     * @return string
     */
    private String getUploadToken(String bucketName, String key) {
        return auth.uploadToken(bucketName, key);
    }

}
