package com.bonus.system.interceptor;

import com.bonus.common.core.utils.encryption.Sm4Utils;
import com.bonus.system.api.domain.SysDept;
import com.bonus.system.api.domain.SysUser;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.sql.Statement;
/**
 * mybatis 拦截器
 * 对用户和部门实体里的邮箱和电话号码进行加密存库，并从库里查询后解密
 * @author weiweiwang
 */
@Intercepts({
        @Signature(type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class}),
        @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
public class DataEnDecryptInterceptor implements Interceptor {
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
    final  static String USER_MAPPER_ID = "com.bonus.system.mapper.yyyyyyyyyyyyyyyyyy";
    final  static String DEPT_MAPPER_ID = "com.bonus.system.mapper.zzzzzzxxxxxxxxxxxxx";
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        try {
            if (invocation.getTarget() instanceof ParameterHandler) {
                // Handle encryption before setting parameters
                ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
                MappedStatement mappedStatement = getMappedStatement(parameterHandler);
                PreparedStatement preparedStatement = (PreparedStatement) invocation.getArgs()[0];
                Object parameterObject = parameterHandler.getParameterObject();
                String sqlId = mappedStatement.getId();
                if (sqlId.contains(USER_MAPPER_ID)){
                    encryptUserObject(parameterObject);
                } else if (sqlId.contains(DEPT_MAPPER_ID)){
                    encryptDeptObject(parameterObject);
                }
                return invocation.proceed();

            } else if (invocation.getTarget() instanceof ResultSetHandler) {
                // Handle decryption after result set is obtained
                ResultSetHandler resultSetHandler = (ResultSetHandler) invocation.getTarget();

                MappedStatement mappedStatement = getMappedStatement(resultSetHandler);
                Object result = invocation.proceed();

                String sqlId = mappedStatement.getId();
                if (sqlId.contains(USER_MAPPER_ID))
                {
                    decryUserObject(result);
                } else if (sqlId.contains(DEPT_MAPPER_ID)){
                    decryDeptObject(result);
                }
                return result;
            }

            return invocation.proceed();
        }
        catch (Exception e){
            logger.error("mybatis对敏感数据加解密拦截器异常报错，{}",e.getMessage ());
            return invocation.proceed();
        }
    }

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

    @Override
    public void setProperties(Properties properties) {
    }

    private void encryptUserObject(Object parameterObject){
        if (parameterObject instanceof SysUser) {
            SysUser user = (SysUser) parameterObject;
            // 加密敏感字段
            if (user.getEmail() != null) {
                user.setEmail(Sm4Utils.encrypt(user.getEmail()));
            }
            if (user.getPhonenumber() != null) {
                user.setPhonenumber(Sm4Utils.encrypt(user.getPhonenumber()));
            }
        }
    }

    private void encryptDeptObject(Object parameterObject) {
        if (parameterObject instanceof SysDept) {
            SysDept dept = (SysDept) parameterObject;
            // 加密敏感字段
            if (dept.getEmail() != null) {
                dept.setEmail(Sm4Utils.encrypt(dept.getEmail()));
            }
            if (dept.getPhone() != null) {
                dept.setPhone(Sm4Utils.encrypt(dept.getPhone()));
            }
        }

    }

    private void decryUserObject(Object result){
        try {
            if (result instanceof ArrayList) {
                List<?> list = (List<?>) result;
                for (Object obj : list) {
                    if (obj instanceof SysUser) {
                        decryptUser ((SysUser) obj);
                    }
                }
            } else if (result instanceof SysUser) {
                decryptUser ((SysUser) result);
            }
        } catch (Exception ingore) {
        }
    }
    private void decryptUser(SysUser user) {
        if (user.getEmail() != null) {
            user.setEmail(Sm4Utils.decrypt(user.getEmail()));
        }
        if (user.getPhonenumber() != null) {
            user.setPhonenumber(Sm4Utils.decrypt(user.getPhonenumber()));
        }
    }

    private void decryDeptObject(Object result){
        try {
            if (result instanceof ArrayList) {
                List<?> list = (List<?>) result;
                for (Object obj : list) {
                    if (obj instanceof SysDept) {
                        decryptDept ((SysDept) obj);
                    }
                }
            } else if (result instanceof SysDept) {
                decryptDept ((SysDept) result);
            }
        } catch (Exception ingore) {
        }
    }

    private void decryptDept(SysDept dept) {
        if (dept.getEmail() != null) {
            dept.setEmail(Sm4Utils.decrypt(dept.getEmail()));
        }
        if (dept.getPhone() != null) {
            dept.setPhone(Sm4Utils.decrypt(dept.getPhone()));
        }
    }

    private MappedStatement getMappedStatement(ParameterHandler parameterHandler) {
        try {
            // Use reflection to access the private field `mappedStatement` (or appropriate field)
            Field mappedStatementField = parameterHandler.getClass().getDeclaredField("mappedStatement");
            mappedStatementField.setAccessible(true);
            return (MappedStatement) mappedStatementField.get(parameterHandler);
        } catch (Exception e) {
            throw new RuntimeException("Failed to get MappedStatement from ParameterHandler", e);
        }
    }


    private MappedStatement getMappedStatement(ResultSetHandler resultSetHandler) {
        try {
            // Use reflection to access the private field `mappedStatement` (or appropriate field)
            Field mappedStatementField = resultSetHandler.getClass().getDeclaredField("mappedStatement");
            mappedStatementField.setAccessible(true);
            return (MappedStatement) mappedStatementField.get(resultSetHandler);
        } catch (Exception e) {
            throw new RuntimeException("Failed to get MappedStatement from ResultSetHandler", e);
        }
    }
}