package org.junior.utils;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
* @Description: 短码生成工具类（混淆+62进制转换）
* @Author: Junior
* @Date: 2026/1/3
*/
public class ShortLinkUtil {
    // 高度混乱的62进制字符集（无规律打乱，建议生产环境再随机调整一次）
    private static final String CHARSET = "H5tLvJw9sXq4hRzYbGcDp7nMkZfA3iU1oP6lKjT8uV2eC0QdNxWrFmSgIyB";

    private static final int BASE = 62;
    private static final int SHORT_CODE_LENGTH = 6;
    // 混淆盐值（建议生产环境替换为随机长数字）
    private static final long MAIN_SALT = 723498252793847L;
    // 辅助盐值（哈希盐，字符串形式）
    private static final String HASH_SALT = "sdf34592jds9304509062ejg9345";
    private static final Random RANDOM = new Random(System.currentTimeMillis());


    /**
     * 生成短码（彻底降低相似度）
     * @param longUrl 长链接
     * @return 6位低相似度短码
     */
    public static String generateShortCode(String longUrl) {
        // 1. 生成雪花ID + 长链接哈希（双重唯一标识）
        long snowFlakeId = SnowFlakeUtil.getInstance().nextId();
        String urlHash = getMd5Hash(longUrl + HASH_SALT + snowFlakeId);

        // 2. 哈希值转长整数（分段处理，避免溢出）
        long hashNum = safeHashToLong(urlHash);
        // 3. 多重混淆（彻底打乱数值）
        long confusedId = multiConfuse(snowFlakeId, hashNum);
        // 4. 62进制转换（基于随机字符集）
        String base62Code = convertToBase62(confusedId);
        // 5. 混合随机字符生成最终6位短码（核心去重逻辑）
        return generateLowSimilarityCode(base62Code);
    }

    /**
     * MD5哈希（打散长链接+ID的特征）
     */
    private static String getMd5Hash(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] digest = md.digest(input.getBytes());
            // 转16进制字符串
            StringBuilder sb = new StringBuilder();
            for (byte b : digest) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("MD5算法不存在", e);
        }
    }

    /**
     * 安全的哈希字符串转长整数（分段处理，避免溢出）
     * 解决 Long.parseLong 超出取值范围的问题
     */
    private static long safeHashToLong(String hash) {
        // 1. 截取哈希值前15位（15位16进制转十进制最大为 10^18，小于long最大值9e18）
        String subHash = hash.substring(0, 15);
        // 2. 16进制转十进制（15位不会溢出）
        try {
            return Long.parseLong(subHash, 16);
        } catch (NumberFormatException e) {
            // 兜底：若仍溢出（极端情况），取哈希值的哈希码
            return Math.abs((long) subHash.hashCode());
        }
    }

    /**
     * 多重混淆（彻底打散连续ID）
     */
    private static long multiConfuse(long snowId, long hashNum) {
        // 1. 异或主盐值 + 哈希值
        long confused = snowId ^ MAIN_SALT ^ hashNum;
        // 2. 不规则位移（动态位移量，基于哈希值）
        int shift1 = (int) (hashNum % 7) + 1; // 1-7位位移
        int shift2 = (int) (hashNum % 9) + 1; // 1-9位位移
        confused = (confused << shift1) ^ (confused >>> shift2);
        // 3. 随机数扰动（大范围）
        confused += RANDOM.nextInt(99999999);
        // 4. 再次异或随机长数
        confused ^= RANDOM.nextLong();
        // 5. 取绝对值（防止负数）
        return Math.abs(confused);
    }

    /**
     * 62进制转换
     */
    private static String convertToBase62(long num) {
        if (num == 0) {
            return String.valueOf(CHARSET.charAt(RANDOM.nextInt(BASE)));
        }
        StringBuilder sb = new StringBuilder();
        while (num > 0) {
            int remainder = (int) (num % BASE);
            sb.append(CHARSET.charAt(remainder));
            num = num / BASE;
        }
        return sb.reverse().toString();
    }

    /**
     * 生成低相似度6位短码（核心：混合随机字符+无规律截取）
     */
    private static String generateLowSimilarityCode(String base62Code) {
        // 1. 拼接随机字符（长度=12，确保足够随机）
        StringBuilder randomStr = new StringBuilder();
        for (int i = 0; i < 12; i++) {
            randomStr.append(CHARSET.charAt(RANDOM.nextInt(BASE)));
        }
        // 2. 将62进制结果插入随机字符串的随机位置
        int insertPos = RANDOM.nextInt(randomStr.length() + 1);
        randomStr.insert(insertPos, base62Code);
        // 3. 随机截取6位（彻底打破连续性）
        int start = RANDOM.nextInt(randomStr.length() - SHORT_CODE_LENGTH);
        return randomStr.substring(start, start + SHORT_CODE_LENGTH);
    }

    /**
     * 测试方法
     */
    public static void main(String[] args) throws InterruptedException {
        // 测试参数
        int totalCount = 10_000_000; // 一千万个短码
        Map<String, Integer> codeCountMap = new HashMap<>(); // 统计每个短码出现的次数
        long duplicateCount = 0; // 重复总数（注意：重复n次会累计n-1次）

        System.out.println("开始生成一千万个短码...");
        long startTime = System.currentTimeMillis();

        // 批量生成短码并统计重复
        for (int i = 0; i < totalCount; i++) {
            String url = "https://www.example.com/" + i;
            String shortCode = generateShortCode(url);

            // 统计重复
            if (codeCountMap.containsKey(shortCode)) {
                duplicateCount++;
                codeCountMap.put(shortCode, codeCountMap.get(shortCode) + 1);
            } else {
                codeCountMap.put(shortCode, 1);
            }

            // 每生成100万条打印进度
            if (i % 1_000_000 == 0 && i > 0) {
                System.out.printf("已生成%d百万条，当前重复数：%d%n", i / 1_000_000, duplicateCount);
            }
        }

        long endTime = System.currentTimeMillis();
        long costTime = (endTime - startTime) / 1000; // 耗时（秒）

        // 输出统计结果
        System.out.println("====================================");
        System.out.printf("总生成短码数：%d%n", totalCount);
        System.out.printf("重复短码总数：%d%n", duplicateCount);
        System.out.printf("唯一短码数：%d%n", codeCountMap.size());
        System.out.printf("重复率：%.6f%%%n", (duplicateCount * 100.0) / totalCount);
        System.out.printf("总耗时：%d秒%n", costTime);
    }
}
