package com.yunfeiyun.agmp.common.framework.interceptor;

import com.yunfeiyun.agmp.common.constant.Constants;
import com.yunfeiyun.agmp.common.core.domain.model.LoginUser;
import com.yunfeiyun.agmp.common.annotation.IdentifyId;
import com.yunfeiyun.agmp.common.utils.DateUtils;
import com.yunfeiyun.agmp.common.utils.StringUtils;
import com.yunfeiyun.agmp.common.utils.uuid.IdUtils;
import com.yunfeiyun.agmp.common.utils.uuid.Uuids;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

@Intercepts({@Signature(type= Executor.class, method = "update", args = {MappedStatement.class,Object.class})})
@Component
@SuppressWarnings("unchecked")
public class MybatisInterceptor implements Interceptor {
    private static final Logger log = LoggerFactory.getLogger(MybatisInterceptor.class);

    @Override
    public Object intercept(Invocation invocation) throws InvocationTargetException, IllegalAccessException {
        Object[] args = invocation.getArgs();
        MappedStatement mappedStatement = (MappedStatement) args[0];

        String sqlId = mappedStatement.getId();
        Object parameter = invocation.getArgs()[1];
        SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
        log.info("sqlId={} sqlCommandType={}  method={}" + sqlId, sqlCommandType,invocation.getMethod());
        if (parameter == null) {
            return invocation.proceed();
        }

        if (SqlCommandType.INSERT.equals(sqlCommandType))  //判断类型为INSERT
        {
            Field[] fields = getAllFields(parameter);
            for (Field field : fields) {
                IdentifyId annotation = field.getAnnotation(IdentifyId.class);
                if (annotation != null) {
                    Object attrValue = getField(parameter, field.getName());
                    if(StringUtils.isNull(attrValue) || StringUtils.isEmpty(attrValue.toString())){
                        setField(parameter, field.getName(),  getId());
                    }
                    break;
                }
            }
            return handleInsert(invocation, parameter);
        }

        if (SqlCommandType.UPDATE.equals(sqlCommandType))  //判断类型为UPDATE
            return handleUpdate(invocation, parameter);

        return invocation.proceed();
    }

    private Object handleInsert(Invocation invocation, Object parameter) throws InvocationTargetException, IllegalAccessException {
        Field[] fields = getAllFields(parameter);
        for (Field field : fields) {
            if (field.getName().indexOf(Constants.FILELDS_CREATEDDATE) > -1 || field.getName().indexOf(Constants.FILELDS_MODIFIEDDATE) > -1 ) {
                Object attrValue = getField(parameter, field.getName());
                if(StringUtils.isNull(attrValue) || StringUtils.isEmpty(attrValue.toString())){
                    setField(parameter, field.getName(),  DateUtils.dateTimeNow());
                }
            }
            if (field.getName().indexOf(Constants.FILELDS_CREATOR) > -1 || field.getName().indexOf(Constants.FILELDS_MODIFIER) > -1 ) {
                Object attrValue = getField(parameter, field.getName());
                if(StringUtils.isNull(attrValue) || StringUtils.isEmpty(attrValue.toString())){
                    setField(parameter, field.getName(),  getLoginUser().getUserId());
                }
            }
        }
        return invocation.proceed();
    }

    private Object handleUpdate(Invocation invocation, Object parameter) throws InvocationTargetException, IllegalAccessException
    {
        Field[] fields = getAllFields(parameter);
        for (Field field : fields) {
            if (field.getName().indexOf(Constants.FILELDS_MODIFIEDDATE)>-1) {
                Object attrValue = getField(parameter, field.getName());
                if(StringUtils.isNull(attrValue) || StringUtils.isEmpty(attrValue.toString())){
                    setField(parameter, field.getName(),  DateUtils.dateTimeNow());
                }
            }
            if (field.getName().indexOf(Constants.FILELDS_MODIFIER) > -1 ) {
                Object attrValue = getField(parameter, field.getName());
                if(StringUtils.isNull(attrValue) || StringUtils.isEmpty(attrValue.toString())){
                    setField(parameter, field.getName(),  getLoginUser().getUserId());
                }
            }
        }
        return invocation.proceed();
    }

    /**
     * 获取类的所有属性，包括父类
     *
     * @param object
     * @return
     */
    public static Field[] getAllFields(Object object){
        Class<?> clazz = object.getClass();
        List<Field> fieldList = new ArrayList<>();
        while (clazz != null) {
            fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
            clazz = clazz.getSuperclass();
        }
        Field[] fields = new Field[fieldList.size()];
        fieldList.toArray(fields);
        return fields;
    }

    public static String getId()
    {
        try {
            return (Uuids.create().toString());
        }catch(Exception e){
            return IdUtils.fastUUID();
        }
    }


    public static void setField(Object o, String args, Object attributeValue) {
        Class cls = o.getClass();
        //判断该属性是否存在
        Field field = null;
        try {
            field = cls.getDeclaredField(args);
            if (field == null) {
                field = cls.getField(args);
            }
            if (field == null) {
                return;
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

        String fieldName = "set" + args.substring(0, 1).toUpperCase() + (args.length() > 1 ? args.substring(1) : "");
        Method method = null;
        try {
            if(attributeValue !=null){
                method = cls.getMethod(fieldName, attributeValue.getClass());
                method.invoke(o, attributeValue);
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }


    public static Object getField(Object o, String args) {
        Class cls = o.getClass();
        //判断该属性是否存在
        Field field = null;
        try {
            field = cls.getDeclaredField(args);
            if (field == null) {
                field = cls.getField(args);
            }
            if (field == null) {
                return null;
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

        try {
            field.setAccessible(true);
            Object value = field.get(o);
            field.setAccessible(false);
            return value;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static LoginUser getLoginUser()
    {
        if(getAuthentication() !=null){
            Object o = getAuthentication().getPrincipal();
            if( o instanceof  LoginUser){
                return (LoginUser) getAuthentication().getPrincipal();
            }
            return new LoginUser();
        }
        return new LoginUser();
    }

    public static Authentication getAuthentication()
    {
        return SecurityContextHolder.getContext().getAuthentication();
    }

}
