package com.ruoyi.common.utils;

import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.ruoyi.project.business.domain.TbAgent;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

import java.beans.Introspector;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author fangk
 */
public class SpecialCharacterEscaper {
    // TODO 是否有必要缓存这个Map？
    private static final Map<SFunction<?, Object>, Field> FUNCTION_CACHE = new ConcurrentHashMap<>();

    //public static <R> void escapeSpecialCharacters(R r, Class<R> clazz, SFunction<R, Object>... consumers) throws NoSuchMethodException {
    @SafeVarargs()
    public static <R> void escapeSpecialCharacters(R r, SFunction<R, Object>... consumers) {


        //SFunction;

        for (SFunction<R, Object> fun : consumers) {

            Object value = fun.apply(r);
            if (value != null) {
                if (value instanceof String) {
                    // TODO 这里的需要转义的特殊字符暂时写死。。。。后续再考虑可配置化吧
                    value = value.toString().replaceAll("%", "\\\\%").replaceAll("_", "\\\\_");
                }
                try {
                    //String fieldName = getFieldName(fun);
                    //Field field = clazz.getDeclaredField(fieldName);
                    Field field = getField(fun);
                    // 获取字段所在类的Class对象
                    //Class<?> declaringClass = field.getDeclaringClass();

                    field.setAccessible(true);
                    field.set(r, value);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    // TODO 下面的这些反射方法可以抽取到一个工具类中
    // 通过反射获取字段名
    public static <T> String getFieldName(SFunction<T, Object> function) {
        Field field = getField(function);
        return field.getName();
    }

    public static <T> Field getField(SFunction<T, Object> function) {
        return FUNCTION_CACHE.computeIfAbsent(function, SpecialCharacterEscaper::findField);
    }

    public static <T> Field findField(SFunction<T, Object> function) {
        // 第1步 获取SerializedLambda
        final SerializedLambda serializedLambda = getSerializedLambda(function);
        // 第2步 implMethodName 即为Field对应的Getter方法名
        final String implClass = serializedLambda.getImplClass();
        final String implMethodName = serializedLambda.getImplMethodName();
        final String fieldName = convertToFieldName(implMethodName);
        // 第3步  Spring 中的反射工具类获取Class中定义的Field
        final Field field = getField(fieldName, serializedLambda);

        // 第4步 如果没有找到对应的字段应该抛出异常
        if (field == null) {
            throw new RuntimeException("No such class 「" + implClass + "」 field 「" + fieldName + "」.");
        }

        return field;
    }

    static Field getField(String fieldName, SerializedLambda serializedLambda) {
        try {
            // 获取的Class是字符串，并且包名是“/”分割，需要替换成“.”，才能获取到对应的Class对象
            String declaredClass = serializedLambda.getImplClass().replace("/", ".");
            Class<?> aClass = Class.forName(declaredClass, false, ClassUtils.getDefaultClassLoader());
            return ReflectionUtils.findField(aClass, fieldName);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("get class field exception.", e);
        }
    }

    static String convertToFieldName(String getterMethodName) {
        // 获取方法名
        String prefix = null;
        if (getterMethodName.startsWith("get")) {
            prefix = "get";
        } else if (getterMethodName.startsWith("is")) {
            prefix = "is";
        }

        if (prefix == null) {
            throw new IllegalArgumentException("invalid getter method: " + getterMethodName);
        }

        // 截取get/is之后的字符串并转换首字母为小写
        return Introspector.decapitalize(getterMethodName.replace(prefix, ""));
    }

    static <T> SerializedLambda getSerializedLambda(SFunction<T, Object> function) {
        try {
            Method method = function.getClass().getDeclaredMethod("writeReplace");
            method.setAccessible(Boolean.TRUE);
            return (SerializedLambda) method.invoke(function);
        } catch (Exception e) {
            throw new RuntimeException("get SerializedLambda exception.", e);
        }
    }


    public static void main(String[] args) throws NoSuchMethodException {
        TbAgent tbAgent = new TbAgent();
        tbAgent.setAgentName("test%");
        tbAgent.setAgentNo("test*");
        SFunction<TbAgent, Object> func1 = TbAgent::getAgentName;
        String fieldName = getFieldName(func1);
        System.out.println("fieldName = " + fieldName);

        escapeSpecialCharacters(tbAgent, TbAgent::getAgentName, TbAgent::getAgentNo);
        
        System.out.println("after:" + tbAgent.getAgentName());

    }
}

