/**
 * @ProjectName: 智慧评标-主观分助手
 * @Package: com.zhpb.gdyd_zhpb_zgf.utils
 * @ClassName: VectorizeUtils
 * @Description: 向量化工具类，调用外部向量化接口将文本转换为向量
 * @Author: 张志锋
 * @Date: 2025-10-31
 * @Version: 1.0
 * @Copyright: © 2025 智慧评标-主观分助手
 */
package com.zhpb.gdyd_zhpb_zgf.utils;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

/**
 * 向量化工具类
 */
public class VectorizeUtils {

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

    /**
     * 调用向量化接口将文本转换为向量
     *
     * @param text 要向量化的文本
     * @param baseUrl 向量化服务基础URL
     * @param apiPath 向量化接口路径
     * @return 向量化结果，包含vector数组和dimension维度
     * @throws Exception 向量化失败时抛出异常
     */
    public static VectorizeResult vectorize(String text, String baseUrl, String apiPath) throws Exception {
        return vectorize(text, baseUrl, apiPath, null, null);
    }

    /**
     * 调用向量化接口将文本转换为向量（支持 OpenAI 格式）
     *
     * @param text 要向量化的文本
     * @param baseUrl 向量化服务基础URL
     * @param apiPath 向量化接口路径
     * @param apiKey API密钥（可选，用于 OpenAI 格式）
     * @param model 模型名称（可选，用于 OpenAI 格式）
     * @return 向量化结果，包含vector数组和dimension维度
     * @throws Exception 向量化失败时抛出异常
     */
    public static VectorizeResult vectorize(String text, String baseUrl, String apiPath, String apiKey, String model) throws Exception {
        if (text == null || text.trim().isEmpty()) {
            throw new IllegalArgumentException("文本内容不能为空");
        }

        String url = baseUrl.endsWith("/") ? baseUrl + apiPath.substring(1) : baseUrl + apiPath;

        logger.debug("调用向量化接口: {}", url);
        logger.debug("文本长度: {} 字符", text.length());

        // 配置HTTP客户端，设置超时时间（30秒连接，60秒响应）
        org.apache.hc.core5.util.Timeout connectTimeout = org.apache.hc.core5.util.Timeout.ofSeconds(30);
        org.apache.hc.core5.util.Timeout responseTimeout = org.apache.hc.core5.util.Timeout.ofSeconds(60);
        org.apache.hc.client5.http.config.RequestConfig requestConfig = org.apache.hc.client5.http.config.RequestConfig.custom()
                .setConnectTimeout(connectTimeout)
                .setResponseTimeout(responseTimeout)
                .build();
        
        long startTime = System.currentTimeMillis();
        try (CloseableHttpClient httpClient = HttpClients.custom()
                .setDefaultRequestConfig(requestConfig)
                .build()) {
            HttpPost httpPost = new HttpPost(url);
            httpPost.setHeader("Content-Type", "application/json;charset=UTF-8");
            
            // 如果提供了 API Key，添加 Authorization header（OpenAI 格式）
            if (apiKey != null && !apiKey.trim().isEmpty()) {
                httpPost.setHeader("Authorization", "Bearer " + apiKey.trim());
            }

            // 构建请求体
            JSONObject requestBody = new JSONObject();
            
            // 判断是否为 OpenAI 格式（如果提供了 model 参数）
            if (model != null && !model.trim().isEmpty()) {
                // OpenAI 格式
                requestBody.put("model", model);
                requestBody.put("input", text);
            } else {
                // 原有格式
                requestBody.put("text", text);
            }
            
            String requestJson = requestBody.toJSONString();
            httpPost.setEntity(new StringEntity(requestJson, StandardCharsets.UTF_8));

            logger.info("调用向量化接口: {}, 文本长度: {} 字符", url, text.length());

            // 执行请求
            return httpClient.execute(httpPost, response -> {
                int statusCode = response.getCode();
                String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);

                if (statusCode != 200) {
                    String errorMsg = String.format("向量化接口调用失败，URL: %s, 状态码: %d, 响应: %s", url, statusCode, responseBody);
                    logger.error(errorMsg);
                    throw new VectorizeRequestException(
                        errorMsg, requestJson, responseBody, url, apiKey, model,
                        System.currentTimeMillis() - startTime, statusCode, null);
                }

                // 解析响应
                JSONObject responseJson = JSON.parseObject(responseBody);
                
                // 判断响应格式：OpenAI 格式或原有格式
                List<Number> vectorList = null;
                Integer dimension = null;
                
                if (responseJson.containsKey("data") && responseJson.getJSONArray("data") != null) {
                    // OpenAI 格式：{"data": [{"embedding": [...]}]}
                    JSONObject dataItem = responseJson.getJSONArray("data").getJSONObject(0);
                    if (dataItem != null && dataItem.containsKey("embedding")) {
                        vectorList = dataItem.getList("embedding", Number.class);
                        dimension = vectorList != null ? vectorList.size() : null;
                    }
                } else if (responseJson.containsKey("vector")) {
                    // 原有格式：{"vector": [...], "dimension": ...}
                    vectorList = responseJson.getList("vector", Number.class);
                    dimension = responseJson.getInteger("dimension");
                }
                
                if (vectorList == null || vectorList.isEmpty()) {
                    String errorMsg = String.format("向量化接口返回格式错误，无法解析向量数据，URL: %s, 响应: %s", url, responseBody);
                    logger.error(errorMsg);
                    throw new RuntimeException(errorMsg);
                }
                
                float[] vector = new float[vectorList.size()];
                for (int i = 0; i < vectorList.size(); i++) {
                    vector[i] = vectorList.get(i).floatValue();
                }

                // 获取维度
                if (dimension == null) {
                    dimension = vector.length;
                }

                Integer promptTokens = null;
                Integer completionTokens = null;
                Integer totalTokens = null;
                JSONObject usage = responseJson.getJSONObject("usage");
                if (usage != null) {
                    promptTokens = usage.getInteger("prompt_tokens");
                    completionTokens = usage.getInteger("completion_tokens");
                    totalTokens = usage.getInteger("total_tokens");
                }

                logger.info("向量化成功，URL: {}, 维度: {}", url, dimension);

                return new VectorizeResult(
                    vector,
                    dimension,
                    requestJson,
                    responseBody,
                    url,
                    apiKey,
                    model,
                    System.currentTimeMillis() - startTime,
                    promptTokens,
                    completionTokens,
                    totalTokens
                );
            });
        } catch (java.net.ConnectException e) {
            String errorMsg = String.format("无法连接到向量化服务，URL: %s, 错误: %s。请检查向量化服务是否已启动（默认地址: http://localhost:8001）", url, e.getMessage());
            logger.error(errorMsg, e);
            throw new VectorizeRequestException(errorMsg, null, null, url, apiKey, model,
                System.currentTimeMillis() - startTime, null, e);
        } catch (org.apache.hc.core5.http.ConnectionClosedException e) {
            String errorMsg = String.format("向量化服务连接已关闭，URL: %s, 错误: %s", url, e.getMessage());
            logger.error(errorMsg, e);
            throw new VectorizeRequestException(errorMsg, null, null, url, apiKey, model,
                System.currentTimeMillis() - startTime, null, e);
        } catch (java.net.SocketTimeoutException e) {
            String errorMsg = String.format("向量化接口请求超时，URL: %s, 错误: %s", url, e.getMessage());
            logger.error(errorMsg, e);
            throw new VectorizeRequestException(errorMsg, null, null, url, apiKey, model,
                System.currentTimeMillis() - startTime, null, e);
        } catch (java.io.IOException e) {
            String errorMsg = String.format("向量化接口网络IO异常，URL: %s, 错误: %s", url, e.getMessage());
            logger.error(errorMsg, e);
            throw new VectorizeRequestException(errorMsg, null, null, url, apiKey, model,
                System.currentTimeMillis() - startTime, null, e);
        } catch (Exception e) {
            String errorMsg = String.format("向量化接口调用异常，URL: %s, 错误: %s", url, e.getMessage());
            logger.error(errorMsg, e);
            throw new VectorizeRequestException(errorMsg, null, null, url, apiKey, model,
                System.currentTimeMillis() - startTime, null, e);
        }
    }

    /**
     * 向量化结果
     */
    public static class VectorizeResult {
        private final float[] vector;
        private final Integer dimension;
        private final String requestPayload;
        private final String responsePayload;
        private final String requestUrl;
        private final String apiKey;
        private final String model;
        private final Long durationMs;
        private final Integer promptTokens;
        private final Integer completionTokens;
        private final Integer totalTokens;

        public VectorizeResult(float[] vector,
                               Integer dimension,
                               String requestPayload,
                               String responsePayload,
                               String requestUrl,
                               String apiKey,
                               String model,
                               Long durationMs,
                               Integer promptTokens,
                               Integer completionTokens,
                               Integer totalTokens) {
            this.vector = vector;
            this.dimension = dimension;
            this.requestPayload = requestPayload;
            this.responsePayload = responsePayload;
            this.requestUrl = requestUrl;
            this.apiKey = apiKey;
            this.model = model;
            this.durationMs = durationMs;
            this.promptTokens = promptTokens;
            this.completionTokens = completionTokens;
            this.totalTokens = totalTokens;
        }

        public float[] getVector() {
            return vector;
        }

        public Integer getDimension() {
            return dimension;
        }

        public String getRequestPayload() {
            return requestPayload;
        }

        public String getResponsePayload() {
            return responsePayload;
        }

        public String getRequestUrl() {
            return requestUrl;
        }

        public String getApiKey() {
            return apiKey;
        }

        public String getModel() {
            return model;
        }

        public Long getDurationMs() {
            return durationMs;
        }

        public Integer getPromptTokens() {
            return promptTokens;
        }

        public Integer getCompletionTokens() {
            return completionTokens;
        }

        public Integer getTotalTokens() {
            return totalTokens;
        }
    }

    public static class VectorizeRequestException extends RuntimeException {
        private final String requestPayload;
        private final String responsePayload;
        private final String requestUrl;
        private final String apiKey;
        private final String model;
        private final Long durationMs;
        private final Integer statusCode;

        public VectorizeRequestException(String message,
                                         String requestPayload,
                                         String responsePayload,
                                         String requestUrl,
                                         String apiKey,
                                         String model,
                                         Long durationMs,
                                         Integer statusCode,
                                         Throwable cause) {
            super(message, cause);
            this.requestPayload = requestPayload;
            this.responsePayload = responsePayload;
            this.requestUrl = requestUrl;
            this.apiKey = apiKey;
            this.model = model;
            this.durationMs = durationMs;
            this.statusCode = statusCode;
        }

        public String getRequestPayload() {
            return requestPayload;
        }

        public String getResponsePayload() {
            return responsePayload;
        }

        public String getRequestUrl() {
            return requestUrl;
        }

        public String getApiKey() {
            return apiKey;
        }

        public String getModel() {
            return model;
        }

        public Long getDurationMs() {
            return durationMs;
        }

        public Integer getStatusCode() {
            return statusCode;
        }
    }
}

