package com.zzsn.knowbase.service.impl;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zzsn.knowbase.constant.Constants;
import com.zzsn.knowbase.entity.*;
import com.zzsn.knowbase.enums.KnowTypeEnum;
import com.zzsn.knowbase.kafka.message.KnowledgeMessage;
import com.zzsn.knowbase.kafka.producer.ProduceInfo;
import com.zzsn.knowbase.service.IKnowledgeService;
import com.zzsn.knowbase.service.KbKnowledgeProjectService;
import com.zzsn.knowbase.util.*;
import com.zzsn.knowbase.vo.KnowledgeParam;
import com.zzsn.knowbase.vo.KnowledgeVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.jsoup.Jsoup;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description: 知识
 * @Author: chenshiqiang
 * @Version: V1.0
 */
@Service
@Slf4j
class KnowledgeServiceImpl implements IKnowledgeService {
    @Autowired
    private RestHighLevelClient client;
    @Autowired
    private EsOpUtil esOpUtil;
    @Autowired
    private CodeGenerateUtil codeGenerateUtil;
    @Autowired
    private KbKnowledgeProjectService knowledgeProjectService;
    @Autowired
    private ProduceInfo produceInfo;
    @Value("${python.searchUrl:}")
    private String searchUrl;
    @Value("${python.deleteUrl:}")
    private String deleteUrl;

    @Autowired
    private RedisUtil redisUtil;
    @Value("${files.storage}")
    String filesStorage;


    @Override
    public void addKnowledge(KnowFile knowFile, Knowledge knowledge, KbAuthorizedUser userInfo) {
        knowledge.setAuthor(userInfo.getName());
        if (null == knowledge.getId()) {
            knowledge.setId(codeGenerateUtil.geneIdNo(Constants.FINANCE, 8));
        }
        if (null == knowledge.getVerifyStatus()) {
            knowledge.setVerifyStatus(0);
        }


        knowledge.setCreateTime(cn.hutool.core.date.DateUtil.formatDateTime(new Date()).replace(" ", "T"));
        knowledge.setDeleteFlag(0);
        List<KnowFile> knowFileList = new ArrayList<>();
        knowFileList.add(knowFile);
        knowledge.setFiles(knowFileList);
        List<Content> contentList = new ArrayList<>();
        List<String> contentStringList = new ArrayList<>();
        String html = null;
        try {
            File file = new File(filesStorage + knowledge.getFiles().get(0).getFilePath());
            html =DocUtil.convertDocStream2Html(new FileInputStream(file));
            String htmlWithTable = html.replace("<p>", "");
            contentStringList = Arrays.asList(htmlWithTable.split("</p>"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        List<String> messageContentList = contentStringList.stream()
                .filter(item -> !item.contains("<img"))
                .filter(item -> !item.contains("<table"))
                .filter(item -> !item.contains("<tr>"))
                .filter(item -> !item.contains("</tr>"))
                .filter(item -> !item.contains("<td>"))
                .filter(item -> !item.contains("</td>"))
                .map(item -> Jsoup.parse(item).text())
                .collect(Collectors.toList());
        messageContentList = messageContentList.
                stream().filter(item -> item.length() > 17)
                .collect(Collectors.toList());
        if (!messageContentList.isEmpty()) {
            boolean mark = true;
            if (null != knowledge.getTitle()) {
                mark = false;
            }
            for (String tem : messageContentList) {

                log.info("info:{}", tem);
                if (mark && (!tem.isEmpty())) {
                    knowledge.setTitle(tem);
                    mark = false;
                }
                contentList.add(Content.builder()
                        .contentId(codeGenerateUtil.geneIdNo(Constants.FINANCE, 8))
                        .content(tem)
                        .build());
            }
        }
        knowledge.setContents(contentList);
        esOpUtil.docSavaByEntity(Constants.ES_DATA_FOR_KNOWLEDGE, knowledge.getId(), knowledge);

        //删除
        deleteForPython(knowledge.getId());
        KnowledgeMessage knowledgeMessage = new KnowledgeMessage();
        BeanUtils.copyProperties(knowledge, knowledgeMessage);
//        CleanerProperties props = new CleanerProperties();
//        props.setPruneTags("table");
//        String htmlWithoutTable = new HtmlCleaner(props).clean(html).getText().toString();
//         htmlWithoutTable = htmlWithoutTable.replace("<p>", "");
//        List<String>contentStringWithoutTableList = Arrays.asList(htmlWithoutTable.split("</p>"));

        knowledgeMessage.setContents(contentList);
        produceInfo.sendKnowledgeContents(knowledgeMessage);
    }

    @Override
    public void updateKnowledge(Knowledge knowledge) {
        esOpUtil.docSavaByEntity(Constants.ES_DATA_FOR_KNOWLEDGE, knowledge.getId(), knowledge);
    }

    @Override
    public void deleteKnowledge(String ids) {
        String[] split = ids.split(",");
        for (String id : split) {
            esOpUtil.docDeleteById(Constants.ES_DATA_FOR_KNOWLEDGE, id);
            deleteForPython(id);
        }

    }

    public void deleteForPython(String id) {
        JSONObject params = new JSONObject();
        params.put("knowledge_base_id", id);
        try {
            HttpUtil.doPost(deleteUrl, params, 120000);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Override
    public IPage<KnowledgeVO> queryPageList(KnowledgeParam knowledgeParam, Integer pageNo, Integer pageSize, String column, String order) {

        List<String> permitKnowList = new ArrayList<>();
        List<String> catList;
        List<String> treeList = knowledgeProjectService.getTreeListBy(knowledgeParam.getKnowledgeProjectId())
                .stream().map(KbKnowledgeProject::getId).collect(Collectors.toList());
        //获取用户拥有的分类
        KbAuthorizedUser userInfo = SpringContextUtils.getUserInfo();
        if (null == userInfo) {
            return null;
        }
        //查看全部且是非管理员的时候
        if ((!Integer.valueOf("0").equals(userInfo.getIsAll())) && knowledgeParam.getKnowledgeProjectId().equals("0")) {
            List<KbAuthuserKnowledgeprojectMap> permitKnowProjectList = knowledgeProjectService.getKnowledgeProjectListByUserId(userInfo.getId());
            //all permit
            List<String> allPermitList = permitKnowProjectList.stream()
                    .filter(item -> item.getSign() == 0)
                    .map(KbAuthuserKnowledgeprojectMap::getKnowledgeProjectId)
                    .collect(Collectors.toList());
            //know permit
            List<String> knowPermitList = permitKnowProjectList.stream()
                    .filter(item -> item.getSign() == 1)
                    .map(KbAuthuserKnowledgeprojectMap::getKnowledgeProjectId)
                    .collect(Collectors.toList());

            if (!knowPermitList.isEmpty()) {
                permitKnowList = knowledgeProjectService.getKnowledgeListByUserId(userInfo.getId(), knowPermitList);
            }

            catList = (List<String>) CollectionUtils.intersection(treeList, allPermitList);
        } else {
            catList = treeList;
        }

        SearchRequest searchRequest = new SearchRequest(Constants.ES_DATA_FOR_KNOWLEDGE);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //设置分页参数
        searchSourceBuilder.size(pageSize);
        searchSourceBuilder.from((pageNo - 1) * pageSize);
        //默认按照置顶以及时间倒序排列
        //根据topNum正序查找，查询置顶数据
        searchSourceBuilder.sort("publishDate", SortOrder.DESC);
        if ("publishDate".equals(column)) {
            if ("desc".equals(order)) {
                searchSourceBuilder.sort("publishDate", SortOrder.DESC);
            } else if ("asc".equals(order)) {
                searchSourceBuilder.sort("publishDate", SortOrder.ASC);
            }
        }
        if ("verifyTime".equals(column)) {
            if ("desc".equals(order)) {
                searchSourceBuilder.sort("verifyTime", SortOrder.DESC);
            } else if ("asc".equals(order)) {
                searchSourceBuilder.sort("verifyTime", SortOrder.ASC);
            }
        }
        //默认最大数量是10000，设置为true后，显示准确数量
        searchSourceBuilder.trackTotalHits(true);
        //创建查询对象
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();


//        if (StringUtils.isNotEmpty(knowledgeParam.getTitle())) {
//            boolQuery.must(QueryBuilders.matchPhraseQuery("title", knowledgeParam.getTitle()));
//        }
//        if (StringUtils.isNotEmpty(knowledgeParam.getContent())) {
//            BoolQueryBuilder nestedBoolQueryBuilder = QueryBuilders.boolQuery();
//            nestedBoolQueryBuilder.must(QueryBuilders.matchPhraseQuery("contents.content", knowledgeParam.getContent()));
//            NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("contents", nestedBoolQueryBuilder, ScoreMode.None);
//            boolQuery.must(nestedQueryBuilder);
//        }

        //搜索条件
        if (StringUtils.isNotBlank(knowledgeParam.getSearchInfo())) {
            MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(knowledgeParam.getSearchInfo());
            if ("精确".equals(knowledgeParam.getSearchAccuracy())) {
                multiMatchQueryBuilder.type(MultiMatchQueryBuilder.Type.PHRASE);
                if (null != knowledgeParam.getSearchScope() && Integer.valueOf(1).equals(knowledgeParam.getSearchScope())) {
                    multiMatchQueryBuilder.field("title");
                } else if (null != knowledgeParam.getSearchScope() && Integer.valueOf(2).equals(knowledgeParam.getSearchScope())) {
                    BoolQueryBuilder nestedBoolQueryBuilder = QueryBuilders.boolQuery();
                    multiMatchQueryBuilder.field("contents.content");
                    nestedBoolQueryBuilder.must(multiMatchQueryBuilder);
                    NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("contents", nestedBoolQueryBuilder, ScoreMode.None);
                    boolQuery.must(nestedQueryBuilder);

                } else {
                    multiMatchQueryBuilder.field("title", 60);
                    BoolQueryBuilder nestedBoolQueryBuilder = QueryBuilders.boolQuery();
                    multiMatchQueryBuilder.field("contents.content", 20);
                    nestedBoolQueryBuilder.must(multiMatchQueryBuilder);
                    NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("contents", nestedBoolQueryBuilder, ScoreMode.None);
                    boolQuery.must(nestedQueryBuilder);
                }
            } else {
                if (null != knowledgeParam.getSearchScope() && Integer.valueOf(1).equals(knowledgeParam.getSearchScope())) {
                    multiMatchQueryBuilder.field("title");
                } else if (null != knowledgeParam.getSearchScope() && Integer.valueOf(2).equals(knowledgeParam.getSearchScope())) {
                    BoolQueryBuilder nestedBoolQueryBuilder = QueryBuilders.boolQuery();
                    multiMatchQueryBuilder.field("contents.content");
                    nestedBoolQueryBuilder.must(multiMatchQueryBuilder);
                    NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("contents", nestedBoolQueryBuilder, ScoreMode.None);
                    boolQuery.must(nestedQueryBuilder);
                } else {
                    multiMatchQueryBuilder.field("title", 3);
                    BoolQueryBuilder nestedBoolQueryBuilder = QueryBuilders.boolQuery();
                    multiMatchQueryBuilder.field("contents.content", 1);
                    nestedBoolQueryBuilder.must(multiMatchQueryBuilder);
                    NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("contents", nestedBoolQueryBuilder, ScoreMode.None);
                    boolQuery.must(nestedQueryBuilder);
                }
            }
            boolQuery.must(multiMatchQueryBuilder);
        }
        if (StringUtils.isNotEmpty(knowledgeParam.getOrigin())) {
            boolQuery.must(QueryBuilders.matchPhraseQuery("origin", knowledgeParam.getOrigin()));
        }

        if (StringUtils.isNotEmpty(knowledgeParam.getId())) {
            boolQuery.must(QueryBuilders.termsQuery("id", permitKnowList));
        }
        if (!catList.isEmpty()) {
            boolQuery.must(QueryBuilders.termsQuery("knowledgeProjectId", catList));
        }
        if (StringUtils.isNotEmpty(knowledgeParam.getTypeIds())) {
            boolQuery.must(QueryBuilders.termsQuery("typeId",Arrays.asList(knowledgeParam.getTypeIds().split(",")) ));
        }
        if (null != knowledgeParam.getVerifyStatus()) {
            boolQuery.must(QueryBuilders.termQuery("verifyStatus", knowledgeParam.getVerifyStatus()));
        }

        //时间过滤筛选
        if (StringUtils.isNotBlank(knowledgeParam.getStartTime())) {
            boolQuery.filter(QueryBuilders.rangeQuery("publishDate").gte(EsDateUtil.esFieldDateFormat(knowledgeParam.getStartTime())));
        }
        if (StringUtils.isNotBlank(knowledgeParam.getEndTime())) {
            boolQuery.filter(QueryBuilders.rangeQuery("publishDate").lte(EsDateUtil.esFieldDateFormat(knowledgeParam.getEndTime())));
        }

        searchSourceBuilder.query(boolQuery);
        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = null;
        try {
            searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        SearchHit[] searchHits = searchResponse.getHits().getHits();
        List<KnowledgeVO> list = new ArrayList<>();
        for (SearchHit hit : searchHits) {
            String queryInfo = hit.getSourceAsString();
            KnowledgeVO info = JSONUtil.toBean(queryInfo, KnowledgeVO.class);
            info.setPublishDate(EsDateUtil.esFieldDateMapping(info.getPublishDate()));
            list.add(info);
        }

        IPage<KnowledgeVO> pageData = new Page<>(pageNo, pageSize, searchResponse.getHits().getTotalHits().value);

        pageData.setRecords(list);
        return pageData;
    }

    @Override
    public IPage<KnowledgeVO> listFromPython(KnowledgeParam knowledgeParam, Integer pageNo, Integer pageSize, String column, String order) {

        Object data = redisUtil.get(genKey(knowledgeParam));
        if (org.springframework.util.StringUtils.isEmpty(data)) {
            List<String> treeList = knowledgeProjectService.getTreeListBy(knowledgeParam.getKnowledgeProjectId())
                    .stream().map(KbKnowledgeProject::getId).collect(Collectors.toList());
            List<String> permitKnowList = new ArrayList<>();
            List<String> catList = new ArrayList<>();
            KbAuthorizedUser userInfo = SpringContextUtils.getUserInfo();
            if (null == userInfo) {
                return null;
            }
            if (Integer.valueOf("0").equals(userInfo.getIsAll())) {
                List<KbAuthuserKnowledgeprojectMap> permitKnowProjectList = knowledgeProjectService.getKnowledgeProjectListByUserId(userInfo.getId());
                //all permit
                List<String> allPermitList = permitKnowProjectList.stream()
                        .filter(item -> item.getSign() == 0)
                        .map(KbAuthuserKnowledgeprojectMap::getKnowledgeProjectId)
                        .collect(Collectors.toList());
                //know permit
                List<String> knowPermitList = permitKnowProjectList.stream()
                        .filter(item -> item.getSign() == 1)
                        .map(KbAuthuserKnowledgeprojectMap::getKnowledgeProjectId)
                        .collect(Collectors.toList());
                permitKnowList = knowledgeProjectService.getKnowledgeListByUserId(userInfo.getId(), knowPermitList);
                catList = (List<String>) CollectionUtils.intersection(treeList, allPermitList);
            } else {
                catList = treeList;
            }
            StringBuilder typeNames = new StringBuilder();
            if (null != knowledgeParam.getTypeIds()) {
                for (String type : knowledgeParam.getTypeIds().split(",")) {
                    typeNames.append(KnowTypeEnum.getByType(type).getDes()).append(",");
                }
            }
            JSONObject params = new JSONObject();
            params.put("type", typeNames);
            params.put("startTime", knowledgeParam.getStartTime());
            params.put("endTime", knowledgeParam.getEndTime());
            params.put("author", knowledgeParam.getAuthor());
            params.put("origin", knowledgeParam.getOrigin());
            params.put("verifierName", knowledgeParam.getVerifierName());
            params.put("verifyStatus", knowledgeParam.getVerifyStatus());
            params.put("verifyStartTime", knowledgeParam.getVerifyStartTime());
//            params.put("knowledge_base_id", treeList);
            params.put("id", permitKnowList);
            params.put("knowledgeProjectId", catList);
            params.put("verifyEndTime", knowledgeParam.getVerifyEndTime());
            params.put("score_threshold", 600);//score
            params.put("vector_search_top_k", 100);//number

            if (knowledgeParam.getSearchScope().equals(2)) {
                params.put("question", knowledgeParam.getSearchInfo());
            }
            String result = null;
            try {
                result = HttpUtil.doPost(searchUrl, params, 120000);
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (result == null || !result.contains("results")) {
                return null;
            }
            JSONObject jsonObject = JSONObject.parseObject(result);
            data = jsonObject.get("results");
            redisUtil.set(genKey(knowledgeParam), data, 100000000);
        }
        JSONArray jsonArray = JSONObject.parseArray(data.toString());
        List<KnowledgeVO> knowledgeList = new ArrayList<>(jsonArray.size());
        for (Object o : jsonArray) {
            JSONObject one = JSONObject.parseObject(o.toString());
            knowledgeList.add(KnowledgeVO.builder()
                    .id((String) one.get("id"))
                    .score((int) one.get("score"))
                    .verifyStatus("".equals(one.get("verifyStatus")) ? 0 : Integer.parseInt(one.get("verifyStatus").toString()))
                    .verifyTime(one.get("verifyTime").toString())
                    .publishDate(one.get("publishDate").toString())
                    .type(one.get("type").toString())
                    .verifierName(one.get("verifierName").toString())
                    .content(one.get("content").toString())
                    .build());
        }
        IPage<KnowledgeVO> pageData = new Page<>(pageNo, pageSize, knowledgeList.size());
        pageData.setRecords(knowledgeList);

        return pageData;
    }

    @Override
    public KnowledgeVO getById(String id) {
        Map<String, Object> map = esOpUtil.searchDoc(Constants.ES_DATA_FOR_KNOWLEDGE, id);
        if (map != null) {
            return JSON.parseObject(JSON.toJSONString(map), KnowledgeVO.class);
        }
        return null;
    }

    private String genKey(KnowledgeParam knowledgeParam) {
        String type = (knowledgeParam.getTypeIds() == null ? "" : knowledgeParam.getTypeIds());
        String knowledgeProjectId = (knowledgeParam.getKnowledgeProjectId() == null ? "" : knowledgeParam.getKnowledgeProjectId());
        String startTime = (knowledgeParam.getStartTime() == null ? "" : knowledgeParam.getStartTime());
        String endTime = (knowledgeParam.getEndTime() == null ? "" : knowledgeParam.getEndTime());
        String author = (knowledgeParam.getAuthor() == null ? "" : knowledgeParam.getAuthor());
        String origin = (knowledgeParam.getOrigin() == null ? "" : knowledgeParam.getOrigin());
        String verifierName = (knowledgeParam.getVerifierName() == null ? "" : knowledgeParam.getVerifierName());
        String verifyStatus = (knowledgeParam.getVerifyStatus() == null ? "" : String.valueOf(knowledgeParam.getVerifyStatus()));
        String verifyStartTime = (knowledgeParam.getVerifyStartTime() == null ? "" : knowledgeParam.getVerifyStartTime());
        String verifyEndTime = (knowledgeParam.getVerifyEndTime() == null ? "" : knowledgeParam.getVerifyEndTime());
        String searchInfo = (knowledgeParam.getSearchInfo() == null ? "" : knowledgeParam.getSearchInfo());
        return type + "#" +
                knowledgeProjectId + "#" +
                startTime + "#" +
                endTime + "#" +
                author + "#" +
                origin + "#" +
                verifierName + "#" +
                verifyStatus + "#" +
                verifyStartTime + "#" +
                verifyEndTime + "#" +
                searchInfo + "#";
    }

}
