package org.shop.crop;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.tencent.wework.Finance;
import lombok.extern.slf4j.Slf4j;
import org.shop.crop.constant.GlobalConstants;
import org.shop.crop.dao.CropConfig;
import org.shop.crop.dao.CropFile;
import org.shop.crop.dao.CropMessage;
import org.shop.crop.dto.BaseMessageDto;
import org.shop.crop.dto.ChatData;
import org.shop.crop.dto.MessagePullResponse;
import org.shop.crop.mapper.CropConfigMapper;
import org.shop.crop.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

@Slf4j
@RestController
public class CropController {

    @Autowired
    FinanceSdkManager sdkManager;

    @Autowired
    CropMessageService cropMessageService;

    @Autowired
    CropFileService cropFileService;

    @Autowired
    CropConfigMapper cropConfigMapper;

    @Autowired
    MediaContext mediaContext;

    @Autowired
    OssService ossService;

    private static final Object SDK_LOCK = new Object();

    @GetMapping("/crop/chatData")
    public List<CropMessage> chatData(Long startSeq, String corpId) throws IOException {
        synchronized (SDK_LOCK) {
            long limit = 100;
            long slice = Finance.NewSlice();
            long sdk = 0;

            try {
                sdk = sdkManager.initSdk(corpId, "mEizahrSF6axdfWtSK_f73a3j6-sV02hhyGG7ogmTpM");

                int ret = Finance.GetChatData(sdk, startSeq, limit, "", "", 1000, slice);
                if (ret != 0) {
                    System.out.println("调用sdk拉取消息接口失败,失败消息为 ret = " + ret);
                    //Finance.FreeSlice(slice);
                    return null;
                }
                String contentResult = Finance.GetContentFromSlice(slice);
                log.info("内容字符串" + contentResult);
                return decodeChatData(contentResult, startSeq, sdk, corpId);
            } catch (Exception e) {
                log.error("获取chat数据异常{}", e.getMessage());
                // 收集失败情况数据
                e.printStackTrace();
            } finally {
                Finance.FreeSlice(slice); // ✅ 无论如何都释放
                if (sdk != 0) {
                    try {
                        Finance.DestroySdk(sdk); // 重要！
                    } catch (Throwable t) {
                        log.warn("DestroySdk 失败", t);
                    }
                }
            }
        }
        return Collections.emptyList() ;
    }

    /**
     * 解密chatData数据
     *
     * @param contentResult 拉取到的JSON原文
     */
    public List<CropMessage> decodeChatData(String contentResult, Long startSeq, Long sdk, String corpId) throws IOException {
        // 1.基础信息解析
        List<CropMessage> messageList = new ArrayList<>();
        MessagePullResponse messagePullResponse = JSON.parseObject(contentResult, MessagePullResponse.class);
        if (messagePullResponse == null || messagePullResponse.getChatdata() == null) {
            return Collections.emptyList();
        }
        List<ChatData> chatdataList = messagePullResponse.getChatdata();
        // 2.解密数据
        ClassPathResource resource = new ClassPathResource("private_key.pem");
        if (!resource.exists()) {
            return Collections.emptyList();
        }
        String privateKey = StreamUtils.copyToString(resource.getInputStream(), StandardCharsets.UTF_8);
        // 3. 提取所有 seq
        log.info("chatdataList={} 加密数据", chatdataList);
        // 4. 查询数据库中已存在的 seq
        // 直接在业务代码中（或封装到 service）
        List<Long> existingSeqs = cropMessageService.lambdaQuery()
                .gt(CropMessage::getSeq, startSeq)
                .select(CropMessage::getSeq)
                .list()
                .stream()
                .map(CropMessage::getSeq)
                .collect(Collectors.toList());
        Set<Long> existingSeqSet = new HashSet<>(existingSeqs);
        log.info("existingSeqSet={} 加密数据", existingSeqSet);

        CropConfig config = cropConfigMapper.selectById(1);//后续优化
        Long maxSeq = config.getLastSeq();
        Set<String> blockWords = config.getBlockWordSet();
        /*List<CropMessageSensitiveRecord> sensitiveRecords = new ArrayList<>();*/

        // 改为：
        List<CropFile> mediaFileInfos = new ArrayList<>();
        for (ChatData chatData : chatdataList) {
            // ✅ 如果 seq 已存在，跳过
            if (existingSeqSet.contains(chatData.getSeq())) {
                log.debug("seq={} 已存在于数据库，跳过解密和插入", chatData.getSeq());
                continue;
            }
            try {
                // 解密ChatData数据
                String plainTextJson = decrypt(chatData, privateKey, sdk);
                BaseMessageDto messageDto = JSON.parseObject(plainTextJson, BaseMessageDto.class);
                // 转换dto数据到entity
                CropMessage message = messageDto.convertToMessage();
                message.setCropId(corpId);
                message.setSeq(chatData.getSeq());
                message.setContent(plainTextJson);
                messageList.add(message);
                // ✅ 提取 sdkfileid
                List<CropFile> files = extractMediaFiles(plainTextJson, chatData.getSeq());
                mediaFileInfos.addAll(files); // 收集所有待下载文件

                if (chatData.getSeq() > maxSeq) {
                    maxSeq = chatData.getSeq();
                }
                /*if (!blockWords.isEmpty()&&messageDto.getMsgType()=="text") {
                    Set<String> matched = blockWords.stream()
                            .filter(messageDto.getText().getContent()::contains) // 简单包含判断
                            .collect(Collectors.toSet());
                    // 命中！构造记录
                    CropMessageSensitiveRecord record = CropMessageSensitiveRecord.builder()
                            .seq(chatData.getSeq())
                            .msgid(message.getMsgId()) // 提取 msgid
                            .cropId(message.getCropId())
                            .content(messageDto.getText().getContent())
                            .keyword(String.join(",", matched))
                            .fromId(message.getFrom())
                            .roomid(message.getRoomid())
                            .type(message.getRoomid() == null || message.getRoomid().isEmpty() ? 1 : 2)
                            .build();
                    sensitiveRecords.add(record);
                }*/

            } catch (Exception e) {
                log.error("解密失败{}", e.getMessage());
                // 收集失败情况数据
                e.printStackTrace();
            }
        }


        return saveData(messageList, maxSeq, config, mediaFileInfos, sdk, corpId);
    }

    /**
     * 异步下载待处理的媒体文件
     *
     * @return List<CropMessage>
     */
    @Transactional
    public List<CropMessage> saveData(List<CropMessage> messageList, Long maxSeq, CropConfig config
            , List<CropFile> mediaFileInfos, Long sdk, String corpId) {

        // 4.保存消息和失败任务
        cropMessageService.saveBatch(messageList);
        config.setLastSeq(maxSeq);
        cropConfigMapper.updateById(config);

        if (!mediaFileInfos.isEmpty()) {
            List<CropFile> mediaFiles = mediaFileInfos.stream().map(info -> {
                CropFile mf = new CropFile();
                mf.setSdkFileId(info.getSdkFileId());
                mf.setMsgType(info.getMsgType());
                mf.setSeq(info.getSeq());
                mf.setCropId(corpId);
                mf.setStatus(0);
                mf.setExt(info.getExt());
                return mf;
            }).collect(Collectors.toList());

            // 保存到数据库
            cropFileService.saveBatch(mediaFiles);

            // ✅ 异步触发下载任务（可以轮询数据库）
            CompletableFuture.runAsync(() -> {
                try {
                    downloadPendingFiles(mediaFiles, sdk); // 也可以不传参，改为查数据库
                } catch (Exception e) {
                    log.error("异步下载任务异常", e);
                }
            });
        }
        return messageList;
    }

    /**
     * 异步下载待处理的媒体文件
     */
    private void downloadPendingFiles(List<CropFile> mediaFiles, Long sdk) {
        for (CropFile mf : mediaFiles) {
            try {
                String msgType = getSuffixByMsgType(mf);
                String url = downFile(mf.getSdkFileId(), msgType, sdk);
                if (url != null && !url.isEmpty()) {
                    mf.setUrl(url);
                    mf.setStatus(1);
                } else {
                    mf.setStatus(2);
                    mf.setErrorMsg("下载或上传失败");
                }
            } catch (Exception e) {
                log.error("下载文件失败 sdkfileid={}", mf.getSdkFileId(), e);
                mf.setStatus(2);
                mf.setErrorMsg(e.getMessage().substring(0, Math.min(e.getMessage().length(), 500)));
            } finally {
                // 更新数据库状态
                cropFileService.updateById(mf);
            }
        }
    }

    /**
     * 提取所有需要下载的媒体文件信息
     */
    private List<CropFile> extractMediaFiles(String plainTextJson, Long seq) {
        List<CropFile> result = new ArrayList<>();
        try {
            JSONObject root = JSONObject.parseObject(plainTextJson);

            // ===== 区分是单个消息 还是 msglist 数组 =====
            JSONArray msgList;

            if (root.containsKey("msglist") && root.get("msglist") instanceof JSONArray) {
                // 格式1: { "msglist": [ {...}, {...} ] }
                msgList = root.getJSONArray("msglist");
            } else {
                // 格式2: { "msgid": "...", "msgtype": "image", "image": { ... } }
                // 把单个消息包装成数组
                msgList = new JSONArray();
                msgList.add(root);
            }

            for (int i = 0; i < msgList.size(); i++) {
                JSONObject msg = msgList.getJSONObject(i);
                String msgType = msg.getString("msgtype");

                if (Arrays.asList("image", "voice", "video", "file").contains(msgType)) {
                    if (msg.containsKey(msgType)) {
                        String sdkFileId = msg.getJSONObject(msgType).getString("sdkfileid");
                        if (sdkFileId != null && !sdkFileId.trim().isEmpty()) {
                            CropFile cropFile = new CropFile();
                            cropFile.setMsgType(msgType);
                            cropFile.setSeq(seq);
                            cropFile.setSdkFileId(sdkFileId);
                            cropFile.setSeq(seq);
                            cropFile.setExt(msgType);
                            if (msgType.equals("file")) {
                                String fileext = msg.getJSONObject(msgType).getString("fileext");
                                cropFile.setExt(fileext);
                            }

                            result.add(cropFile);
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error("提取媒体文件失败 seq={}: {}", seq, e.getMessage(), e);
        }
        return result;
    }

    @GetMapping("/crop/downFile")
    public String downFile(String sdkField, String msgType, Long sdk) throws IOException {
        File tempFile = File.createTempFile(GlobalConstants.DOWNLOAD_TEMP_FILE_NAME_PREFIX,
                msgType, mediaContext.getTempMergeFileDir());

        String indexbuf = null; // 初始化 indexbuf
        try (FileOutputStream fileOutputStream = new FileOutputStream(tempFile)) {
            while (true) {
                long mediaData = Finance.NewMediaData();
                int ret = Finance.GetMediaData(sdk, indexbuf, sdkField, null, null, 3, mediaData);
                if (ret != 0) {
                    Finance.FreeMediaData(mediaData);
                    tempFile.delete();
                    break;
                }
                System.out.println(Finance.GetOutIndexBuf(mediaData));
                fileOutputStream.write(Finance.GetData(mediaData));
                if (Finance.IsMediaDataFinish(mediaData) == 1) {
                    Finance.FreeMediaData(mediaData);
                    return ossService.upload(tempFile);
                } else {
                    indexbuf = Finance.GetOutIndexBuf(mediaData);
                    Finance.FreeMediaData(mediaData);
                }
            }
        }
        return "";
    }

    public String getSuffixByMsgType(CropFile cropFile) {
        switch (cropFile.getMsgType()) {
            case "image":
                return ".jpg";
            case "voice":
                return ".amr";
            case "video":
                return ".mp4";
            case "file":
                return "." + cropFile.getExt(); // 取真实后缀
            default:
                return ".bin";
        }
    }


    /**
     * 解密数据
     *
     * @param chatData      待解密数据
     * @param privateKeyStr 私钥字符串
     * @return 解密后的原文
     */
    private String decrypt(ChatData chatData, String privateKeyStr, Long sdk) throws Exception {
        // 1.解密EncryptRandomKey
        String randomKey = EncodeUtils.decryptRandomKey(chatData.getEncryptRandomKey(), privateKeyStr);
        // 2.调用SDK方法解密密文数据
        long msg = Finance.NewSlice();
        Finance.DecryptData(sdk, randomKey, chatData.getEncryptChatMsg(), msg);
        String plaintext = Finance.GetContentFromSlice(msg);
        Finance.FreeSlice(msg);
        return plaintext;
    }


}
