package com.ruoyi.cms.util.oauth;

import com.alibaba.fastjson2.JSON;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * 优化版 HTTP 客户端工具类
 */
public class HttpUtils {
    private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);

    // 默认超时配置（可通过方法参数覆盖）
    private static final int DEFAULT_CONNECT_TIMEOUT = 10;
    private static final int DEFAULT_READ_TIMEOUT = 30;
    private static final int DEFAULT_WRITE_TIMEOUT = 30;

    // 连接池配置
    private static final int MAX_IDLE_CONNECTIONS = 10;
    private static final long KEEP_ALIVE_DURATION = 300;

    // 媒体类型常量
    public static final MediaType JSON_MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8");
    public static final MediaType FORM_MEDIA_TYPE = MediaType.parse("application/x-www-form-urlencoded; charset=utf-8");
    public static final MediaType XML_MEDIA_TYPE = MediaType.parse("application/xml; charset=utf-8");

    // OkHttp 客户端（单例）
    private static final OkHttpClient OK_HTTP_CLIENT;

    static {
        OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .connectTimeout(DEFAULT_CONNECT_TIMEOUT, TimeUnit.SECONDS)
                .readTimeout(DEFAULT_READ_TIMEOUT, TimeUnit.SECONDS)
                .writeTimeout(DEFAULT_WRITE_TIMEOUT, TimeUnit.SECONDS)
                .connectionPool(new ConnectionPool(MAX_IDLE_CONNECTIONS, KEEP_ALIVE_DURATION, TimeUnit.SECONDS))
                .retryOnConnectionFailure(false) // 关闭默认重试，避免非幂等请求重复提交
                .addInterceptor(chain -> {
                    Request originalRequest = chain.request();
                    Request modifiedRequest = originalRequest.newBuilder()
                            .header("User-Agent", "Java-HttpUtils/1.0")
                            .header("Accept", "*/*")
                            .build();
                    return chain.proceed(modifiedRequest);
                });

        // 生产环境建议移除以下 HTTPS 忽略证书配置，改用合法证书
        // 仅在测试环境启用（通过配置控制更佳）
        try {
            builder.sslSocketFactory(createIgnoreSSLSocketFactory(), new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] chain, String authType) {}

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

                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0];
                }
            });
            builder.hostnameVerifier((hostname, session) -> true);
        } catch (Exception e) {
            log.error("初始化 HTTPS 忽略证书失败", e);
        }

        OK_HTTP_CLIENT = builder.build();
    }

    // GET 请求（无参）
    public static HttpResult doGet(String url) throws TimeoutException, IOException {
        return doGet(url, null, DEFAULT_CONNECT_TIMEOUT, DEFAULT_READ_TIMEOUT, DEFAULT_WRITE_TIMEOUT);
    }

    // GET 请求（带参+自定义超时）
    public static HttpResult doGet(String url, Map<String, Object> params, int connectTimeout, int readTimeout, int writeTimeout)
            throws TimeoutException, IOException {
        String finalUrl = buildUrlWithParams(url, params);
        Request request = new Request.Builder().url(finalUrl).get().build();
        return executeRequest(request, connectTimeout, readTimeout, writeTimeout);
    }

    // POST JSON（Map参数+自定义超时）
    public static HttpResult doPostJson(String url, Map<String, Object> params, int connectTimeout, int readTimeout, int writeTimeout)
            throws TimeoutException, IOException {
        String jsonParams = CollectionUtils.isEmpty(params) ? "{}" : JSON.toJSONString(params);
        return doPost(url, jsonParams, JSON_MEDIA_TYPE, null, connectTimeout, readTimeout, writeTimeout);
    }

    // POST JSON（字符串参数+头）
    public static HttpResult doPostJson(String url, String jsonParams, Map<String, String> headers, int connectTimeout, int readTimeout, int writeTimeout)
            throws TimeoutException, IOException {
        return doPost(url, jsonParams, JSON_MEDIA_TYPE, headers, connectTimeout, readTimeout, writeTimeout);
    }

    // POST 表单（默认超时）
    public static HttpResult doPostForm(String url, Map<String, Object> params) throws TimeoutException, IOException {
        return doPostForm(url, params, null, DEFAULT_CONNECT_TIMEOUT, DEFAULT_READ_TIMEOUT, DEFAULT_WRITE_TIMEOUT);
    }

    // POST 表单（自定义头+超时）
    public static HttpResult doPostForm(String url, Map<String, Object> params, Map<String, String> headers,
                                        int connectTimeout, int readTimeout, int writeTimeout)
            throws TimeoutException, IOException {
        FormBody.Builder formBuilder = new FormBody.Builder();
        if (!CollectionUtils.isEmpty(params)) {
            params.forEach((key, value) -> {
                if (value != null) {
                    formBuilder.add(encodeParam(key), value.toString());
                }
            });
        }
        RequestBody requestBody = formBuilder.build();

        Request.Builder requestBuilder = new Request.Builder()
                .url(url)
                .post(requestBody)
                .header("Content-Type", FORM_MEDIA_TYPE.toString());

        addHeaders(requestBuilder, headers);
        return executeRequest(requestBuilder.build(), connectTimeout, readTimeout, writeTimeout);
    }

    // 通用 POST 方法
    private static HttpResult doPost(String url, String content, MediaType mediaType, Map<String, String> headers,
                                     int connectTimeout, int readTimeout, int writeTimeout)
            throws TimeoutException, IOException {
        if (mediaType == null) {
            mediaType = JSON_MEDIA_TYPE;
        }
        RequestBody requestBody = RequestBody.create(mediaType ,content);

        Request.Builder requestBuilder = new Request.Builder()
                .url(url)
                .post(requestBody)
                .header("Content-Type", mediaType.toString());

        addHeaders(requestBuilder, headers);
        return executeRequest(requestBuilder.build(), connectTimeout, readTimeout, writeTimeout);
    }

    // 执行请求核心方法
    private static HttpResult executeRequest(Request request, int connectTimeout, int readTimeout, int writeTimeout)
            throws TimeoutException, IOException {
        OkHttpClient tempClient = OK_HTTP_CLIENT.newBuilder()
                .connectTimeout(connectTimeout, TimeUnit.SECONDS)
                .readTimeout(readTimeout, TimeUnit.SECONDS)
                .writeTimeout(writeTimeout, TimeUnit.SECONDS)
                .build();

        long startTime = System.currentTimeMillis();
        Response response = null;
        try {
            response = tempClient.newCall(request).execute();
            String responseBody = response.body() != null ? response.body().string() : "";
            long costTime = System.currentTimeMillis() - startTime;

            // 限制响应体日志长度（最多1024字符）
            String logBody = responseBody.length() > 1024 ? responseBody.substring(0, 1024) + "..." : responseBody;
            log.debug("HTTP 请求完成 | URL: {} | 状态码: {} | 耗时: {}ms | 响应体: {}",
                    request.url(), response.code(), costTime, logBody);

            HttpResult result = new HttpResult();
            result.setUrl(request.url().toString());
            result.setStatusCode(response.code());
            result.setSuccess(response.isSuccessful());
            result.setResponseBody(responseBody);
            result.setCostTime(costTime);
            return result;
        } catch (SocketTimeoutException e) {
            // 精确捕获超时异常
            long costTime = System.currentTimeMillis() - startTime;
            log.error("HTTP 请求超时 | URL: {} | 耗时: {}ms", request.url(), costTime, e);
            throw new TimeoutException(String.format("HTTP 请求超时 | URL: %s | 超时配置: 连接%d秒, 读取%d秒, 写入%d秒",
                    request.url(), connectTimeout, readTimeout, writeTimeout));
        } catch (IOException e) {
            long costTime = System.currentTimeMillis() - startTime;
            log.error("HTTP 请求异常 | URL: {} | 耗时: {}ms | 错误信息: {}",
                    request.url(), costTime, e.getMessage(), e);
            throw e;
        } finally {
            if (response != null && response.body() != null) {
                response.body().close();
            }
        }
    }

    // 拼接URL参数（修复参数名编码问题）
    private static String buildUrlWithParams(String url, Map<String, Object> params) {
        if (CollectionUtils.isEmpty(params) || StringUtils.isEmpty(url)) {
            return url;
        }

        StringBuilder urlBuilder = new StringBuilder(url);
        urlBuilder.append(url.contains("?") ? "&" : "?");

        params.forEach((key, value) -> {
            if (value != null) {
                String encodedKey = encodeParam(key);
                String valueStr = value.toString();
                String encodedValue = encodeParam(valueStr);
                urlBuilder.append(encodedKey).append("=").append(encodedValue).append("&");
            }
        });

        return urlBuilder.substring(0, urlBuilder.length() - 1);
    }

    // 参数编码工具方法
    private static String encodeParam(String param) {
        try {
            return java.net.URLEncoder.encode(param, "UTF-8");
        } catch (Exception e) {
            log.warn("参数编码失败 | param: {}", param, e);
            return param; // 编码失败时返回原始值（可能有风险，但避免中断流程）
        }
    }

    // 添加请求头
    private static void addHeaders(Request.Builder requestBuilder, Map<String, String> headers) {
        if (!CollectionUtils.isEmpty(headers)) {
            headers.forEach(requestBuilder::header);
        }
    }

    // 创建忽略SSL证书的SocketFactory（仅测试环境用）
    private static SSLSocketFactory createIgnoreSSLSocketFactory() throws NoSuchAlgorithmException, KeyManagementException {
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, new TrustManager[]{new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) {}

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

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        }}, new java.security.SecureRandom());
        return sslContext.getSocketFactory();
    }

    // 响应结果封装类
    public static class HttpResult {
        private String url;
        private int statusCode;
        private boolean isSuccess;
        private String responseBody;
        private long costTime;

        public <T> T toObject(Class<T> clazz) {
            if (StringUtils.isEmpty(responseBody)) {
                return null;
            }
            try {
                return JSON.parseObject(responseBody, clazz);
            } catch (Exception e) {
                log.error("响应体转对象失败 | URL: {} | 响应体: {} | 目标类型: {}", url, responseBody, clazz.getName(), e);
                return null;
            }
        }

        // Getter/Setter
        public String getUrl() { return url; }
        public void setUrl(String url) { this.url = url; }
        public int getStatusCode() { return statusCode; }
        public void setStatusCode(int statusCode) { this.statusCode = statusCode; }
        public boolean isSuccess() { return isSuccess; }
        public void setSuccess(boolean success) { isSuccess = success; }
        public String getResponseBody() { return responseBody; }
        public void setResponseBody(String responseBody) { this.responseBody = responseBody; }
        public long getCostTime() { return costTime; }
        public void setCostTime(long costTime) { this.costTime = costTime; }

        @Override
        public String toString() {
            return "HttpResult{" +
                    "url='" + url + '\'' +
                    ", statusCode=" + statusCode +
                    ", isSuccess=" + isSuccess +
                    ", costTime=" + costTime +
                    ", responseBody='" + responseBody + '\'' +
                    '}';
        }
    }
}