package com.dora.service.impl;

import com.dora.config.CosConfig;
import com.dora.dto.AdminOssUploadRequest;
import com.dora.service.AdminOssService;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qcloud.cos.COSClient;
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服务实现类（保持接口兼容）
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class AdminOssServiceImpl implements AdminOssService {

    private final CosConfig cosConfig;
    private final COSClient cosClient;

    // 管理员上传文件过期时间，单位为秒（2小时）
    private static final Long ADMIN_EXPIRE_TIME = 7200L;

    @Override
    public Map<String, Object> generateAdminPostSignature(AdminOssUploadRequest request, String adminId) throws Exception {
        String fileName = request.getFileName();
        
        // 验证文件类型
        if (!isAllowedAdminFileType(fileName)) {
            throw new IllegalArgumentException("不支持的文件类型，管理端支持格式：图片、文档、压缩包、音视频等");
        }
        
        // 获取文件大小限制
        long maxFileSize = getMaxAdminFileSize(request.getMaxSizeMB());
        
        String secretId = cosConfig.getSecretId();
        String secretKey = cosConfig.getSecretKey();
        
        // 生成 KeyTime（签名有效时间）
        long currentSeconds = System.currentTimeMillis() / 1000;
        long expireSeconds = currentSeconds + ADMIN_EXPIRE_TIME;
        String keyTime = currentSeconds + ";" + expireSeconds;

        // 构造 COS Policy
        ObjectMapper mapper = new ObjectMapper();
        Map<String, Object> policy = new HashMap<>();
        policy.put("expiration", generateExpiration(ADMIN_EXPIRE_TIME));

        List<Object> conditions = new ArrayList<>();
        // bucket 条件
        conditions.add(Map.of("bucket", cosConfig.getBucketName()));
        // 文件大小限制
        conditions.add(Arrays.asList("content-length-range", 1, maxFileSize));
        // key 前缀限制
        String dir = getAdminUploadDir(request.getDirectory());
        conditions.add(Arrays.asList("starts-with", "$key", dir));
        // 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));

        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);
        
        // 兼容字段
        response.put("version", "COS");
        response.put("x_oss_credential", "");
        response.put("x_oss_date", "");
        response.put("x_oss_signature_version", "");
        response.put("x_oss_signature", "");
        
        // 管理端使用专门的目录
        response.put("dir", dir);
        response.put("host", "https://" + cosConfig.getBucketName() + ".cos." + cosConfig.getRegion() + ".myqcloud.com");
        response.put("adminId", adminId);
        response.put("fileName", fileName);
        
        // 添加管理端特有的信息
        response.put("fileType", getAdminFileType(fileName));
        response.put("maxFileSize", maxFileSize);
        response.put("maxFileSizeMB", maxFileSize / (1024 * 1024));
        response.put("supportedFormats", getSupportedAdminFormats());
        response.put("uploadTips", getAdminUploadTips(request.getFileCategory()));
        
        log.info("Generated admin COS POST signature for admin {}: {}, fileType: {}, maxSize: {}MB", 
                adminId, fileName, getAdminFileType(fileName), maxFileSize / (1024 * 1024));
        return response;
    }

    @Override
    public boolean deleteAdminFile(String objectKey, String adminId) throws Exception {
        try {
            cosClient.deleteObject(cosConfig.getBucketName(), objectKey);
            log.info("Admin {} deleted COS file: {}", adminId, objectKey);
            return true;
        } catch (Exception e) {
            log.error("Admin {} failed to delete COS file {}: {}", adminId, objectKey, e.getMessage(), e);
            return false;
        }
    }

    @Override
    public Map<String, Object> getAdminFileInfo(String objectKey) throws Exception {
        // 这里可以添加获取文件信息的逻辑
        Map<String, Object> fileInfo = new HashMap<>();
        fileInfo.put("objectKey", objectKey);
        fileInfo.put("size", 0L);
        fileInfo.put("lastModified", new Date());
        fileInfo.put("contentType", "application/octet-stream");
        return fileInfo;
    }

    @Override
    public Map<String, Object> batchDeleteAdminFiles(String[] objectKeys, String adminId) throws Exception {
        Map<String, Object> result = new HashMap<>();
        List<String> success = new ArrayList<>();
        List<String> failed = new ArrayList<>();
        
        for (String objectKey : objectKeys) {
            try {
                if (deleteAdminFile(objectKey, adminId)) {
                    success.add(objectKey);
                } else {
                    failed.add(objectKey);
                }
            } catch (Exception e) {
                failed.add(objectKey);
                log.error("Failed to delete file: {}", objectKey, e);
            }
        }
        
        result.put("success", success);
        result.put("failed", failed);
        result.put("total", objectKeys.length);
        result.put("successCount", success.size());
        result.put("failedCount", failed.size());
        
        return result;
    }

    /**
     * 生成过期时间
     */
    private String generateExpiration(long seconds) {
        long now = Instant.now().getEpochSecond();
        long expirationTime = now + seconds;
        Instant instant = Instant.ofEpochSecond(expirationTime);
        ZoneId zone = ZoneOffset.UTC;
        ZonedDateTime zonedDateTime = instant.atZone(zone);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        return zonedDateTime.format(formatter);
    }

    /**
     * 验证管理端允许的文件类型
     */
    private boolean isAllowedAdminFileType(String fileName) {
        if (fileName == null || fileName.isEmpty()) {
            return false;
        }
        
        String extension = "";
        if (fileName.contains(".")) {
            extension = fileName.substring(fileName.lastIndexOf(".")).toLowerCase();
        }
        
        // 管理端支持更多文件格式
        String[] allowedExtensions = {
            // 图片格式
            ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".svg", ".ico", ".tiff",
            // 压缩包格式
            ".zip", ".rar", ".7z", ".tar", ".gz", ".bz2", ".xz",
            // 文档格式
            ".pdf", ".txt", ".md", ".json", ".xml", ".csv", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx",
            // 音频格式
            ".mp3", ".wav", ".flac", ".aac", ".ogg", ".wma",
            // 视频格式
            ".mp4", ".avi", ".mov", ".wmv", ".flv", ".mkv", ".webm",
            // 其他格式
            ".html", ".css", ".js", ".sql", ".log"
        };
        
        for (String allowedExt : allowedExtensions) {
            if (allowedExt.equals(extension)) {
                return true;
            }
        }
        
        return false;
    }

    /**
     * 获取管理端文件大小限制
     */
    private long getMaxAdminFileSize(Integer maxSizeMB) {
        if (maxSizeMB != null && maxSizeMB > 0) {
            // 最大不能超过500MB
            return Math.min(maxSizeMB * 1024L * 1024L, 500L * 1024L * 1024L);
        }
        // 默认50MB
        return 50L * 1024L * 1024L;
    }

    /**
     * 获取管理端上传目录（与用户端使用相同目录）
     */
    private String getAdminUploadDir(String directory) {
        // 使用与用户端相同的目录
        String baseDir = cosConfig.getUserImgFolder();
        if (baseDir.startsWith("/")) {
            baseDir = baseDir.substring(1);
        }
        if (!baseDir.endsWith("/")) {
            baseDir += "/";
        }
        
        // 如果指定了子目录，可以在用户目录下创建子目录
        if (directory != null && !directory.isEmpty()) {
            String subDir = directory;
            if (subDir.startsWith("/")) {
                subDir = subDir.substring(1);
            }
            if (!subDir.endsWith("/")) {
                subDir += "/";
            }
            baseDir += subDir;
        }
        
        return baseDir;
    }

    /**
     * 获取管理端文件类型
     */
    private String getAdminFileType(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|svg|ico|tiff)")) {
            return "image";
        } else if (extension.matches("\\.(pdf|txt|md|json|xml|csv|doc|docx|xls|xlsx|ppt|pptx)")) {
            return "document";
        } else if (extension.matches("\\.(mp3|wav|flac|aac|ogg|wma)")) {
            return "audio";
        } else if (extension.matches("\\.(mp4|avi|mov|wmv|flv|mkv|webm)")) {
            return "video";
        } else {
            return "other";
        }
    }

    /**
     * 获取支持的格式列表
     */
    private String[] getSupportedAdminFormats() {
        return new String[]{
            "图片: jpg, jpeg, png, gif, bmp, webp, svg, ico, tiff",
            "文档: pdf, txt, md, json, xml, csv, doc, docx, xls, xlsx, ppt, pptx", 
            "压缩包: zip, rar, 7z, tar, gz, bz2, xz",
            "音频: mp3, wav, flac, aac, ogg, wma",
            "视频: mp4, avi, mov, wmv, flv, mkv, webm",
            "其他: html, css, js, sql, log"
        };
    }

    /**
     * 获取上传提示信息
     */
    private String getAdminUploadTips(String fileCategory) {
        if (fileCategory == null) {
            return "管理端支持多种文件格式，最大支持500MB文件上传。";
        }
        
        switch (fileCategory.toLowerCase()) {
            case "image":
                return "支持常见图片格式，建议使用JPG/PNG格式以获得更好的兼容性。";
            case "document":
                return "支持Office文档和PDF等格式，适合上传说明文档、报告等。";
            case "video":
                return "支持主流视频格式，建议使用MP4格式以获得更好的兼容性。";
            case "audio":
                return "支持多种音频格式，建议使用MP3格式以获得更好的兼容性。";
            case "compressed":
                return "支持常见压缩包格式，适合批量文件上传。";
            default:
                return "管理端支持多种文件格式，请根据实际需要选择合适的文件。";
        }
    }

    /**
     * 计算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();
    }
}
