package com.usky.common.core.util;

import com.fasterxml.jackson.core.type.TypeReference;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author chenpeng
 * @date 2019-03-21
 * @time 10:42
 */
public final class HttpUtils {

    private static final Logger LOGGER = LoggerFactory.getLogger(HttpUtils.class);

    private static final JsonMapper JSON_MAPPER = new JsonMapper();
    private static final String DEFAULT_CLIENT = "default";
    private static final MediaType MEDIA_TYPE_JSON = MediaType.parse("application/json; charset=utf-8");
    private static final Map<String, OkHttpClient> OK_HTTP_CLIENT_MAP = new HashMap<>(16);

    static {
        OK_HTTP_CLIENT_MAP.put(DEFAULT_CLIENT, produceClient(2L, 5L, 5L, 10));
    }

    private HttpUtils() {
    }

    /**
     * get请求
     *
     * @param url 请求地址
     * @return 返回结果
     * @throws IOException 错误
     */
    public static String get(String url,Map<String, String> headerMap) throws IOException {
        OkHttpClient okHttpClient = getClient(url);
        Request request = buildRequest(url,headerMap);
        Response response = okHttpClient.newCall(request).execute();
        return responseToString(response);
    }

    /**
     * get异步请求
     *
     * @param url      请求地址
     * @param callback 回调
     */
    public static void get(String url, Callback callback,Map<String, String> headerMap) {
        OkHttpClient okHttpClient = getClient(url);
        Request request = buildRequest(url,headerMap);
        if (callback == null) {
            callback = getDefaultCallback();
        }
        okHttpClient.newCall(request).enqueue(callback);
    }

    /**
     * post 表单
     *
     * @param url    请求地址
     * @param reqObj 请求参数
     * @return 返回结果
     * @throws IOException 异常
     */
    public static String postForm(String url, Object reqObj,Map<String, String> headerMap) throws IOException {
        OkHttpClient okHttpClient = getClient(url);
        return postForm(okHttpClient,url, reqObj,headerMap);
    }

    public static String postForm(OkHttpClient okHttpClient, String url, Object reqObj,Map<String, String> headerMap) throws IOException {
        if (okHttpClient == null) {
            okHttpClient = getClient(url);
        }
        RequestBody formBody = buildFormBody(reqObj);
        Request request = buildRequest(url, formBody,headerMap);
        Response response = okHttpClient.newCall(request).execute();
        return responseToString(response);
    }

    public static String responseToString(Response response) throws IOException {
        if (response.isSuccessful()) {
            String result = null;
            if (response.body() != null) {
                result = response.body().string();
                response.body().close();
            }
            response.close();
            return result;
        } else {
            throw new IOException("Unexpected code " + response);
        }
    }

    /**
     * 异步 post 表单 提交
     *
     * @param url      请求地址
     * @param reqObj   请求参数
     * @param callback 回调处理
     */
    public static void postForm(String url, Object reqObj, Callback callback,Map<String, String> headerMap) {
        OkHttpClient okHttpClient = getClient(url);
        FormBody formBody = buildFormBody(reqObj);
        Request request = buildRequest(url, formBody,headerMap);
        if (callback == null) {
            callback = getDefaultCallback();
        }
        okHttpClient.newCall(request).enqueue(callback);

    }

    /**
     * post json
     *
     * @param url    请求地址
     * @param reqObj 请求参数
     * @return 返回字符串
     * @throws IOException 异常
     */
    public static String postJson(String url, Object reqObj,Map<String, String> headerMap) throws IOException {
        OkHttpClient okHttpClient = getClient(url);
        return postJson(okHttpClient,url, reqObj,headerMap);
    }

    public static String postJson(OkHttpClient okHttpClient, String url, Object reqObj,Map<String, String> headerMap) throws IOException {
        if (okHttpClient == null) {
            okHttpClient = getClient(url);
        }
        RequestBody body = RequestBody.create(MEDIA_TYPE_JSON, JSON_MAPPER.toJson(reqObj));
        Response response = okHttpClient.newCall(buildRequest(url, body,headerMap)).execute();
        if (response.isSuccessful()) {
            return Objects.requireNonNull(response.body()).string();
        } else {
            throw new IOException("Unexpected code " + response);
        }
    }

    /**
     * 异步提交
     *
     * @param url      请求地址
     * @param reqObj   请求参数
     * @param callback 回调处理
     */
    public static void postJson(String url, Object reqObj, Callback callback,Map<String, String> headerMap) {
        OkHttpClient okHttpClient = getClient(url);
        RequestBody body = RequestBody.create(MEDIA_TYPE_JSON, JSON_MAPPER.toJson(reqObj));
        if (callback == null) {
            callback = getDefaultCallback();
        }
        okHttpClient.newCall(buildRequest(url, body,headerMap)).enqueue(callback);
    }

    public static Request buildRequest(String url, RequestBody body,Map<String, String> headerMap) {
        Request.Builder builder = new Request.Builder().url(url).post(body);
        addHeaders(headerMap,builder);
        return builder.build();
    }

    public static Request buildRequest(String url,Map<String, String> headerMap) {
        Request.Builder builder = new Request.Builder().url(url);
        addHeaders(headerMap,builder);
        return builder.build();
    }

    public static FormBody buildFormBody(Object reqObj) {
        FormBody.Builder builder = new FormBody.Builder();

        Map<String, Object> mapObj = beanToMap(reqObj);
        for (Map.Entry<String, Object> entry : mapObj.entrySet()) {
            if (entry.getValue() != null) {
                builder.add(entry.getKey(), entry.getValue().toString());
            }
        }

        return builder.build();
    }


    private static OkHttpClient getClient(String url) {

        String hostName = getHost(url);
        if (OK_HTTP_CLIENT_MAP.containsKey(hostName)) {
            return OK_HTTP_CLIENT_MAP.get(hostName);
        } else {
            return OK_HTTP_CLIENT_MAP.get(DEFAULT_CLIENT);
        }
    }

    /**
     * 获取默认callback处理
     *
     * @return
     */
    private static Callback getDefaultCallback() {
        return new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    response.close();
                } else {
                    LOGGER.error("请求：{} 失败", call.request().url());
                }
            }
        };
    }

    /**
     * 获取 url 的 host
     *
     * @param url 请求地址
     * @return host
     */
    public static String getHost(String url) {
        URI uri = URI.create(url);
        String hostName = uri.getHost();
        if (StringUtils.isEmpty(hostName)) {
            hostName = DEFAULT_CLIENT;
        }
        return hostName;
    }

    /**
     * 构造 httpClient
     *
     * @param connectTime        连接超时（秒）
     * @param writeTime          写入超时（秒）
     * @param readTime           读取超时（秒）
     * @param maxRequestsPerHost 正在执行的总任务数及相同host下正在执行的任务数小于阈值时，直接执行任务
     * @return OkHttpClient
     */
    public static OkHttpClient produceClient(Long connectTime, Long writeTime, Long readTime,
            Integer maxRequestsPerHost) {
        return produceClient(connectTime, writeTime, readTime, maxRequestsPerHost, null);
    }

    public static OkHttpClient produceClient(Long connectTime, Long writeTime, Long readTime,
            Integer maxRequestsPerHost, List<Interceptor> interceptors) {

        OkHttpClient.Builder builder = builder(connectTime, writeTime, readTime);
        if (interceptors != null && interceptors.size() > 0) {
            for (Interceptor interceptor : interceptors) {
                builder.addInterceptor(interceptor);
            }
        }
        OkHttpClient client = builder.build();
        if (maxRequestsPerHost != null && maxRequestsPerHost > 0) {
            client.dispatcher().setMaxRequestsPerHost(maxRequestsPerHost);
        }
        return client;
    }

    /**
     * 预设 httpClientMap 超时时间
     *
     * @param url
     * @param connectTime
     * @param writeTime
     * @param readTime
     * @param maxRequestsPerHost
     */
    public static void putHttpClient(String url, Long connectTime, Long writeTime, Long readTime,
            Integer maxRequestsPerHost) {
        String hostName = getHost(url);
        synchronized (hostName.intern()) {
            OK_HTTP_CLIENT_MAP.put(hostName, produceClient(connectTime, writeTime, readTime, maxRequestsPerHost));
        }
    }

    /**
     * 设置url 对应的客户端
     *
     * @param url          url
     * @param okHttpClient ok http client
     */
    public static void putHttpClient(String url, OkHttpClient okHttpClient) {
        String hostName = getHost(url);
        synchronized (hostName.intern()) {
            OK_HTTP_CLIENT_MAP.put(hostName, okHttpClient);
        }
    }

    /**
     * 获取默认的客户端
     *
     * @return ok http client
     */
    public static OkHttpClient getDefaultClient() {
        return OK_HTTP_CLIENT_MAP.get(DEFAULT_CLIENT);
    }

    public static OkHttpClient.Builder builder(Long connectTime, Long writeTime, Long readTime) {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        //连接超时
        if (connectTime != null && connectTime.intValue() > 0) {
            builder.connectTimeout(connectTime, TimeUnit.SECONDS);
        }
        //连接超时
        if (writeTime != null && writeTime.intValue() > 0) {
            builder.writeTimeout(writeTime, TimeUnit.SECONDS);
        }
        //连接超时
        if (readTime != null && readTime.intValue() > 0) {
            builder.readTimeout(readTime, TimeUnit.SECONDS);
        }
        return builder;
    }

    public static Map<String, Object> beanToMap(Object bean) {
        return JSON_MAPPER.fromJson(JSON_MAPPER.toJson(bean), new TypeReference<Map<String, Object>>() {
        });
    }


    /**
     * 添加header信息
     *
     * @param headerMap
     * @param builder
     */
    private static void addHeaders(Map<String, String> headerMap, Request.Builder builder) {
        if (headerMap != null && !headerMap.isEmpty()) {
            headerMap.forEach(builder::addHeader);
        }
    }

}
