package com.dora.service;

import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyuncs.sts.model.v20150401.AssumeRoleRequest;
import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse;
import com.dora.config.OssConfig;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.stereotype.Service;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * OSS POST签名版本4服务类
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class OssPostSignatureService {

    private final OssConfig ossConfig;

    // 指定过期时间，单位为秒
    private static final Long EXPIRE_TIME = 3600L;

    /**
     * 通过指定有效的时长（秒）生成过期时间
     * @param seconds 有效时长（秒）
     * @return ISO8601 时间字符串，如："2014-12-01T12:00:00.000Z"
     */
    public static String generateExpiration(long seconds) {
        // 获取当前时间戳（以秒为单位）
        long now = Instant.now().getEpochSecond();
        // 计算过期时间的时间戳
        long expirationTime = now + seconds;
        // 将时间戳转换为Instant对象，并格式化为ISO8601格式
        Instant instant = Instant.ofEpochSecond(expirationTime);
        // 定义时区为UTC
        ZoneId zone = ZoneOffset.UTC;
        // 将 Instant 转换为 ZonedDateTime
        ZonedDateTime zonedDateTime = instant.atZone(zone);
        // 定义日期时间格式，例如2023-12-03T13:00:00.000Z
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        // 格式化日期时间
        String formattedDate = zonedDateTime.format(formatter);
        // 输出结果
        return formattedDate;
    }

    /**
     * 获取STS临时凭证
     * @return AssumeRoleResponse 对象
     */
    public AssumeRoleResponse getCredential() throws Exception {
        try {
            // 使用新的STS SDK
            com.aliyuncs.profile.DefaultProfile profile = com.aliyuncs.profile.DefaultProfile.getProfile("", 
                ossConfig.getAccessKeyId(), 
                ossConfig.getAccessKeySecret()
            );
            
            com.aliyuncs.DefaultAcsClient client = new com.aliyuncs.DefaultAcsClient(profile);
            
            AssumeRoleRequest request = new AssumeRoleRequest();
            request.setSysEndpoint("sts.aliyuncs.com");
            request.setSysMethod(com.aliyuncs.http.MethodType.POST);
            request.setRoleArn("acs:ram::1234567890123456:role/aliyunosstokengeneratorrole");
            request.setRoleSessionName("oss-upload-session");
            
            return client.getAcsResponse(request);
        } catch (Exception e) {
            log.error("STS AssumeRole failed: {}", e.getMessage());
            throw e;
        }
    }

    /**
     * 获取OSS POST签名
     * @param userId 用户ID
     * @param fileName 文件名
     * @return POST签名信息
     */
    public Map<String, Object> getPostSignatureForOssUpload(String userId, String fileName) throws Exception {
        // 由于STS需要RAM角色，我们暂时使用直接的方式
        // 在实际生产环境中，建议使用STS临时凭证
        
        String accessKeyId = ossConfig.getAccessKeyId();
        String accessKeySecret = ossConfig.getAccessKeySecret();
        String securityToken = ""; // 直接使用时不使用SecurityToken

        // 获取x-oss-credential里的date，当前日期，格式为yyyyMMdd
        ZonedDateTime today = ZonedDateTime.now().withZoneSameInstant(ZoneOffset.UTC);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
        String date = today.format(formatter);
        
        // 获取x-oss-date
        ZonedDateTime now = ZonedDateTime.now().withZoneSameInstant(ZoneOffset.UTC);
        DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'");
        String x_oss_date = now.format(formatter2);

        // 步骤1：创建policy
        String x_oss_credential = accessKeyId + "/" + date + "/" + ossConfig.getRegion() + "/oss/aliyun_v4_request";

        ObjectMapper mapper = new ObjectMapper();

        Map<String, Object> policy = new HashMap<>();
        policy.put("expiration", generateExpiration(EXPIRE_TIME));

        List<Object> conditions = new ArrayList<>();

        Map<String, String> bucketCondition = new HashMap<>();
        bucketCondition.put("bucket", ossConfig.getBucketName());
        conditions.add(bucketCondition);

        Map<String, String> signatureVersionCondition = new HashMap<>();
        signatureVersionCondition.put("x-oss-signature-version", "OSS4-HMAC-SHA256");
        conditions.add(signatureVersionCondition);

        Map<String, String> credentialCondition = new HashMap<>();
        credentialCondition.put("x-oss-credential", x_oss_credential);
        conditions.add(credentialCondition);

        Map<String, String> dateCondition = new HashMap<>();
        dateCondition.put("x-oss-date", x_oss_date);
        conditions.add(dateCondition);

        // 设置文件大小限制为10MB (10 * 1024 * 1024 = 10485760)
        conditions.add(Arrays.asList("content-length-range", 1, 10485760));
        conditions.add(Arrays.asList("eq", "$success_action_status", "200"));
        
        // 添加Content-Type条件，确保压缩包文件能够正确上传
        String fileExtension = "";
        if (fileName.contains(".")) {
            fileExtension = fileName.substring(fileName.lastIndexOf(".")).toLowerCase();
        }
        
        // 根据文件类型设置Content-Type条件
        if (fileExtension.matches("\\.(zip|rar|7z|tar|gz|bz2|xz)")) {
            // 压缩包文件
            conditions.add(Arrays.asList("starts-with", "$Content-Type", "application/"));
        } else if (fileExtension.matches("\\.(jpg|jpeg|png|gif|bmp|webp)")) {
            // 图片文件
            conditions.add(Arrays.asList("starts-with", "$Content-Type", "image/"));
        } else {
            // 其他文件类型
            conditions.add(Arrays.asList("starts-with", "$Content-Type", "application/"));
        }
        
        // 移除key前缀限制，允许任意key
        // conditions.add(Arrays.asList("starts-with", "$key", ossConfig.getUserImgFolder()));

        policy.put("conditions", conditions);

        String jsonPolicy = mapper.writeValueAsString(policy);

        // 步骤2：构造待签名字符串（StringToSign）
        String stringToSign = new String(Base64.encodeBase64(jsonPolicy.getBytes()));

        // 步骤3：计算SigningKey
        byte[] dateKey = hmacsha256(("aliyun_v4" + accessKeySecret).getBytes(), date);
        byte[] dateRegionKey = hmacsha256(dateKey, ossConfig.getRegion());
        byte[] dateRegionServiceKey = hmacsha256(dateRegionKey, "oss");
        byte[] signingKey = hmacsha256(dateRegionServiceKey, "aliyun_v4_request");

        // 步骤4：计算Signature
        byte[] result = hmacsha256(signingKey, stringToSign);
        String signature = BinaryUtil.toHex(result);

        Map<String, Object> response = new HashMap<>();
        // 将数据添加到 map 中
        response.put("version", "OSS4-HMAC-SHA256");
        response.put("policy", stringToSign);
        response.put("x_oss_credential", x_oss_credential);
        response.put("x_oss_date", x_oss_date);
        response.put("signature", signature);
        response.put("security_token", securityToken);
        // 确保dir路径格式正确，不以斜杠开头
        String dir = ossConfig.getUserImgFolder();
        if (dir.startsWith("/")) {
            dir = dir.substring(1);
        }
        response.put("dir", dir);
        response.put("host", "https://" + ossConfig.getBucketName() + "." + ossConfig.getEndpoint().replace("https://", ""));
        response.put("accessKeyId", accessKeyId);
        response.put("userId", userId);
        response.put("fileName", fileName);
        
        // 添加文件类型和大小限制信息
        response.put("fileType", getFileType(fileName));
        response.put("maxFileSize", 10485760); // 10MB in bytes
        response.put("maxFileSizeMB", 10);
        
        log.info("Generated POST signature for user {}: {}, fileType: {}, maxSize: {}MB", 
                userId, fileName, getFileType(fileName), 10);
        return response;
    }

    /**
     * 获取文件类型
     */
    private String getFileType(String fileName) {
        if (fileName == null || fileName.isEmpty()) {
            return "unknown";
        }
        
        String extension = "";
        if (fileName.contains(".")) {
            extension = fileName.substring(fileName.lastIndexOf(".")).toLowerCase();
        }
        
        if (extension.matches("\\.(zip|rar|7z|tar|gz|bz2|xz)")) {
            return "compressed";
        } else if (extension.matches("\\.(jpg|jpeg|png|gif|bmp|webp)")) {
            return "image";
        } else if (extension.matches("\\.(pdf|txt|md|json|xml|csv)")) {
            return "document";
        } else {
            return "other";
        }
    }

    /**
     * 计算HMAC-SHA256
     */
    public static byte[] hmacsha256(byte[] key, String data) {
        try {
            // 初始化HMAC密钥规格，指定算法为HMAC-SHA256并使用提供的密钥。
            SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacSHA256");

            // 获取Mac实例，并通过getInstance方法指定使用HMAC-SHA256算法。
            Mac mac = Mac.getInstance("HmacSHA256");
            // 使用密钥初始化Mac对象。
            mac.init(secretKeySpec);

            // 执行HMAC计算，通过doFinal方法接收需要计算的数据并返回计算结果的数组。
            byte[] hmacBytes = mac.doFinal(data.getBytes());

            return hmacBytes;
        } catch (Exception e) {
            throw new RuntimeException("Failed to calculate HMAC-SHA256", e);
        }
    }
} 