package org.dromara.inspection.controller;

import cn.dev33.satoken.annotation.SaCheckPermission;
import com.tencent.cloud.CosStsClient;
import com.tencent.cloud.Policy;
import com.tencent.cloud.Response;
import com.tencent.cloud.Statement;
import com.tencent.cloud.cos.util.Jackson;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.inspection.config.TencentCosProperties;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

/**
 * 腾讯云COS临时凭证Controller
 *
 * @author LionLi
 */
@Slf4j
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/inspection/cos")
public class TencentCosController {

    private final TencentCosProperties cosProperties;

    private static final String CACHE_KEY = "inspection:cos:credential";
    private static final int CACHE_EXPIRE_SECONDS = 1500; // 缓存25分钟,临时密钥30分钟过期

    /**
     * 获取腾讯云COS临时上传凭证
     */
    @SaCheckPermission("inspection:step:edit")
    @Log(title = "获取COS临时凭证", businessType = BusinessType.OTHER)
    @GetMapping("/credential")
    public R<Response> getCredential() {
        // 先从缓存获取
        Response cachedCredential = RedisUtils.getCacheObject(CACHE_KEY);
        if (cachedCredential != null) {
            return R.ok(cachedCredential);
        }

        try {
            // 构建配置参数
            TreeMap<String, Object> config = new TreeMap<>();
            config.put("secretId", cosProperties.getSecretId());
            config.put("secretKey", cosProperties.getSecretKey());
            config.put("durationSeconds", cosProperties.getDurationSeconds());
            config.put("bucket", cosProperties.getBucket());
            config.put("region", cosProperties.getRegion());

            // 初始化 policy
            Policy policy = new Policy();

            // 开始构建一条 statement
            Statement statement = new Statement();
            // 声明设置的结果是允许操作
            statement.setEffect("allow");

            // 添加操作权限
            statement.addActions(new String[]{
                // 简单上传
                "cos:PutObject",
                "cos:PostObject",
                // 分块上传
                "cos:InitiateMultipartUpload",
                "cos:ListMultipartUploads",
                "cos:ListParts",
                "cos:UploadPart",
                "cos:CompleteMultipartUpload"
            });

            // 设置允许操作的资源路径(限定只能上传到audio目录)
            // 格式: qcs::cos:{region}:uid/{appid}:{bucket}/{path}
            statement.addResources(new String[]{
                "qcs::cos:" + cosProperties.getRegion() +
                    ":uid/" + cosProperties.getAppId() +
                    ":" + cosProperties.getBucket() +
                    "/audio/*"
            });

            // 把一条 statement 添加到 policy
            policy.addStatement(statement);

            // 将 Policy 实例转化成 String
            config.put("policy", Jackson.toJsonPrettyString(policy));

            // 获取临时密钥
            Response response = CosStsClient.getCredential(config);

            // 缓存凭证(25分钟,临时密钥30分钟过期)
            RedisUtils.setCacheObject(CACHE_KEY, response, Duration.ofSeconds(CACHE_EXPIRE_SECONDS));

            return R.ok(response);
        } catch (Exception e) {
            log.error("获取临时COS凭证失败", e);
            return R.fail("获取临时凭证失败:" + e.getMessage());
        }
    }

    /**
     * 获取COS配置信息(供前端使用)
     */
    @SaCheckPermission("inspection:step:edit")
    @GetMapping("/config")
    public R<Map<String, String>> getConfig() {
        Map<String, String> config = new HashMap<>();
        config.put("bucket", cosProperties.getBucket());
        config.put("region", cosProperties.getRegion());
        return R.ok(config);
    }
}
