package com.dora.service;

import com.dora.config.CosConfig;
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.nio.charset.StandardCharsets;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * COS POST签名服务类（保持接口兼容）
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class OssPostSignatureService {

    private final CosConfig cosConfig;

    // 指定过期时间，单位为秒
    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;
    }


    /**
     * 获取COS POST签名（保持接口兼容）
     * @param userId 用户ID
     * @param fileName 文件名
     * @return POST签名信息
     */
    public Map<String, Object> getPostSignatureForOssUpload(String userId, String fileName) throws Exception {
        String secretId = cosConfig.getSecretId();
        String secretKey = cosConfig.getSecretKey();
        
        // 生成 KeyTime（签名有效时间）
        long currentSeconds = System.currentTimeMillis() / 1000;
        long expireSeconds = currentSeconds + EXPIRE_TIME;
        String keyTime = currentSeconds + ";" + expireSeconds;
        
        // 构造 Policy
        ObjectMapper mapper = new ObjectMapper();
        Map<String, Object> policy = new HashMap<>();
        policy.put("expiration", generateExpiration(EXPIRE_TIME));
        
        List<Object> conditions = new ArrayList<>();
        // bucket 条件
        conditions.add(Map.of("bucket", cosConfig.getBucketName()));
        // 文件大小限制 10MB
        conditions.add(Arrays.asList("content-length-range", 1, 10485760));
        // key 前缀限制
        conditions.add(Arrays.asList("starts-with", "$key", cosConfig.getUserImgFolder()));
        // COS 必需的签名相关条件
        conditions.add(Map.of("q-sign-algorithm", "sha1"));
        conditions.add(Map.of("q-ak", secretId));
        conditions.add(Map.of("q-sign-time", keyTime));
        
        policy.put("conditions", conditions);
        String jsonPolicy = mapper.writeValueAsString(policy);
        
        // 生成 SignKey
        String signKey = hmacSha1(secretKey, keyTime);
        
        // 生成 StringToSign（Policy JSON 原文的 SHA1 哈希）
        String stringToSign = sha1(jsonPolicy);
        
        // 生成 Signature
        String signature = hmacSha1(signKey, stringToSign);
        
        // Base64 编码 Policy（用于表单提交）
        String encodedPolicy = Base64.encodeBase64String(jsonPolicy.getBytes(StandardCharsets.UTF_8));
        
        // 目录路径
        String dir = cosConfig.getUserImgFolder();
        if (dir.startsWith("/")) {
            dir = dir.substring(1);
        }
        if (!dir.endsWith("/")) {
            dir += "/";
        }
        
        // COS 域名
        String host = "https://" + cosConfig.getBucketName() + ".cos." + cosConfig.getRegion() + ".myqcloud.com";
        
        // 构造返回结果（COS 标准字段 + 兼容字段）
        Map<String, Object> response = new HashMap<>();
        
        // === COS 标准字段（前端提交表单时使用） ===
        response.put("policy", encodedPolicy);
        response.put("q-sign-algorithm", "sha1");
        response.put("q-ak", secretId);
        response.put("q-key-time", keyTime);  // 必需字段
        response.put("q-sign-time", keyTime);  // 必需字段
        response.put("q-signature", signature);
        
        // === 兼容 OSS 的字段（前端可能还在用） ===
        response.put("signature", signature);
        response.put("accessKeyId", secretId);
        response.put("security_token", "");
        response.put("OSSAccessKeyId", secretId);  // OSS 字段名
        
        // === 通用字段 ===
        response.put("dir", dir);
        response.put("host", host);
        response.put("userId", userId);
        response.put("fileName", fileName);
        
        // 文件类型和大小限制
        response.put("fileType", getFileType(fileName));
        response.put("maxFileSize", 10485760);
        response.put("maxFileSizeMB", 10);
        
        // 兼容字段
        response.put("version", "COS");
        response.put("x_oss_credential", "");
        response.put("x_oss_date", "");
        
        log.info("Generated COS POST signature for user {}: {}, fileType: {}, maxSize: {}MB", 
                userId, fileName, getFileType(fileName), 10);
        log.debug("COS POST signature calculation:");
        log.debug("  KeyTime: {}", keyTime);
        log.debug("  Policy JSON: {}", jsonPolicy);
        log.debug("  StringToSign (SHA1 of Policy): {}", stringToSign);
        log.debug("  SignKey: {}", signKey);
        log.debug("  Signature: {}", signature);
        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-SHA1（COS 使用）
     */
    private String hmacSha1(String key, String data) {
        try {
            SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA1");
            Mac mac = Mac.getInstance("HmacSHA1");
            mac.init(secretKeySpec);
            byte[] hmacBytes = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
            return toHexString(hmacBytes);
        } catch (Exception e) {
            throw new RuntimeException("Failed to calculate HMAC-SHA1", e);
        }
    }
    
    /**
     * 计算 SHA1 哈希
     */
    private String sha1(String data) {
        try {
            java.security.MessageDigest digest = java.security.MessageDigest.getInstance("SHA-1");
            byte[] hash = digest.digest(data.getBytes(StandardCharsets.UTF_8));
            return toHexString(hash);
        } catch (Exception e) {
            throw new RuntimeException("Failed to calculate SHA1", e);
        }
    }
    
    /**
     * 字节数组转十六进制字符串
     */
    private String toHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }
} 