package com.cy.guoyan.admin.module.ai.service.aiservice;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.cy.guoyan.admin.framework.common.util.object.BeanUtils;
import com.cy.guoyan.admin.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.cy.guoyan.admin.framework.security.core.util.SecurityFrameworkUtils;
import com.cy.guoyan.admin.module.ai.controller.admin.chatdocument.vo.ChatDocumentSaveReqVO;
import com.cy.guoyan.admin.module.ai.controller.admin.chatknowledgedataset.vo.ChatKnowledgeDatasetSaveReqVO;
import com.cy.guoyan.admin.module.ai.controller.admin.chatmessage.vo.ChatMessageRespVO;
import com.cy.guoyan.admin.module.ai.controller.admin.chatmessage.vo.ChatMessageSendReqVO;
import com.cy.guoyan.admin.module.ai.dal.dataobject.chatapp.ChatAppDO;
import com.cy.guoyan.admin.module.ai.dal.dataobject.chatdocument.ChatDocumentDO;
import com.cy.guoyan.admin.module.ai.dal.dataobject.chatknowledgedataset.ChatKnowledgeDatasetDO;
import com.cy.guoyan.admin.module.ai.dal.dataobject.chatmessage.ChatMessageDO;
import com.cy.guoyan.admin.module.ai.dal.dataobject.chatsession.ChatSessionDO;
import com.cy.guoyan.admin.module.ai.dal.mysql.chatapp.ChatAppMapper;
import com.cy.guoyan.admin.module.ai.dal.mysql.chatdocument.ChatDocumentMapper;
import com.cy.guoyan.admin.module.ai.dal.mysql.chatknowledgedataset.ChatKnowledgeDatasetMapper;
import com.cy.guoyan.admin.module.ai.dal.mysql.chatmessage.ChatMessageMapper;
import com.cy.guoyan.admin.module.ai.dal.mysql.chatsession.ChatSessionMapper;
import com.cy.guoyan.admin.module.ai.util.DifyClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
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.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

import static com.cy.guoyan.admin.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.cy.guoyan.admin.module.system.enums.ErrorCodeConstants.CHAT_APP_NOT_EXISTS;
import static com.cy.guoyan.admin.module.system.enums.ErrorCodeConstants.CHAT_KNOWLEDGE_DATASET_NOT_EXISTS;
import static java.nio.charset.StandardCharsets.UTF_8;

@Service
@Qualifier("ragflow")
@Slf4j
public class RagFlowService implements AiService {

    @Resource
    private ChatKnowledgeDatasetMapper chatKnowledgeDatasetMapper;

    @Resource
    private ChatDocumentMapper chatDocumentMapper;

    @Value("${ragflow.apiKey}")
    private String ragflowApiKey;


    @Value("${ragflow.endpoint}")
    private String ragflowBaseUrl;

    @Resource
    private ChatSessionMapper chatSessionMapper;

    @Resource
    private ChatMessageMapper chatMessageMapper;

    @Resource
    private ChatAppMapper chaAppMapper;

    private final DifyClient difyClient;

    public RagFlowService(DifyClient difyClient) {
        this.difyClient = difyClient;
    }

    private HttpURLConnection prepare(String method, String path) throws IOException {
        URL url = new URL(ragflowBaseUrl + path);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod(method);
        conn.setRequestProperty("Authorization", "Bearer " + ragflowApiKey);
        return conn;
    }

    /** 读取响应 body 或抛异常 */
    private String readResponse(HttpURLConnection conn) throws IOException {
        int code = conn.getResponseCode();
        InputStream is = (code >= 200 && code < 300) ? conn.getInputStream() : conn.getErrorStream();
        if (is == null) throw new IOException("无返回流，HTTP " + code);
        String body;
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, UTF_8))) {
            body = reader.lines().collect(Collectors.joining());
        }
        if (code < 200 || code >= 300) {
            throw new IOException("Dify 错误 HTTP " + code + "，" + body);
        }
        return body;
    }

    private LocalDateTime epoch2Local(Long epoch) {
        return epoch == null ? null : LocalDateTime.ofInstant(
                Instant.ofEpochSecond(epoch), ZoneId.systemDefault());
    }

    @Override
    public JSONObject createDataset(ChatKnowledgeDatasetSaveReqVO vo)throws IOException {
        JSONObject body = new JSONObject();
        body.put("name", vo.getName());
        body.put("description", vo.getDescription());
//        body.put("language", "Chinese");
//        body.put("embedding_model", vo.getEmbeddingModel());
//        body.put("permission", "team");

        HttpURLConnection conn = prepare("POST", "/api/v1/datasets");
        conn.setDoOutput(true);
        conn.setRequestProperty("Content-Type", "application/json");
        try (OutputStream os = conn.getOutputStream()) {
            os.write(body.toJSONString().getBytes(UTF_8));
        }
        String resp = readResponse(conn);

        return JSONObject.parseObject(resp);
    }

    @Override
    public void updateDataset(ChatKnowledgeDatasetSaveReqVO updateReqVO) {
        // 校验存在
        validateChatKnowledgeDatasetExists(updateReqVO.getId());
        // 更新
        ChatKnowledgeDatasetDO updateObj = BeanUtils.toBean(updateReqVO, ChatKnowledgeDatasetDO.class);
        chatKnowledgeDatasetMapper.updateById(updateObj);
    }

    @Override
    public String deleteDataset(String datasetId) throws IOException {
        JSONArray idsArray = new JSONArray(Collections.singletonList(datasetId));
        JSONObject body = new JSONObject();
        body.put("ids", idsArray);
        HttpURLConnection conn = prepare("DELETE", "/api/v1/datasets");
        log.info("删除知识库：{}", datasetId);

        conn.setDoOutput(true);
        conn.setRequestProperty("Content-Type", "application/json");
        try (OutputStream os = conn.getOutputStream()) {
            os.write(body.toJSONString().getBytes(UTF_8));
        }

        return readResponse(conn);

    }

    private void validateChatKnowledgeDatasetExists(Long id) {
        if (chatKnowledgeDatasetMapper.selectById(id) == null) {
            throw exception(CHAT_KNOWLEDGE_DATASET_NOT_EXISTS);
        }
    }

    @Override
    public JSONObject createDocumentByFile(MultipartFile file, ChatDocumentSaveReqVO createReqVO,File tmpFile) throws IOException {

        return difyClient.ragflowCreateDocumentByFile(Collections.singletonList(tmpFile),createReqVO.getName(), createReqVO.getDatasetId());
    }

    @Override
    public JSONObject getDocumentIndexingStatus(String datasetId, String documentId) throws IOException {

        ChatDocumentDO doc = chatDocumentMapper.selectById(documentId);

        new Thread(() -> {
            try {
                int waited = 0;
                while (waited < 60 * 60 * 24) {
                    // 查询文档解析状态
                    JSONObject status = difyClient.getRagflowDocumentStatus(datasetId, doc.getRagflowDocumentId());
                    JSONArray array = status.getJSONObject("data").getJSONArray("docs");
                    if (array == null || array.isEmpty()) {
                        log.warn("ragflow文档处理状态为空 name={} 状态：{}", doc.getName(), status);
                        return;
                    }
                    JSONObject item = array.getJSONObject(0);
                    String state = item.getString("run");
                    log.info("ragflow文档处理状态 name={} 状态：{}", doc.getName(), state);

                    if ("DONE".equals(state)) {
                        doc.setIndexingStatus("completed");

                        log.info("ragflow文档处理完成 name={} ✅", doc.getName());
                        // 更新文档状态
                        doc.setRagflowStatus("DONE");

                        chatDocumentMapper.updateById(doc);
                        return;
                    } else if ("FAILED".equals(state)) {
                        log.warn("ragflow文档处理失败 name={} ❌ 错误：{}", doc.getName(), status.getString("error"));
                        // 可更新失败状态
                        doc.setRagflowStatus("FAIL");
                        chatDocumentMapper.updateById(doc);
                        return;
                    }
                    Thread.sleep(5000);
                    waited += 5;
                }
                log.warn("轮询超时 name={} ⏰", doc.getName());
            } catch (Exception e) {
                log.error("异步轮询 ragflow文件 状态失败", e);
            }
        }).start();
        return difyClient.ragflowDocumentChunks(datasetId, Collections.singletonList(doc.getRagflowDocumentId()));
    }

    @Override
    public void deleteDocument(String datasetId, String documentId) throws IOException {
        difyClient.deleteRagFlowDocuments(datasetId, documentId);
    }

    @Override
    public ChatMessageRespVO sendChatMessageBlock(ChatMessageSendReqVO sendReqVO) throws IOException {
        String conversationId = sendReqVO.getConversationId();
        // 1. 从登录上下文取用户昵称
        String nickname = String.valueOf(SecurityFrameworkUtils.getLoginUser().getInfo().get("nickname"));
        ChatMessageDO msg = new ChatMessageDO();
        msg.setUserId(nickname);
        msg.setContent(sendReqVO.getQuery());
        msg.setRole("user");
        if (StringUtils.isNotBlank(conversationId)) {
            msg.setConversationId(conversationId);
        }
        ChatAppDO appDO = getAppKey(sendReqVO);

        // 2. 构造请求体
        JSONObject body = new JSONObject();
        body.put("question", sendReqVO.getQuery());
        if (StringUtils.isNotBlank(conversationId)) {
            body.put("session_id", conversationId);
        }else {
            String session = createChatSession(appDO.getAppKey(), sendReqVO.getQuery(), nickname);
            body.put("session_id", session);
        }
        body.put("user_id", nickname);
        body.put("stream", false);
        chatMessageMapper.insert(msg);

        log.info("ragflow 请求体: {}", body.toJSONString());
        // 3. 发起 HTTP POST
        URL url = new URL(ragflowBaseUrl + "/api/v1/chats/"+appDO.getAppKey()+"/completions");
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestProperty("Authorization", "Bearer " + ragflowApiKey);
        conn.setRequestProperty("Content-Type", "application/json");
        conn.setDoOutput(true);
        conn.setRequestMethod("POST");
        try (OutputStream os = conn.getOutputStream()) {
            os.write(body.toJSONString().getBytes(StandardCharsets.UTF_8));
        }

        // 5. 读取响应（
        int code = conn.getResponseCode();
        InputStream rawStream = (code >= 200 && code < 300)
                ? conn.getInputStream()
                : conn.getErrorStream();

        String respStr;
        if (rawStream == null) {
            respStr = "ragflow API 无返回流，HTTP 状态码：" + code;
        } else {
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(rawStream, StandardCharsets.UTF_8))) {
                respStr = reader.lines().collect(Collectors.joining());
            }
        }

        if (code < 200 || code >= 300) {
            throw new IOException("ragflow API返回错误: HTTP " + code + "，响应: " + respStr);
        }

        // 6. 解析并保存
        return parseAndSave(respStr, nickname,msg, appDO);
    }

    @Override
    public String getDocId(String docId, Integer appType) {
        ChatDocumentDO doc = chatDocumentMapper.selectOne(new LambdaQueryWrapperX<ChatDocumentDO>().eq(ChatDocumentDO::getRagflowDocumentId, docId));
        return doc.getFilePath();
    }

    private ChatMessageRespVO parseAndSave(String jsonStr, String nickname, ChatMessageDO msg,  ChatAppDO appDO) {
        JSONObject data = JSONObject.parseObject(jsonStr);
        log.info("ragflow API 响应: {}", jsonStr.toString());
        JSONObject json = JSON.parseObject(data.getString("data"));
        String did = json.getString("session_id");

        // 6.1 本地如果尚未存 ragflow 的 conversation_id，则更新
        ChatSessionDO session = chatSessionMapper.selectOne(new LambdaQueryWrapperX<ChatSessionDO>().eq(ChatSessionDO::getConversationId, did));
        if (session == null) {
            session = new ChatSessionDO();
            session.setName(msg.getContent());
            session.setUserId(nickname);
            session.setConversationId(did);
            session.setAppId(appDO.getId());
            chatSessionMapper.insert(session);
        }
        chatMessageMapper.updateById(msg.setConversationId(did).setSessionId(session.getId()));
        // 6.2 保存消息
        ChatMessageDO m = new ChatMessageDO();
        m.setConversationId(did);
        m.setSessionId(session.getId());
        m.setUserId(nickname);
        m.setMessageId(json.getString("id"));
        m.setContent(json.getString("answer"));
        m.setRole("assistant");
        m.setMetadataJson(json.toJSONString());
        chatMessageMapper.insert(m);

        // 7. 组装 VO 返回
        ChatMessageRespVO vo = new ChatMessageRespVO();
        vo.setSessionId(session.getId());
        vo.setConversationId(did);
        vo.setUserId(nickname);
        vo.setMessageId(m.getMessageId());
        vo.setContent(m.getContent());
        vo.setRole(m.getRole());
        vo.setMessageTime(m.getCreateTime());
        vo.setStatus("completed");
        vo.setResponseJson(jsonStr);
        vo.setMetadataJson(json);
        return vo;
    }

    public ChatAppDO getAppKey(ChatMessageSendReqVO sendReqVO) {
        ChatAppDO appDO = new ChatAppDO();
        if (StringUtils.isBlank(sendReqVO.getName())) {
            throw exception(CHAT_APP_NOT_EXISTS);
        } else {
            if (sendReqVO.getNetworking() == true) {
                appDO = chaAppMapper.selectOne(new LambdaQueryWrapperX<ChatAppDO>().like(ChatAppDO::getAppName, sendReqVO.getName()).eq(ChatAppDO::getNetworking, 1).eq(ChatAppDO::getAppType, 2));
            }else {
                appDO = chaAppMapper.selectOne(new LambdaQueryWrapperX<ChatAppDO>().like(ChatAppDO::getAppName, sendReqVO.getName()).eq(ChatAppDO::getNetworking, 0).eq(ChatAppDO::getAppType, 2));
            }
            if (appDO == null) {
                throw exception(CHAT_APP_NOT_EXISTS);
            }
            return appDO;
        }
    }

    public String createChatSession(String chatId, String name, String userId) throws IOException {
        String url = String.format("%s/api/v1/chats/%s/sessions", ragflowBaseUrl, chatId);

        // 创建请求体
        JSONObject body = new JSONObject();
        body.put("name", name);
        if (userId != null && !userId.isEmpty()) {
            body.put("user_id", userId);
        }

        // 创建 POST 请求
        HttpPost post = new HttpPost(url);
        post.setHeader("Content-Type", "application/json");
        post.setHeader("Authorization", "Bearer " + ragflowApiKey);
        post.setEntity(new StringEntity(body.toJSONString(), StandardCharsets.UTF_8));

        // 执行请求
        try (CloseableHttpClient client = HttpClients.createDefault();
             CloseableHttpResponse response = client.execute(post)) {

            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode != 200) {
                throw new IOException("Failed to create session, status: " + statusCode);
            }

            // 读取响应体
            String jsonString = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
            JSONObject json = JSONObject.parseObject(jsonString);

            // 提取会话 ID
            return json.getJSONObject("data").getString("id");
        }
    }
}
