package com.microservices.common.httpClient.service;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.microservices.common.core.constant.Constants;
import com.microservices.common.core.constant.HttpStatus;
import com.microservices.common.httpClient.domain.CustomHttpDelete;
import com.microservices.system.api.domain.SysFileBytes;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

import static com.microservices.common.core.web.domain.AjaxResult.DATA_TAG;

/**
 * @author otto
 */
@Component
public class HttpAPIService {

    private static final Logger logger = LoggerFactory.getLogger(HttpAPIService.class);

    @Autowired
    private CloseableHttpClient httpClient;
    @Autowired
    private RequestConfig config;

    /**
     * 带参数的post请求
     *
     * @param url
     * @param jsonObject
     * @return
     * @throws Exception
     */
    public JSONObject doPost(String url, JSONObject jsonObject) throws Exception {
        return doPost(url, jsonObject, null);
    }

    /**
     * 带参数的post请求
     *
     * @param url
     * @param jsonArray
     * @return
     * @throws Exception
     */
    public JSONObject doPost(String url, JSONArray jsonArray) throws Exception {
        return doPost(url, jsonArray, null);
    }

    /**
     * 带参数以及Header的post请求
     *
     * @param url
     * @param jsonObject
     * @param headers
     * @return
     * @throws Exception
     */
    public JSONObject doPost(String url, JSONObject jsonObject, HashMap<String, String> headers) throws Exception {
        // 声明httpPost请求
        HttpPost httpPost = new HttpPost(url);
        // 加入配置信息
        httpPost.setConfig(config);

        if (jsonObject != null) {
            String contentJsonString = jsonObject.toJSONString();
            StringEntity stringEntity = new StringEntity(contentJsonString, Constants.UTF8);
            stringEntity.setContentEncoding(Constants.UTF8);
            stringEntity.setContentType(Constants.JSON_CONTENT_TYPE);
            // 把表单放到post里
            httpPost.setEntity(stringEntity);
        }
        return doRequest(httpPost, headers);
    }

    /**
     * 带参数以及Header的post请求
     *
     * @param url
     * @param jsonArray
     * @param headers
     * @return
     * @throws Exception
     */
    public JSONObject doPost(String url, JSONArray jsonArray, HashMap<String, String> headers) throws Exception {
        // 声明httpPost请求
        HttpPost httpPost = new HttpPost(url);
        // 加入配置信息
        httpPost.setConfig(config);

        if (jsonArray != null) {
            String contentJsonString = jsonArray.toJSONString();
            StringEntity stringEntity = new StringEntity(contentJsonString, Constants.UTF8);
            stringEntity.setContentEncoding(Constants.UTF8);
            stringEntity.setContentType(Constants.JSON_CONTENT_TYPE);
            // 把表单放到post里
            httpPost.setEntity(stringEntity);
        }
        return doRequest(httpPost, headers);
    }

    public JSONObject doPostFile(String url, MultipartFile file, HashMap<String, String> headers) throws Exception {
        HttpPost httpPost = new HttpPost(url);

        // 创建一个输入流
        InputStream inputStream = file.getInputStream();
        try {
            // 构建文件上传请求的实体
            MultipartEntityBuilder builder = MultipartEntityBuilder.create();
            builder.addBinaryBody(
                    "file",
                    inputStream,
                    ContentType.create("application/octet-stream"),
                    URLEncoder.encode(file.getOriginalFilename(), StandardCharsets.UTF_8.toString())
            );

            HttpEntity entity = builder.build();
            httpPost.setEntity(entity);
            return doRequest(httpPost, headers);
        } finally {
            // 确保输入流被关闭，以释放资源
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    // Log the exception
                    logger.error("Failed to close input stream", e);
                }
            }
        }
    }

    public JSONObject doPostFormData(String url, HttpEntity httpEntity, HashMap<String, String> headers) throws Exception {
        HttpPost httpPost = new HttpPost(url);
        httpPost.setEntity(httpEntity);

        return doRequest(httpPost, headers);
    }

    /**
     * 带参数以及Header的put请求
     *
     * @param url
     * @param jsonObject
     * @param headers
     * @return
     * @throws Exception
     */
    public JSONObject doPut(String url, JSONObject jsonObject, HashMap<String, String> headers) throws Exception {
        // 声明httpPut请求
        HttpPut httpPut = new HttpPut(url);
        // 加入配置信息
        httpPut.setConfig(config);

        if (jsonObject != null) {
            StringEntity stringEntity = new StringEntity(jsonObject.toJSONString(), Constants.UTF8);
            stringEntity.setContentEncoding(Constants.UTF8);
            stringEntity.setContentType(Constants.JSON_CONTENT_TYPE);
            // 把表单放到post里
            httpPut.setEntity(stringEntity);
        }
        return doRequest(httpPut, headers);
    }

    /**
     * 带Header的delete请求
     *
     * @param url
     * @param headers
     * @return
     * @throws Exception
     */
    public JSONObject doDelete(String url, JSONObject jsonObject, HashMap<String, String> headers) throws Exception {
        // 声明httpDelete请求
        CustomHttpDelete httpDelete = new CustomHttpDelete(url);
        // 加入配置信息
        httpDelete.setConfig(config);
        if (jsonObject != null) {
            StringEntity stringEntity = new StringEntity(jsonObject.toJSONString(), Constants.UTF8);
            stringEntity.setContentEncoding(Constants.UTF8);
            stringEntity.setContentType(Constants.JSON_CONTENT_TYPE);
            // 把表单放到post里
            httpDelete.setEntity(stringEntity);
        }

        return doRequest(httpDelete, headers);
    }

    /**
     * 带Header的get请求
     *
     * @param url
     * @param headers
     * @return
     * @throws Exception
     */
    public JSONObject doGet(String url, HashMap<String, String> headers) throws Exception {
        // 声明httpGet请求
        HttpGet httpGet = new HttpGet(url);
        // 加入配置信息
        httpGet.setConfig(config);

        return doRequest(httpGet, headers);
    }

    public void doGetResponse(String url, HttpServletResponse resp, HashMap<String, String> headers) throws Exception {
        // 声明httpGet请求
        HttpGet httpGet = new HttpGet(url);
        // 加入配置信息
        httpGet.setConfig(config);
        doRequestForResponse(httpGet, resp, headers);
    }

    public JSONObject doPatch(String url, JSONObject jsonParam, HashMap<String, String> header) throws Exception {

        HttpPatch httpPatch = new HttpPatch(url);
        //请求头
        if (MapUtils.isNotEmpty(header)) {
            for (Map.Entry<String, String> entry : header.entrySet()) {
                httpPatch.addHeader(entry.getKey(), entry.getValue());
            }
        }

        if (jsonParam != null) {
            StringEntity entity = new StringEntity(jsonParam.toString(), Constants.UTF8);
            entity.setContentEncoding(Constants.UTF8);
            entity.setContentType(Constants.JSON_CONTENT_TYPE);
            httpPatch.setEntity(entity);
        }
        return doRequest(httpPatch, header);
    }

    /**
     * 带参数以及Header的请求
     *
     * @param request
     * @param headers
     * @return
     * @throws Exception
     */
    public JSONObject doRequest(HttpUriRequest request, HashMap<String, String> headers) throws Exception {
        if (headers != null && !headers.isEmpty()) {
            headers.forEach(request::setHeader);
        }

        // 发起请求
        try (CloseableHttpResponse response = httpClient.execute(request)) {
            if (response != null) {
                int httpStatusCode = response.getStatusLine().getStatusCode();

                if (HttpStatus.checkSuccess(httpStatusCode)) {
                    HttpEntity httpEntity = response.getEntity();
                    if (httpEntity == null) {
                        return createStatusObject(httpStatusCode);
                    } else {
                        // length>0代表有数据，length=-1代表分块传输，length=0代表未返回数据
                        long length = httpEntity.getContentLength();
                        if (length != 0L) {
                            String result = EntityUtils.toString(httpEntity);
                            Object resultObj = JSON.parse(result);
                            if (resultObj instanceof JSONArray) {
                                JSONObject jsonObject = new JSONObject();
                                jsonObject.put(DATA_TAG, resultObj);
                                return jsonObject;
                            }
                            return JSONObject.parseObject(result);
                        } else {
                            return createStatusObject(httpStatusCode);
                        }
                    }
                }
                HttpEntity httpEntity = response.getEntity();
                if (httpEntity != null) {
                    String result = EntityUtils.toString(httpEntity);
                    throw new Exception("请求响应失败：" + result);
                }
                throw new Exception("请求响应失败，状态码：" + response.getStatusLine().getStatusCode());
            }
            throw new Exception("请求响应失败");
        }
    }

    /**
     * 构建响应状态码JSON对象
     *
     * @param status 请求响应状态码
     * @return JSON对象
     */
    private JSONObject createStatusObject(int status) {
        JSONObject resObject = new JSONObject();
        resObject.put("status", status);
        return resObject;
    }


    public void doRequestForResponse(HttpUriRequest request, HttpServletResponse resp, HashMap<String, String> headers) throws Exception {
        if (headers != null && !headers.isEmpty()) {
            headers.forEach(request::setHeader);
        }

        try (CloseableHttpResponse response = httpClient.execute(request)) {
            if (response != null) {
                if (response.getStatusLine().getStatusCode() == HttpStatus.SUCCESS) {
                    copyResponse(response, resp);
                }
                throw new Exception("请求响应失败，状态码：" + response.getStatusLine().getStatusCode());
            }
            throw new Exception("请求响应失败");
        }
    }

    private void copyResponse(CloseableHttpResponse closeableHttpResponse, HttpServletResponse httpResponse) throws IOException {
        // 设置响应状态码
        httpResponse.setStatus(closeableHttpResponse.getStatusLine().getStatusCode());

        // 复制响应头
        for (org.apache.http.Header header : closeableHttpResponse.getAllHeaders()) {
            httpResponse.addHeader(header.getName(), header.getValue());
        }

        // 复制响应体
        try (InputStream input = closeableHttpResponse.getEntity().getContent();
             OutputStream output = httpResponse.getOutputStream()) {
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = input.read(buffer)) != -1) {
                output.write(buffer, 0, bytesRead);
            }
        }
    }

    public SysFileBytes doGetFileInputStream(String url, HashMap<String, String> headers) throws Exception {
        // 声明httpGet请求
        HttpGet request = new HttpGet(url);
        // 加入配置信息
        request.setConfig(config);
        if (headers != null && !headers.isEmpty()) {
            headers.forEach(request::setHeader);
        }

        try (CloseableHttpResponse response = httpClient.execute(request)) {
            if (response != null) {
                int httpStatusCode = response.getStatusLine().getStatusCode();
                if (HttpStatus.checkSuccess(httpStatusCode)) {
                    HttpEntity httpEntity = response.getEntity();
                    if (httpEntity != null) {
                        InputStream input = httpEntity.getContent();
                        InputStream cloneInput = IOUtils.toBufferedInputStream(input);
                        String fileName = url.substring(url.lastIndexOf('/') + 1);
                        SysFileBytes sysFileBytes = new SysFileBytes();
                        sysFileBytes.setFileInputStream(cloneInput);
                        sysFileBytes.setFileName(fileName);
                        return sysFileBytes;
                    }
                }
            }
            throw new Exception("请求响应失败");
        }
    }
}