package com.redant.core.controller;


import com.redant.core.common.exception.InvocationException;
import com.redant.core.common.exception.ValidationException;
import com.redant.core.common.util.GenericsUtil;
import com.redant.core.common.util.HttpRequestUtil;
import com.redant.core.context.RedantContext;
import com.redant.core.converter.PrimitiveConverter;
import com.redant.core.converter.PrimitiveTypeUtil;
import com.redant.core.controller.annotation.Param;
import net.sf.cglib.reflect.FastClass;
import net.sf.cglib.reflect.FastMethod;
import org.apache.commons.beanutils.BeanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;


/**
 * 封装了ControllerProxy的调用过程
 * @author houyi.wh
 * @date 2017-10-20
 */
public class ProxyInvocation {

	private static Invocation invocation = new Invocation();

	public static Object invoke(ControllerProxy proxy) throws Exception{
		Object controller = proxy.getController();
		Method method = proxy.getMethod();
		String methodName = proxy.getMethodName();
		return invocation.invoke(controller,method,methodName);
	}


	private static class Invocation {

		private static Logger logger = LoggerFactory.getLogger(Invocation.class);

		private final int KEY_VALUE_SIZE = 2;

		public Invocation(){

		}

		/**
		 * 获得方法调用的参数
		 * @param method 方法
		 * @param parameterTypes 方法参数类型
		 * @return 参数
		 * @throws Exception 参数异常
		 */
		private Object[] getParameters(Method method,Class<?>[] parameterTypes) throws Exception {
			//用于存放调用参数的对象数组
			Object[] parameters = new Object[parameterTypes.length];

			//获得所调用方法的参数的Annotation数组
			Annotation[][] annotationArray = method.getParameterAnnotations();

			//获取参数列表
			Map<String, List<String>> paramMap = HttpRequestUtil.getParameterMap(RedantContext.currentContext().getRequest());

			//构造调用所需要的参数数组
			for (int i = 0; i < parameterTypes.length; i++) {
				Object parameter;
				Class<?> type = parameterTypes[i];
				Annotation[] annotation = annotationArray[i];
				// 如果该参数没有 Param 注解
				if (annotation == null || annotation.length == 0) {
					// 如果该参数类型是基础类型,则需要加 Param 注解
					if(PrimitiveTypeUtil.isPriType(type)){
						logger.warn("Must specify a @Param annotation for primitive type parameter in method={}", method.getName());
						continue;
					}
					// 封装对象类型的parameter
					parameter = type.newInstance();
					BeanUtils.populate(parameter,paramMap);
					parameters[i] = parameter;
				}else{
					Param param = (Param) annotation[0];
					try{
						// 生成当前的调用参数
						parameter = parseParameter(paramMap, type, param, method, i);
						if(param.notNull()){
							GenericsUtil.checkNull(param.key(), parameter);
						}
						if(param.notBlank()){
							GenericsUtil.checkBlank(param.key(), parameter);
						}
						parameters[i] = parameter;
					}catch(Exception e){
					    logger.error("param ["+param.key()+"] is invalid,cause:"+e.getMessage());
						throw new IllegalArgumentException("参数 "+param.key()+" 不合法:"+e.getMessage());
					}
				}
			}
			return parameters;
		}


		/**
		 * GET 参数解析
		 */
		@SuppressWarnings({ "rawtypes", "unchecked" })
		private Object parseParameter(Map<String, List<String>> paramMap, Class<?> type, Param param, Method method, int index) throws InstantiationException, IllegalAccessException{
			Object value = null;
			String key = param.key();
			String defaultValue= param.defaultValue();
			if(key.length() > 0){
				// 如果参数是map类型
				if(Map.class.isAssignableFrom(type)){
					if(index > 0){
						throw new ValidationException("Must have only one Map type parameter");
					}

					List<Class> types = GenericsUtil.getMethodGenericParameterTypes(method, index);
					if(types.size() == KEY_VALUE_SIZE && (types.get(0) != String.class || types.get(1) != String.class)){
						throw new ValidationException("Map type parameter must both be String, Occurring Point: " + method.toGenericString());
					}

					Map<String, String> valueMap = new HashMap<String, String>(paramMap.size());
					for(Map.Entry<String, List<String>> entry : paramMap.entrySet()){
						List<String> valueList = entry.getValue();
						valueMap.put(entry.getKey(), valueList.get(0));
					}
					value = valueMap;
				}else{
					List<String> params = paramMap.get(key);
					if(params != null){
						// 基础类型
						if(PrimitiveTypeUtil.isPriType(type)){
							value = PrimitiveConverter.getInstance().convert(params.get(0), type);

						// 数组
						}else if(type.isArray()){
							String[] strArray = params.toArray(new String[]{});
							value = PrimitiveConverter.getInstance().convert(strArray, type);

						// List
						}else if(List.class.isAssignableFrom(type)){
							List<Object> list;
							List<Class> types = GenericsUtil.getMethodGenericParameterTypes(method, index);
							Class<?> listType = types.size() == 1?types.get(0):String.class;
							if(List.class == type){
								list = new ArrayList<Object>();
							}else{
								list = (List<Object>) type.newInstance();
							}
							for (String param1 : params) {
								if (param1.length() > 0) {
									list.add(PrimitiveConverter.getInstance().convert(param1, listType));
								}
							}
							value = list;
						}
					}else{
						if(PrimitiveTypeUtil.isPriType(type)){
							value = PrimitiveConverter.getInstance().convert(defaultValue, type);
						}
					}
				}
			}
			return value;
		}


		/**
		 * 返回调用异常
		 * @param msg 消息
		 * @param cause 异常
		 * @return 调用异常
		 */
		private InvocationException getInvokeException(String msg, Throwable cause){
			return new InvocationException(msg,cause);
		}


		//==================================


		/**
		 * 执行方法的调用
		 * @param controller 控制器
		 * @param method 方法
		 * @param methodName 方法名
		 * @return 渲染结果
		 * @throws Exception 异常
		 */
        Object invoke(Object controller, Method method, String methodName) throws Exception {
			if (method == null) {
				throw new NoSuchMethodException("Can not find specified method: " + methodName);
			}

			Class<?> clazz = controller.getClass();
			Class<?>[] parameterTypes = method.getParameterTypes();
			Object[] parameters = null;
			Object result;
			try {
				parameters = getParameters(method,parameterTypes);
				// 使用 CGLib 执行反射调用
				FastClass fastClass = FastClass.create(clazz);
				FastMethod fastMethod = fastClass.getMethod(methodName, parameterTypes);
				// 调用,并得到调用结果
				result = fastMethod.invoke(controller, parameters);

			} catch(InvocationTargetException e){
				String msg = "调用出错,请求类["+controller.getClass().getName()+"],方法名[" + method.getName() + "],参数[" + Arrays.toString(parameters)+"]";
				throw getInvokeException(msg, e);
			}
			return result;
		}

	}



}