package com.ruoyi.common.utils;

import java.util.regex.Pattern;

/**
 * 身份证号校验工具类
 *
 * @author ruoyi
 */
public class IdCardUtil {

    /**
     * 18位身份证号正则表达式
     */
    private static final Pattern ID_CARD_PATTERN = Pattern.compile(
            "^[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[\\dXx]$"
    );

    /**
     * 身份证号加权因子
     */
    private static final int[] ID_CARD_WEIGHT = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};

    /**
     * 身份证号校验码对应值
     */
    private static final char[] ID_CARD_CHECK_CODE = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'};

    /**
     * 校验身份证号格式
     *
     * @param idCard 身份证号
     * @return 是否有效
     */
    public static boolean isValid(String idCard) {
        if (StringUtils.isEmpty(idCard)) {
            return false;
        }

        // 基本格式校验
        if (!ID_CARD_PATTERN.matcher(idCard).matches()) {
            return false;
        }

        // 校验码验证
        return checkVerifyCode(idCard);
    }

    /**
     * 校验身份证校验码
     *
     * @param idCard 身份证号
     * @return 校验码是否正确
     */
    private static boolean checkVerifyCode(String idCard) {
        char[] chars = idCard.toCharArray();
        int sum = 0;

        // 计算前17位与加权因子的乘积之和
        for (int i = 0; i < 17; i++) {
            sum += (chars[i] - '0') * ID_CARD_WEIGHT[i];
        }

        // 取模得到校验码索引
        int checkCodeIndex = sum % 11;
        char expectedCheckCode = ID_CARD_CHECK_CODE[checkCodeIndex];

        // 比较校验码（不区分大小写）
        return Character.toUpperCase(chars[17]) == expectedCheckCode;
    }

    /**
     * 获取校验错误信息
     *
     * @param idCard 身份证号
     * @return 错误信息，如果正确返回null
     */
    public static String getErrorMessage(String idCard) {
        if (StringUtils.isEmpty(idCard)) {
            return "身份证号不能为空";
        }

        if (idCard.length() != 18) {
            return "身份证号长度必须为18位";
        }

        if (!ID_CARD_PATTERN.matcher(idCard).matches()) {
            return "身份证号格式不正确";
        }

        if (!checkVerifyCode(idCard)) {
            return "身份证号校验码不正确";
        }

        return null;
    }
}
