package org.xyzh.common.utils.validation.method;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * @description 身份证号码校验方法（支持15位和18位身份证）
 * @filename IdCardValidateMethod.java
 * @author yslg
 * @copyright xyzh
 * @since 2025-10-16
 */
public class IdCardValidateMethod implements ValidateMethod {
    
    // 加权因子
    private static final int[] WEIGHT = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
    
    // 校验码对应值
    private static final char[] VALIDATE_CODE = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'};
    
    // 省份代码
    private static final Map<String, String> PROVINCE_CODES = new HashMap<>();
    
    static {
        PROVINCE_CODES.put("11", "北京");
        PROVINCE_CODES.put("12", "天津");
        PROVINCE_CODES.put("13", "河北");
        PROVINCE_CODES.put("14", "山西");
        PROVINCE_CODES.put("15", "内蒙古");
        PROVINCE_CODES.put("21", "辽宁");
        PROVINCE_CODES.put("22", "吉林");
        PROVINCE_CODES.put("23", "黑龙江");
        PROVINCE_CODES.put("31", "上海");
        PROVINCE_CODES.put("32", "江苏");
        PROVINCE_CODES.put("33", "浙江");
        PROVINCE_CODES.put("34", "安徽");
        PROVINCE_CODES.put("35", "福建");
        PROVINCE_CODES.put("36", "江西");
        PROVINCE_CODES.put("37", "山东");
        PROVINCE_CODES.put("41", "河南");
        PROVINCE_CODES.put("42", "湖北");
        PROVINCE_CODES.put("43", "湖南");
        PROVINCE_CODES.put("44", "广东");
        PROVINCE_CODES.put("45", "广西");
        PROVINCE_CODES.put("46", "海南");
        PROVINCE_CODES.put("50", "重庆");
        PROVINCE_CODES.put("51", "四川");
        PROVINCE_CODES.put("52", "贵州");
        PROVINCE_CODES.put("53", "云南");
        PROVINCE_CODES.put("54", "西藏");
        PROVINCE_CODES.put("61", "陕西");
        PROVINCE_CODES.put("62", "甘肃");
        PROVINCE_CODES.put("63", "青海");
        PROVINCE_CODES.put("64", "宁夏");
        PROVINCE_CODES.put("65", "新疆");
        PROVINCE_CODES.put("71", "台湾");
        PROVINCE_CODES.put("81", "香港");
        PROVINCE_CODES.put("82", "澳门");
    }
    
    @Override
    public boolean validate(Object value) {
        if (value == null || !(value instanceof String)) {
            return false;
        }
        
        String idCard = ((String) value).toUpperCase();
        
        // 长度校验
        if (idCard.length() != 15 && idCard.length() != 18) {
            return false;
        }
        
        // 格式校验
        if (idCard.length() == 15) {
            return validate15IdCard(idCard);
        } else {
            return validate18IdCard(idCard);
        }
    }
    
    /**
     * @description 校验15位身份证
     */
    private boolean validate15IdCard(String idCard) {
        // 15位身份证格式：省(2位)市(2位)县(2位)年(2位)月(2位)日(2位)顺序号(3位)
        if (!Pattern.matches("^\\d{15}$", idCard)) {
            return false;
        }
        
        // 省份代码校验
        String provinceCode = idCard.substring(0, 2);
        if (!PROVINCE_CODES.containsKey(provinceCode)) {
            return false;
        }
        
        // 出生日期校验
        String year = "19" + idCard.substring(6, 8);
        String month = idCard.substring(8, 10);
        String day = idCard.substring(10, 12);
        
        return validateDate(year, month, day);
    }
    
    /**
     * @description 校验18位身份证
     */
    private boolean validate18IdCard(String idCard) {
        // 18位身份证格式：省(2位)市(2位)县(2位)年(4位)月(2位)日(2位)顺序号(3位)校验码(1位)
        if (!Pattern.matches("^\\d{17}[0-9Xx]$", idCard)) {
            return false;
        }
        
        // 省份代码校验
        String provinceCode = idCard.substring(0, 2);
        if (!PROVINCE_CODES.containsKey(provinceCode)) {
            return false;
        }
        
        // 出生日期校验
        String year = idCard.substring(6, 10);
        String month = idCard.substring(10, 12);
        String day = idCard.substring(12, 14);
        
        if (!validateDate(year, month, day)) {
            return false;
        }
        
        // 校验码校验
        return validateCheckCode(idCard);
    }
    
    /**
     * @description 校验日期是否合法
     */
    private boolean validateDate(String year, String month, String day) {
        try {
            int y = Integer.parseInt(year);
            int m = Integer.parseInt(month);
            int d = Integer.parseInt(day);
            
            // 年份范围：1900-当前年份
            int currentYear = java.time.Year.now().getValue();
            if (y < 1900 || y > currentYear) {
                return false;
            }
            
            // 月份范围：1-12
            if (m < 1 || m > 12) {
                return false;
            }
            
            // 日期范围
            int[] daysInMonth = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
            
            // 闰年2月29天
            if (isLeapYear(y)) {
                daysInMonth[1] = 29;
            }
            
            return d >= 1 && d <= daysInMonth[m - 1];
        } catch (NumberFormatException e) {
            return false;
        }
    }
    
    /**
     * @description 判断是否为闰年
     */
    private boolean isLeapYear(int year) {
        return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
    }
    
    /**
     * @description 校验18位身份证的校验码
     */
    private boolean validateCheckCode(String idCard) {
        int sum = 0;
        for (int i = 0; i < 17; i++) {
            sum += (idCard.charAt(i) - '0') * WEIGHT[i];
        }
        
        char checkCode = VALIDATE_CODE[sum % 11];
        return checkCode == idCard.charAt(17);
    }
    
    @Override
    public String getErrorMessage() {
        return "请输入有效的身份证号码（15位或18位）";
    }
    
    @Override
    public String getName() {
        return "身份证号码校验";
    }
}

