package com.wxz.admin.asyc.spring;

import com.google.common.base.Preconditions;
import com.wxz.admin.asyc.annotation.InterfaceTarget;
import com.wxz.admin.asyc.domain.RequestInfo;
import com.wxz.common.open.response.OpenErrorCode;
import com.wxz.common.open.exception.OpenSecurityException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cglib.reflect.FastClass;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

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

    private static final SpringContainer SPRING_CONTAINER = new SpringContainer();

    public static SpringContainer getSpringContainer() {
        return SPRING_CONTAINER;
    }

    private AbstractApplicationContext abstractApplicationContext;

    private Map<String, InterfaceHandler> interfaceHandlerHashMap = new HashMap<>();

    private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

    private SpringContainer() {
    }

    public synchronized void load(AbstractApplicationContext abstractApplicationContext) {
        this.abstractApplicationContext = Objects.requireNonNull(abstractApplicationContext, "unexpect error");
        Map<String, Action> actionMap = abstractApplicationContext.getBeansOfType(Action.class);
        for (Map.Entry<String, Action> entry : actionMap.entrySet()) {
            Action action = entry.getValue();
            parse(action);
        }
    }

    private void parse(Action action) {
        Class clazz = action.getClass();
        FastClass fastClass = FastClass.create(clazz);
        for (Method method : clazz.getDeclaredMethods()) {
            InterfaceTarget interfaceTarget = method.getAnnotation(InterfaceTarget.class);
            if (interfaceTarget == null) {
                continue;
            }
            String interfaceKey = interfaceTarget.path() + "_" + interfaceTarget.version();
            InterfaceHandler interfaceHandler = new InterfaceHandler(interfaceKey, action, method, fastClass.getMethod(method));

            // 返回表示按照声明顺序对此 Method对象所表示方法的形参进行注释的那个数组的数组。
            String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
            Class<?>[] parameterTypes = method.getParameterTypes();
            Annotation[][] parameterAnnotations = method.getParameterAnnotations();
            Preconditions.checkArgument(parameterNames.length == parameterTypes.length);
            Preconditions.checkArgument(parameterNames.length == parameterAnnotations.length);
            int parameterCount = parameterTypes.length;
            for (int i = 0; i < parameterCount; i++) {
                Annotation[] annotations = parameterAnnotations[i];
                Class<?> parameterClazz = parameterTypes[i];
                String parameterName = parameterNames[i];
                resolveParameter(parameterName, parameterClazz, annotations, interfaceHandler);
            }

            LOGGER.info("path {} ,handle {}", interfaceKey, interfaceHandler);
            interfaceHandlerHashMap.put(interfaceKey, interfaceHandler);
        }
    }

    private void resolveParameter(String parameterName, Class<?> parameterClazz, Annotation[] annotations, InterfaceHandler interfaceHandler) {
        DataConverter<?> dataConverter = DataConverters.getDataConverter(parameterClazz);

        for (Annotation annotation : annotations) {
            if (annotation.annotationType().equals(RequestParam.class)) {
                RequestParam requestParam = (RequestParam) annotation;
                RequestParamResolveBean requestParamResolveBean = new RequestParamResolveBean(parameterClazz, requestParam.value(), requestParam.required(), requestParam.defaultValue(), dataConverter);
                interfaceHandler.put(requestParam.value(), requestParamResolveBean);
                return;
            }
        }
        RequestParamResolveBean requestParamResolveBean = new RequestParamResolveBean(parameterClazz, parameterName, false, dataConverter);
        interfaceHandler.put(parameterName, requestParamResolveBean);
    }

    public Object process(RequestInfo requestInfo, HttpServletRequest req, HttpServletResponse resp) throws OpenSecurityException {
        InterfaceHandler interfaceHandler = interfaceHandlerHashMap.get(requestInfo.getInterfaceInfo().toUniqueKey());
        if (interfaceHandler == null) {
            throw new OpenSecurityException(OpenErrorCode.SYSTEM_ERROR);
        }
        Object[] args = interfaceHandler.generateArgs(requestInfo, req, resp);
        try {
            return interfaceHandler.invoke(args);
        } catch (Exception e) {
            throw new OpenSecurityException(OpenErrorCode.SYSTEM_ERROR);
        }

    }


}