package com.ruoyi.common.utils.http;

import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.*;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.*;
import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.Map;

/**
 * 通用http发送方法
 * 
 * @author ruoyi
 */
public class HttpUtils
{
    private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);

    /**
     * 向指定 URL 发送GET方法的请求
     *
     * @param url 发送请求的 URL
     * @return 所代表远程资源的响应结果
     */
//    public static String sendGet(String url)
//    {
//        return sendGet(url, StringUtils.EMPTY);
//    }

    /**
     * 向指定 URL 发送GET方法的请求
     *
     * @param url 发送请求的 URL
     * @param param 请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String sendGet(String url, String param, String token)
    {
        return sendGet(url, param, token, Constants.UTF8);
    }

    /**
     * 向指定 URL 发送GET方法的请求
     *
     * @param url 发送请求的 URL
     * @param param 请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
     * @param contentType 编码类型
     * @return 所代表远程资源的响应结果
     */
    public static String sendGet(String url, String param, String token, String contentType)
    {
        StringBuilder result = new StringBuilder();
        BufferedReader in = null;
        try
        {
            String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url;
            log.info("sendGet - {}", urlNameString);
            URL realUrl = new URL(urlNameString);
            URLConnection connection = realUrl.openConnection();

            connection.setRequestProperty("Authorization", "Bearer " + token);

            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
            connection.connect();
            in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
            String line;
            while ((line = in.readLine()) != null)
            {
                result.append(line);
            }
            log.info("recv - {}", result);
        }
        catch (ConnectException e)
        {
            log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
        }
        catch (SocketTimeoutException e)
        {
            log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
        }
        catch (IOException e)
        {
            log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e);
        }
        catch (Exception e)
        {
            log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
        }
        finally
        {
            try
            {
                if (in != null)
                {
                    in.close();
                }
            }
            catch (Exception ex)
            {
                log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
            }
        }
        return result.toString();
    }

    /**
     * 向指定 URL 发送POST方法的请求
     *
     * @param url 发送请求的 URL
     * @param param 请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String sendPost(String url, String param, String token)
    {
        PrintWriter out = null;
        BufferedReader in = null;
        StringBuilder result = new StringBuilder();
        try
        {
            log.info("sendPost - {}", url);
            URL realUrl = new URL(url);
            URLConnection conn = realUrl.openConnection();
            conn.setRequestProperty("Authorization", "Bearer " + token);
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
            conn.setRequestProperty("Accept-Charset", "utf-8");
            conn.setRequestProperty("contentType", "utf-8");
            conn.setDoOutput(true);
            conn.setDoInput(true);
            out = new PrintWriter(conn.getOutputStream());
            out.print(param);
            out.flush();
            in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
            String line;
            while ((line = in.readLine()) != null)
            {
                result.append(line);
            }
            log.info("recv - {}", result);
        }
        catch (ConnectException e)
        {
            log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e);
        }
        catch (SocketTimeoutException e)
        {
            log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e);
        }
        catch (IOException e)
        {
            log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e);
        }
        catch (Exception e)
        {
            log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e);
        }
        finally
        {
            try
            {
                if (out != null)
                {
                    out.close();
                }
                if (in != null)
                {
                    in.close();
                }
            }
            catch (IOException ex)
            {
                log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
            }
        }
        return result.toString();
    }

    /**
     * POST请求，json参数在body里
     * @param url
     * @param jsonBody
     * @param token
     * @return
     */
    public static String doPost(String url, String jsonBody, String token) {
        HttpURLConnection conn =null;
        InputStream is =null;
        log.info("当前登陆账号:" + SecurityUtils.getUsername() + ", 请求url:" + url + ", 请求体body:" + jsonBody + ", 请求token:" + token);
        try{
            URL realUrl = new URL(url);
            conn = (HttpURLConnection) realUrl.openConnection();

            conn.setRequestMethod("POST");
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);

            conn.setRequestProperty("Accept", "application/json, text/plain, */*");
            conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
            if (token != null && !token.isEmpty()) {
                conn.setRequestProperty("Authorization", "Bearer " + token);
            }
            if (jsonBody != null){
                try (OutputStream os = conn.getOutputStream()) {
                    byte[] input = jsonBody.getBytes(StandardCharsets.UTF_8);
                    os.write(input);
                    os.flush();
                }
            }
            int status = conn.getResponseCode();
            if (status != 200){
                InputStream errorStream = conn.getErrorStream();
                StringBuilder errMsg = new StringBuilder();
                if (errorStream != null){
                    byte[] buffer = new byte[1024];
                    int len;
                    while ((len = errorStream.read(buffer)) != -1){
                        errMsg.append(new String(buffer,0, len, StandardCharsets.UTF_8));
                    }
                    errorStream.close();
                }
                throw new ServiceException("HTTP 请求失败, 状态码: " + status + ", 错误信息: " + errMsg);
            }
            is = conn.getInputStream();
            StringBuilder response = new StringBuilder();
            byte[] buffer = new byte[1024];
            int len;
            while ((len = is.read(buffer)) != -1) {
                response.append(new String(buffer, 0, len, StandardCharsets.UTF_8));
            }
            is.close();
            conn.disconnect();
            log.info("当前登陆账号:" + SecurityUtils.getUsername() + ", 请求成功, 状态码:" + status + ", 响应内容:" + response);
            return response.toString();
        }catch (Exception e){
            throw new ServiceException("调用HttpsUtil.sendPost Exception, url= " + url + ", param= " + jsonBody + ", 错误信息: " + e.getMessage());
        }finally {
            if (is != null){
                try {
                    is.close();
                }catch (Exception ignored){}
            }
            if (conn != null){
                conn.disconnect();
            }
        }
    }

    public static String sendSSLPost(String url, String param)
    {
        StringBuilder result = new StringBuilder();
        String urlNameString = url + "?" + param;
        try
        {
            log.info("sendSSLPost - {}", urlNameString);
            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom());
            URL console = new URL(urlNameString);
            HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
            conn.setRequestProperty("Accept-Charset", "utf-8");
            conn.setRequestProperty("contentType", "utf-8");
            conn.setDoOutput(true);
            conn.setDoInput(true);

            conn.setSSLSocketFactory(sc.getSocketFactory());
            conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
            conn.connect();
            InputStream is = conn.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            String ret = "";
            while ((ret = br.readLine()) != null)
            {
                if (ret != null && !"".equals(ret.trim()))
                {
                    result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8));
                }
            }
            log.info("recv - {}", result);
            conn.disconnect();
            br.close();
        }
        catch (ConnectException e)
        {
            log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e);
        }
        catch (SocketTimeoutException e)
        {
            log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e);
        }
        catch (IOException e)
        {
            log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e);
        }
        catch (Exception e)
        {
            log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e);
        }
        return result.toString();
    }

    private static class TrustAnyTrustManager implements X509TrustManager
    {
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType)
        {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType)
        {
        }

        @Override
        public X509Certificate[] getAcceptedIssuers()
        {
            return new X509Certificate[] {};
        }
    }

    private static class TrustAnyHostnameVerifier implements HostnameVerifier
    {
        @Override
        public boolean verify(String hostname, SSLSession session)
        {
            return true;
        }
    }

    /**
     * 发送 GET 请求
     * @param url 请求地址
     * @param params URL 参数（可为 null）
     * @param headers 请求头（可为 null）
     * @param responseType 响应类型
     * @param <T> 泛型返回值
     * @return 响应结果
     */
    public static <T> T doGet(String url, Map<String, String> params, HttpHeaders headers, Class<T> responseType) {
        // 拼接 URL 参数
        if (params != null && !params.isEmpty()) {
            StringBuilder urlBuilder = new StringBuilder(url).append("?");
            for (Map.Entry<String, String> entry : params.entrySet()) {
                urlBuilder.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
            }
            url = urlBuilder.substring(0, urlBuilder.length() - 1);
        }

        // 构建请求头
        HttpHeaders requestHeaders = headers == null ? new HttpHeaders() : headers;
        HttpEntity<Void> requestEntity = new HttpEntity<>(requestHeaders);
        RestTemplate restTemplate = new RestTemplate();

        // 发送请求并处理响应
        try {
            ResponseEntity<T> response = restTemplate.exchange(
                    url,
                    HttpMethod.GET,
                    requestEntity,
                    responseType
            );
            return response.getBody();
        } catch (Exception e) {
            throw new RuntimeException("GET 请求失败，URL：" + url + "，异常信息：" + e.getMessage(), e);
        }
    }

    /**
     * 发送 POST 请求（application/x-www-form-urlencoded 格式）
     * @param url 请求地址
     * @param params 表单参数
     * @param headers 请求头（可为 null）
     * @param responseType 响应类型
     * @param <T> 泛型返回值
     * @return 响应结果
     */
    public static <T> T doPostFormUrlEncoded(String url, Map<String, String> params, HttpHeaders headers, Class<T> responseType) {
        // 构建表单参数
        MultiValueMap<String, String> formParams = new LinkedMultiValueMap<>();
        if (params != null && !params.isEmpty()) {
            formParams.setAll(params);
        }

        // 构建请求头（指定 Content-Type）
        HttpHeaders requestHeaders = headers == null ? new HttpHeaders() : headers;
        requestHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        requestHeaders.setAcceptCharset(Collections.singletonList(StandardCharsets.UTF_8));

        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formParams, requestHeaders);
        RestTemplate restTemplate = new RestTemplate();
        // 发送请求
        try {
            ResponseEntity<T> response = restTemplate.exchange(
                    url,
                    HttpMethod.POST,
                    requestEntity,
                    responseType
            );
            log.info("POST(form-urlencoded) 请求成功，URL：{}，响应结果：{}", url, response.getBody());
            return response.getBody();
        } catch (Exception e) {
            throw new RuntimeException("POST(form-urlencoded) 请求失败，URL：" + url + "，异常信息：" + e.getMessage(), e);
        }
    }

    // ========== 新增：JSON 格式 POST 请求 ==========
    /**
     * 发送 POST 请求（application/json 格式）
     * @param url 请求地址
     * @param requestBody 请求体（Java 对象，自动序列化为 JSON）
     * @param headers 请求头（可为 null，默认已设置 Content-Type: application/json）
     * @param responseType 响应类型
     * @param <T> 响应泛型类型
     * @param <R> 请求体类型
     * @return 响应结果
     */
    public static <T, R> T doPostJson(String url, R requestBody, HttpHeaders headers, Class<T> responseType) {
        // 构建请求头，默认设置 JSON 格式
        HttpHeaders requestHeaders = headers == null ? new HttpHeaders() : headers;
        // 确保 Content-Type 为 JSON，避免外部传入覆盖
        if (!requestHeaders.containsKey(HttpHeaders.CONTENT_TYPE)) {
            requestHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
        }

        // 构建请求实体（请求体 + 请求头）
        HttpEntity<R> requestEntity = new HttpEntity<>(requestBody, requestHeaders);
        RestTemplate restTemplate = new RestTemplate();
        // 发送 JSON POST 请求
        try {
            ResponseEntity<T> response = restTemplate.exchange(
                    url,
                    HttpMethod.POST,
                    requestEntity,
                    responseType
            );
            log.info("POST(JSON) 请求成功，URL：{}，响应结果：{}", url, response.getBody());
            return response.getBody();
        } catch (Exception e) {
            throw new RuntimeException("POST(JSON) 请求失败，URL：" + url + "，异常信息：" + e.getMessage(), e);
        }
    }
}