package com.seer.teach.common.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson2.JSON;
import com.seer.teach.common.enums.ResultCodeEnum;
import com.seer.teach.common.enums.Salt;
import com.seer.teach.common.exception.FileUploadException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.BinaryOperator;
import java.util.function.Function;

/**
 * @Author: Captain
 * @Autograph: 安稳
 * @Description: 通用工具类，提供各种常用方法
 * @Date: 2023-07-03 20:32:08
 */
@Slf4j
@Component
public class CommonUtils {

    /**
     * 格式化数字，单位为 K（千）或 W（万）
     *
     * @param number 原始整数
     * @return 格式化后的字符串，带单位
     */
    public static String formatNumber(int number) {
        if (number >= 10000) {
            // 单位为 W（万）
            double wValue = number / 10000.0;
            return String.format("%.2fW", wValue);
        } else {
            // 单位为 K（千）
            double kValue = number / 1000.0;
            return String.format("%.2fK", kValue);
        }
    }

    /**
     * 将 Linux 秒级时间戳字符串转换为 "MM月dd日" 格式
     *
     * @param timestampStr 秒级时间戳字符串，如 "1703558400"
     * @return 格式化后的日期字符串，如 "12月26日"
     */
    public static String formatTimestampToMonthDay(String timestampStr) {
        long timestamp = Long.parseLong(timestampStr);
        LocalDateTime dateTime = LocalDateTime.ofInstant(Instant.ofEpochSecond(timestamp), ZoneId.systemDefault());
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("M月d日");
        return dateTime.format(formatter);
    }

    /**
     * 将比例小数（如 0.9500）转换为百分比字符串（如 "95%"）
     *
     * @param ratio 比例小数，范围应在 0 ~ 1 之间
     * @return 百分比字符串，例如 "95%", "87.5%"
     * @throws IllegalArgumentException 如果比例不在 0~1 之间
     */
    public static String formatToPercentage(BigDecimal ratio) {
        if (ratio == null || ratio.compareTo(BigDecimal.ZERO) < 0 || ratio.compareTo(BigDecimal.ONE) > 0) {
            throw new IllegalArgumentException("比例必须在 0 和 1 之间");
        }

        // 比例 × 100，保留最多一位小数（如 87.5%），也可以改为 setScale(0) 表示整数百分比
        BigDecimal percentage = ratio.multiply(BigDecimal.valueOf(100)).setScale(1, RoundingMode.HALF_UP);
        return percentage.stripTrailingZeros().toPlainString() + "%";
    }

    /**
     * 将比例小数（如 0.9500）转换为百分比数值（如 95.0）
     *
     * @param ratio 比例小数，范围应在 0 ~ 1 之间
     * @return 百分比数值，例如 95.0, 87.5
     * @throws IllegalArgumentException 如果比例不在 0~1 之间
     */
    public static BigDecimal formatToPercentageAsDecimal(BigDecimal ratio) {
        if (ratio == null || ratio.compareTo(BigDecimal.ZERO) < 0 || ratio.compareTo(BigDecimal.ONE) > 0) {
            throw new IllegalArgumentException("比例必须在 0 和 1 之间");
        }

        // 比例 × 100，保留最多一位小数
        BigDecimal percentage = ratio.multiply(BigDecimal.valueOf(100)).setScale(1, RoundingMode.HALF_UP);
        return percentage;
    }

    /**
     * 计算百分比，返回整数形式（如：95% 返回 95）
     *
     * @param part  部分值（分子）
     * @param total 总值（分母）
     * @return 百分比整数，如果总值为 0 则返回 0
     */
    public static int calculatePercentage(int part, int total) {
        if (total == 0) {
            // 防止除以0
            return 0;
        }
        return (int) Math.round((double) part * 100 / total);
    }

    /**
     * 获取当前时间的Linux时间戳
     *
     * @return 当前时间的Linux时间戳字符串
     */
    public static String getCurrentLinuxTimeCode() {
        return Long.toString(Instant.now().getEpochSecond());
    }


    /**
     * 生成基于 userId 和当前时间戳的唯一文件名，保留文件原始扩展名
     *
     * @param fileName   文件名称
     * @return 唯一文件名（包含扩展名）
     */
    public static String generateUniqueFileName(String fileName) {
        long timestamp = System.currentTimeMillis();
        String randomPart = UUID.randomUUID().toString().replace("-", "").substring(0, 8);

        // 获取原始文件名
        String originalFilename = fileName;
        // 提取扩展名
        String extension = "";
        if (originalFilename != null && originalFilename.contains(".")) {
            extension = originalFilename.substring(originalFilename.lastIndexOf("."));
        }

        // 返回组合后的唯一文件名
        return timestamp + randomPart + extension;
    }

    public static String generateUniqueFileName() {
       return generateUniqueFileName("");
    }

    public static String generateUniqueFileName(Integer userId,MultipartFile file) {
        long timestamp = System.currentTimeMillis();

        String randomPart = UUID.randomUUID().toString().replace("-", "").substring(0, 8);
        if(Objects.nonNull(userId)){
            randomPart = randomPart + "_" + userId;
        }
        // 获取原始文件名
        String originalFilename = file.getOriginalFilename();
        // 提取扩展名
        String extension = "";
        if (originalFilename != null && originalFilename.contains(".")) {
            extension = originalFilename.substring(originalFilename.lastIndexOf("."));
        }

        // 返回组合后的唯一文件名
        return timestamp + randomPart + extension;
    }

    /**
     * 生成基于 childrenId 和当前时间戳的唯一 .wav 文件名
     *
     * @param childrenId 用户ID
     * @return 唯一的 .wav 文件名
     */
    public static String generateWavFileName(Integer childrenId) {
        long timestamp = System.currentTimeMillis();
        String randomPart = UUID.randomUUID().toString().replace("-", "").substring(0, 8);

        return childrenId + "_" + timestamp + "_" + randomPart + ".flac";
    }


    /**
     * 基于当前时间生成唯一UUID格式的消息ID
     *
     * @return UUID风格的消息ID
     */
    public static String generateUUIDMessageId() {
        try {
            String timestamp = getCurrentLinuxTimeCode();
            String randomPart = UUID.randomUUID().toString();
            String base = timestamp + "_" + randomPart;

            // 使用 SHA-1 生成摘要
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            byte[] hash = md.digest(base.getBytes(StandardCharsets.UTF_8));

            // 构造 UUID
            long mostSigBits = 0;
            long leastSigBits = 0;
            for (int i = 0; i < 8; i++) {
                mostSigBits = (mostSigBits << 8) | (hash[i] & 0xff);
            }
            for (int i = 8; i < 16; i++) {
                leastSigBits = (leastSigBits << 8) | (hash[i] & 0xff);
            }

            return new UUID(mostSigBits, leastSigBits).toString();
        } catch (Exception e) {
            throw new RuntimeException("生成UUID消息ID失败", e);
        }
    }

    /**
     * 验证上传的文件是否为合法的图片类型
     *
     * @param file 上传的文件
     */
    public static void isIllegalImageType(MultipartFile file) {
        long size = file.getSize();
        String originalFilename = file.getOriginalFilename();
        String contentType = file.getContentType();
        log.info("上传文件信息 -> 文件名: {}, 大小: {} bytes (≈{} KB), 类型: {}",
                originalFilename,
                size,
                size / 1024,
                contentType);

        String filename = file.getOriginalFilename();
        if (filename == null || !filename.contains(".")) {
            throw new FileUploadException(ResultCodeEnum.FILE_FORMAT_ERROR);
        }

        String extension = filename.substring(filename.lastIndexOf('.') + 1).toLowerCase();
        List<String> allowedTypes = Arrays.asList("jpg", "jpeg", "png", "mp3", "apk");

        if (!allowedTypes.contains(extension)) {
            throw new FileUploadException(ResultCodeEnum.FILE_FORMAT_ERROR);
        }
    }

    /**
     * 将data对象转换成List<clazz对象>
     *
     * @param data  要转换的数据对象
     * @param clazz 目标列表元素类型
     * @return 转换后的列表对象
     */
    public static <T> List<T> parseJsonToListObject(Object data, Class<T> clazz) {
        return JSON.parseArray(JSON.toJSONString(data), clazz);
    }

    /**
     * 将data对象转换成clazz对象
     *
     * @param data  要转换的数据对象
     * @param clazz 目标对象类型
     * @return 转换后的对象
     */
    public static <T> T parseJsonToObject(Object data, Class<T> clazz) {
        return JSON.parseObject(JSON.toJSONString(data), clazz);
    }

    /**
     * 加密密码
     *
     * @param password 需要加密的原始密码
     * @return 加密后的密码字符串
     */
    public static String encryptPassword(String password) {
        return SecureUtil.md5(Salt.PREFIX.getMsg() + password + Salt.SUFFIX.getMsg());
    }

    /**
     * 将时间戳转换为LocalDateTime
     *
     * @param timestampStr 时间戳字符串
     * @return 转换后的LocalDateTime对象
     */
    public static LocalDateTime parseTimestamp(String timestampStr) {
        if (timestampStr == null || timestampStr.trim().isEmpty()) {
            return null;
        }
        long seconds = Long.parseLong(timestampStr);
        return LocalDateTime.ofInstant(Instant.ofEpochSecond(seconds), ZoneId.systemDefault());
    }

    /**
     * 从列表中获取指定属性的最小值
     *
     * @param from      数据源列表
     * @param valueFunc 属性提取函数
     * @param <T>       数据源类型
     * @param <V>       属性类型
     * @return 最小值
     */
    public static <T, V extends Comparable<? super V>> V getMinValue(List<T> from, Function<T, V> valueFunc) {
        if (CollUtil.isEmpty(from)) {
            return null;
        }
        // 断言，避免告警
        assert from.size() > 0;
        T t = from.stream().min(Comparator.comparing(valueFunc)).get();
        return valueFunc.apply(t);
    }

    /**
     * 计算评价最终综合评分
     *
     * @param descriptionScores 商品评星
     * @param benefitScores     服务评星
     * @return 综合评分结果
     */
    public static Integer calcScores(Integer descriptionScores, Integer benefitScores) {
        // 计算评价最终综合评分 最终星数 = （商品评星 + 服务评星） / 2
        BigDecimal sumScore = new BigDecimal(descriptionScores + benefitScores);
        BigDecimal divide = sumScore.divide(BigDecimal.valueOf(2L), 0, RoundingMode.DOWN);
        return divide.intValue();
    }

    /**
     * 对集合中的元素按指定属性进行求和
     *
     * @param from         数据源集合
     * @param valueFunc    属性提取函数
     * @param accumulator  累加器函数
     * @param <T>          数据源类型
     * @param <V>          属性类型
     * @return 求和结果
     */
    public static <T, V extends Comparable<? super V>> V getSumValue(Collection<T> from, Function<T, V> valueFunc,
                                                                     BinaryOperator<V> accumulator) {
        return getSumValue(from, valueFunc, accumulator, null);
    }

    /**
     * 对集合中的元素按指定属性进行求和
     *
     * @param from         数据源集合
     * @param valueFunc    属性提取函数
     * @param accumulator  累加器函数
     * @param defaultValue 默认值
     * @param <T>          数据源类型
     * @param <V>          属性类型
     * @return 求和结果
     */
    public static <T, V extends Comparable<? super V>> V getSumValue(Collection<T> from, Function<T, V> valueFunc,
                                                                     BinaryOperator<V> accumulator, V defaultValue) {
        if (CollUtil.isEmpty(from)) {
            return defaultValue;
        }
        // 断言，避免告警
        assert !from.isEmpty();
        return from.stream().map(valueFunc).filter(Objects::nonNull).reduce(accumulator).orElse(defaultValue);
    }
}
