package com.backendsys.modules.sdk.deepseek.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.backendsys.exception.CustException;
import com.backendsys.modules.ai.chat.entity.Chat;
import com.backendsys.modules.ai.chat.entity.ChatCompletionParam;
import com.backendsys.modules.ai.chat.entity.ChatResult;
import com.backendsys.modules.ai.chat.entity.ChatSseMessage;
import com.backendsys.modules.common.config.redis.utils.RedisUtil;
import com.backendsys.modules.sdk.bocha.entity.BochaParam;
import com.backendsys.modules.sdk.bocha.service.BochaService;
import com.backendsys.modules.sdk.deepseek.entity.DSRequest;
import com.backendsys.modules.sdk.deepseek.entity.DSRequestMessage;
import com.backendsys.modules.sdk.deepseek.service.DeepSeekClient;
import com.backendsys.modules.sse.entity.SseResponse;
import com.backendsys.modules.sse.entity.SseResponseEnum;
import com.backendsys.modules.sse.utils.SseUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;

import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * DeepseekAPI 工具类 (by Mure 2025/02/27)
 *
 * 开放平台：https://platform.deepseek.com/
 * 官方文档：https://api-docs.deepseek.com/zh-cn/
 */
@Service
public class DeepSeekClientImpl implements DeepSeekClient {

    @Value("${deepseek-api.url}")
    private String API_URL;
    @Value("${deepseek-api.api-key}")
    private String API_KEY;

    @Value("${spring.application.name}")
    private String APPLICATION_NAME;

    @Autowired
    private SseUtil sseUtil;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private BochaService bochaService;

    /**
     * [DeepSeek] 发起对话
     * - 文档：https://api-docs.deepseek.com/zh-cn/api/create-chat-completion
     */
    @Override
    public ChatResult chatCompletion(ChatCompletionParam chatCompletionParam) {

        // 参数化
        Long user_id = chatCompletionParam.getUser_id();
        String model = chatCompletionParam.getModel();
        String prompt = chatCompletionParam.getPrompt();
        String history_code = chatCompletionParam.getHistory_code();
        List<Chat> chatList = chatCompletionParam.getChatList();
        Boolean internet = chatCompletionParam.getInternet();

        // 定义作用于全局的变量
        Long replyDuration = 0L;
        Boolean isThinking = false;
        String requestOfRedisKey = APPLICATION_NAME + "-chat-history-" + history_code;
        StringBuilder allReplyContent = new StringBuilder();
        StringBuilder allThinkContent = new StringBuilder();

        ChatResult chatResult = new ChatResult();
        try {
            System.out.println("向模型: " + model + " 提问: " + prompt);

            // 记录请求开始时间
            long allStartTime = System.currentTimeMillis();

            // 【多轮对话】
            // https://api-docs.deepseek.com/zh-cn/guides/multi_round_chat

            // - (还没做) 接口做最大问答限制 (1-字数限制, 2-次数限制)
            // - (还没做) 超过50条记录的限制

            // 加入上下文历史对话
            System.out.println("---- 历史对话 (history_code): " + history_code + " ----");

            List<DSRequestMessage> messages = new ArrayList<>();
            if (chatList != null && !chatList.isEmpty()) {
                chatList.stream().forEach(chat -> {
                    if (!"THINK".equals(chat.getContent_type())) {
                        messages.add(new DSRequestMessage(chat.getRole(), chat.getContent()));
                    }
                });
                // 反转列表
                Collections.reverse(messages);
            }

            // -- [博查] Web Search API ----------------------------------------------
            if (internet) {

                // 远程查询、统计接口时间、设置返回参数
                long internetStartTime = System.currentTimeMillis();
                JsonNode searchResult = bochaService.WebSearch(new BochaParam(prompt));
                String context = bochaService.WebSearchToString(searchResult);
                long internetEndTime = System.currentTimeMillis();
                chatResult.setInternet_duration(internetStartTime - internetEndTime);
                chatResult.setInternet_content(context);

                // 将搜索结果作为上下文添加到消息中
                messages.add(new DSRequestMessage("system", context));
                messages.add(new DSRequestMessage("user", "在回答时引用以上全部数据进行分析")); // 的 "name"、"summary"
                messages.add(new DSRequestMessage("assistant", "好的"));

                // [SSE] 发送消息
                ChatSseMessage chatSearchSseMessage = new ChatSseMessage("SEARCH", context, null, history_code);
                sseUtil.send(user_id, new SseResponse(SseResponseEnum.OLLAMA, chatSearchSseMessage).toJsonStr());
            }
            // -----------------------------------------------------------------------

            // 新的对话内容
            messages.add(new DSRequestMessage("user", prompt));

            // 输出全部对话内容
            messages.stream().forEach(msg -> {
                System.out.println("[" + msg.getRole() + "]: " + msg.getContent());
            });
            System.out.println("---------------------------------------------------------------------");


            // 构建请求体
            // - model: (deepseek-chat: 对话模型, deepseek-reasoner: 推理模型)
            DSRequest body = new DSRequest();
            body.setModel(model);
            body.setMessages(messages);
            body.setStream(true);

            // 调用 Deepseek API
            try (CloseableHttpClient client = HttpClients.createDefault()) {

                HttpPost request = new HttpPost(API_URL + "/chat/completions");
                request.setHeader("Content-Type", "application/json");
                request.setHeader("Authorization", "Bearer " + API_KEY);

                ObjectMapper objectMapper = new ObjectMapper();
                String requestBody = objectMapper.writeValueAsString(body);
                request.setEntity(new StringEntity(requestBody, StandardCharsets.UTF_8));

                try (
                    CloseableHttpResponse response = client.execute(request);
                    BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), StandardCharsets.UTF_8))
                ) {

                    long apiDuration = System.currentTimeMillis() - allStartTime;   // 接口耗时
                    
                    long thinkStartTime = 0L;                            // 开始思考时间
                    long thinkDuration = 0L;                             // 思考耗时


                    System.out.println("API 调用耗时: " + apiDuration + " 毫秒");
                    System.out.println("---- 开始流式回答: ------------------------------------");

                    // [SSE] 发送消息
                    ChatSseMessage chatLoadingSseMessage = new ChatSseMessage("LOADING", "正在思考", null, history_code);
                    sseUtil.send(user_id, new SseResponse(SseResponseEnum.DEEPSEEK, chatLoadingSseMessage).toJsonStr());



                    String line;
                    while ((line = reader.readLine()) != null) {

                        // 判断是否中止
                        if (ObjectUtil.isEmpty(redisUtil.getCacheObject(requestOfRedisKey))) {
                            System.out.println("中止!");
                            request.abort();
                            // 流程结束后，删除锁
                            redisUtil.delete(requestOfRedisKey);
                            break;
                        }

                        // System.out.println(line);
                        /*
                            ---------------------- line ----------------------
                            data: {
                                "id":"6fc7b0e0-6ba1-4050-b7f7-924dd3559102", "object":"chat.completion.chunk",
                                "created":1740550788, "model":"deepseek-chat", "system_fingerprint":"fp_3a5770e1b4_prod0225",
                                "choices":[
                                    {
                                        "index":0,
                                        "delta":{ "content": "我是", "reasoning_content":null },
                                        "logprobs":null, "finish_reason":null
                                    }
                                ]
                            }
                            --------------------------------------------------
                         */

                        if (line.startsWith("data: ")) {
                            String jsonData = line.substring(6);
                            if ("[DONE]".equals(jsonData)) break;
                            JsonNode node = objectMapper.readTree(jsonData);
                            JsonNode delta = node.path("choices").path(0).path("delta");

                            // [推理] Think (仅 deepseek-reasoner: 推理模型 支持)
                            String reasoning_content = delta.path("reasoning_content").asText("");
                            if (!reasoning_content.isEmpty()) {

                                // 开始思考
                                if (!isThinking) {
                                    isThinking = true;
                                    thinkStartTime = System.currentTimeMillis();
                                }

                                System.out.println("reasoning_content: " + reasoning_content);

                                // [SSE] 发送消息
                                ChatSseMessage chatSseMessage = new ChatSseMessage("THINK", reasoning_content, null, history_code);
                                sseUtil.send(user_id, new SseResponse(SseResponseEnum.DEEPSEEK, chatSseMessage).toJsonStr());

                                // 收集推理内容
                                allThinkContent.append(reasoning_content);
                            }

                            // [回答] Reply
                            String content = delta.path("content").asText("");
                            if (!content.isEmpty()) {

                                // System.out.println("content: " + content);

                                // 停止思考，并计算思考耗时
                                if (isThinking) {
                                    isThinking = false;
                                    thinkDuration = thinkStartTime - allStartTime;
                                    System.out.println("推理耗时: " + thinkDuration + "毫秒");
                                    System.out.println("-----------------------------------------------");

                                    // [SSE] 发送消息
                                    ChatSseMessage chatSseMessage = new ChatSseMessage("THINK", "[DONE][THINK]", thinkDuration, history_code);
                                    sseUtil.send(user_id, new SseResponse(SseResponseEnum.DEEPSEEK, chatSseMessage).toJsonStr());

                                }

                                // [SSE] 发送消息
                                ChatSseMessage chatSseMessage = new ChatSseMessage("REPLY", content, null, history_code);
                                sseUtil.send(user_id, new SseResponse(SseResponseEnum.DEEPSEEK, chatSseMessage).toJsonStr());

                                // 收集回答内容
                                allReplyContent.append(content);
                            }


                        }
                    }

                    System.out.println("-------------------- 结束流式回答. --------------------");
                    replyDuration = System.currentTimeMillis() - allStartTime;

                    System.out.println("全部推理: " + allThinkContent);
                    System.out.println("全部回答: " + allReplyContent);
                    System.out.println("总输出耗时: " + replyDuration + " 毫秒");

                    chatResult.setReasoning_content(allThinkContent.toString());
                    chatResult.setReasoning_duration(thinkDuration);
                    chatResult.setContent(allReplyContent.toString());
                    chatResult.setContent_duration(replyDuration);

                    redisUtil.delete(requestOfRedisKey);

                    return chatResult;

                }
            } catch (Exception e) {
                System.out.println("Exception(1): " + e.getMessage());
                String message = e.getMessage();
                if (message.contains("Premature end of chunk coded message body: closing chunk expected")) {
                    message = "(请求中止)";
                }
                // [SSE] 发送消息
                String contentType = (isThinking ? "THINK_ABORT" : "REPLY_ABORT");
                ChatSseMessage chatSseMessage = new ChatSseMessage(contentType, e.getMessage(), replyDuration, history_code);
                sseUtil.send(user_id, new SseResponse(SseResponseEnum.DEEPSEEK, chatSseMessage).toJsonStr());
                redisUtil.delete(requestOfRedisKey);

                // chatResult.setContent(e.getMessage());
                // 由于中止导致的错误信息叠加 (一并保存进数据库)
                if (StrUtil.isNotEmpty(allThinkContent.toString())) {
                    if (isThinking) {
                        chatResult.setReasoning_content(allThinkContent.toString() + " " + message);
                    } else {
                        chatResult.setReasoning_content(allThinkContent.toString());
                    }
                }
                chatResult.setContent(allReplyContent.toString() + " " + message);

                return chatResult;
            }
        } catch (Exception e) {
            System.out.println("Exception(2): " + e.getMessage());
            // [SSE] 发送消息
            String contentType = (isThinking ? "THINK_ABORT" : "REPLY_ABORT");
            ChatSseMessage chatSseMessage = new ChatSseMessage(contentType, e.getMessage(), replyDuration, history_code);
            sseUtil.send(user_id, new SseResponse(SseResponseEnum.DEEPSEEK, chatSseMessage).toJsonStr());
            redisUtil.delete(requestOfRedisKey);
            chatResult.setContent(e.getMessage());
            return chatResult;
        } finally {
            // [SSE] 发送消息
            ChatSseMessage chatSseMessage = new ChatSseMessage("REPLY", "[DONE][REPLY]", replyDuration, history_code);
            sseUtil.send(user_id, new SseResponse(SseResponseEnum.DEEPSEEK, chatSseMessage).toJsonStr());
            redisUtil.delete(requestOfRedisKey);
        }

    }

    /**
     * [DeepSeek] 获得模型
     */
    @Override
    public JSONArray getModels(Long user_id) {

        // 调用 Deepseek API
        try (CloseableHttpClient client = HttpClients.createDefault()) {

            HttpGet request = new HttpGet(API_URL + "/models");
            request.setHeader("Content-Type", "application/json");
            request.setHeader("Authorization", "Bearer " + API_KEY);

            try (CloseableHttpResponse response = client.execute(request)) {

                // 检查响应状态码
                if (response.getStatusLine().getStatusCode() == 200) {

                    // 获取响应体
                    String responseBody = EntityUtils.toString(response.getEntity());
                    JSONObject jsonResponse = JSONUtil.parseObj(responseBody);
                    JSONArray models = jsonResponse.getJSONArray("data");
                    return models;

                } else {
                    throw new CustException("Failed to retrieve models. Status Code: " + response.getStatusLine().getStatusCode());
                }

            } catch (Exception e) {
                sseUtil.send(user_id, (new SseResponse(SseResponseEnum.DEEPSEEK, e.getMessage())).toJsonStr());
                throw new CustException(e.getMessage());
            }
        } catch (Exception e) {
            sseUtil.send(user_id, (new SseResponse(SseResponseEnum.DEEPSEEK, e.getMessage())).toJsonStr());
            throw new CustException(e.getMessage());
        }

    }

}