package cn.ibizlab.util.aspect;

import cn.ibizlab.util.annotation.DEField;
import cn.ibizlab.util.enums.DEFieldDefaultValueType;
import cn.ibizlab.util.enums.DEPredefinedFieldType;
import cn.ibizlab.util.helper.DEFieldCacheMap;
import cn.ibizlab.util.security.AuthenticationUser;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.AlternativeJdkIdGenerator;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 实体属性默认值切面，只有新建（Create）时才会填充默认值
 */
@Aspect
@Order(0)
@Component
public class DEFieldDefaultValueAspect
{
    /**
     * 新建数据切入点
     * @param point
     * @throws Exception
     */
    @Before(value = "execution(* cn.ibizlab.core.*.service.*.create(..))")
    public void BeforeCreate(JoinPoint point) throws Exception {
        fillDEFieldDefaultValue(point);
    }

    /**
     * 更新数据切入点
     * @param point
     * @throws Exception
     */
    @Before(value = "execution(* cn.ibizlab.core.*.service.*.update(..))")
    public void BeforeUpdate(JoinPoint point) throws Exception {
        fillDEFieldDefaultValue(point);
    }

    /**
     * 保存数据切入点
     * @param point
     * @throws Exception
     */
    @Before(value = "execution(* cn.ibizlab.core.*.service.*.save(..))")
    public void BeforeSave(JoinPoint point) throws Exception {
        checkAction(point);
    }

    /**
     * 判断当前是否为新建操作，若为新建，则填充属性默认值
     * @param joinPoint
     * @throws Exception
     */
    private void checkAction(JoinPoint joinPoint) throws Exception {

        Object[] args = joinPoint.getArgs();
        if (args.length > 0) {
            Object obj = args[0];
            String className=obj.getClass().getName();
            //获取当前do对象中的属性
            DEFieldCacheMap.getFieldMap(className);
            //从属性列表中过滤出主键属性
            Field keyPSDEField = this.getKeyPSDEField(className);

            if(ObjectUtils.isEmpty(keyPSDEField))
                return ;

            String filename = keyPSDEField.getName();
            PropertyDescriptor field = new PropertyDescriptor(filename, obj.getClass());
            Method fieldGetMethod = field.getReadMethod();
            //获取主键值
            Object fieldValue = fieldGetMethod.invoke(obj);
            //实体数据主键为空，则为新建操作，填充属性默认值
            if(ObjectUtils.isEmpty(fieldValue)){
                fillDEFieldDefaultValue(joinPoint);
            }
        }
    }

    /**
     * 获取主键属性
     * @param className 实体类名
     * @return 主键属性
     */
    private Field getKeyPSDEField(String className){

        Field keyField =null;
        List<Field> fields =  DEFieldCacheMap.getFields(className);

        for(Field field:fields){
            DEField deField=field.getAnnotation(DEField.class);
            if(!ObjectUtils.isEmpty(deField) && deField.isKeyField()) {
                return field;
            }
        }

        return keyField;
    }

    /**
     * 填充属性默认值
     * @param joinPoint
     * @return
     * @throws Exception
     */
    public Object fillDEFieldDefaultValue(JoinPoint joinPoint) throws Exception {

        Object[] args = joinPoint.getArgs();
        if (args.length > 0) {
            Object obj = args[0];
            String className=obj.getClass().getName();
            //获取当前po对象中的属性
            DEFieldCacheMap.getFieldMap(className);
            //从属性列表中过滤出预置属性
            Map<Field, DEField> deFields = this.SearchDEField(className);
            //填充预置属性
            fillDEField(obj, deFields,joinPoint);
            return true;
        }
        return true;
    }

    /**
     *获取含有@DEField注解的实体属性
     * @param className do对象类名
     * @return
     */
    private Map <Field, DEField> SearchDEField(String className){

        List<Field> fields =  DEFieldCacheMap.getFields(className);
        Map <Field, DEField> deFieldMap =new HashMap<>();
        for(Field field:fields){
            DEField deField=field.getAnnotation(DEField.class);
            if(!ObjectUtils.isEmpty(deField)) {
                deFieldMap.put(field,deField);
            }
        }
        return deFieldMap;
    }

    /**
     * 填充系统预置属性
     * @param et   当前实体对象
     */
    private void fillDEField(Object et, Map<Field, DEField> deFields, JoinPoint joinPoint) throws Exception {

        if(deFields.size()==0)
            return ;

        for (Map.Entry<Field, DEField> entry : deFields.entrySet()) {

            Field deField=entry.getKey();
            String fileName=deField.getName();
            //获取注解
            DEField fieldAnnotation=entry.getValue();
            //获取默认值类型
            DEFieldDefaultValueType deFieldType=fieldAnnotation.defaultValueType();
            //获取属性默认值
            String deFieldDefaultValue = fieldAnnotation.defaultValue();
            //获取预置属性类型
            DEPredefinedFieldType predefinedFieldType = fieldAnnotation.preType();

            //填充系统默认值
            if( deFieldType!= DEFieldDefaultValueType.NONE  ||  (!StringUtils.isEmpty(deFieldDefaultValue)) ){
                fillFieldDefaultValue(fileName,  deFieldType,  deFieldDefaultValue,  et , fieldAnnotation) ;
            }
            //填充系统预置属性
            if(predefinedFieldType != DEPredefinedFieldType.NONE){
                fillPreFieldValue( fileName, predefinedFieldType , et ,joinPoint ,fieldAnnotation);
            }
        }
    }

    /**
     * 填充属性默认值
     * @param fileName 实体属性名
     * @param deFieldType 默认值类型
     * @param deFieldDefaultValue 默认值
     * @param et 当前实体对象
     * @throws Exception
     */
    private void fillFieldDefaultValue(String fileName , DEFieldDefaultValueType deFieldType,String deFieldDefaultValue,Object et ,DEField fieldAnnotation) throws Exception {

        AuthenticationUser curUser=AuthenticationUser.getAuthenticationUser();
        BeanMap beanMap = BeanMap.create(et);
        Object fieldValue = beanMap.get(fileName);

        //获取当前所需填充属性的get、set方法及字段值
        PropertyDescriptor field = new PropertyDescriptor(fileName, et.getClass());

        if(org.springframework.util.StringUtils.isEmpty(fieldValue)){
            //填充直接值及其余默认值类型
            if( (deFieldType== DEFieldDefaultValueType.NONE && !StringUtils.isEmpty(deFieldDefaultValue))   || (deFieldType != DEFieldDefaultValueType.NONE) ){
                switch(deFieldType){
                    case SESSION:
                        String sessionField=fieldAnnotation.defaultValue();
                        if(!StringUtils.isEmpty(sessionField)){
                            Object sessionFieldValue = curUser.getSessionParams().get(sessionField);
                            if(!ObjectUtils.isEmpty(sessionFieldValue)){
                                beanMap.put(fileName,sessionFieldValue);
                            }
                        }
                        break;
                    case APPLICATION:
                        //暂未实现
                        break;
                    case UNIQUEID:
                        String uuid=(new AlternativeJdkIdGenerator()).generateId().toString();
                        Object objUuId=fieldValueConvert(uuid,field);
                        beanMap.put(fileName,objUuId);
                        break;
                    case CONTEXT:
                        //暂未实现
                        break;
                    case PARAM:
                        String paramField=fieldAnnotation.defaultValue();
                        if(!StringUtils.isEmpty(paramField)){
                            Object paramFieldValue=beanMap.get(paramField);
                            if(!ObjectUtils.isEmpty(paramFieldValue)){
                                beanMap.put(fileName,paramFieldValue);
                            }
                        }
                        break;
                    case OPERATOR:
                        beanMap.put(fileName,curUser.getUserid());
                        break;
                    case OPERATORNAME:
                        beanMap.put(fileName,curUser.getPersonname());
                        break;
                    case CURTIME:
                        beanMap.put(fileName,new Timestamp(new Date().getTime()));
                        break;
                    case APPDATA:
                        //暂未实现
                        break;
                    case NONE:
                        Object deFieldDefaultValueObj=fieldValueConvert(deFieldDefaultValue,field);
                        beanMap.put(fileName,deFieldDefaultValueObj);
                        break;
                }
            }
        }
    }

    /**
     * 填充系统预置属性
     * @param fileName 实体属性名
     * @param preFieldType 预置类型
     * @param et 当前实体对象
     * @param joinPoint 切点
     * @param fieldAnnotation 属性注解
     * @throws Exception
     */
    private void fillPreFieldValue(String fileName ,  DEPredefinedFieldType preFieldType ,Object et , JoinPoint joinPoint ,DEField fieldAnnotation) throws Exception {

        AuthenticationUser curUser=AuthenticationUser.getAuthenticationUser();
        BeanMap beanMap = BeanMap.create(et);

        //当前操作行为
        String actionName=joinPoint.getSignature().getName();
        Object fieldValue = beanMap.get(fileName);

        //获取当前所需填充属性的get、set方法及字段值
        PropertyDescriptor field = new PropertyDescriptor(fileName, et.getClass());

        //为预置属性进行赋值
        if( (actionName.equalsIgnoreCase("create") && org.springframework.util.StringUtils.isEmpty(fieldValue) )||
                preFieldType== DEPredefinedFieldType.UPDATEDATE|| preFieldType== DEPredefinedFieldType.UPDATEMAN||
                preFieldType== DEPredefinedFieldType.UPDATEMANNAME){

                switch(preFieldType){//根据注解给预置属性填充值
                    case CREATEMAN:
                        beanMap.put(fileName,curUser.getUserid());
                        break;
                    case CREATEMANNAME:
                        beanMap.put(fileName,curUser.getPersonname());
                        break;
                    case UPDATEMAN:
                        beanMap.put(fileName,curUser.getUserid());
                        break;
                    case UPDATEMANNAME:
                        beanMap.put(fileName,curUser.getPersonname());
                        break;
                    case CREATEDATE:
                        beanMap.put(fileName,new Timestamp(new Date().getTime()));
                        break;
                    case UPDATEDATE:
                        beanMap.put(fileName,new Timestamp(new Date().getTime()));
                        break;
                    case ORGID:
                        beanMap.put(fileName,curUser.getOrgid());
                        break;
                    case ORGNAME:
                        beanMap.put(fileName,curUser.getOrgname());
                        break;
                    case ORGSECTORID:
                        beanMap.put(fileName,curUser.getMdeptid());
                        break;
                    case ORGSECTORNAME:
                        beanMap.put(fileName,curUser.getMdeptname());
                        break;
                    case LOGICVALID:
                        String logicValue=fieldAnnotation.logicval();
                        Object objLogicValue=fieldValueConvert(logicValue,field);
                        if(!StringUtils.isEmpty(objLogicValue)){
                            beanMap.put(fileName,objLogicValue);
                        }
                        break;
                }
        }
    }

    /**
    * 值类型转换
    * @param fieldValue
    * @param field
    */
    private Object fieldValueConvert(String fieldValue,PropertyDescriptor field){

        Object resultValue=fieldValue;

        String targetType=field.getPropertyType().getSimpleName();

        if(targetType.equals("Boolean")){
            resultValue=Boolean.valueOf(fieldValue);
        }
        else if(targetType.equals("Character")){
            resultValue=fieldValue.toCharArray();
        }
        else if(targetType.equals("Byte")){
            resultValue=Byte.valueOf(fieldValue);
        }
        else if(targetType.equals("Short")){
            resultValue=Short.valueOf(fieldValue);
        }
        else if(targetType.equals("Integer")){
            resultValue= Integer.valueOf(fieldValue);
        }
        else if(targetType.equals("Long")){
            resultValue=Long.valueOf(fieldValue);
        }
        else if(targetType.equals("Float")){
            resultValue= Float.valueOf(fieldValue);
        }
        else if(targetType.equals("Double")){
            resultValue= Double.valueOf(fieldValue);
        }
        return resultValue;
    }
}
