package com.microservices.file.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * MD5工具类
 *
 * @author microservices
 */
public class Md5Utils {

    private static final Logger logger = LoggerFactory.getLogger(Md5Utils.class);

    private static final int BUFFER_SIZE = 8192;
    private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    /**
     * 计算文件的MD5值
     *
     * @param file 文件
     * @return MD5字符串
     */
    public static String calculateMd5(File file) {
        if (file == null || !file.exists()) {
            throw new IllegalArgumentException("文件不存在");
        }

        try (FileInputStream fis = new FileInputStream(file)) {
            return calculateMd5(fis);
        } catch (IOException e) {
            logger.error("计算文件MD5失败: {}", file.getAbsolutePath(), e);
            throw new RuntimeException("计算文件MD5失败", e);
        }
    }

    /**
     * 计算MultipartFile的MD5值
     *
     * @param file MultipartFile对象
     * @return MD5字符串
     */
    public static String calculateMd5(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            throw new IllegalArgumentException("文件为空");
        }

        try (InputStream is = file.getInputStream()) {
            return calculateMd5(is);
        } catch (IOException e) {
            logger.error("计算文件MD5失败: {}", file.getOriginalFilename(), e);
            throw new RuntimeException("计算文件MD5失败", e);
        }
    }

    /**
     * 计算输入流的MD5值
     *
     * @param inputStream 输入流
     * @return MD5字符串
     */
    public static String calculateMd5(InputStream inputStream) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead;

            while ((bytesRead = inputStream.read(buffer)) != -1) {
                md.update(buffer, 0, bytesRead);
            }

            byte[] digest = md.digest();
            return bytesToHex(digest);
        } catch (NoSuchAlgorithmException | IOException e) {
            logger.error("计算MD5失败", e);
            throw new RuntimeException("计算MD5失败", e);
        }
    }

    /**
     * 计算字节数组的MD5值
     *
     * @param data 字节数组
     * @return MD5字符串
     */
    public static String calculateMd5(byte[] data) {
        if (data == null || data.length == 0) {
            throw new IllegalArgumentException("数据为空");
        }

        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] digest = md.digest(data);
            return bytesToHex(digest);
        } catch (NoSuchAlgorithmException e) {
            logger.error("计算MD5失败", e);
            throw new RuntimeException("计算MD5失败", e);
        }
    }

    /**
     * 字节数组转十六进制字符串
     *
     * @param bytes 字节数组
     * @return 十六进制字符串
     */
    private static String bytesToHex(byte[] bytes) {
        char[] result = new char[bytes.length * 2];
        for (int i = 0; i < bytes.length; i++) {
            int b = bytes[i] & 0xFF;
            result[i * 2] = HEX_DIGITS[b >>> 4];
            result[i * 2 + 1] = HEX_DIGITS[b & 0x0F];
        }
        return new String(result);
    }

    /**
     * 验证文件的MD5值
     *
     * @param file    文件
     * @param expectMd5 期望的MD5值
     * @return 是否匹配
     */
    public static boolean verifyMd5(File file, String expectMd5) {
        String actualMd5 = calculateMd5(file);
        return actualMd5.equalsIgnoreCase(expectMd5);
    }

    /**
     * 验证MultipartFile的MD5值
     *
     * @param file        MultipartFile对象
     * @param expectMd5   期望的MD5值
     * @return 是否匹配
     */
    public static boolean verifyMd5(MultipartFile file, String expectMd5) {
        String actualMd5 = calculateMd5(file);
        return actualMd5.equalsIgnoreCase(expectMd5);
    }
}
