package cn.reghao.autodop.common.http;

import lombok.extern.slf4j.Slf4j;
import org.apache.http.HeaderElement;
import org.apache.http.HeaderElementIterator;
import org.apache.http.NameValuePair;
import org.apache.http.client.CookieStore;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author reghao
 * @date 2019-11-29 10:03:18
 */
@Slf4j
public class DefaultWebRequest implements WebRequest {
    private CloseableHttpClient client;
    private String charset;

    public DefaultWebRequest() {
        init();
    }

    public DefaultWebRequest(String charset) {
        this.charset = charset;
        init();
    }

    ConnectionKeepAliveStrategy strategy = (response, context) -> {
        HeaderElementIterator iterator =
                new BasicHeaderElementIterator(response.headerIterator(HTTP.CONN_KEEP_ALIVE));
        while (iterator.hasNext()) {
            HeaderElement element = iterator.nextElement();
            String name = element.getName();
            String value = element.getValue();
            if (value != null && "timeout".equalsIgnoreCase(name)) {
                return Long.parseLong(value) * 1000;
            }
        }

        return 60*1000;
    };

    private void init() {
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
        connectionManager.setMaxTotal(50);
        connectionManager.setDefaultMaxPerRoute(20);

        RequestConfig defaultRequestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(10_000)
                .setConnectTimeout(10_000)
                .setSocketTimeout(10_000)
                .setCookieSpec(CookieSpecs.STANDARD)
                .build();

        CookieStore cookieStore = new BasicCookieStore();
        client = HttpClients.custom()
                .setDefaultRequestConfig(defaultRequestConfig)
                .setDefaultCookieStore(cookieStore)
                .setConnectionManager(connectionManager)
                .setKeepAliveStrategy(strategy)
                .build();
    }
    
    private void setDefaultHeaders(HttpRequestBase method) {
        String desktop = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36";
        method.setHeader("user-agent", desktop);
        method.setHeader("connection", "keep-alive");
    }

    @Override
    public int head(String url) {
        HttpHead head = new HttpHead(url);
        try (CloseableHttpResponse response = client.execute(head)) {
            return response.getStatusLine().getStatusCode();
        } catch (Exception e) {
            return 600;
        }
    }

    @Override
    public WebResponse get(String url) {
        HttpGet get = new HttpGet(url);
        try {
            return client.execute(get, response -> {
                int statusCode = response.getStatusLine().getStatusCode();
                // TODO 根据 content-type 中的编码获取字符串
                String contentType = response.getFirstHeader("content-type").getValue();
                String body;
                if (charset != null) {
                    body = EntityUtils.toString(response.getEntity(), charset);
                } else {
                    body = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
                }
                return new WebResponse(statusCode, body);
            });
        } catch (Exception e) {
            // TODO 是否应该放在 finally 块中？
            return new WebResponse(600, e.getMessage());
        }
    }

    @Override
    public WebResponse postFormData(String url, Map<String, String> formData) {
        List<NameValuePair> params = new ArrayList<>();
        formData.forEach((k, v) -> {
            params.add(new BasicNameValuePair(k, v));
        });
        UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(params, StandardCharsets.UTF_8);

        HttpPost post = new HttpPost(url);
        //post.addHeader("Content-Type", "application/json;charset=UTF-8");
        post.setEntity(urlEncodedFormEntity);

        try (CloseableHttpResponse response = client.execute(post)) {
            int statusCode = response.getStatusLine().getStatusCode();
            String body;
            if (charset != null) {
                body = EntityUtils.toString(response.getEntity(), charset);
            } else {
                body = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
            }
            return new WebResponse(statusCode, body);
        } catch (Exception e) {
            return new WebResponse(600, e.getMessage());
        }
    }

    @Override
    public WebResponse postJson(String url, String json) {
        StringEntity entity = new StringEntity(json, StandardCharsets.UTF_8);
        entity.setContentEncoding("UTF-8");

        HttpPost post = new HttpPost(url);
        post.addHeader("Content-Type", "application/json;charset=UTF-8");
        post.setEntity(entity);

        try (CloseableHttpResponse response = client.execute(post)) {
            int statusCode = response.getStatusLine().getStatusCode();
            String body;
            if (charset != null) {
                body = EntityUtils.toString(response.getEntity(), charset);
            } else {
                body = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
            }
            return new WebResponse(statusCode, body);
        } catch (Exception e) {
            return new WebResponse(600, e.getMessage());
        }
    }
}
