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

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.service.chatdocument.ChatDocumentService;
import com.cy.guoyan.admin.module.ai.util.DifyClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
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("dify")
@Slf4j
public class DifyService implements AiService {

    @Resource
    private ChatKnowledgeDatasetMapper chatKnowledgeDatasetMapper;

    @Resource
    private ChatDocumentService chatDocumentService;

    @Value("${dify.datasetsKey}")
    private String difyApiKey;

    @Value("${dify.baseUrl}")
    private String difyBaseUrl;

    @Resource
    private ChatSessionMapper chatSessionMapper;

    @Resource
    private ChatMessageMapper chatMessageMapper;

    @Resource
    private ChatDocumentMapper chatDocumentMapper;

    @Resource
    private ChatAppMapper chaAppMapper;

    private final DifyClient difyClient;

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

    private HttpURLConnection prepare(String method, String path) throws IOException {
        URL url = new URL(difyBaseUrl + path);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod(method);
        conn.setRequestProperty("Authorization", "Bearer " + difyApiKey);
        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;
    }


    @Override
    public JSONObject createDataset(ChatKnowledgeDatasetSaveReqVO vo)throws IOException {
        JSONObject body = new JSONObject();
        body.put("name", vo.getName());
        body.put("description", vo.getDescription());
        body.put("indexing_technique", "high_quality");
//        body.put("embedding_model", vo.getEmbeddingModel());
        body.put("auto_update", vo.getAutoUpdate());
        body.put("provider", "vendor");
        body.put("permission", "all_team_members");

        HttpURLConnection conn = prepare("POST", "/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 {
        HttpURLConnection conn = prepare("DELETE", "/v1/datasets/" + datasetId);
        log.info("删除知识库：{}", datasetId);
        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 {
        String datasetId = createReqVO.getDatasetId();
        String originalFilename = createReqVO.getName();

        Map<String, Object> requestData = new HashMap<>();
        requestData.put("datasetId", datasetId);
        requestData.put("name", originalFilename);
        requestData.put("indexing_technique", "high_quality");
        requestData.put("doc_form", "text_model");

        Map<String, Object> processRule = new HashMap<>();
        if (Objects.equals("custom",createReqVO.getProcessRuleMode())) {
            processRule.put("mode", "custom");
            // 预处理规则
            Map<String, Object> rules = new HashMap<>();
            List<Map<String, Object>> preProcessingRules = new ArrayList<>();
            Map<String, Object> rule1 = new HashMap<>();
            rule1.put("id", "remove_extra_spaces");
            rule1.put("enabled", true);
            preProcessingRules.add(rule1);
            Map<String, Object> rule2 = new HashMap<>();
            rule2.put("id", "remove_urls_emails");
            rule2.put("enabled", true);
            preProcessingRules.add(rule2);
            rules.put("pre_processing_rules", preProcessingRules);

            // 分段规则
            Map<String, Object> segmentation = new HashMap<>();
            segmentation.put("separator", "\n\n");
            segmentation.put("max_tokens", 500);
            rules.put("segmentation", segmentation);
            processRule.put("rules", rules);
        }else {
            processRule.put("mode", "automatic");
        }
        requestData.put("process_rule", processRule);

        return  difyClient.createDocumentByFile(tmpFile, originalFilename, requestData);
    }

    @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.getDocumentIndexingStatus(datasetId, doc.getBatch());
                    JSONArray array = status.getJSONArray("data");
                    if (array == null || array.isEmpty()) {
                        log.warn("dify文档处理状态为空 name={} 状态：{}", doc.getName(), status);
                        return;
                    }
                    JSONObject item = array.getJSONObject(0);
                    String state = item.getString("indexing_status");
                    Integer completedSegments = item.getInteger("completed_segments");
                    log.info("dify文档处理状态 name={} 状态：{},分段：{}", doc.getName(), state,  completedSegments);
                    if ("completed".equals(state)) {
                        if (doc.getId() == null) {
                            ChatDocumentDO aDo = chatDocumentMapper.selectOne("dataset_id", doc.getDatasetId(), "batch", doc.getBatch());
                            doc.setId(aDo.getId());
                            doc.setCreator(aDo.getCreator());
                            doc.setUpdater(aDo.getUpdater());
                            doc.setName(aDo.getName());
                        }
                        log.info("dify文档处理完成 name={} ✅",  doc.getName());
//                doc.setHitCount(completedSegments);
                        doc.setIndexingStatus("completed");
                        doc.setCompletedSegments(completedSegments);
                        try {
                            JSONObject jsonObject = difyClient.listDocuments(datasetId, doc.getName(), null, null);
                            JSONArray dataArray = jsonObject.getJSONArray("data");
                            if (!dataArray.isEmpty()) {
                                JSONObject firstDocument = dataArray.getJSONObject(0);
                                // 提取 "word_count" 字段
                                int wordCount = firstDocument.getIntValue("word_count");
                                doc.setWordCount(wordCount);
                            }
                        } catch (Exception e) {
                            log.error("dify获取文档信息失败", e);
                        }
                        chatDocumentMapper.updateById(doc);
                        return;
                    } else if ("failed".equals(state)) {
                        log.warn("dify文档处理失败 name={} ❌ 错误：{}", doc.getName(), status.getString("error"));
                        // 可更新失败状态
                        return;
                    }
                    Thread.sleep(5000);
                    waited += 5;
                }
                log.warn("轮询超时 name={} ⏰", doc.getName());


            } catch (Exception e) {
                log.error("异步轮询 Dify 状态失败", e);
            }
        }).start();


        return difyClient.getDocumentIndexingStatus(datasetId, doc.getBatch());

    }



    @Override
    public void deleteDocument(String datasetId, String documentId) throws IOException {
        difyClient.deleteDocument(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);
        }
        chatMessageMapper.insert(msg);
        ChatAppDO appDO = getAppKey(sendReqVO);

        // 2. 构造请求体
        JSONObject body = new JSONObject();
        body.put("query", sendReqVO.getQuery());
        body.put("inputs", Optional.ofNullable(sendReqVO.getInputs()).orElse(new HashMap<>()));
        if (StringUtils.isNotBlank(conversationId)) {
            body.put("conversation_id", conversationId);
        }
        body.put("user", nickname);
        body.put("response_mode", "blocking");
        body.put("auto_generate_name", true);
        body.put("enable_retrieval", true);
//        ChatKnowledgeDatasetDO datasetDO = chatKnowledgeDatasetMapper.selectById(sendReqVO.getDatasetId());
//        body.put("datasets", Collections.singletonList(datasetDO.getDatasetId()));
        log.info("Dify 请求体: {}", body.toJSONString());
        // 3. 发起 HTTP POST
        URL url = new URL(difyBaseUrl + "/v1/chat-messages");
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestProperty("Authorization", "Bearer " + appDO.getAppKey());
        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 = "Dify 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("Dify 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::getDifyDocumentId, docId));
        return doc.getFilePath();
    }

    private ChatMessageRespVO parseAndSave(String jsonStr, String nickname, ChatMessageDO msg,  ChatAppDO appDO) {
        JSONObject json = JSONObject.parseObject(jsonStr);
        String did = json.getString("conversation_id");

        // 6.1 本地如果尚未存 Dify 的 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);
            // ======== 补充：从 Dify 获取会话名称（同步 + 异步）=======
//            asyncUpdateSessionName(did, session.getId());

        }
        chatMessageMapper.updateById(msg.setConversationId(did).setSessionId(session.getId()));
        JSONObject metadata = json.getJSONObject("metadata");
        String metadataStr = metadata.toJSONString();
        // 6.2 保存消息
        ChatMessageDO m = new ChatMessageDO();
        m.setConversationId(did);
        m.setSessionId(session.getId());
        m.setUserId(nickname);
        m.setMessageId(json.getString("message_id"));
        m.setContent(json.getString("answer"));
        m.setRole("assistant");
        m.setMetadataJson(metadataStr);
        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(metadata);
        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, 1));
            }else {
                appDO = chaAppMapper.selectOne(new LambdaQueryWrapperX<ChatAppDO>().like(ChatAppDO::getAppName, sendReqVO.getName()).eq(ChatAppDO::getNetworking, 0).eq(ChatAppDO::getAppType, 1));
            }
            if (appDO == null) {
                throw exception(CHAT_APP_NOT_EXISTS);
            }
            return appDO;
        }
    }


}
