package com.trade.common.utils.http;

import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

import org.apache.http.Header;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.protocol.HttpContext;
import org.springframework.util.MultiValueMap;
import org.springframework.web.multipart.MultipartFile;

/**
 * Http Request 配置类
 * <p>
 * Copyright (c) 2019 - 2029 摸金校尉 All Rights Reserved.
 * <p>
 * Created by wanxiangZ 2019/8/15 10:33
 */
public class HttpConfig {

    private HttpConfig(){
    }

    /**
     * 获取实例
     *
     * @return 配置实例
     */
    public static HttpConfig custom() {
        return new HttpConfig();
    }

    /**
     * HttpClient对象
     */
    private HttpClient client;
    /**
     * Header头信息
     */
    private Header[] headers;
    /**
     * 是否返回response的headers
     */
    private boolean isReturnRespHeaders;
    /**
     * 请求方法
     */
    private HttpMethods method = HttpMethods.GET;
    /**
     * 请求方法名称
     */
    private String methodName;
    /**
     * 用于cookie操作
     */
    private HttpContext context;
    /**
     * 传递参数
     */
    // private Map<String, Object> map;
    /**
     * 以json格式作为输入参数
     */
    private String json;
    /**
     * 输入输出编码
     */
    private String encoding = Charset.defaultCharset().displayName();
    /**
     * 输入编码
     */
    private String inenc;
    /**
     * 输出编码
     */
    private String outenc;
    /**
     * 设置RequestConfig
     */
    private RequestConfig requestConfig;
    /**
     * 设置ssl协议版本名称
     *
     * @see SSLs
     */
    private String sslProtocolVersion;
    /**
     * 解决多线程下载时，stream被close的问题
     */
    private static final ThreadLocal<OutputStream> outs = new ThreadLocal<>();
    /**
     * 解决多线程处理时，url被覆盖问题
     */
    private static final ThreadLocal<String> urls = new ThreadLocal<>();
    /**
     * 解决多线程处理时，url被覆盖问题
     */
    private static final ThreadLocal<Map<String, Object>> maps = new ThreadLocal<>();

    /**
     * @param client HttpClient对象
     * @return 返回当前对象
     */
    public HttpConfig client(HttpClient client) {
        this.client = client;
        return this;
    }

    /**
     * @param url 资源url
     * @return 返回当前对象
     */
    public HttpConfig url(String url) {
        urls.set(url);
        return this;
    }

    /**
     * 设置ssl 协议
     *
     * @param sslProtocolVersion ssl 协议版本名称
     * @return com.sunxung.ecommerce.factoring.util.http.HttpConfig
     * @author ZhouYu Created in 2021/8/27
     */
    public HttpConfig ssl(String sslProtocolVersion) {
        this.sslProtocolVersion = sslProtocolVersion;
        return this;
    }

    /**
     * @param headers Header头信息
     * @return 返回当前对象
     */
    public HttpConfig headers(Header[] headers) {
        this.headers = headers;
        return this;
    }

    /**
     * Header头信息(是否返回response中的headers)
     *
     * @param headers Header头信息
     * @param isReturnRespHeaders 是否返回response中的headers
     * @return 返回当前对象
     */
    public HttpConfig headers(Header[] headers, boolean isReturnRespHeaders) {
        this.headers = headers;
        this.isReturnRespHeaders = isReturnRespHeaders;
        return this;
    }

    /**
     * @param method 请求方法
     * @return 返回当前对象
     */
    public HttpConfig method(HttpMethods method) {
        this.method = method;
        return this;
    }

    /**
     * @param methodName 请求方法
     * @return 返回当前对象
     */
    public HttpConfig methodName(String methodName) {
        this.methodName = methodName;
        return this;
    }

    /**
     * @param context cookie操作相关
     * @return 返回当前对象
     */
    public HttpConfig context(HttpContext context) {
        this.context = context;
        return this;
    }

    /**
     * 注意：调用此方法之前，必须使用 clearMaps()，否则会产生BUG
     * 
     * @param map 传递参数
     * @return 返回当前对象
     */
    public HttpConfig map(Map<String, Object> map) {
        // synchronized (getClass()) {
        // if(this.map==null || map==null){
        // this.map = map;
        // }else {
        // this.map.putAll(map);;
        // }
        // }
        Map<String, Object> m = maps.get();
        if (m == null || map == null) {
            m = map;
        } else {
            m.putAll(map);
        }
        maps.set(m);
        return this;
    }

    /**
     * @param json 以json格式字符串作为参数
     * @return 返回当前对象
     */
    public HttpConfig json(String json) {
        this.json = json;
        Map<String, Object> map = new HashMap<>();
        map.put(HttpParams.ENTITY_JSON, json);
        maps.set(map);
        return this;
    }

    /**
     * 清空maps值，避免出现一次请求中多个不同的请求方式导致maps内容混乱而无法达到请求效果，例如一次请求中现有了json(),后设置map()那么maps参数在解析的时候，其实有json会break;
     *
     * @return 返回当前对象
     */
    public HttpConfig clearMaps() {
        this.json = null;
        maps.remove();
        return this;
    }

    /**
     * @param filePaths 待上传文件所在路径
     * @return 返回当前对象
     */
    public HttpConfig files(String[] filePaths) {
        return files(filePaths, "file");
    }

    /**
     * @param map 待上传文件
     * @return 返回当前对象
     */
    public HttpConfig multipartFiles(MultiValueMap<String, MultipartFile> map) {
        Map<String, Object> m = maps.get();
        if (m == null) {
            m = new HashMap<>();
        }
        m.put(HttpParams.ENTITY_MULTIPART_FILE, map);
        maps.set(m);
        return this;
    }

    /**
     * 上传文件时用到
     *
     * @param filePaths 待上传文件所在路径
     * @param inputName 即file input 标签的name值，默认为file
     * @return 返回当前对象
     */
    public HttpConfig files(String[] filePaths, String inputName) {
        return files(filePaths, inputName, false);
    }

    /**
     * 上传文件时用到
     *
     * @param filePaths 待上传文件所在路径
     * @param inputName 即file input 标签的name值，默认为file
     * @param forceRemoveContentTypeCharset 是否强制一处content-type中设置的编码类型
     * @return 返回当前对象
     */
    public HttpConfig files(String[] filePaths, String inputName, boolean forceRemoveContentTypeCharset) {
        // synchronized (getClass()) {
        // if(this.map==null){
        // this.map= new HashMap<String, Object>();
        // }
        // }
        // map.put(Utils.ENTITY_MULTIPART, filePaths);
        // map.put(Utils.ENTITY_MULTIPART+".name", inputName);
        // map.put(Utils.ENTITY_MULTIPART+".rmCharset", forceRemoveContentTypeCharset);
        Map<String, Object> m = maps.get();
        if (m == null || m == null) {
            m = new HashMap<>();
        }
        m.put(HttpParams.ENTITY_MULTIPART, filePaths);
        m.put(HttpParams.ENTITY_MULTIPART + ".name", inputName);
        m.put(HttpParams.ENTITY_MULTIPART + ".rmCharset", forceRemoveContentTypeCharset);
        maps.set(m);
        return this;
    }

    /**
     * @param encoding 输入输出编码
     * @return 返回当前对象
     */
    public HttpConfig encoding(String encoding) {
        // 设置输入输出
        inenc(encoding);
        outenc(encoding);
        this.encoding = encoding;
        return this;
    }

    /**
     * @param inenc 输入编码
     * @return 返回当前对象
     */
    public HttpConfig inenc(String inenc) {
        this.inenc = inenc;
        return this;
    }

    /**
     * @param outenc 输出编码
     * @return 返回当前对象
     */
    public HttpConfig outenc(String outenc) {
        this.outenc = outenc;
        return this;
    }

    /**
     * @param out 输出流对象
     * @return 返回当前对象
     */
    public HttpConfig out(OutputStream out) {
        outs.set(out);
        return this;
    }

    /**
     * 设置超时时间
     *
     * @param timeout 超市时间，单位-毫秒
     * @return 返回当前对象
     */
    public HttpConfig timeout(int timeout) {
        return timeout(timeout, true);
    }

    /**
     * 设置超时时间以及是否允许网页重定向（自动跳转 302）
     *
     * @param timeout 超时时间，单位-毫秒
     * @param redirectEnable 自动跳转
     * @return 返回当前对象
     */
    public HttpConfig timeout(int timeout, boolean redirectEnable) {
        // 配置请求的超时设置
        RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(timeout).setConnectTimeout(timeout)
                .setSocketTimeout(timeout).setRedirectsEnabled(redirectEnable).build();
        return timeout(config);
    }

    /**
     * 设置代理、超时时间、允许网页重定向等
     *
     * @param requestConfig 超时时间，单位-毫秒
     * @return 返回当前对象
     */
    public HttpConfig timeout(RequestConfig requestConfig) {
        this.requestConfig = requestConfig;
        return this;
    }

    public HttpClient client() {
        return client;
    }

    public Header[] headers() {
        return headers;
    }

    public boolean isReturnRespHeaders() {
        return isReturnRespHeaders;
    }

    public String url() {
        return urls.get();
    }

    public HttpMethods method() {
        return method;
    }

    public String methodName() {
        return methodName;
    }

    public HttpContext context() {
        return context;
    }

    public Map<String, Object> map() {
        // return map;
        return maps.get();
    }

    public String json() {
        return json;
    }

    public String ssl() {
        return sslProtocolVersion;
    }

    public String encoding() {
        return encoding;
    }

    public String inenc() {
        return inenc == null ? encoding : inenc;
    }

    public String outenc() {
        return outenc == null ? encoding : outenc;
    }

    public OutputStream out() {
        return outs.get();
    }

    public RequestConfig requestConfig() {
        return requestConfig;
    }
}
