package com.wxz.provider.validate;

import com.wxz.common.open.exception.BusinessRuntimeException;
import com.wxz.common.open.response.ErrorCode;
import com.wxz.provider.validate.annotation.ValidateField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cglib.reflect.FastClass;
import org.springframework.cglib.reflect.FastMethod;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * @author [email protected]
 * @type class
 * @date 2017/9/28 -15:36
 */
public class BeanAbstractFieldValidator extends AbstractFieldValidator {
    private static final Logger LOGGER = LoggerFactory.getLogger(BeanAbstractFieldValidator.class);

    private Map<String, List<AbstractConditionValidator>> validatorsMap;
    private Class<?> clazz;
    private Map<String, FieldGetter> fields;
    private FastClass fastClass;

    public BeanAbstractFieldValidator(Class<?> clazz, ValidateField... validateFields) {
        super(validateFields);
        this.clazz = clazz;
        this.fastClass = FastClass.create(clazz);
        try {
            init();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void init() throws Exception {
        validatorsMap = new HashMap<>(1<<4);
        for (ValidateField validateField : validateFields) {
            String key = validateField.fieldName();
            if (validatorsMap.containsKey(key)) {
                List<AbstractConditionValidator> abstractConditionValidators = AbstractConditionValidator.valueOf(validateField);
                for (AbstractConditionValidator abstractConditionValidator : abstractConditionValidators) {
                    validatorsMap.get(key).add(abstractConditionValidator);
                }
            } else {
                validatorsMap.put(key, AbstractConditionValidator.valueOf(validateField));
            }
        }

        BeanInfo beanInfo = Introspector.getBeanInfo(this.clazz);
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();

        Field[] declaredFields = clazz.getDeclaredFields();
        fields = new HashMap<>(1<<4);
        for (Field field : declaredFields) {
            if (Modifier.isStatic(field.getModifiers())) {
                continue;
            }
            field.setAccessible(true);
            PropertyDescriptor getter = null;
            for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
                if (propertyDescriptor.getName().equals(field.getName())) {
                    getter = propertyDescriptor;
                    break;
                }
            }
            if (getter == null) {
                LOGGER.warn("can find getter method of {}", field);
                fields.put(field.getName(), new FieldGetter(field, null, null));
            } else {
                fields.put(field.getName(), new FieldGetter(field, getter.getReadMethod(), this.fastClass.getMethod(getter.getReadMethod())));
            }
        }
    }

    @Override
    public boolean accept(Object object, FailedReason failedReason) {
        if (null == object) {
            return false;
        }
        for (Entry<String, List<AbstractConditionValidator>> validators : validatorsMap.entrySet()) {
            List<AbstractConditionValidator> cs = validators.getValue();
            String fieldName = validators.getKey();
            Object fieldValue = fields.get(fieldName).get(object);
            for (AbstractConditionValidator abstractConditionValidator : cs) {
                if (!abstractConditionValidator.accept(fieldValue)) {
                    failedReason.setReason(abstractConditionValidator.refuseReason()).setFieldName(fieldName);
                    return false;
                }
            }
        }
        return true;
    }

    @Override
    public String toString() {
        return "BeanAbstractFieldValidator [validatorsMap=" + validatorsMap + ", clazz=" + clazz + ", fields=" + fields + "]";
    }

    private static class FieldGetter {
        private Field field;
        private Method getMethod;
        private FastMethod fastGetMethod;

        FieldGetter(Field field, Method getMethod, FastMethod fastGetMethod) {
            this.field = field;
            this.getMethod = getMethod;
            this.fastGetMethod = fastGetMethod;
        }

        /**
         * wxz:为了性能,优先采用fastmethod去获取值
         */
        Object get(Object object) {
            try {
                return this.fastGetMethod.invoke(object, null);
            } catch (Exception e) {
                try {
                    return this.getMethod.invoke(object, null);
                } catch (Exception e1) {
                    try {
                        return this.field.get(object);
                    } catch (Exception e2) {
                    }
                }
            }
            LOGGER.error("获取{}的值异常,object {}", this.field.getName(), object);
            throw new BusinessRuntimeException(ErrorCode.SERVER_ERROR);
        }
    }

}