package com.artfess.assembly.feign.impl;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.client.RestTemplate;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

public class FeignClientInvocationHandler implements InvocationHandler{
	
	private final Logger log = LoggerFactory.getLogger(getClass());

	RestTemplate restTemplate;
	
	String rootUrl = "http://localhost:9155/dataShare";
	
	public void setRestTemplate(RestTemplate restTemplate) {
		this.restTemplate = restTemplate;
	}
	public void setRootUrl(String rootUrl) {
		this.rootUrl = rootUrl;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
		String pathValue = requestMapping.value()[0];
		String url = rootUrl + pathValue;
		RequestMethod methodType = requestMapping.method()[0];
		Map<String, Object> paramsMap = new HashMap<String, Object>();
		Object body = null;
		int paramIndex = 0;
		for (Parameter parameter : method.getParameters()) {
			for (Annotation annotation : parameter.getAnnotations()) {
				boolean paramEmptyError = false;
				if (annotation instanceof RequestParam) {
					String paramName = ((RequestParam)annotation).value();
					RequestParam requestParam = (RequestParam) annotation;
					if (requestParam.required() && args[paramIndex] == null) {
						paramEmptyError = true;
					}
					paramsMap.put(paramName, args[paramIndex]);
					if (url.indexOf("?") == -1) {
						url += "?" + paramName + "={" + paramName + "}";
					} else {
						url += "&" + paramName + "={" + paramName + "}";
					}
				} else if (annotation instanceof RequestBody) {
					RequestBody requestBody = (RequestBody) annotation;
					body = args[paramIndex];
					if (requestBody.required() && body == null) {
						paramEmptyError = true;
					}
				} else if (annotation instanceof PathVariable) {
					PathVariable variable = (PathVariable) annotation;
					if (variable.required() && args[paramIndex] == null) {
						paramEmptyError = true;
					}
					paramsMap.put(variable.value(), args[paramIndex]);
				} else {
					throw new RuntimeException("参数配置不正确！接口【"+proxy.getClass().getInterfaces()[0].getName()+"】中方法【"+method.getName()+"】的参数【" + parameter.getName() + "】未定义注解！");
				}
				if (paramEmptyError) {
					throw new RuntimeException("参数配置不正确！接口【"+proxy.getClass().getInterfaces()[0].getName()+"】中方法【"+method.getName()+"】的参数【" + parameter.getName() + "】不能为空！");
				}
			}
			paramIndex ++;
		}
		Type returnType = method.getGenericReturnType();
		if (returnType instanceof ParameterizedType){
			HttpMethod httpMethod = HttpMethod.GET;
			HttpEntity<?> request = new HttpEntity<Object>(null);
			if (methodType == RequestMethod.POST) {
				httpMethod = HttpMethod.POST;
				if (body != null) {
					request = new HttpEntity<Object>(body, getPostHeaders(requestMapping));
				}
				else {
					log.warn(String.format("请求地址：%s 的类型为POST，但是没有获取到Body数据", url));
				}
			}
			return restTemplate.exchange(url, httpMethod, request, getReference(returnType), paramsMap).getBody();
		} else {
			if (methodType == RequestMethod.GET) {
				return restTemplate.getForObject(url, method.getReturnType(), paramsMap);
			} else if (methodType == RequestMethod.POST) {
				if (body != null) {
					HttpEntity<Object> request = new HttpEntity<Object>(body, getPostHeaders(requestMapping));
					return restTemplate.postForObject(url, request, method.getReturnType(), paramsMap);
				} else {
					return restTemplate.postForObject(url, body, method.getReturnType(), paramsMap);
				}
			} else if (methodType == RequestMethod.PUT) {
				restTemplate.put(url, body, paramsMap);
			} else if (methodType == RequestMethod.DELETE) {
				restTemplate.delete(url, paramsMap);
			}
		}
		return null;
	}

	private HttpHeaders getPostHeaders(RequestMapping requestMapping) {
		HttpHeaders headers = new HttpHeaders();
		if (requestMapping.consumes().length > 0) {
			headers.set(HttpHeaders.CONTENT_TYPE, requestMapping.consumes()[0]);
		} else {
			headers.setContentType(MediaType.APPLICATION_JSON);
		}
		return headers;
	}

	private <T> ParameterizedTypeReference<T> getReference(Type returnType) {
		return ParameterizedTypeReference.forType(returnType);
	}

}
