package cn.com.capcloud.core.lucene;


import cn.com.capcloud.core.noentitydb.JtPage;
import org.apache.lucene.document.*;
import org.apache.lucene.index.*;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.search.*;
import org.apache.lucene.search.highlight.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@Service
public class LuceneIndexManager {
    private Map<String,LuceneInfo> luceneInfoMap;

    @Resource
    private JdbcTemplate jdbcTemplate;

    @Resource
    private LuceneConfig thisConfig;

    @Autowired
    public LuceneIndexManager(LuceneConfig config) throws IOException {
        this.luceneInfoMap=config.indexDirectory();
    }

    public void reInitLuceneDir() throws IOException {
        this.luceneInfoMap=thisConfig.indexDirectory();
    }

    public List<Map<String,Object>> getAllDocuments(String tableName){
        return jdbcTemplate.queryForList("select * from "+tableName+" where status=0");
    }

    public boolean reBuildIndex(String tableName,String dsUUID)  {
        // 删除所有文档并重新创建索引
        boolean res = deleteAllDocuments(tableName);
        if(res){
            List<LuceneIndexBuilderParam> params = getFieldsParam(dsUUID);
            res = createIndex(params,tableName);
            return res;
        }else{
            return res;
        }
    }

    public boolean createIndex(List<LuceneIndexBuilderParam> params ,String tableName)  {
        LuceneInfo luceneInfo=luceneInfoMap.get(tableName);
        IndexWriterConfig config = new IndexWriterConfig(luceneInfo.getAnalyzer());
        config.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND);
        List<Map<String,Object>> objList = getAllDocuments(tableName);
        try (IndexWriter writer = new IndexWriter(luceneInfo.getDirectory(), config)) {
            for (Map<String,Object> obj : objList) {
                Document doc = new Document();
                String uuidStr = obj.get("uuid").toString();
                FieldType fieldTypeUuid = new FieldType();
                fieldTypeUuid.setStored(true);
                fieldTypeUuid.setTokenized(false);
                fieldTypeUuid.setIndexOptions(IndexOptions.DOCS);
                Field fieldUuid = new Field("uuid", uuidStr, fieldTypeUuid);
                doc.add(fieldUuid);
                for(LuceneIndexBuilderParam param : params){
                    if(param.getFieldName().equals("uuid")){
                        continue;
                    }
                    FieldType fieldType = new FieldType();
                    fieldType.setStored(true);
                    if(param.getIndexType().equals("text")) {
                        fieldType.setTokenized(true);
                        fieldType.setIndexOptions(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS);
                    } else if (param.getIndexType().equals("string")) {
                        fieldType.setIndexOptions(IndexOptions.DOCS);
                        fieldType.setTokenized(false);
                    }else if(param.getIndexType().equals("long")){//long
                        fieldType.setIndexOptions(IndexOptions.DOCS);
                        fieldType.setDocValuesType(DocValuesType.NUMERIC);
                        fieldType.setTokenized(false);
                    }else{
                        continue;
                    }
                    String value=obj.get(param.getFieldName()).toString();
                    value=value.replaceAll("&nbsp;"," ");
                    Field field = new Field(param.getFieldName(), value, fieldType);
                    doc.add(field);
                }
                writer.addDocument(doc);
            }
            writer.commit();
            writer.close();
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
        return true;
    }
    Map<String,Object> getAllFieldsByUuid(String tableName,String uuid){
        return jdbcTemplate.queryForMap("select * from "+tableName+" where uuid=?",uuid);
    }

    public boolean updateIndex(String tableName,String dsUUID,String dataUUID)  {
        List<LuceneIndexBuilderParam> params = getFieldsParam(dsUUID);
        return updateIndex(params, tableName,dataUUID);
    }

    //更新所有需要索引的字段
    public boolean updateIndex(List<LuceneIndexBuilderParam> params ,String tableName,String dataUUID){
        LuceneInfo luceneInfo=luceneInfoMap.get(tableName);
        // 更新索引
        IndexWriterConfig  config = new IndexWriterConfig(luceneInfo.getAnalyzer());
        config.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND);

        try (IndexWriter indexWriter = new IndexWriter(luceneInfo.getDirectory(),  config)) {
            indexWriter.deleteDocuments(new Term("uuid", dataUUID));
            Document doc = new Document();
            FieldType fieldTypeUuid = new FieldType();
            fieldTypeUuid.setStored(true);
            fieldTypeUuid.setTokenized(false);
            fieldTypeUuid.setIndexOptions(IndexOptions.DOCS);
            Field fieldUuid = new Field("uuid", dataUUID, fieldTypeUuid);
            doc.add(fieldUuid);
            Map<String,Object> valueMap = getAllFieldsByUuid(tableName,dataUUID);
            for(LuceneIndexBuilderParam param:params){
                if(param.getFieldName().equals("uuid")){
                    continue;
                }
                if(valueMap.containsKey(param.getFieldName())) {
                    String value = valueMap.get(param.getFieldName()).toString();//获得值
                    FieldType fieldType = new FieldType();
                    fieldType.setStored(true);
                    if(param.getIndexType().equals("text")){
                        fieldType.setIndexOptions(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS);
                        fieldType.setTokenized(true);
                    } else if (param.getIndexType().equals("string")) {
                        fieldType.setIndexOptions(IndexOptions.DOCS);
                        fieldType.setTokenized(false);
                    }else if(param.getIndexType().equals("long")){
                        fieldType.setIndexOptions(IndexOptions.DOCS);
                        fieldType.setDocValuesType(DocValuesType.NUMERIC);
                    }else{
                        continue;
                    }
                    value=value.replaceAll("&nbsp;"," ");
                    Field field = new Field(param.getFieldName(), value, fieldType);
                    doc.add(field);
                }
            }
            indexWriter.addDocument(doc);
            indexWriter.commit();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return true;
    }


    public void deleteIndexByUuid(String tableName,String dataUUID) throws IOException {
        LuceneInfo luceneInfo=luceneInfoMap.get(tableName);
        IndexWriterConfig  config = new IndexWriterConfig(luceneInfo.getAnalyzer());
        config.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND);
        try (IndexWriter indexWriter = new IndexWriter(luceneInfo.getDirectory(), config)) {
            // 删除指定id的文档
            indexWriter.deleteDocuments(new Term("uuid", dataUUID));
            indexWriter.commit();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public List<LuceneIndexBuilderParam> getFieldsParam(String dsUUID){
        List<LuceneIndexBuilderParam>  params = new ArrayList<>();
        // 查询数据库，获取表的字段信息 tbl_data_lucene_index_field 必须有值
        String sql = "select * from tbl_data_lucene_index_field  where status=0 and page_parent_uuid = '"+dsUUID+"'";
        List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);
        for (Map<String, Object> map : list) {
            LuceneIndexBuilderParam param = new LuceneIndexBuilderParam();
            param.setFieldName(map.get("field_name").toString());
            param.setIndexType(map.get("index_type").toString());
            param.setWantReturn(map.get("want_return").toString());
            params.add(param);
        }
        return params;
    }

    private boolean deleteAllDocuments(String tableName) {
        LuceneInfo luceneInfo=luceneInfoMap.get(tableName);
        try (IndexWriter indexWriter = new IndexWriter(luceneInfo.getDirectory(), new IndexWriterConfig(luceneInfo.getAnalyzer()))) {
            indexWriter.deleteAll();
            indexWriter.commit();
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }



    private TopDocs searchByPageNew(int page, int perPage, IndexSearcher searcher, BooleanQuery.Builder booleanQueryBuilder) throws IOException {

        TopDocs result;
        if (booleanQueryBuilder == null) {
            System.out.println(" Query is null return null ");
            return null;
        }
        ScoreDoc before = null;
        if (page != 1) {
            TopDocs docsBefore = searcher.search(booleanQueryBuilder.build(), (page - 1) * perPage);
            ScoreDoc[] scoreDocs = docsBefore.scoreDocs;
            if (scoreDocs.length > 0) {
                before = scoreDocs[scoreDocs.length - 1];
            }
        }
        result = searcher.searchAfter(before, booleanQueryBuilder.build(), perPage);
        return result;
    }


    public Query buildQuery(SearchParam  searchParam,String tableName) throws ParseException {
        LuceneInfo luceneInfo=luceneInfoMap.get(tableName);
        String keyword = searchParam.getKeyword();
        if(keyword==null||keyword.equals("")){
            return null;
        }

        String searchField = searchParam.getSearchField();
        if(searchField==null||searchField.equals("")){
            return null;
        }
        String searchFieldAry[] =  searchField.split(",");

        int searchType = searchParam.getSearchType();
        if(searchType==0) {//对于检索词不分词
            keyword ="\"" + keyword + "\"";
            System.out.println("关键词不分词："+keyword);
        }else {
            System.out.println("关键词分词："+keyword);
        }
        Map<String,Object> filterMap = searchParam.getFilterMap();
        BooleanQuery.Builder booleanQueryBuilder = new BooleanQuery.Builder();//过滤时

        for(String filterFieldName: filterMap.keySet()){
            Term term = new Term(filterFieldName, filterMap.get(filterFieldName).toString());
            TermQuery termQuery =  new TermQuery(term);
            booleanQueryBuilder.add(termQuery, BooleanClause.Occur.MUST);
        }
        MultiFieldQueryParser parser = new MultiFieldQueryParser(searchFieldAry, luceneInfo.getAnalyzer());
        Query query = parser.parse(keyword);
        return query;
    }

    public List<Map<String,Object>> processResult(TopDocs topDocs,IndexReader reader,Map<String,Highlighter> highlighterMap,String tableName,String dsUUID) throws IOException, InvalidTokenOffsetsException {
        LuceneInfo luceneInfo=luceneInfoMap.get(tableName);
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        List<Map<String,Object>> list = new ArrayList<>();
        for (ScoreDoc scoreDoc : scoreDocs) {
            // 取出文档编号
            Map<String ,Object>  map = new HashMap<>();
            int docId = scoreDoc.doc;
            // 根据编号去找文档
            Document doc = reader.document(docId);
            String documentUuid = doc.get("uuid");
            System.out.println("uuid:" + documentUuid);
            map.put("uuid",documentUuid);

           map.put("score", scoreDoc.score);
            Map<String,Object> objectMap = getAllFieldsByUuid(tableName,documentUuid);
            List<LuceneIndexBuilderParam> fields = getFieldsParam(dsUUID);
            for(LuceneIndexBuilderParam field:fields){
                if(field.getWantReturn().equals("Y")&&objectMap.get(field.getFieldName())!=null){
                    map.put(field.getFieldName(),objectMap.get(field.getFieldName()));
                }
                if(field.getWantReturn().equals("Y")&&field.getIndexType().equals("text")){
                    Highlighter highlighter=highlighterMap.get(field.getFieldName());
                    String textFieldValue = null;
                    if(highlighter!=null){
                        textFieldValue=highlighter.getBestFragment(luceneInfo.getAnalyzer(), field.getFieldName(), doc.get(field.getFieldName()));
                    }

                    if(textFieldValue!=null){
                        map.put(field.getFieldName(),textFieldValue);
                    }else{
                        if(objectMap.get(field.getFieldName())!=null){
                            textFieldValue = objectMap.get(field.getFieldName())+"";
                            if(textFieldValue.length()>200){
                                textFieldValue = textFieldValue.substring(0,200);
                            }
                            map.put(field.getFieldName(),textFieldValue);
                        }
                    }
                }
            }
            list.add(map);
        }
        reader.close();
        return list;
    }

    public JtPage comPage(int page, int pageSize, TopDocs topDocs, List<Map<String,Object>> list){
        /*KHCResponseEntity responseEntity  = new KHCResponseEntity();
        if(page<0||pageSize<1){
            responseEntity.setStatus("0");
            responseEntity.setMessage("page相关参数有问题：page="+page+" pageSize="+pageSize);
            return responseEntity;
        }
        if(list.size()==0){
            responseEntity.setStatus("0");
            responseEntity.setMessage("查询成功");
            return responseEntity;
        }*/
        JtPage jtPage=new JtPage();
        jtPage.setTotalPages((int)topDocs.totalHits.value/pageSize+1);
        jtPage.setTotalElements((int)topDocs.totalHits.value);
        jtPage.setSize(pageSize);
        jtPage.setNumber(page);
        jtPage.setContent(list);
        jtPage.setNumberOfElements(list.size());
        /*responseEntity.setData(jtPage);
        return  responseEntity;*/
        return jtPage;
    }

    public JtPage searchMultipleQuery(List<SearchParam> searchParams,int page ,int pageSize,String tableName,String dsUUID) throws IOException {
        LuceneInfo luceneInfo=luceneInfoMap.get(tableName);
        IndexReader reader = DirectoryReader.open(luceneInfo.getDirectory());
        // 索引搜索工具
        IndexSearcher searcher = new IndexSearcher(reader);
        BooleanQuery.Builder booleanQueryBuilder = new BooleanQuery.Builder();

        try{
            Map<String,Highlighter> highlighterMap=new HashMap<>();
            for(SearchParam searchParam : searchParams){
                Query query = buildQuery(searchParam,tableName);
                Highlighter highlighter = highlightDoc(query);
                if(query==null){
                    continue;
                }
                booleanQueryBuilder.add(query, BooleanClause.Occur.MUST);
                String field=searchParam.getSearchField();
                String[] arr = field.split(",");
                for (int i = 0; i < arr.length; i++) {
                    highlighterMap.put(arr[i],highlighter);
                }
            }
            TopDocs topDocs = searchByPageNew(page, pageSize, searcher, booleanQueryBuilder);
            List<Map<String,Object>> list = processResult(topDocs,reader,highlighterMap,tableName,dsUUID);

            return comPage(page,pageSize,topDocs,list);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    public Highlighter highlightDoc(Query query){
        SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter("<span style='color:red'>", "</span>");
        Highlighter highlighter = new Highlighter(simpleHTMLFormatter, new QueryScorer(query));
        //高亮后的段落范围在150字内
        Fragmenter fragmenter = new SimpleFragmenter(150);
        highlighter.setTextFragmenter(fragmenter);
        return highlighter;
    }


}
