package com.tuoheng.admin.service.aliyun;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.aliyuncs.sts.model.v20150401.AssumeRoleRequest;
import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse;
import com.tuoheng.admin.config.AliyuncsVodConfig;
import com.tuoheng.admin.service.aliyun.IAliyunOssService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Slf4j
@Service
public class AliyunOssServiceImpl implements IAliyunOssService {

    /**
     * 获取临时访问凭证
     *
     * @param roleSessionName 自定义角色会话名称，用来区分不同的令牌，例如可填写为SessionTest。
     * @return
     */
    public static AssumeRoleResponse.Credentials getSecurityToken(String roleSessionName) {
        // STS接入地址，例如sts.cn-shanghai.aliyuncs.com。
        String endpoint = "sts.cn-shanghai.aliyuncs.com";
        // 填写步骤1生成的访问密钥AccessKey ID和AccessKey Secret。
        String AccessKeyId = AliyuncsVodConfig.accessKeyId;
        String accessKeySecret = AliyuncsVodConfig.accessKeySecret;
        // 填写步骤3获取的角色ARN。
        String roleArn = AliyuncsVodConfig.roleArn;
        // 自定义角色会话名称，用来区分不同的令牌，例如可填写为SessionTest。
//        String roleSessionName = "<yourRoleSessionName>";
        // 以下Policy用于限制仅允许使用临时访问凭证向目标存储空间examplebucket上传文件。
        // 临时访问凭证最后获得的权限是步骤4设置的角色权限和该Policy设置权限的交集，即仅允许将文件上传至目标存储空间examplebucket下的exampledir目录。
        String policy = "{\n" +
                "    \"Version\": \"1\", \n" +
                "    \"Statement\": [\n" +
                "        {\n" +
                "            \"Action\": [\n" +
                "                \"oss:PutObject\"\n" +
                "            ], \n" +
                "            \"Resource\": [\n" +
                "                \"acs:oss:*:*:ta-tech-image/imagedir/*\" \n" +
                "            ], \n" +
                "            \"Effect\": \"Allow\"\n" +
                "        }\n" +
                "    ]\n" +
                "}";
        try {
            // regionId表示RAM的地域ID。以华东1（杭州）地域为例，regionID填写为cn-hangzhou。也可以保留默认值，默认值为空字符串（""）。
            String regionId = "";
            // 添加endpoint。适用于Java SDK 3.12.0及以上版本。
            DefaultProfile.addEndpoint(regionId, "Sts", endpoint);
            // 添加endpoint。适用于Java SDK 3.12.0以下版本。
            // DefaultProfile.addEndpoint("",regionId, "Sts", endpoint);
            // 构造default profile。
            IClientProfile profile = DefaultProfile.getProfile(regionId, AccessKeyId, accessKeySecret);
            // 构造client。
            DefaultAcsClient client = new DefaultAcsClient(profile);
            final AssumeRoleRequest request = new AssumeRoleRequest();
            // 适用于Java SDK 3.12.0及以上版本。
            request.setSysMethod(MethodType.POST);
            // 适用于Java SDK 3.12.0以下版本。
            //request.setMethod(MethodType.POST);
            request.setRoleArn(roleArn);
            request.setRoleSessionName(roleSessionName);
            request.setPolicy(policy); // 如果policy为空，则用户将获得该角色下所有权限。
            request.setDurationSeconds(3600L); // 设置临时访问凭证的有效时间为3600秒。
            final AssumeRoleResponse response = client.getAcsResponse(request);
            log.info("Expiration: " + response.getCredentials().getExpiration());
            log.info("Access Key Id: " + response.getCredentials().getAccessKeyId());
            log.info("Access Key Secret: " + response.getCredentials().getAccessKeySecret());
            log.info("Security Token: " + response.getCredentials().getSecurityToken());
            log.info("RequestId: " + response.getRequestId());
            return response.getCredentials();
        } catch (ClientException e) {
            log.error("Error code: " + e.getErrCode());
            log.error("Error message: " + e.getErrMsg());
            log.error("RequestId: " + e.getRequestId());
        }
        return null;
    }

    @Override
    public URL generatePresignedUrl(String objectName) throws Throwable{
        // Endpoint以华东2（上海）为例，其它Region请按实际情况填写。
        String endpoint = "https://oss-cn-shanghai.aliyuncs.com";
        // 阿里云账号AccessKey拥有所有API的访问权限，风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维，请登录RAM控制台创建RAM用户。
        String accessKeyId = AliyuncsVodConfig.accessKeyId;
        String accessKeySecret = AliyuncsVodConfig.accessKeySecret;
        // 从STS服务获取的安全令牌（SecurityToken）。
        AssumeRoleResponse.Credentials credentials = getSecurityToken("SessionTest");
        String securityToken = credentials.getSecurityToken();
        // 填写Bucket名称，例如examplebucket。
        String bucketName = AliyuncsVodConfig.bucketName;
        // 填写Object完整路径，例如exampleobject.txt。Object完整路径中不能包含Bucket名称。
//        String objectName = "exampleobject.txt";

        // 从STS服务获取临时访问凭证后，您可以通过临时访问密钥和安全令牌生成OSSClient。
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret, securityToken);

        try {
            // 设置签名URL过期时间为3600秒（1小时）。
            Date expiration = new Date(new Date().getTime() + 3600 * 1000);
            // 生成以GET方法访问的签名URL，访客可以直接通过浏览器访问相关内容。
            URL url = ossClient.generatePresignedUrl(bucketName, objectName, expiration);
            return url;
        } catch (OSSException oe) {
            log.error("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            log.error("Error Message:" + oe.getErrorMessage());
            log.error("Error Code:" + oe.getErrorCode());
            log.error("Request ID:" + oe.getRequestId());
            log.error("Host ID:" + oe.getHostId());
        } catch (com.aliyun.oss.ClientException ce) {
            log.error("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.error("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return null;
    }

    @Override
    public List<URL> generatePresignedUrls(String[] objectNameList) throws Throwable {
        // Endpoint以华东2（上海）为例，其它Region请按实际情况填写。
        String endpoint = "https://oss-cn-shanghai.aliyuncs.com";
        // 阿里云账号AccessKey拥有所有API的访问权限，风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维，请登录RAM控制台创建RAM用户。
        String accessKeyId = AliyuncsVodConfig.accessKeyId;
        String accessKeySecret = AliyuncsVodConfig.accessKeySecret;
        // 从STS服务获取的安全令牌（SecurityToken）。
        AssumeRoleResponse.Credentials credentials = getSecurityToken("SessionTest");
        String securityToken = credentials.getSecurityToken();
        // 填写Bucket名称，例如examplebucket。
        String bucketName = AliyuncsVodConfig.bucketName;
        // 填写Object完整路径，例如exampleobject.txt。Object完整路径中不能包含Bucket名称。
        // 此处请填写多个Object完整路径，用于一次性获取多个Object的签名URL。
//        String objectNameList [] = {"exampleobject.txt","exampleimage.jpg"};

        // 从STS服务获取临时访问凭证后，您可以通过临时访问密钥和安全令牌生成OSSClient。
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret, securityToken);

        try {
            // 设置签名URL过期时间为3600秒（1小时）。
            Date expiration = new Date(new Date().getTime() + 3600 * 1000);

            List<URL> urlList = new ArrayList<URL>();
            for(int i=0; i<objectNameList.length; i++){
                URL url = ossClient.generatePresignedUrl(bucketName, objectNameList[i], expiration);
                urlList.add(url);
            }
            return urlList;
        } catch (OSSException oe) {
            log.error("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            log.error("Error Message:" + oe.getErrorMessage());
            log.error("Error Code:" + oe.getErrorCode());
            log.error("Request ID:" + oe.getRequestId());
            log.error("Host ID:" + oe.getHostId());
        } catch (com.aliyun.oss.ClientException ce) {
            log.error("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.error("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return null;
    }
}
