package com.sonic.shark.service.impl;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.securitytoken.AWSSecurityTokenService;
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder;
import com.amazonaws.services.securitytoken.model.AssumeRoleRequest;
import com.amazonaws.services.securitytoken.model.AssumeRoleResult;
import com.sonic.common.enums.AppEnv;
import com.sonic.shark.domain.bo.S3StsToken;
import com.sonic.shark.enums.S3BizTypeMappingEnum;
import com.sonic.shark.enums.S3BucketNameEnum;
import com.sonic.shark.enums.ToastResultCode;
import com.sonic.shark.service.S3StsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.Random;

@Slf4j
@Service
public class S3StsServiceImpl implements S3StsService {

    @Value("${aws.s3.sts.endpoint}")
    private String endpoint;
    @Value("${aws.s3.sts.accessKeyId}")
    private String accessKeyId;
    @Value("${aws.s3.sts.accessKeySecret}")
    private String accessKeySecret;
    @Value("${aws.s3.sts.roleArn}")
    private String roleArn;
    @Value("${aws.s3.sts.roleSessionName}")
    private String roleSessionName;
    @Value("${site.type:main}")
    private String siteType;

    @Value("${spring.profiles.active}")
    private String runMode;

    /**
     * 测试环境的前缀目录
     */
    private static final String DEV_ENV_PREFIX = "dev/";

    /**
     * 根据业务类型获取用户上传文件的 STS token 文件目录授权
     * @param s3BizTypeMappingEnum
     * @param userId
     * @param suffix
     * @return
     */
    public S3StsToken getStsToken(S3BizTypeMappingEnum s3BizTypeMappingEnum, Long userId, String suffix) {
        //环境参数
        String prefix = AppEnv.product.name().equals(runMode) ? s3BizTypeMappingEnum.getS3BucketNameEnum().getPrefix() : DEV_ENV_PREFIX;
        //根据业务类型拼接出用户的相对路径
        String path = prefix + siteType + "/" + s3BizTypeMappingEnum.getFilePath() + userId;
        //生成文件名
        Random random = new Random();
        String fileName;
        if(S3BizTypeMappingEnum.SOUND_PATH == s3BizTypeMappingEnum) {
            fileName = "*";
        } else {
            fileName = System.currentTimeMillis() + "" + random.nextInt(10000) + "." + suffix;
        }
        S3StsToken s3StsToken = getStsTokenPolicy(s3BizTypeMappingEnum.getS3BucketNameEnum(), path, fileName);
        s3StsToken.setFileName(fileName);
        s3StsToken.setPath(s3StsToken.getPath() + fileName);
        return s3StsToken;
    }

    /**
     * 获取STS token基础方法 到文件
     * @param bucketNameEnum
     * @param path
     * @param fileName
     * @return
     */
    public S3StsToken getStsTokenPolicy(S3BucketNameEnum bucketNameEnum, String path, String fileName) {
        //判断哪些bucket是不能获取sts token的，直接抛出异常
        if(!bucketNameEnum.getS3StsTokenBl()) {
            ToastResultCode.NO_OPERATION_AUTH.check(true);
        }
        String policy = "{\n" +
                "    \"Version\": \"2012-10-17\",\n" +
                "    \"Statement\": [{\n" +
                "        \"Effect\": \"Allow\",\n" +
                "        \"Action\": [\n" +
                "            \"s3:PutObject\",\n" +
                "            \"s3:PutObjectAcl\",\n" +
                "            \"s3:GetObject\",\n" +
                "            \"s3:PutObjectTagging\"\n" +
                "        ],\n" +
                "        \"Resource\": [\n" +
                "            \"arn:aws:s3:::" + bucketNameEnum.getBucketName() + "/" + path + "/" + fileName + "\"\n" +
                "        ]\n" +
                "    }]\n" +
                "}";
        log.info("===> getStsToken policy : {}", policy);
        return getStsToken(bucketNameEnum.getRegion().toString(), path, policy);
    }

    /**
     * 获取S3的STS授权token 底层方法
     *
     * @param policy 授权策略
     * @return
     */
    public S3StsToken getStsToken(String region, String path, String policy) {
        try {
            // Creating the STS client is part of your trusted code. It has
            // the security credentials you use to obtain temporary security credentials.
            AWSCredentials credentials = new BasicAWSCredentials(accessKeyId, accessKeySecret);

            AWSSecurityTokenService stsClient = AWSSecurityTokenServiceClientBuilder.standard()
                    .withCredentials(new AWSStaticCredentialsProvider(credentials))
                    .withRegion(region)
                    .build();

            // Obtain credentials for the IAM role. Note that you cannot assume the role of an AWS root account;
            // Amazon S3 will deny access. You must use credentials for an IAM user or an IAM role.
            // 默认情况下，会话的持续时间为一个小时。如果您使用了 IAM 用户凭证，则可在请求临时安全凭证时指定持续时间（15 分钟到角色的最长会话持续时间）
            //可配置范围：15 分钟 – 12 小时
            // https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/userguide/AuthUsingTempSessionToken.html
            com.amazonaws.services.securitytoken.model.AssumeRoleRequest roleRequest = new AssumeRoleRequest()
                    .withRoleArn(roleArn)
                    .withRoleSessionName(roleSessionName)
                    .withPolicy(policy)
                    .withDurationSeconds(60 * 60);
            AssumeRoleResult response = stsClient.assumeRole(roleRequest);

            S3StsToken s3StsToken = S3StsToken.builder()
                    .expiration(response.getCredentials().getExpiration().toString())
                    .accessKeyId(response.getCredentials().getAccessKeyId())
                    .accessKeySecret(response.getCredentials().getSecretAccessKey())
                    .securityToken(response.getCredentials().getSessionToken())
                    .path(path + "/")
                    .build();

            return s3StsToken;
        } catch (Exception e) {
            // The call was transmitted successfully, but Amazon S3 couldn't process
            // it, so it returned an error response.
            e.printStackTrace();
        }
        return null;
    }

}
