package com.ruoyi.web.llm.controller;

import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.framework.config.ServerConfig;
import com.ruoyi.llm.domain.CmcChat;
import com.ruoyi.llm.service.ICmcAgentService;
import com.ruoyi.llm.service.ICmcChatService;
import com.ruoyi.llm.service.ICmcTopicService;
import org.noear.solon.ai.chat.ChatModel;
import org.noear.solon.ai.chat.ChatResponse;
import org.noear.solon.ai.chat.ChatSession;
import org.noear.solon.ai.chat.message.AssistantMessage;
import org.noear.solon.ai.chat.message.ChatMessage;
import org.noear.solon.ai.chat.session.InMemoryChatSession;
import org.noear.solon.ai.mcp.McpChannel;
import org.noear.solon.ai.mcp.client.McpClientProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.ruoyi.common.utils.http.HttpUtils;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;


/**
 * mcp模型上下文协议Controller
 * 
 * @author cmc
 * @date 2025-04-08
 */
@RestController
@RequestMapping("/llm/mcp")
public class McpController extends BaseController
{
    private static final Logger log = LoggerFactory.getLogger(McpController.class);

    /** 仅允许上传 .docx 文件 */
    private static final String[] DOCX_EXTENSION = {"docx"};

    /** Python 脚本输出的文件名 */
    private static final String FORMATTED_OUTPUT_NAME = "formatted_output.docx";

    @Autowired
    private ICmcChatService cmcChatService;

    @Autowired
    private ICmcTopicService cmcTopicService;

    @Autowired
    private ICmcAgentService cmcAgentService;

    @Autowired
    private ServerConfig serverConfig;

    @Value("${cmc.llmService.url}")
    private String llmServiceUrl;

    @Value("${cmc.milvusService.url}")
    private String milvusServiceUrl;

    @Value("${cmc.pythonService.url:${cmc.docFormatter.url:}}")
    private String pythonServiceUrl;

    @Value("${cmc.docFormatter.templatePath}")
    private String docFormatterTemplatePath;

    /**
     * 自动调用mcp工具问答
     * @return
     */
    @GetMapping("/answer")
    public AssistantMessage answer(String topicId, String question) throws IOException {
        McpClientProvider clientProvider = McpClientProvider.builder()
                .channel(McpChannel.STREAMABLE_STATELESS )
                .url("http://localhost:8087/mcp/sse")
                .build();
        ChatModel chatModel = ChatModel.of(llmServiceUrl)
                .model("Qwen")
                .defaultToolAdd(clientProvider)
                .build();

        List<ChatMessage> messages = new ArrayList<>();
        CmcChat cmcChat = new CmcChat();
        cmcChat.setTopicId(topicId);
        List<CmcChat> cmcChatList = cmcChatService.selectCmcChatList(cmcChat);
        for (CmcChat chat : cmcChatList) {
            messages.add(ChatMessage.ofUser(chat.getInput()));
            messages.add(ChatMessage.ofAssistant(chat.getOutput()));
        }
        messages.add(ChatMessage.ofUser(question));
        ChatSession chatSession =  InMemoryChatSession.builder().messages(messages).build();
        ChatResponse response = chatModel.prompt(chatSession).call();
        String resultContent = response.lastChoice().getMessage().getResultContent();
        AssistantMessage assistantMessage;
        if (resultContent.startsWith("<tool_call>")) {
            String content = resultContent.replace("<tool_call>\n", "").replace("\n</tool_call>", "");
            JSONObject jsonObject = JSONObject.parseObject(content);
            String name = jsonObject.getString("name");
            JSONObject arguments = jsonObject.getJSONObject("arguments");
            if (arguments.getString("templatePath").contains("招标") || arguments.getString("templatePath").contains("询价")) {
                arguments.put("collectionName", "technical");
                String agentName = cmcAgentService.selectCmcAgentByAgentId(cmcTopicService.selectCmcTopicByTopicId(topicId).getAgentId()).getAgentName();
                arguments.put("agentName", agentName);
                arguments.put("title", question);
            }
            resultContent = clientProvider.callTool(name, arguments).getContent();
            assistantMessage = ChatMessage.ofAssistant(resultContent);
        }
        else
            throw new IOException("模型上下文工具未准备就绪，请重试");
        return assistantMessage;
    }

    /**
     * 上传 Word 文档并调用 Python 脚本进行格式化
     *
     * @param file 前端上传的 .docx 文件
     * @return 格式化后的文件下载路径
     */
    @PostMapping("/formatDoc")
    public AjaxResult formatDoc(MultipartFile file)
    {
        try
        {
            if (file == null || file.isEmpty())
            {
                return AjaxResult.error("上传文件不能为空");
            }

            // 1. 使用若依 FileUploadUtils 暂存上传文件到本地
            String uploadBaseDir = RuoYiConfig.getUploadPath();
            String resourcePath = FileUploadUtils.upload(uploadBaseDir, "docFormat", file, DOCX_EXTENSION);

            // 2. 将资源路径转换为服务器上的绝对路径
            String uploadedAbsolutePath = toAbsolutePath(resourcePath);
            log.info("Word 文档已暂存，绝对路径: {}", uploadedAbsolutePath);

            // 校验模版与脚本配置
            validateFormatterConfig();

            // 3. 调用 Python 格式化 HTTP 服务
            String serviceOutput = callDocFormatterService(uploadedAbsolutePath, docFormatterTemplatePath);
            log.info("文档格式化服务响应: {}", serviceOutput);

            // 4. 定位格式化后的输出文件（与上传文件同目录下的 formatted_output.docx）
            File outputFile = new File(new File(uploadedAbsolutePath).getParentFile(), FORMATTED_OUTPUT_NAME);
            if (!outputFile.exists())
            {
                return AjaxResult.error("文档格式化失败，未找到输出文件");
            }

            // 构造前端可访问的资源路径与下载 URL
            String outputResourcePath = buildOutputResourcePath(resourcePath);
            String downloadUrl = serverConfig.getUrl() + outputResourcePath;

            AjaxResult ajax = AjaxResult.success("文档格式化成功");
            ajax.put("fileName", outputResourcePath);
            ajax.put("url", downloadUrl);
            ajax.put("newFileName", FORMATTED_OUTPUT_NAME);
            ajax.put("originalFilename", file.getOriginalFilename());
            return ajax;
        }
        catch (Exception e)
        {
            log.error("Word 文档格式化失败", e);
            return AjaxResult.error("文档格式化失败: " + e.getMessage());
        }
    }

    /**
     * 将 /profile/... 形式的资源路径转为本地绝对路径
     */
    private String toAbsolutePath(String resourcePath)
    {
        return RuoYiConfig.getProfile() + StringUtils.substringAfter(resourcePath, Constants.RESOURCE_PREFIX);
    }

    /**
     * 根据上传文件的资源路径，推导格式化输出文件的资源路径
     */
    private String buildOutputResourcePath(String uploadedResourcePath)
    {
        int lastSlash = uploadedResourcePath.lastIndexOf('/');
        return uploadedResourcePath.substring(0, lastSlash + 1) + FORMATTED_OUTPUT_NAME;
    }

    /**
     * 校验格式化服务与模版文件是否可用
     */
    private void validateFormatterConfig() throws IOException
    {
        if (StringUtils.isEmpty(pythonServiceUrl))
        {
            throw new IOException("Python 服务 URL 未配置: cmc.pythonService.url");
        }
        if (StringUtils.isEmpty(docFormatterTemplatePath) || !new File(docFormatterTemplatePath).exists())
        {
            throw new IOException("Word 模版文件路径未配置或文件不存在: " + docFormatterTemplatePath);
        }

        String healthUrl = pythonServiceUrl.endsWith("/")
                ? pythonServiceUrl + "health"
                : pythonServiceUrl + "/health";
        String healthResponse = HttpUtils.sendGet(healthUrl);
        if (StringUtils.isEmpty(healthResponse) || !healthResponse.contains("\"status\":\"ok\""))
        {
            throw new IOException("Python 服务不可用，请确认已启动: " + pythonServiceUrl);
        }
    }

    /**
     * 调用 Python 格式化 HTTP 服务
     *
     * @param uploadedAbsolutePath 上传文件的绝对路径
     * @param templateAbsolutePath 模版文件的绝对路径
     * @return 服务返回的消息内容
     */
    private String callDocFormatterService(String uploadedAbsolutePath, String templateAbsolutePath) throws IOException
    {
        JSONObject body = new JSONObject();
        body.put("uploaded_file_path", uploadedAbsolutePath);
        body.put("template_file_path", templateAbsolutePath);
        body.put("output_filename", FORMATTED_OUTPUT_NAME);

        String formatUrl = pythonServiceUrl.endsWith("/")
                ? pythonServiceUrl + "doc/format"
                : pythonServiceUrl + "/doc/format";

        HttpURLConnection conn = (HttpURLConnection) new URL(formatUrl).openConnection();
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
        conn.setConnectTimeout(30_000);
        conn.setReadTimeout(300_000);
        conn.setDoOutput(true);

        try (OutputStream os = conn.getOutputStream())
        {
            os.write(body.toJSONString().getBytes(StandardCharsets.UTF_8));
        }

        int status = conn.getResponseCode();
        InputStream responseStream = status >= 400 ? conn.getErrorStream() : conn.getInputStream();
        if (responseStream == null)
        {
            throw new IOException("文档格式化服务无响应，HTTP 状态码: " + status);
        }

        StringBuilder responseBuilder = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(responseStream, StandardCharsets.UTF_8)))
        {
            String line;
            while ((line = reader.readLine()) != null)
            {
                responseBuilder.append(line);
            }
        }

        String responseBody = responseBuilder.toString();
        if (status >= 400)
        {
            throw new IOException("文档格式化服务请求失败，HTTP " + status + "，响应: " + responseBody);
        }

        JSONObject result = JSONObject.parseObject(responseBody);
        if (!Boolean.TRUE.equals(result.getBoolean("success")))
        {
            throw new IOException("文档格式化失败: " + result.getString("message"));
        }
        return result.getString("message");
    }
}
