package com.gitee.sop.admin.service.doc;

import com.alibaba.fastjson2.JSON;
import com.gitee.fastmybatis.core.util.TreeUtil;
import com.gitee.sop.admin.common.constants.YesOrNo;
import com.gitee.sop.admin.common.enums.DocSourceTypeEnum;
import com.gitee.sop.admin.common.user.User;
import com.gitee.sop.admin.dao.entity.DocApp;
import com.gitee.sop.admin.dao.entity.DocInfo;
import com.gitee.sop.admin.dao.mapper.DocAppMapper;
import com.gitee.sop.admin.service.doc.dto.torna.DocIdParam;
import com.gitee.sop.admin.service.doc.dto.torna.DocIdsParam;
import com.gitee.sop.admin.service.doc.dto.torna.TornaDocDTO;
import com.gitee.sop.admin.service.doc.dto.torna.TornaDocInfoDTO;
import com.gitee.sop.admin.service.doc.dto.torna.TornaDocInfoViewDTO;
import com.gitee.sop.admin.service.doc.dto.torna.TornaDocParamDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 接口信息同步
 *
 * @author 六如
 */
@Service
public class DocInfoSyncService {

    @Autowired
    private TornaClient tornaClient;
    @Autowired
    private DocAppMapper docAppMapper;
    @Autowired
    private DocInfoService docInfoService;
    @Autowired
    private DocContentService docContentService;

    public void syncAppDoc(Long docAppId, User user) {
        DocApp docApp = docAppMapper.getById(docAppId);
        this.syncDocInfo(docApp, null, user);
    }

    public void syncDoc(Long docInfoId, User user) {
        DocInfo docInfo = docInfoService.getById(docInfoId);
        DocApp docApp = docAppMapper.getById(docInfo.getDocAppId());
        this.syncDocInfo(docApp, docInfoId, user);
    }

    /**
     * 同步远程文档
     *
     * @param docApp    应用
     * @param docInfoId 同步某个文档,如果为null则同步整个应用文档
     * @param user      登录用户
     */
    public void syncDocInfo(DocApp docApp, Long docInfoId, User user) {
        Long docAppId = docApp.getId();
        Map<String, DocInfo> nameVersionMap = docInfoService.list(DocInfo::getDocAppId, docAppId)
                .stream()
                .collect(Collectors.toMap(docInfo -> docInfo.getDocName() + ":" + docInfo.getDocVersion(), Function.identity(), (v1, v2) -> v2));

        String token = docApp.getToken();
        // add doc
        DocIdsParam docIdsParam = buildSearchParam(docInfoId);
        TornaDocDTO tornaDocDTO = tornaClient.execute("doc.list", docIdsParam, token, TornaDocDTO.class);
        List<TornaDocInfoDTO> docList = tornaDocDTO.getDocList();
        if (CollectionUtils.isEmpty(docList)) {
            return;
        }

        List<DocInfo> updateList = new ArrayList<>();
        for (TornaDocInfoDTO tornaDocInfoDTO : docList) {
            String key = buildKey(tornaDocInfoDTO);
            DocInfo docInfo = nameVersionMap.get(key);
            // 需要修改的文档
            if (docInfo != null) {
                docInfo.setDocId(tornaDocInfoDTO.getId());
                docInfo.setDocTitle(tornaDocInfoDTO.getName());
                docInfo.setDocCode("");
                if (YesOrNo.yes(tornaDocInfoDTO.getIsFolder())) {
                    docInfo.setIsPublish(YesOrNo.YES);
                    docInfo.setDocName(tornaDocInfoDTO.getName());
                }
                docInfo.setDocId(tornaDocInfoDTO.getId());
                docInfo.setDocType(tornaDocInfoDTO.getType().intValue());
                docInfo.setDescription(tornaDocInfoDTO.getDescription());
                docInfo.setIsFolder(tornaDocInfoDTO.getIsFolder().intValue());
                docInfo.setParentId(tornaDocInfoDTO.getParentId());
                docInfo.setUpdateBy(user.getUserId());
                updateList.add(docInfo);
            }
        }
        for (DocInfo docInfo : updateList) {
            docInfoService.update(docInfo);
        }

        // 新增的文档
        List<DocInfo> saveList = docList.stream()
                .filter(tornaDocInfoDTO -> {
                    String key = buildKey(tornaDocInfoDTO);
                    return !nameVersionMap.containsKey(key);
                })
                .map(tornaDocInfoDTO -> {
                    DocInfo docInfo = new DocInfo();
                    docInfo.setDocAppId(docAppId);
                    docInfo.setDocId(tornaDocInfoDTO.getId());
                    docInfo.setDocTitle(tornaDocInfoDTO.getName());
                    docInfo.setDocCode("");
                    docInfo.setDocType(tornaDocInfoDTO.getType().intValue());
                    docInfo.setSourceType(DocSourceTypeEnum.TORNA.getValue());
                    if (YesOrNo.yes(tornaDocInfoDTO.getIsFolder())) {
                        docInfo.setIsPublish(YesOrNo.YES);
                        docInfo.setDocName(tornaDocInfoDTO.getName());
                    } else {
                        docInfo.setIsPublish(YesOrNo.NO);
                        docInfo.setDocName(tornaDocInfoDTO.getUrl());
                    }
                    docInfo.setDocVersion(tornaDocInfoDTO.getVersion());
                    docInfo.setDescription(tornaDocInfoDTO.getDescription());
                    docInfo.setIsFolder(tornaDocInfoDTO.getIsFolder().intValue());
                    docInfo.setParentId(tornaDocInfoDTO.getParentId());
                    docInfo.setAddBy(user.getUserId());
                    return docInfo;
                })
                .collect(Collectors.toList());
        docInfoService.saveBatch(saveList);

        Set<Long> docIds = docList.stream().map(TornaDocInfoDTO::getId).collect(Collectors.toSet());
        this.syncContent(docApp, docIds);
    }

    private void syncContent(DocApp docApp, Set<Long> docIds) {
        List<DocInfo> list = docInfoService.query()
                .eq(DocInfo::getDocAppId, docApp.getId())
                .in(DocInfo::getDocId, docIds)
                .list();

        Map<Long, String> docIdMap = this.getContentMap(docApp.getToken(), docIds);
        for (DocInfo docInfo : list) {
            String content = docIdMap.getOrDefault(docInfo.getDocId(), "");
            docContentService.saveContent(
                    docInfo.getId(),
                    content
            );
        }
    }

    /**
     * 批量获取Torna文档内容
     *
     * @param token  token
     * @param docIds Torna文档id
     * @return key:文档id, value:文档内容
     */
    private Map<Long, String> getContentMap(String token, Collection<Long> docIds) {
        // 获取torna文档信息
        List<TornaDocInfoViewDTO> tornaDocInfoViewList = tornaClient.executeList(
                "doc.details",
                new DocIdsParam(docIds),
                token,
                TornaDocInfoViewDTO.class
        );
        for (TornaDocInfoViewDTO docInfoViewDTO : tornaDocInfoViewList) {
            convertTree(docInfoViewDTO);
        }
        return tornaDocInfoViewList.stream()
                .collect(Collectors.toMap(TornaDocInfoViewDTO::getId, JSON::toJSONString, (v1, v2) -> v1));
    }

    private String getContent(String token, Long docId) {
        // 获取torna文档信息
        TornaDocInfoViewDTO tornaDocInfoViewDTO = tornaClient.execute(
                "doc.detail",
                new DocIdParam(docId),
                token,
                TornaDocInfoViewDTO.class
        );
        convertTree(tornaDocInfoViewDTO);
        return JSON.toJSONString(tornaDocInfoViewDTO);
    }

    private void convertTree(TornaDocInfoViewDTO tornaDocInfoViewDTO) {
        List<TornaDocParamDTO> requestParams = tornaDocInfoViewDTO.getRequestParams();
        List<TornaDocParamDTO> responseParams = tornaDocInfoViewDTO.getResponseParams();
        List<TornaDocParamDTO> requestTree = TreeUtil.convertTree(requestParams, 0L);
        List<TornaDocParamDTO> responseTree = TreeUtil.convertTree(responseParams, 0L);

        tornaDocInfoViewDTO.setRequestParams(requestTree);
        tornaDocInfoViewDTO.setResponseParams(responseTree);
    }

    private DocIdsParam buildSearchParam(Long docInfoId) {
        if (docInfoId == null) {
            return null;
        }
        DocIdsParam docIdsParam = new DocIdsParam();
        DocInfo docInfo = docInfoService.getById(docInfoId);
        List<Long> docIdList = new ArrayList<>();
        docIdList.add(docInfo.getDocId());
        // 如果是文件夹,找下面的子文档
        if (YesOrNo.yes(docInfo.getIsFolder())) {
            List<Long> docIds = this.listChildrenDocId(docInfo.getDocId());
            docIdList.addAll(docIds);
        }
        docIdsParam.setDocIds(docIdList);
        return docIdsParam;
    }

    private List<Long> listChildrenDocId(Long parentId) {
        return docInfoService.query()
                .eq(DocInfo::getParentId, parentId)
                .listValue(DocInfo::getDocId);
    }

    private String buildKey(TornaDocInfoDTO tornaDocInfoDTO) {
        return YesOrNo.yes(tornaDocInfoDTO.getIsFolder()) ?
                tornaDocInfoDTO.getName() + ":" + tornaDocInfoDTO.getVersion()
                : tornaDocInfoDTO.getUrl() + ":" + tornaDocInfoDTO.getVersion();
    }

}
