package com.ruoyi.cms.config;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.cms.domain.chat.ChatRequest;
import com.ruoyi.common.utils.StringUtils;
import lombok.var;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Component
public class ChatClient {
    // 超时设置（单位：秒）
    private static final int CONNECT_TIMEOUT = 30;
    private static final int WRITE_TIMEOUT = 30;
    private static final int READ_TIMEOUT = 300; // 流式响应不设置读取超时

    // 单例 OkHttp 客户端（复用连接池）
    private static final OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
            .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
            .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
            .build();

    private static final String CHAT_ENDPOINT = "chat";

    private static final String CHAT_HISTORY = "history";
    public static final List<String> TEXT_FILE_EXTENSIONS= Arrays.asList(".txt", ".md", ".html", ".doc", ".docx", ".pdf", ".ppt", ".pptx", ".csv", ".xls", ".xlsx");
    public static final List<String> IMAGE_FILE_EXTENSIONS=Arrays.asList(".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp");


    private ChatConfig chatConfig;
    @Autowired
    public void setChatConfig(ChatConfig chatConfig) {
        this.chatConfig = chatConfig;
    }
    @Value("${spring.profiles.active}")
    private String env;
    /**
     * 发送流式聊天请求
     * @param chatRequest 查询请求体
     * @param callback 流式响应回调接口
     */
    public void sendStreamingChat(ChatRequest chatRequest, StreamCallback callback) {
        String url=chatConfig.getBaseUrl()+chatConfig.getChatUrl();
        // 构建请求体
        String jsonBody = buildChatRequestBody(chatRequest, CHAT_ENDPOINT);
        // 构建请求
        Request request = null;

        try {
            RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"),jsonBody);
            request = new Request.Builder()
                    .url(url)
                    .addHeader("Content-Type", "application/json")
                    .addHeader("Authorization", "Bearer " + chatConfig.getApiKey())
                    .post(body).build();
        }catch (Exception e){
            e.printStackTrace();
            callback.onError(new RuntimeException("构建请求失败: " + e.getMessage(), e));
            return;
        }

        // 发送异步请求
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                callback.onError(new RuntimeException("请求发送失败: " + e.getMessage(), e));
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                try {
                    if (!response.isSuccessful()) {
                        String errorBody = response.body() != null ? response.body().string() : "无错误信息";
                        String errorMsg = String.format("API 响应错误: 状态码=%d, 错误信息=%s",
                                response.code(), errorBody);
                        System.err.println(errorMsg); // 打印详细错误
                        callback.onError(new RuntimeException(errorMsg));
                        return;
                    }

                    // 处理流式响应
                    ResponseBody body = response.body();
                    if (body == null) {
                        callback.onError(new RuntimeException("响应体为空"));
                        return;
                    }

                    // 逐行读取 SSE 格式的响应
                    try (var bufferedSource = body.source()) {
                        while (!bufferedSource.exhausted()) {
                            String chunk = bufferedSource.readUtf8Line();
                            if (chunk != null && !chunk.trim().isEmpty()) {
                                callback.onData(chunk);
                            }
                        }
                    }

                    // 通知流结束
                    callback.onComplete();

                } catch (Exception e) {
                    String errorMsg = "处理响应失败: " + e.getMessage();
                    System.err.println(errorMsg);
                    callback.onError(new RuntimeException(errorMsg, e));
                } finally {
                    response.close();
                }
            }
        });
    }

    /**
     * 构建聊天请求的 JSON 体
     */
    /**
     * 构建聊天请求的 JSON 体
     * 根据文档 [cite: 162-172] 修改为 Vision API 兼容格式
     */
    private String buildChatRequestBody(ChatRequest chatRequest, String key) {
        JSONObject chatObject = new JSONObject();

        // 1. 设置会话ID (如果有)
        if(StringUtils.isNotEmpty(chatRequest.getSessionId())){
            chatObject.put("chatId", chatRequest.getSessionId());
        }

        if("chat".equals(key)){
            // 基础参数设置
            chatObject.put("stream", true);
            chatObject.put("model", chatConfig.getModel()); // 需确保为 "qd-job-turbo"
            chatObject.put("user", chatRequest.getSessionId());

            // 2. 获取历史消息列表，如果为空则初始化
            JSONArray messages = chatRequest.getMessages();
            if (messages == null) {
                messages = new JSONArray();
            }

            // 3. 构建当前用户的多模态消息内容 (Multimodal Content) [cite: 162]
            JSONArray contentArray = new JSONArray();

            // 3.1 添加文本内容 [cite: 166, 172]
            if(StringUtils.isNotEmpty(chatRequest.getData())){
                JSONObject textPart = new JSONObject();
                textPart.put("type", "text");
                textPart.put("text", chatRequest.getData());
                contentArray.add(textPart);
            }

            // 3.2 添加文件内容 (图片、PDF、Excel等) [cite: 174, 180, 232]
            // 文档说明：image_url 字段兼容 PDF, Excel, PPT 等所有 OCR 支持的文件
            if(!CollectionUtils.isEmpty(chatRequest.getFileUrl())){
                for(String url : chatRequest.getFileUrl()){
                    String finalUrl = "";
                    if(Objects.equals(env, "pro")){
                         finalUrl = url.replace("https://fw.rc.qingdao.gov.cn/rgpp-api/api/ng", "http://10.213.6.207:19010");
                    }else {
                        finalUrl = url;
                    }
                    // 处理内网/外网地址映射 (保留你原有的逻辑)

                    JSONObject filePart = new JSONObject();
                    filePart.put("type", "image_url"); // 固定为 image_url [cite: 174]

                    JSONObject imageUrlObj = new JSONObject();
                    imageUrlObj.put("url", finalUrl); // 文件地址 [cite: 172]

                    filePart.put("image_url", imageUrlObj);
                    contentArray.add(filePart);
                }
            }

            // 4. 将当前消息封装为 User Message 对象并加入消息列表 [cite: 151, 163]
            // 只有当有内容（文本或文件）时才添加
            if (!contentArray.isEmpty()) {
                JSONObject currentUserMessage = new JSONObject();
                currentUserMessage.put("role", "user");
                currentUserMessage.put("content", contentArray); // content 为数组格式
                messages.add(currentUserMessage);
            }

            // 5. 将完整的消息列表放入请求体 [cite: 135]
            chatObject.put("messages", messages);

        } else {
            // 非 chat 场景的逻辑保留
            chatObject.put("appId", chatConfig.getAppId());
        }

        return chatObject.toJSONString();
    }

    /**
     * 简单的 JSON 转义处理（防止特殊字符破坏 JSON 格式）
     */
    private String escapeJson(String value) {
        if (value == null) return "";
        return value
                .replace("\\", "\\\\")
                .replace("\"", "\\\"")
                .replace("\b", "\\b")
                .replace("\f", "\\f")
                .replace("\n", "\\n")
                .replace("\r", "\\r")
                .replace("\t", "\\t");
    }

    /**
     * 发送聊天请求并返回完整JSON响应
     * @param chatRequest 用户输入的查询内容
     * @return 完整的JSON响应字符串
     * @throws IOException 网络请求异常
     */
    public String sendChatGuest(ChatRequest chatRequest) throws IOException {
        String url=chatConfig.getBaseUrl()+chatConfig.getGuestUrl();

        JSONArray array = chatRequest.getMessages();
        if(array==null||array.isEmpty()||array.size()==0){
            array = new JSONArray();
        }
        JSONObject contentObject = new JSONObject();
        contentObject.put("content","你是一个岗位招聘专家，请根据用户的问题生成用户下一步想要提出的问题。需要以用户的口吻进行生成。结合上下文中用户提出的问题以及助手回复的答案，需要猜测用户更进一步的需求，例如期望的薪资，期望的工作地点，掌握的技能。生成的问题举例：有没有薪资在9000以上的工作？我的学历是本科。我希望找国企。注意不仅限于这些，还要根据上下文。其次所有的问题应该限定在喀什。并且只生成3到4个。");
        contentObject.put("role","system");
        array.add(contentObject);
        JSONObject jsonBody = new JSONObject();
        jsonBody.put("stream",false);
        jsonBody.put("model",chatConfig.getModel());
        jsonBody.put("messages",array);

        // 构建请求（使用非流式响应模式）
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), String.valueOf(jsonBody));
        Request request = new Request.Builder()
                .url(url)
                .addHeader("Content-Type", "application/json")
                .addHeader("Authorization", "Bearer " + chatConfig.getApiKey())
                .post(requestBody).build();

        // 发送同步请求
        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                String errorBody = response.body() != null ? response.body().string() : "无错误信息";
                String errorMsg = String.format("API 响应错误: 状态码=%d, 错误信息=%s",
                        response.code(), errorBody);
                throw new IOException(errorMsg);
            }
            ResponseBody body = response.body();
            if (body == null) {
                throw new IOException("响应体为空");
            }
            JSONObject object = JSONObject.parseObject(body.string());
            String choices = object.getString("choices");
            if (choices != null && !choices.trim().isEmpty()) {
                JSONArray jsonArray = JSONArray.parseArray(choices);
                object = JSONObject.parseObject(jsonArray.getString(0));
                object = object.getJSONObject("message");
                String content = object.getString("content");// 消息内容

                return content;
            }
            return body.string();
        }
    }

    /**
     * 流式响应回调接口
     */
    public interface StreamCallback {
        /**
         * 接收分片数据
         * @param chunk SSE 格式的分片数据
         */
        void onData(String chunk);

        /**
         * 响应结束
         */
        void onComplete();

        /**
         * 发生错误
         * @param e 异常信息
         */
        void onError(Throwable e);
    }
}
