package com.gci.boot.common.util;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.gci.boot.constant.CommonConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class ValidatorUtil {

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    public @interface NotEmpty {
        String message() default "";
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    public @interface PhoneNumber {
        String message() default "";
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    public @interface IDCard {
        String message() default "";
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    public @interface DateValue {
        String format() default "yyyy-MM-dd";
        String message() default "";
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    public @interface NotEmptyByField {
        String field() default "";
        String message() default "";
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    public @interface NumericValue {
        double minValue() default Double.MIN_VALUE;
        double maxValue() default Double.MAX_VALUE;
        String message() default "";
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    public @interface NumericValueWithOutEqual {
        double minValue() default Double.MIN_VALUE;
        double maxValue() default Double.MAX_VALUE;
        String message() default "";
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    public @interface IntValue {
        double minValue() default Double.MIN_VALUE;
        double maxValue() default Double.MAX_VALUE;
        String message() default "";
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    public @interface StrMaxLength {
        int strMaxLength() default 0;
        String message() default "";
        String fieldNameCN()  default "";
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    public @interface CollectionStr {
        String collectionStr() default "";
        String message() default "";
    }

    public static class ValidationError {
        private String fieldName;
        private String message;

        public ValidationError(String fieldName, String message) {
            this.fieldName = fieldName;
            this.message = message;
        }

        public String getFieldName() {
            return fieldName;
        }

        public String getMessage() {
            return message;
        }
    }

    public static <T> List<ValidationError> validate(T entity) {
        List<ValidationError> errors = new ArrayList<>();
        Class<?> clazz = entity.getClass();
        Field[] fields = clazz.getDeclaredFields();

        for (Field field : fields) {
            field.setAccessible(true);

            try {
                Object value = field.get(entity);
                String fieldName = field.getName();

                if (field.isAnnotationPresent(NotEmpty.class)) {
                    NotEmpty annotation = field.getAnnotation(NotEmpty.class);
                    String message = annotation.message();
                    if (isEmpty(value)) {
                        errors.add(new ValidationError(fieldName, message.isEmpty() ? "字段不能为空" : message));
                    }
                }
                if (field.isAnnotationPresent(StrMaxLength.class)) {
                    StrMaxLength annotation = field.getAnnotation(StrMaxLength.class);
                    int strLength = annotation.strMaxLength();
                    String message = annotation.message();
                    String fieldNameCN = annotation.fieldNameCN();
                    if (!isEmpty(value)) {
                        //补充一个非法字符的校验
                        Pattern p = Pattern.compile(CommonConstant.SPECIAL_CHAR);
                        Matcher m = p.matcher(value.toString());
                        if (m.find()) {
                            StringBuilder specialCharacters = new StringBuilder();
                            do {
                                specialCharacters.append(m.group());
                            } while (m.find());
                            errors.add(new ValidationError(fieldName, fieldNameCN + "包含特殊字符:" + specialCharacters.toString()));
                        }

                        if(value.toString().length() > strLength){
                            errors.add(new ValidationError(fieldName, message.isEmpty() ? "字段长度不能超过" + strLength : message));
                        }
                    }
                }

                if (field.isAnnotationPresent(PhoneNumber.class)) {
                    PhoneNumber annotation = field.getAnnotation(PhoneNumber.class);
                    String message = annotation.message();
                    if (!isEmpty(value) && !isPhoneNumber(value.toString())) {
                        errors.add(new ValidationError(fieldName, message.isEmpty() ? "手机号格式错误" : message));
                    }
                }

                if (field.isAnnotationPresent(DateValue.class)) {
                    DateValue annotation = field.getAnnotation(DateValue.class);
                    String format = annotation.format();
                    String message = annotation.message();
                    if (!isEmpty(value) && !isValidDate(value.toString(), format)) {
                        errors.add(new ValidationError(fieldName, message.isEmpty() ? "日期格式错误，应为" + format : message));
                    }
                }

                if (field.isAnnotationPresent(NumericValue.class)) {
                    NumericValue annotation = field.getAnnotation(NumericValue.class);
                    double minValue = annotation.minValue();
                    double maxValue = annotation.maxValue();
                    String message = annotation.message();
                    if (!isEmpty(value) && !isNumeric(value.toString())) {
                        errors.add(new ValidationError(fieldName, message.isEmpty() ? "数值格式错误" : message));
                    }else{
                        if (!isEmpty(value) && !isInRange(value.toString(), minValue, maxValue)) {
                            errors.add(new ValidationError(fieldName, message.isEmpty() ? "数值超出范围，应在" + minValue + "和" + maxValue + "之间" : message));
                        }
                    }
                }

                if (field.isAnnotationPresent(NumericValueWithOutEqual.class)) {
                    NumericValueWithOutEqual annotation = field.getAnnotation(NumericValueWithOutEqual.class);
                    double minValue = annotation.minValue();
                    double maxValue = annotation.maxValue();
                    String message = annotation.message();
                    if (!isEmpty(value) && !isNumeric(value.toString())) {
                        errors.add(new ValidationError(fieldName, message.isEmpty() ? "数值格式错误" : message));
                    }else{
                        if (!isEmpty(value) && !isInRangeWithOutEqual(value.toString(), minValue, maxValue)) {
                            errors.add(new ValidationError(fieldName, message.isEmpty() ? "数值超出范围，应在" + minValue + "和" + maxValue + "之间" : message));
                        }
                    }
                }

                if (field.isAnnotationPresent(IntValue.class)) {
                    IntValue annotation = field.getAnnotation(IntValue.class);
                    double minValue = annotation.minValue();
                    double maxValue = annotation.maxValue();
                    String message = annotation.message();
                    if (!isEmpty(value) && !isInt(value.toString())) {
                        errors.add(new ValidationError(fieldName, message.isEmpty() ? "数值格式错误" : message));
                    }else{
                        if (!isEmpty(value) && !isInRange(value.toString(), minValue, maxValue)) {
                            errors.add(new ValidationError(fieldName, message.isEmpty() ? "数值超出范围，应在" + minValue + "和" + maxValue + "之间" : message));
                        }
                    }
                }

                if (field.isAnnotationPresent(CollectionStr.class)) {
                    CollectionStr annotation = field.getAnnotation(CollectionStr.class);
                    String message = annotation.message();
                    String collectionStr = annotation.collectionStr();
                    if (!isEmpty(value)) {
                        if(StrUtil.isNotBlank(collectionStr)){
                            List<String> list = Arrays.asList(collectionStr.toString().split(","));
                            if(CollectionUtil.isNotEmpty(list) && !list.contains(value)){
                                errors.add(new ValidationError(fieldName, message.isEmpty() ? "字段值应该为" + list : message));
                            }
                        }
                    }
                }

                if (field.isAnnotationPresent(IDCard.class)) {
                    IDCard annotation = field.getAnnotation(IDCard.class);
                    String message = annotation.message();
                    if (!isEmpty(value) && !isIDCardNumber(value.toString())) {
                        errors.add(new ValidationError(fieldName, message.isEmpty() ? "身份证号码格式错误" : message));
                    }
                }

                if (field.isAnnotationPresent(NotEmptyByField.class)) {
                    NotEmptyByField annotation = field.getAnnotation(NotEmptyByField.class);
                    String message = annotation.message();
                    String byFieldName = annotation.field();
                    Field byField = Arrays.stream(fields).filter(f->{
                        return f.getName().equals(byFieldName);
                    }).findFirst().orElse(null);
                   Object byFieldValue = Objects.isNull(byField)?"":byField.get(entity);
                   if(Objects.equals(byFieldValue,"是")&&isEmpty(value)){
                       errors.add(new ValidationError(fieldName, message.isEmpty() ? "字段不能为空" : message));
                   }
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                errors.add(new ValidationError("数据行",  "数据行校验异常:" +e.getMessage()));
            }
        }

        return errors;
    }

    private static boolean isEmpty(Object value) {
        return value == null || value.toString().isEmpty();
    }

    private static boolean isPhoneNumber(String value) {
        return value.matches("^1[3456789]\\d{9}$");
    }

    private static boolean isIDCardNumber(String value) {
        return value.matches("^((\\d{17}[0-9Xx])|(\\d{15}))$");
    }

    private static boolean isValidDate(String value, String format) {
        SimpleDateFormat dateFormat = new SimpleDateFormat(format);
        try {
            Date date = dateFormat.parse(value);
            return value.equals(dateFormat.format(date));
        } catch (ParseException e) {
            return false;
        }
    }

    private static boolean isNumeric(String value) {
        return value.matches("-?\\d+(\\.\\d+)?");
    }

    private static boolean isInt(String value) {
        try {
            int intValue = Integer.parseInt(value);
            return (intValue >= Integer.MIN_VALUE && intValue <= Integer.MAX_VALUE);
        } catch (NumberFormatException e) {
            return false;
        }
    }
    private static boolean isInRange(String value, double minValue, double maxValue) {
        try {
            double num = Double.parseDouble(value);
            return num >= minValue && num <= maxValue;
        } catch (NumberFormatException e) {
            return false;
        }
    }

    private static boolean isInRangeWithOutEqual(String value, double minValue, double maxValue) {
        try {
            double num = Double.parseDouble(value);
            return num > minValue && num < maxValue;
        } catch (NumberFormatException e) {
            return false;
        }
    }

    public static <T> Map<String, List<Integer>> findDuplicates(List<T> list, String... fields) {
        Map<String, List<Integer>> duplicatesMap = new HashMap<>();
        for (int i = 0; i < list.size(); i++) {
            T current = list.get(i);
            StringBuilder keyBuilder = new StringBuilder();
            for (String field : fields) {
                try {
                    keyBuilder.append(current.getClass().getMethod("get" + capitalize(field)).invoke(current));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            String key = keyBuilder.toString();
            if (duplicatesMap.containsKey(key)) {
                duplicatesMap.get(key).add(i + 1);
            } else {
                List<Integer> indexList = new ArrayList<>();
                indexList.add(i + 1);
                duplicatesMap.put(key, indexList);
            }
        }
        //删除不重复的记录
        List<String> keysToRemove = new ArrayList<>();
        for (Map.Entry<String, List<Integer>> entry : duplicatesMap.entrySet()) {
            if (entry.getValue().size() == 1) {
                keysToRemove.add(entry.getKey());
            }
        }
        for (String key : keysToRemove) {
            duplicatesMap.remove(key);
        }
        return duplicatesMap;
    }
    private static String capitalize(String str) {
        if (str == null || str.isEmpty()) {
            return str;
        }
        return str.substring(0, 1).toUpperCase() + str.substring(1);
    }

    public static void deleteFile(String filePath) {
        try {
            Path path = Paths.get(filePath);
            Files.deleteIfExists(path);
            log.info("文件删除成功: {}",filePath);
        } catch (IOException e) {
            e.printStackTrace();
            log.info("文件删除失败: {}",filePath);
        }
    }

    public static void trimStringFields(Object obj) {
        Class<?> clazz = obj.getClass();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            try {
                field.setAccessible(true);
                Object value = field.get(obj);
                if (value instanceof String) {
                    String strValue = (String) value;
                    if (StrUtil.isNotBlank(strValue)) {
                        field.set(obj, strValue.trim());
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static <T> boolean allFieldsAreNullInList(List<T> list) {
        for (T obj : list) {
            if (!allFieldsAreNull(obj)) {
                // 只要有一个对象的字段不为null，就返回false
                return false;
            }
        }
        // 如果所有对象的字段都为null，返回true
        return true;
    }
    //判断对象全部字段是否为null
    private static boolean allFieldsAreNull(Object obj) {
        Class<?> clazz = obj.getClass();
        Field[] fields = clazz.getDeclaredFields();

        for (Field field : fields) {
            field.setAccessible(true);
            try {
                if (field.get(obj) != null) {
                    // 只要有一个字段不为null，就返回false
                    return false;
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        // 如果所有字段都为null，返回true
        return true;
    }
    // 校验文件是否为Excel格式
    public static boolean isExcelFile(MultipartFile file) {
        try {
            Workbook workbook = WorkbookFactory.create(file.getInputStream());
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}
