package cn.stylefeng.roses.kernel.security.database.interceptor;

import cn.hutool.core.util.ObjectUtil;
import cn.stylefeng.roses.kernel.security.database.algorithm.EncryptAlgorithmApi;
import cn.stylefeng.roses.kernel.security.database.annotation.EncryptField;
import cn.stylefeng.roses.kernel.security.database.annotation.ProtectedData;
import cn.stylefeng.roses.kernel.security.database.annotation.ProtectedField;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.plugin.*;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.util.Properties;

/**
 * Mybatis拦截器，拦截入库参数
 *
 * @author majianguo
 * @since 2021/7/3 11:58
 */
@Slf4j
@Component
@Intercepts({@Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),})
public class ParameterInterceptor implements Interceptor {

    @Resource
    private EncryptAlgorithmApi encryptAlgorithmApi;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        // 若指定ResultSetHandler ，这里则能强转为ResultSetHandler
        ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();

        // 获取参数对像，即 mapper 中 paramsType 的实例
        Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject");
        parameterField.setAccessible(true);

        // 取出实例
        Object parameterObject = parameterField.get(parameterHandler);
        if (parameterObject != null) {
            Class<?> parameterObjectClass = parameterObject.getClass();

            // 校验该实例的类是否被@ProtectedData所注解
            ProtectedData protectedData = AnnotationUtils.findAnnotation(parameterObjectClass, ProtectedData.class);
            if (ObjectUtil.isNotNull(protectedData)) {

                //取出当前当前类所有字段
                Field[] declaredFields = parameterObjectClass.getDeclaredFields();

                // 处理需要加密的字段
                for (Field declaredField : declaredFields) {

                    // 包含其中任意一个即可
                    ProtectedField protectedField = declaredField.getAnnotation(ProtectedField.class);
                    EncryptField encryptField = declaredField.getAnnotation(EncryptField.class);
                    if (ObjectUtil.isNotNull(protectedField) || ObjectUtil.isNotNull(encryptField)) {
                        declaredField.setAccessible(true);
                        Object fieldData = declaredField.get(parameterObject);
                        // 如果是String就处理
                        if (fieldData instanceof String) {
                            String value = (String) fieldData;
                            try {
                                String encrypt = encryptAlgorithmApi.encrypt(value);
                                declaredField.set(parameterObject, encrypt);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }
}
