/**
 * $Id$
 * Copyright(C) 2015-2020 kowlone - internet center, All Rights Reserved.
 */
package com.liquidnet.commons.lang.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicNameValuePair;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.*;
import java.util.function.Function;

/**
 * @author <a href="mailto:zmli@corp.netease.com">kowlone</a>
 * @version 1.0 2015年04月01日 16:10:00
 */
public class HttpRequest {

	private enum BodyEntityType {
		URL_PARAM {
			@Override HttpEntity createPostEntity(HttpRequest request) throws UnsupportedEncodingException {
				List<NameValuePair> nvps = new ArrayList<>();

				for (Map.Entry<String, Object> entry : request.requestParam.entrySet()) {
					nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));
				}

				return new UrlEncodedFormEntity(nvps,request.requestCharset);

			}
		},
		JSON_STRING {
			@Override HttpEntity createPostEntity(HttpRequest request) {
				String jsonBody = request.paramWrapper == null ?
						JSON.toJSONString(request.requestParam) :
						request.paramWrapper.wrapper(request.requestParam);
				return new StringEntity(jsonBody, request.requestCharset);
			}
		},;

		abstract HttpEntity createPostEntity(HttpRequest request) throws UnsupportedEncodingException;


	}

	public interface ParamWrapper{
		String wrapper(Map<String, Object> requestParam);
	}

	private String url;
	private Map<String, Object> requestParam = new LinkedHashMap<>();
	private Map<String, String> headerParam = new LinkedHashMap<>();

	/** 明确要发送的消息体当指定了该参数时,不在经过BodyEntityType处理 */
	private HttpEntity explicitEntity = null;
	private BodyEntityType entityType;

	private Charset requestCharset = Consts.UTF_8;
	private Charset responseCharset = Consts.UTF_8;
	private String contentType = null;
	private ParamWrapper paramWrapper = null;


	private HttpRequest(String url) {
		this(url, BodyEntityType.URL_PARAM);
	}

	private HttpRequest(String url, BodyEntityType entityType) {
		this.url = url;
		this.entityType = entityType;
	}

	/**
	 * 新请求
	 *
	 * @param url
	 * @return
	 */
	public static HttpRequest newRequest(String url) {
		return new HttpRequest(url);
	}

	/**
	 * 发起http body为json字符串内容的请求
	 *
	 * @param url
	 * @return
	 */
	public static HttpRequest newJSONBodyRequest(String url) {
		return new HttpRequest(url, BodyEntityType.JSON_STRING);
	}

	public HttpRequest withParamWrapper(ParamWrapper paramWrapper) {
		this.paramWrapper = paramWrapper;
		return this;
	}

	/**
	 * 添加请求参数
	 *
	 * @param key
	 * @param value
	 * @return
	 */
	public HttpRequest addParam(String key, Object value) {
		this.requestParam.put(key, value);
		return this;
	}

	public HttpRequest addParam(Map<String, Object> params) {
		this.requestParam.putAll(params);
		return this;
	}

	public HttpRequest contentType(String contentType) {
		this.contentType = contentType;
		return this;
	}

	/**
	 * 访问参数key和参数值,并做相应处理后,返回字符串
	 * @param function
	 *
	 * @return
	 */
	public String visitParam(Function<Set<Map.Entry<String,Object>>,String> function) {
		return function.apply(requestParam.entrySet());
	}


	public HttpRequest setExplicitEntity(HttpEntity explicitEntity){
		this.explicitEntity = explicitEntity;
		return this;
	}

	/**
	 * 添加请求头
	 *
	 * @param key
	 * @param value
	 * @return
	 */
	public HttpRequest addHeader(String key, String value) {
		this.headerParam.put(key, value);
		return this;
	}

	/**
	 * 指定请求字符集
	 * @param charset
	 *
	 * @return
	 */
	public HttpRequest requestCharset(String charset) {
		this.requestCharset = Charset.forName(charset);
		return this;
	}

	/**
	 * 指定响应字符集
	 * @param charset
	 *
	 * @return
	 */
	public HttpRequest responseCharset(String charset) {
		this.responseCharset = Charset.forName(charset);
		return this;
	}

	/**
	 * 获取body
	 *
	 * @return
	 */
	public String getRequestBodyString() {
		StringBuilder sb = new StringBuilder();
		for (Map.Entry<String, Object> entry : requestParam.entrySet()) {
			sb.append("&").append(entry.getKey()).append("=").append(entry.getValue());
		}
		return sb.deleteCharAt(0).toString();
	}

	/**
	 * 发送post请求,返回json对象
	 */
	public JSONObject postAsJson() {
		return JSON.parseObject(postAsString());
	}

	/**
	 * 发送post请求,返回原始字符串
	 *
	 * @return
	 * @throws Exception
	 */
	public String postAsString() {

		try {
			HttpPost httpPost = new HttpPost(url);
			httpPost.setEntity(explicitEntity!=null?explicitEntity:entityType.createPostEntity(this));
//			httpPost.addHeader("Content-Type","application/json");
			if (this.contentType != null) {
				httpPost.addHeader("Content-type",contentType);
			}
			for (Map.Entry<String, String> entry : headerParam.entrySet()) {
				httpPost.setHeader(entry.getKey(), entry.getValue());
			}

			return HttpUtil.post(httpPost,this.responseCharset);
		} catch (Exception e) {
			throw new RuntimeException("post http request error :" + url, e);
		}

	}


	/**
	 * 发送post请求,返回原始字符串
	 *
	 * @return
	 * @throws Exception
	 */
	public String postAsString(int timeout) {
		try {
			HttpPost httpPost = new HttpPost(url);
			httpPost.setEntity(explicitEntity!=null?explicitEntity:entityType.createPostEntity(this));
//			httpPost.addHeader("Content-Type","application/json");
			if (this.contentType != null) {
				httpPost.addHeader("Content-type",contentType);
			}
			for (Map.Entry<String, String> entry : headerParam.entrySet()) {
				httpPost.setHeader(entry.getKey(), entry.getValue());
			}

			RequestConfig requestConfig = RequestConfig.custom()
					.setConnectionRequestTimeout(5000) //连接池中取出连接的超时时间
					.setSocketTimeout(timeout) //服务器获取响应数据需要等待的时间
					.setConnectTimeout(10000) //创建与服务器的socket连接时间
					.setExpectContinueEnabled(false)
					.build();

			httpPost.setConfig(requestConfig);
			return HttpUtil.post(httpPost,this.responseCharset);
		} catch (Exception e) {
			throw new RuntimeException("post http request error :" + url, e);
		}
	}

	/**
	 * 发送返回二进制数组的请求
	 * @return
	 */
	public byte[] postAsBinary() {

		try {
			HttpPost httpPost = new HttpPost(url);
			httpPost.setEntity(explicitEntity!=null?explicitEntity:entityType.createPostEntity(this));

			for (Map.Entry<String, String> entry : headerParam.entrySet()) {
				httpPost.setHeader(entry.getKey(), entry.getValue());
			}

			return	HttpUtil.binaryPost(httpPost);
		} catch (Exception e) {
			throw new RuntimeException("post http request error :" + url, e);
		}

	}

	/**
	 * 发送get请求
	 *
	 * @throws Exception
	 */
	public String getAsString() {

		try {
			StringBuilder urlBuilder = new StringBuilder(url);

			if (!requestParam.isEmpty()) {

				List<NameValuePair> uriParam = new ArrayList<>();
				for (Map.Entry<String, Object> entry : requestParam.entrySet()) {
					if(entry.getValue()!=null) {
                        uriParam.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));
                    }
				}
				urlBuilder.append("?").append(URLEncodedUtils.format(uriParam, Consts.UTF_8));

			}

			URI uri = new URI(urlBuilder.toString());
			HttpGet httpGet = new HttpGet(uri);

			for (Map.Entry<String, String> entry : headerParam.entrySet()) {
				httpGet.setHeader(entry.getKey(), entry.getValue());
			}

//			byte[] bytes = HttpUtil.binaryGet(httpGet);
//			return new String(bytes, this.responseCharset);
			return HttpUtil.get(httpGet, this.responseCharset);
			//			return HttpUtil.get(httpGet);
		} catch (Exception e) {
			throw new RuntimeException("post http request error :" + url, e);
		}

	}

	/**
	 * 发送返回JSON对象的请求
	 * @return
	 */
	public JSONObject getAsJson(){
		return JSON.parseObject(getAsString(), Feature.InternFieldNames);
	}

	public static void main(String[] args) {
		try {
//			System.out.println(HttpRequest.newRequest("http://www.huihui.cn/").postAsString());
//			HttpRequest  ht=HttpRequest.newJSONBodyRequest("https://api.intellicredit.cn/id_check");
//			ht.addParam("access_token", "_RH4Us1yseJfAY3_nsku");
//			JSONArray ja=new JSONArray();
//			JSONObject jo =new JSONObject();
//			jo.put("name", "");
//			jo.put("id_number", "");
//			jo.put("sub_client", "");
//			jo.put("type", "");
//			jo.put("location", "");
//			ja.add(jo);
//			ht.addParam("query", ja);
//			System.out.println(JSON.toJSONString(ht.requestParam));
//			System.out.println(ht.postAsString());
//			Map<String, Object> param = new HashMap<>();
//			param.put("id", 123212);
//			param.put("service", "hehe");
//
//			Map<String, Object> shit = new HashMap<>();
//			shit.put("code", 123);
//			param.put("data", shit);
//			System.out.println(JSON.toJSONString(param));

			HttpRequest ht= HttpRequest.newRequest("http://172.24.132.45:20001/kowlone-provider-user/simple/1");
			System.out.println(ht.postAsString(35000));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public static String getClientIp(HttpServletRequest request) {
		String ip = request.getHeader("x-forwarded-for");
		if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("Proxy-Client-IP");
		}
		if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}
		if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getRemoteAddr();
		}
		return ip;
	}

}
