package com.ruoyi.biemo.business.service;

import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.util.StringUtil;
import com.hankcs.hanlp.classification.classifiers.IClassifier;
import com.hankcs.hanlp.classification.classifiers.NaiveBayesClassifier;
import com.hankcs.hanlp.seg.common.Term;
import com.hankcs.hanlp.tokenizer.NLPTokenizer;
import com.ruoyi.biemo.business.domain.*;
import com.ruoyi.biemo.business.domain.event.DocInfoDeleteEvent;
import com.ruoyi.biemo.business.domain.event.DocInfoSaveEvent;
import com.ruoyi.biemo.business.response.MyResultResponse;
import com.ruoyi.biemo.core.page.Page;
import com.ruoyi.biemo.elasticsearch.util.EsService;
import com.ruoyi.biemo.mongodb.utils.MongoHelper;
import com.ruoyi.biemo.nlp.DependencyParserUtils;
import com.ruoyi.biemo.nlp.SentimentAnalysisUtils;
import com.ruoyi.biemo.nlp.SummaryUtils;
import com.ruoyi.biemo.utils.FormatUtil;
import com.ruoyi.biemo.utils.MyObjects;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.StringUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;

import javax.print.Doc;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.ruoyi.biemo.nlp.SentimentAnalysisUtils.trainOrLoadModel;

/**
 * @author makesoft
 * @version 1.0
 * @date 2021/1/14 11:59
 */
@Service
public class DocInfoService extends EsService<DocInfo> {

    @Autowired
    private MongoHelper mongoHelper;

    @Autowired
    private ApplicationContext applicationContext;

    @Override
    protected DocInfo loadData(SearchSourceBuilder context, SearchHit hit) {
        String id = hit.getId();
        if (Objects.isNull(id) || StringUtil.isEmpty(id)) return null;
        return mongoHelper.findById(id, DocInfo.class);
    }

    @Override
    public List<DocInfo> batchLoadData(SearchSourceBuilder context, SearchHit[] hitArr) {
        List<String> ids = new ArrayList<>();
        for (SearchHit hit : hitArr) {
            String id = hit.getId();
            if (Objects.isNull(id) || StringUtil.isEmpty(id)) continue;
            ids.add(id);
        }
        return mongoHelper.findListByIds(ids, DocInfo.class);
    }

    /**
     * 查询文章管理
     *
     * @param id 文章管理主键
     * @return 文章管理
     */
    public DocInfo selectDocInfoById(String id) {
        if (id == null || id.trim().length() == 0) {
            return null;
        }
//        List<DocInfo> docInfos = esLambdaQuery().eq(DocInfo::getId,id).query();
        List<DocInfo> docInfos = esLambdaQuery().getDoc_InfoBy_Id(DocInfo::getId, id).query();

        return docInfos != null && docInfos.size() > 0 ? docInfos.get(0) : null;
    }

    /**
     * 查询文章管理列表
     *
     * @param docInfo 文章管理
     * @return 文章管理
     */
    public List<DocInfo> selectDocInfoList(DocInfo docInfo) {
        return esLambdaQuery().eqAll(docInfo).query();
    }

    /**
     * 查询文章管理分页列表
     *
     * @param docInfo 文章管理
     * @return 文章管理
     */
    public Page<DocInfo> selectDocInfoPage(DocInfo docInfo, Page<DocInfo> page) {
        return esLambdaQuery().eqAll(docInfo).sort(DocInfo::getCreateTime, SortOrder.DESC).page(page.getPageNum(), page.getPageSize()).queryPage(true);
    }

    /**
     * 新增文章管理
     *
     * @param docInfo 文章管理
     * @return 结果
     */
    public String insertOrUpdateDocInfo(DocInfo docInfo) {
        String id = mongoHelper.insert(docInfo);
        docInfo.setId(id);
        applicationContext.publishEvent(new DocInfoSaveEvent(this, docInfo));
        return id;
    }

    public void insertBatchEs(List<DocInfo> docInfos) {
        insertBatch(docInfos);
    }

    public void insertEs(DocInfo docInfo) {
        List<DocInfo> docInfos = new ArrayList<>();
        docInfos.add(docInfo);
        insertBatch(docInfos);
    }


    /**
     * 批量删除文章管理
     *
     * @param ids 需要删除的文章管理主键
     * @return 结果
     */
    public void deleteDocInfoByIds(String[] ids) {
        List<String> list = Arrays.stream(ids).collect(Collectors.toList());
        mongoHelper.deleteByIds(list, DocInfo.class);
        applicationContext.publishEvent(new DocInfoDeleteEvent(this, list));

    }

    /**
     * 删除文章管理信息
     *
     * @param id 文章管理主键
     * @return 结果
     */
    public void deleteDocInfoById(String id) {
        mongoHelper.deleteById(id, DocInfo.class);
        applicationContext.publishEvent(new DocInfoDeleteEvent(this, id));
    }

    @EventListener(classes = {DocInfoSaveEvent.class})
    public void saveIndex(ApplicationEvent event) {
        DocInfoSaveEvent saveEvent = (DocInfoSaveEvent) event;
        List<DocInfo> infoList = saveEvent.getInfos();
        insertBatch(infoList);
    }

    @EventListener(classes = {DocInfoDeleteEvent.class})
    public void delIndex(ApplicationEvent event) {
        DocInfoDeleteEvent saveEvent = (DocInfoDeleteEvent) event;
        List<String> ids = saveEvent.getIds();
        deleteBatch(ids);
    }


    public MyResultResponse analysis(String id, Boolean reAnalysis) {
        if (StringUtils.isBlank(id)) {
            return null;
        }
        MyResultResponse response = null;
        DocInfo docInfo = this.selectDocInfoById(id);
        String grapData = docInfo.getParserGraphData();
        if (StringUtils.isNotBlank(grapData) && !reAnalysis) {
            response = JSONObject.parseObject(grapData, MyResultResponse.class);
            if (response != null) {
                return response;
            }
        }
        String content = docInfo.getContent();
        List<Node> nodes = new ArrayList<>();
        List<Relationship> relationships = new ArrayList<>();
        //拆句
        if (StringUtils.isNotBlank(content)) {
            content = MyObjects.delHTMLTag(content);
            content = MyObjects.delSpace(content);
            String regx = "\n|!|\\.|\\。|\\;|\\；|\\！|\\,|\\，|\\、|\\@|\\#|\\$|\\￥|\\%|\\&|\\*|\\(|\\)|\\=|\\+|\\-|\\_|\\>|\\<|\\[|\\]|\\【|\\】|\\？|\\?|”|\\\"";
            String[] strArr = content.split(regx);
            Set<String> allRelations = getAllRelations(strArr);
            int i = 1;
            //Map<String,String> nodesMap = getAllNodes(allRelations);

            if (allRelations != null && allRelations.size() > 0) {
                for (String relation : allRelations) {
                    String[] nrn = StringUtils.isNotBlank(relation) ? relation.split(",") : null;
                    if (nrn == null || nrn.length < 3) {
                        continue;
                    }
                    String nodeName1 = nrn[0];
                    String relationName = nrn[1];
                    String nodeName2 = nrn[2];
                    Node node1 = new Node();
                    //node1.setId(nodesMap.get(nodeName1));
                    node1.setId(nodeName1);
                    node1.setLabels(new ArrayList<String>() {{
                        add(nodeName1);
                    }});
                    node1.setProperties(new HashMap<String, Object>() {{
                        put("name", nodeName1);
                        put("email", "9094908@qq.com");
                    }});
                    Node node2 = new Node();
                    node2.setId(nodeName2);
                    node2.setLabels(new ArrayList<String>() {{
                        add(nodeName2);
                    }});
                    node2.setProperties(new HashMap<String, Object>() {{
                        put("name", nodeName2);
                        put("email", "9094908@qq.com");
                    }});
                    nodes.add(node1);
                    nodes.add(node2);
                    Relationship relationship = new Relationship();
                    relationship.setId(String.valueOf(i++));
                    relationship.setType(relationName);
                    relationship.setStartNode(nodeName1);
                    relationship.setEndNode(nodeName2);
                    relationship.setProperties(new HashMap<String, Object>() {{
                        put("source", nodeName1);
                        put("relation", relationName);
                        put("target", nodeName2);
                    }});
                    relationships.add(relationship);
                }
            }
//            获取命名实体
            try {
                Map<String, Set<String>> nerTagSet = DependencyParserUtils.getMyNERTagSet(strArr);
                docInfo.setParserNamedEntity(JSONObject.toJSONString(nerTagSet));
                docInfo.setSummary(SummaryUtils.autoSummary(content));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        response = returnResponse(nodes, relationships);
        docInfo.setParserGraphData(JSONObject.toJSONString(response));
        docInfo.setStatus(1);
        insertOrUpdateDocInfo(docInfo);
        return response;
    }

    private Map<String, String> getAllNodes(Set<String> allRelations) {
        Map<String, String> result = new HashMap<>();
        Set<String> nodeSets = new HashSet<>();
        if (allRelations != null && allRelations.size() > 0) {
            allRelations.forEach(relation -> {
                String[] nrn = StringUtils.isNotBlank(relation) ? relation.split(",") : null;
                if (nrn != null && nrn.length == 3) {
                    nodeSets.add(nrn[0]);
                    nodeSets.add(nrn[2]);
                }
            });
        }
        AtomicInteger i = new AtomicInteger(0);
        if (nodeSets != null && nodeSets.size() > 0) {
            nodeSets.forEach(set -> {
                result.put(set, String.valueOf(i.getAndIncrement()));
            });
        }
        return result;
    }


    private Set<String> getAllRelations(String[] strArr) {
        Set<String> allRelations = new HashSet<>();
        if (strArr != null && strArr.length > 0) {
            for (String text : strArr) {
                Set<String> relations = DependencyParserUtils.analysis(text);
                allRelations.addAll(relations);
            }
        }
        return allRelations;
    }

    public MyResultResponse returnResponse(List<Node> nodes, List<Relationship> relationships) {
        MyResultResponse response = new MyResultResponse();
        List<MyResultResponse.Datas> datass = new ArrayList<>();
        MyResultResponse.Datas datas = new MyResultResponse.Datas();
        List<MyResultResponse.Data> dataList = new ArrayList<>();
        MyResultResponse.Data data = new MyResultResponse.Data();
        MyResultResponse.Graph graph = new MyResultResponse.Graph();
        graph.setNodes(nodes);
        graph.setRelationships(relationships);
        data.setGraph(graph);
        dataList.add(data);
        datas.setColumns(new ArrayList<String>() {{
            add("user");
            add("entity");
        }});
        datas.setData(dataList);
        datass.add(datas);
        response.setResults(datass);
        response.setErrors(new ArrayList<>());
        return response;
    }

    public String summary(String id) {
        DocInfo docInfo = this.selectDocInfoById(id);
        String summary = SummaryUtils.autoSummary(docInfo.getContent());
        docInfo.setSummary(summary);
        this.insertOrUpdateDocInfo(docInfo);
        return summary;
    }


    //    //社会网络分析
//    public List<MyResultResponse> socialNetworkAnalysis(String[] ids) {
//        List<MyResultResponse> list = new ArrayList<>();
//        long stratTime = System.nanoTime();
//        if (ids != null && ids.length > 0) {
//            for (int i = 0; i < ids.length; i++) {
////                list.add(analysis(ids[i], true));
//                list.add(analysis(ids[i], false));
//            }
//        }
//        long endTime = System.nanoTime();
//        System.out.println("方法耗时：" + (endTime - stratTime));
//        return list;
//    }
    //社会网络分析
    public MyResultResponse socialNetworkAnalysis(String[] ids) {
        MyResultResponse response2 = new MyResultResponse();
        MyResultResponse response3 = new MyResultResponse();
        if (ids != null && ids.length > 0) {
            for (int i = 0; i < ids.length; i++) {
                if (i == 0) {
                    response2 = analysis(ids[0], false);
                } else {
                    response3 = analysis(ids[1], false);
                }
            }
        }
        MyResultResponse myResultResponse = new MyResultResponse();
        List<MyResultResponse.Datas> results = new ArrayList<>();
        List<MyResultResponse.Datas> results2 = response2.getResults();
        List<MyResultResponse.Datas> results3 = response3.getResults();

        results.addAll(0, results3);
        results.addAll(1, results2);

        List<String> errors = myResultResponse.getErrors();
        errors.addAll(response2.getErrors());
        errors.addAll(response3.getErrors());
        myResultResponse.setResults(results);
        myResultResponse.setErrors(errors);
        return myResultResponse;
    }

    //文章管理--批量分析
    public String batchAnalysis(String[] ids) {
        if (ids != null && ids.length > 0) {
            for (int i = 0; i < ids.length; i++) {
                analysis(ids[i], true);
            }
        }
        return "ok";
    }


    public MyResultResponse socialAnalysis(String content) {
//        String grapData = docInfo.getParserGraphData();
        MyResultResponse response = null;
//        if(StringUtils.isNotBlank(grapData)&&reAnalysis){
//            response = JSONObject.parseObject(grapData,MyResultResponse.class);
//            if(response!=null){
//                return response;
//            }
//        }
        List<Node> nodes = new ArrayList<>();
        List<Relationship> relationships = new ArrayList<>();
        //拆句
        if (StringUtils.isNotBlank(content)) {
            content = MyObjects.delHTMLTag(content);
            content = MyObjects.delSpace(content);
            String regx = "\n|!|\\.|\\。|\\;|\\；|\\！|\\,|\\，|\\、|\\@|\\#|\\$|\\￥|\\%|\\&|\\*|\\(|\\)|\\=|\\+|\\-|\\_|\\>|\\<|\\[|\\]|\\【|\\】|\\？|\\?|”|\\\"";
            String[] strArr = content.split(regx);
            Set<String> allRelations = getAllRelations(strArr);
            int i = 1;
//            Map<String,String> nodesMap = getAllNodes(allRelations);

            if (allRelations != null && allRelations.size() > 0) {
                for (String relation : allRelations) {
                    String[] nrn = StringUtils.isNotBlank(relation) ? relation.split(",") : null;
                    if (nrn == null || nrn.length < 3) {
                        continue;
                    }
                    String nodeName1 = nrn[0];
                    String relationName = nrn[1];
                    String nodeName2 = nrn[2];
                    Node node1 = new Node();
                    //node1.setId(nodesMap.get(nodeName1));
                    node1.setId(nodeName1);
                    node1.setLabels(new ArrayList<String>() {{
                        add(nodeName1);
                    }});
                    node1.setProperties(new HashMap<String, Object>() {{
                        put("name", nodeName1);
                        put("email", "9094908@qq.com");
                    }});
                    Node node2 = new Node();
                    node2.setId(nodeName2);
                    node2.setLabels(new ArrayList<String>() {{
                        add(nodeName2);
                    }});
                    node2.setProperties(new HashMap<String, Object>() {{
                        put("name", nodeName2);
                        put("email", "9094908@qq.com");
                    }});
                    nodes.add(node1);
                    nodes.add(node2);
                    Relationship relationship = new Relationship();
                    relationship.setId(String.valueOf(i++));
                    relationship.setType(relationName);
                    relationship.setStartNode(nodeName1);
                    relationship.setEndNode(nodeName2);
                    relationship.setProperties(new HashMap<String, Object>() {{
                        put("source", nodeName1);
                        put("relation", relationName);
                        put("target", nodeName2);
                    }});
                    relationships.add(relationship);
                }
            }
//            //获取命名实体
//            try {
//                Map<String,Set<String>> nerTagSet = DependencyParserUtils.getMyNERTagSet(strArr);
//                docInfo.setParserNamedEntity(JSONObject.toJSONString(nerTagSet));
//                docInfo.setSummary(SummaryUtils.autoSummary(content));
//            } catch (Exception e) {
//                e.printStackTrace();
//            }
        }
        response = returnResponse(nodes, relationships);
//        docInfo.setParserGraphData(JSONObject.toJSONString(response));
//        docInfo.setStatus(1);
//        insertOrUpdateDocInfo(docInfo);
        return response;
    }

    public List<WordCloudItem> getWordCloudByCateId(String categoryId) {
        String regex = "<.*?>"; // 匹配HTML标签的正则表达式
        String regEx = "[\n`~1234567890!@#$%^&*()+=|{}':;',\\[\\].<>/?~！@#￥%……&*（）——+|{}【】‘；：”“’。， 、？\n" +
                "　　]";
        Map<String, Integer> temp = new ConcurrentHashMap<>();
        List<WordCloudItem> wordCloudItemList = new ArrayList<>();
        DocInfo docInfo = new DocInfo();
        docInfo.setCateId(categoryId);
        List<DocInfo> docInfoList = selectDocInfoList(docInfo);
        List<Term> termList = docInfoList.parallelStream().filter(ObjectUtils::isNotEmpty).flatMap(_docInfo -> {
            try {
                return NLPTokenizer.
                        segment(FormatUtil.RemovalOfStopWords(_docInfo.getContent().replaceAll(regex, "").replaceAll("\\s+", "").replaceAll(regEx, "").trim())
                        ).stream();
            } catch (IOException e) {
                e.printStackTrace();
                return Stream.empty();
            }
        }).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(termList)) {
            termList.parallelStream().filter(t -> t.word.length() > 1).forEach(term -> {
                String word = term.word;
                Integer value = term.getFrequency();
                if (!temp.containsKey(word)) {
                    temp.put(word, 1);
                } else {
                    temp.put(word, temp.get(word) + 1);
                }
            });
        }

        for (
                Map.Entry<String, Integer> entry : temp.entrySet()) {
            WordCloudItem wordCloudItem = new WordCloudItem();
            wordCloudItem.setName(entry.getKey());
            wordCloudItem.setValue(entry.getValue());
            wordCloudItemList.add(wordCloudItem);
        }
        return wordCloudItemList;
    }


    public EmotionResult getEmotionAnalysis(String categoryId) throws IOException {
        String regex = "<.*?>"; // 匹配HTML标签的正则表达式
        String regEx = "[\n`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~！@#￥%……&*（）——+|{}【】‘；：”“’。， 、？]";
        Map<String, Integer> temp = new ConcurrentHashMap<>();
//        List<EmotionResult> emotionResultItemList = new ArrayList<>();
        EmotionResult emotionResult1 = new EmotionResult();
        DocInfo docInfo = new DocInfo();
        docInfo.setCateId(categoryId);
        List<DocInfo> docInfoList = selectDocInfoList(docInfo);
        if (CollectionUtils.isNotEmpty(docInfoList)) {
            List<Term> termList = docInfoList.parallelStream().filter(ObjectUtils::isNotEmpty)
                    .flatMap(_docInfo -> NLPTokenizer.segment(_docInfo.getContent().replaceAll(regex, "")
                            .replaceAll("\\s+", "").replaceAll("[,，　　'“”.。]", "").trim()).stream()).collect(Collectors.toList());
//            List<Term> termList = docInfoList.parallelStream().filter(ObjectUtils::isNotEmpty).flatMap(_docInfo -> {
//                     NLPTokenizer.segment{_docInfo.getContent().replaceAll(regex, "").replaceAll("\\s+", "").replaceAll(regEx, "").trim()).stream().collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(termList)) {
                termList.parallelStream().filter(t -> t.word.length() > 1).forEach(term -> {
                    String word = term.word;
                    Integer value = term.getFrequency();
                    if (!temp.containsKey(word)) {
                        temp.put(word, 1);
                    } else {
                        temp.put(word, temp.get(word) + 1);
                    }
                });
            }
        }


//        int count = 0;
//        int count2 = 0;
//        for (Map.Entry<String, Integer> entry : temp.entrySet()) {
//            EmotionResult emotionResult = new EmotionResult();
//            String key = entry.getKey();
//            String analysis = SentimentAnalysisUtils.analysis(key);
//            if (analysis.equals("正面")) {
//                count++;
//            } else {
//                count2++;
//            }
//            emotionResult.setUpCount(count);
//            emotionResult.setDownCount(count2);
//            emotionResult1 = emotionResult;
//        }
//        emotionResult1.setDownName("负面");
//        emotionResult1.setUpName("正面");
//        return emotionResult1;

        int count = 0;
        int count2 = 0;
        EmotionResult emotionResult = new EmotionResult();

        IClassifier classifier = new NaiveBayesClassifier(trainOrLoadModel());
        for (Map.Entry<String, Integer> entry : temp.entrySet()) {
            String key = entry.getKey();
            String analysis = SentimentAnalysisUtils.analysis1(key, classifier);
            if (analysis.equals("正面")) {
                count++;
            } else {
                count2++;
            }
        }
        emotionResult.setUpCount(count);
        emotionResult.setDownCount(count2);
        emotionResult.setUpName("正面");
        emotionResult.setDownName("负面");
        return emotionResult;
    }

}


