package com.yeejoin.amos.knowledgebase.face.service;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.yeejoin.amos.component.rule.action.Topic;
import com.yeejoin.amos.component.rule.config.ClazzUtils;
import com.yeejoin.amos.component.rule.config.RuleConfig;
import com.yeejoin.amos.component.rule.model.ConstantCategoryModel;
import com.yeejoin.amos.component.rule.model.ConstantModel;
import com.yeejoin.amos.component.rule.model.DefinitionModel;
import com.yeejoin.amos.knowledgebase.face.model.KnowledgeDocContentModel;
import com.yeejoin.amos.knowledgebase.face.model.KnowledgeTagInstanceModel;
import com.yeejoin.amos.knowledgebase.face.model.KnowledgeTagValueModel;
import com.yeejoin.amos.knowledgebase.face.util.BaseUtil;
import com.yeejoin.amos.knowledgebase.face.util.ConfigLoader;
import com.yeejoin.amos.knowledgebase.face.util.QuoteCountFlushTiming;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.typroject.tyboot.component.emq.EmqKeeper;
import org.typroject.tyboot.core.foundation.context.RequestContext;
import org.typroject.tyboot.core.foundation.exception.BaseException;
import org.typroject.tyboot.core.foundation.utils.StringUtil;
import org.typroject.tyboot.core.foundation.utils.ValidationUtil;
import org.typroject.tyboot.core.restful.exception.instance.DataNotFound;
import org.typroject.tyboot.core.restful.exception.instance.RequestForbidden;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;


/**
 * <p>
 * 知识库文档分类 服务类
 * </p>
 *
 * @author 子杨
 * @since 2020-08-05
 */
@Component
public class DocAuditService {

    /**
     * 审核状态--待提交
     */
    public static final String DOC_AUDIT_STATUS_SAVED = "SAVED";
    /**
     * 审核状态--待审核
     */
    public static final String DOC_AUDIT_STATUS_SUBMITTED = "SUBMITTED";
    /**
     * 审核状态--通过
     */
    public static final String DOC_AUDIT_STATUS_PASSED = "PASSED";
    /**
     * 审核状态--驳回
     */
    public static final String DOC_AUDIT_STATUS_REJECTED = "REJECTED";

    private static final String TAG_INSTANCE_LABEL = "label";

    private static String DELETE_SYNC_PLAN_TOPIC = "DELETE_SYNC_PLAN_DOC";

    @Autowired
    private DocContentService service;
    @Autowired
    private ESDocService eSDocService;
//    @Autowired
//    private StatisticsRecordService statisticsRecordService;
    @Autowired
    private DocLibraryService docLibraryService;
    @Autowired
    private LoadBalancerClient loadBalancerClient;
    @Autowired
    private EmqKeeper emqKeeper;

    @Autowired
    private ConfigLoader configLoader;

    private final Logger logger = LogManager.getLogger(DocAuditService.class);

    /**
     * 文档批量发布
     *
     * @param docSeqList 文档ID列表
     * @return List
     */
    @Transactional(rollbackFor = {Exception.class, BaseException.class})
    public List<KnowledgeDocContentModel> publish(List<Long> docSeqList) {
        List<KnowledgeDocContentModel> resList = docLibraryService.efficientList(docSeqList);
        if (ValidationUtil.isEmpty(resList)) {
            throw new DataNotFound("数据不存在");
        }
        //更新状态
        for (KnowledgeDocContentModel oldDoc : resList) {
            if (ValidationUtil.equals(oldDoc.getDocStatus(), DocContentService.DOC_STATUS_UNPUBLISHED)) {
                oldDoc.setDocStatus(DocContentService.DOC_STATUS_PUBLISHED);
                oldDoc.setAuditStatus(DOC_AUDIT_STATUS_PASSED);
                oldDoc.setAuditorUserId(RequestContext.getExeUserId());
                service.updateWithModel(oldDoc);
            } else {
                throw new RequestForbidden("只允许对未发布状态的文档执行此操作");
            }
        }
        //记录需要统计的标签项
//        statisticsRecordService.addDocRecords(resList);
        //保存到ES存储
        eSDocService.savePublishedDoc(resList);
        // 设置定时刷新
        QuoteCountFlushTiming.needFlushTag();
        QuoteCountFlushTiming.needRulePush();
        return resList;
    }


    /**
     * 文档批量取消发布
     *
     * @param docSeqList 文档ID列表
     * @return List
     */
    @Transactional(rollbackFor = {Exception.class, BaseException.class})
    public List<KnowledgeDocContentModel> unpublish(List<Long> docSeqList) {
        List<KnowledgeDocContentModel> resList = docLibraryService.efficientList(docSeqList);
        if (ValidationUtil.isEmpty(resList)) {
            throw new DataNotFound("数据不存在");
        }
        for (KnowledgeDocContentModel oldDoc : resList) {
            if (ValidationUtil.equals(oldDoc.getDocStatus(), DocContentService.DOC_STATUS_PUBLISHED)) {
                //判断文档是否被引用
                if (service.getReference(oldDoc.getSequenceNbr()) > 0) {
                    throw new RequestForbidden("含被引用文档，不能取消发布");
                }
                oldDoc.setDocStatus(DocContentService.DOC_STATUS_UNPUBLISHED);
                oldDoc.setAuditStatus(DOC_AUDIT_STATUS_SAVED);
                oldDoc.setAuditorUserId(RequestContext.getExeUserId());
                service.updateWithModel(oldDoc);
            } else {
                throw new RequestForbidden("只允许对发布状态的文档执行此操作");
            }
        }
        QuoteCountFlushTiming.needFlushTag();
        QuoteCountFlushTiming.needRulePush();
        // 删除已统计的标签项
//        statisticsRecordService.deleteRecordsByDocIds(resList);
        // 移除ES存储
        eSDocService.deleteDocById(resList);
        // 推送取消发布的文档至数字预案
        pushDeletedDocs2DigitalPlanByMQ(resList);
        return resList;
    }

    /**
     * 文档批量提交
     *
     * @param docSeqList 文档ID列表
     * @return List
     */
    @Transactional(rollbackFor = {Exception.class, BaseException.class})
    public List<KnowledgeDocContentModel> submit(List<Long> docSeqList) {
        List<KnowledgeDocContentModel> resList = service.queryByIds(docSeqList);
        if (ValidationUtil.isEmpty(resList)) {
            throw new DataNotFound("数据不存在");
        }
        for (KnowledgeDocContentModel oldDoc : resList) {
            if (ValidationUtil.equals(oldDoc.getAuditStatus(), DOC_AUDIT_STATUS_SAVED)) {
                //更新审核状态
                oldDoc.setAuditStatus(DOC_AUDIT_STATUS_SUBMITTED);
                service.updateWithModel(oldDoc);
            } else {
                throw new RequestForbidden("只允许对待提交状态的文档执行此操作");
            }
        }
        return resList;
    }

    /**
     * 文档批量审核通过
     *
     * @param docSeqList 文档ID列表
     * @return List
     */
    @Transactional(rollbackFor = {Exception.class, BaseException.class})
    public List<KnowledgeDocContentModel> pass(List<Long> docSeqList) {
        List<KnowledgeDocContentModel> resList = docLibraryService.efficientList(docSeqList);
        if (ValidationUtil.isEmpty(resList)) {
            throw new DataNotFound("数据不存在");
        }
        for (KnowledgeDocContentModel oldDoc : resList) {
            if (ValidationUtil.equals(oldDoc.getAuditStatus(), DOC_AUDIT_STATUS_SUBMITTED)) {
                //更新审核状态
                oldDoc.setAuditStatus(DOC_AUDIT_STATUS_PASSED);
                oldDoc.setAuditorUserId(RequestContext.getExeUserId());
                //更新文档发布状态
                oldDoc.setDocStatus(DocContentService.DOC_STATUS_PUBLISHED);
                service.updateWithModel(oldDoc);
            } else {
                throw new RequestForbidden("只允许对待审核状态的文档执行此操作");
            }
        }
        QuoteCountFlushTiming.needFlushTag();
        QuoteCountFlushTiming.needRulePush();
        //记录需要统计的标签项
//        statisticsRecordService.addDocRecords(resList);
        //保存到ES存储
        eSDocService.savePublishedDoc(resList);
        return resList;
    }

    /**
     * 文档审核驳回
     *
     * @param id               文档ID
     * @param rejectionComment 驳回意见
     * @return List
     */
    @Transactional(rollbackFor = {Exception.class, BaseException.class})
    public KnowledgeDocContentModel reject(Long id, String rejectionComment) {

        KnowledgeDocContentModel oldDoc = service.queryBySeq(id);
        if (ValidationUtil.isEmpty(oldDoc)) {
            throw new DataNotFound("数据不存在");
        }
        if (ValidationUtil.equals(oldDoc.getAuditStatus(), DOC_AUDIT_STATUS_SUBMITTED)) {
            //更新审核状态&文档状态
            oldDoc.setAuditStatus(DOC_AUDIT_STATUS_REJECTED);
            oldDoc.setRejectionComment(rejectionComment);
            oldDoc.setAuditorUserId(RequestContext.getExeUserId());
            oldDoc.setDocStatus(DocContentService.DOC_STATUS_UNPUBLISHED);
            return service.updateWithModel(oldDoc);
        } else {
            throw new RequestForbidden("只允许对待审核状态的文档执行此操作");
        }
    }

    /**
     * 推送取消发布的文档信息至数字预案
     *
     * @param resList
     */
    public void pushDeletedDocs2DigitalPlanByMQ(List<KnowledgeDocContentModel> resList) {
        List<Long> ids = BaseUtil.getModelIds(resList);
        try {
            emqKeeper.getMqttClient().publish(DELETE_SYNC_PLAN_TOPIC, ClazzUtils.serializableObject(ids), RuleConfig.DEFAULT_QOS, false);
            logger.info("文档" + ids.toString() + "取消发布，已推送消息至数字预案服务");
        } catch (Exception e) {
            logger.fatal("文档" + ids.toString() + "取消发布未能成功推送至数字预案服务", e);
        }
    }

    /**
     * 推送文档至规则服务
     */
    public void pushDocs2RuleByMQ() {
        List<Long> allPublishedDocIds = service.getAllPublishedDocIds();
        int index = 0;
        int size = allPublishedDocIds.size();
        ServiceInstance serviceInstance = this.loadBalancerClient.choose("AMOS-API-RULE");
        String host = serviceInstance.getHost();
        String topic = Topic.getTopicStr(Topic.RULE_UPDATE_DEFINITION, host);
        DefinitionModel definitionModel = new DefinitionModel();
        definitionModel.setAgencyCode(configLoader.getAgencyCode());
        List<ConstantCategoryModel> allCategoryModel = new ArrayList<>();
        if (!allPublishedDocIds.isEmpty()) {
            while (index < size) {
                int num = Math.min(index + 50, size);
                List<Long> curDocIds = allPublishedDocIds.subList(index, index + num);
                index += num;
                List<KnowledgeDocContentModel> docContentModels = docLibraryService.efficientList(curDocIds);
                List<ConstantCategoryModel> constantCategoryModels = docs2RuleEntities(docContentModels, configLoader);
                Iterator<ConstantCategoryModel> iterator = constantCategoryModels.iterator();
                while (iterator.hasNext()) {
                    ConstantCategoryModel categoryModel = iterator.next();
                    if (ValidationUtil.isEmpty(categoryModel.getConstant())) {
                        iterator.remove();
                    }
                }
                allCategoryModel.addAll(constantCategoryModels);
            }
        }
        if (!allCategoryModel.isEmpty()) {
            definitionModel.setResources(allCategoryModel);
            // 推mq消息给规则服务
            try {
                emqKeeper.getMqttClient().publish(topic, ClazzUtils.serializableObject(definitionModel), RuleConfig.DEFAULT_QOS, false);
            } catch (Exception e) {
                logger.fatal("同步文档至规则服务出错", e);
            }
        }
    }

    private static List<ConstantCategoryModel> docs2RuleEntities(Collection<KnowledgeDocContentModel> docContentModels, ConfigLoader configLoader) {
        List<ConstantCategoryModel> resList = new ArrayList<>();
        String projects = configLoader.getProjects();

        if (!ValidationUtil.isEmpty(projects)) {
            String[] projectArr = projects.split(",");
            if (!ValidationUtil.isEmpty(docContentModels) && !ValidationUtil.isEmpty(projectArr)) {
                docContentModels.forEach(docContentModel -> {
                    List<ConstantModel> constantModelList = tags2RuleEntities(docContentModel.getDocContentTags(), configLoader);
                    if (!ValidationUtil.isEmpty(constantModelList)) {
                        for (String projectName : projectArr) {
                            ConstantCategoryModel constantCategoryModel = new ConstantCategoryModel();
                            constantCategoryModel.setConstant(constantModelList);
                            constantCategoryModel.setName(docContentModel.getDocTitle());
                            constantCategoryModel.setTitle(constantCategoryModel.getName());
                            constantCategoryModel.setProjectName(projectName);
                            resList.add(constantCategoryModel);
                        }
                    }
                });
            }
        }
        return resList;
    }

    private static List<ConstantModel> tags2RuleEntities(Collection<KnowledgeTagInstanceModel> tagInstanceModels, ConfigLoader configLoader) {
        List<ConstantModel> resList = new ArrayList<>();
        String tags = configLoader.getTags();
        boolean tagWithName = configLoader.isTagWithName();
        if (!ValidationUtil.isEmpty(tags)) {
            List<Long> tagIdList = new ArrayList<>();
            try {
                tagIdList = StringUtil.String2LongList(tags);
            } catch (Exception e) {
            }
            if (!ValidationUtil.isEmpty(tagInstanceModels) && !tagIdList.isEmpty()) {
                List<Long> finalTagIdList = tagIdList;
                tagInstanceModels.forEach(instanceModel -> {
                    if (finalTagIdList.contains(instanceModel.getTagSeq())) {
                        ConstantModel constantModel = new ConstantModel();
                        String tagValueStr = getTagValueStrFromValues(instanceModel.getTagValues());
                        if (!ValidationUtil.isEmpty(tagValueStr)) {
                            if (tagWithName) {
                                constantModel.setLabel(instanceModel.getTagName() + "：" + tagValueStr);
                            } else {
                                constantModel.setLabel(tagValueStr);
                            }
                            constantModel.setValue(getLabel4TagInstance(instanceModel));
                            constantModel.setType("String");
                            resList.add(constantModel);
                        }
                    }
                });
            }
        }
        return resList;
    }

    private static String getTagValueStrFromValues(List<KnowledgeTagValueModel> values) {
        AtomicReference<String> resLeft = new AtomicReference<>("");
        AtomicBoolean isRange = new AtomicBoolean(false);
        AtomicReference<String> resRight = new AtomicReference<>("");
        AtomicReference<String> unit = new AtomicReference<>("");
        if (!ObjectUtils.isEmpty(values)) {
            values.forEach(value -> {
                if (TagValueService.VALUE_TAG_FIELD_RANGE_MAX.equals(value.getFieldName())) {
                    resRight.set(value.getTagValue());
                } else {
                    resLeft.set(value.getTagValue());
                }
                if (TagValueService.VALUE_TAG_FIELD_RANGE_MAX.equals(value.getFieldName())
                        || TagValueService.VALUE_TAG_FIELD_RANGE_MIN.equals(value.getFieldName())) {
                    isRange.set(true);
                }
                unit.set(ValidationUtil.isEmpty(value.getUnit()) || "null".equals(value.getUnit()) ? "" : value.getUnit());
            });
        }
        if (isRange.get()) {
            if (!ValidationUtil.isEmpty(resLeft.get()) || !ValidationUtil.isEmpty(resRight.get())) {
                return ValidationUtil.isEmpty(resLeft.get()) ? "?" : resLeft.get() + " - "
                        + (ValidationUtil.isEmpty(resRight.get()) ? "?" : resRight.get()) + unit.get();
            } else {
                return "";
            }
        } else {
            return resLeft.get() + unit.get();
        }
    }

    private static String getLabel4TagInstance(KnowledgeTagInstanceModel instanceModel) {
        String frontEndConfig = instanceModel.getFrontEndConfig();
        if (!ValidationUtil.isEmpty(frontEndConfig)) {
            try {
                JSONObject configObject = JSON.parseObject(frontEndConfig);
                return (String) configObject.get(TAG_INSTANCE_LABEL);
            } catch (Exception e) {
            }
        }
        return null;
    }

}
