package com.zjty.tynotes.search.subject.service.impl;

import com.zjty.tynotes.search.config.EsConfiguration;
import com.zjty.tynotes.search.subject.entity.CollectionRs;
import com.zjty.tynotes.search.subject.entity.EsSearchArg;
import com.zjty.tynotes.search.subject.entity.EsSource;
import com.zjty.tynotes.search.subject.service.EsUtil;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.*;

/**
 * @author LJJ
 * on 2019-04-24
 */
@Slf4j
@Component
public class EsUtilImpl implements EsUtil {


    private static String ES_PREFIX="note_";

    @Autowired
    private EsConfiguration esConfiguration;

    @Override
    public void createIndex(String indexName) {

        indexName = ES_PREFIX + indexName;

        RestHighLevelClient client = esConfiguration.conn();

        CreateIndexRequest request = new CreateIndexRequest(indexName);

        request.settings(Settings.builder()
                .put("index.number_of_shards", 3)
                .put("index.number_of_replicas", 2)
                .build());

        request.mapping("doc", "{\n" +
                "  \"properties\": {\n" +
                "    \"title\":{\n" +
                "      \"type\": \"text\",\n" +
                "      \"analyzer\": \"ik_max_word\",\n" +
                "      \"search_analyzer\": \"ik_max_word\"\n" +
                "    }\n" +
                "  }\n" +
                "}", XContentType.JSON);

        try {
            client.indices().create(request);
            esConfiguration.close(client);
        } catch (IOException e) {
            log.error("[es] es create index err, the err msg is : {}", e.getMessage());
            esConfiguration.close(client);
        }
    }

    @Override
    public boolean save(String indexName, EsSource data) {

        RestHighLevelClient client = esConfiguration.conn();

        IndexRequest indexRequest = new IndexRequest(indexName, "doc", data.getId())
                .source(data.getJsonMap());
        try {
            client.index(indexRequest);
            esConfiguration.close(client);
            return true;
        } catch (IOException e) {
            log.error("[es] save data err, the err msg is : {}", e.getMessage());
            esConfiguration.close(client);
            return false;
        }
    }

    @Override
    public boolean save(String indexName, List<EsSource> data) {
        RestHighLevelClient client = esConfiguration.conn();

        BulkRequest bulkRequest = new BulkRequest();
        data.forEach(o -> bulkRequest.add(new IndexRequest(indexName, "doc", o.getId()).source(o.getJsonMap())));

        try {
            client.bulk(bulkRequest);
            esConfiguration.close(client);
            return true;
        } catch (IOException e) {
            log.error("[es] bulk es data err,the index is: {},err msg is: {}", indexName, e.getMessage());
            esConfiguration.close(client);
            return false;
        }
    }

    @Override
    public List<Map<String, Object>> search(String indexName, EsSearchArg esSearchArg) {

        RestHighLevelClient client = esConfiguration.conn();

        SearchRequest searchRequest = new SearchRequest(indexName);
        searchRequest.types("doc");

        SearchSourceBuilder builder = new SearchSourceBuilder();

        int from = esSearchArg.getPage() * esSearchArg.getSize();
        builder.size(esSearchArg.getSize()).from(from);

        builder.sort("updateTime", SortOrder.DESC);

        BoolQueryBuilder query = QueryBuilders.boolQuery();

        // 判断执行者信息，如果不为空和null，则进行搜索
        if (!"".equals(esSearchArg.getExecutor()) && esSearchArg.getExecutor() != null) {
            query.must(QueryBuilders.matchQuery("executor", esSearchArg.getExecutor()));
        }

        // 关注者
        if (!"".equals(esSearchArg.getViewer()) && esSearchArg.getViewer() != null) {
            query.must(QueryBuilders.matchQuery("viewer", esSearchArg.getViewer()));
        }

        // 审核者
        if (!"".equals(esSearchArg.getInspector()) && esSearchArg.getInspector() != null) {
            query.must(QueryBuilders.matchQuery("inspector", esSearchArg.getInspector()));
        }

        // 发布者
        if (!"".equals(esSearchArg.getPublisher()) && esSearchArg.getPublisher() != null) {
            query.must(QueryBuilders.matchQuery("publisher", esSearchArg.getPublisher()));
        }

        // 根据任务状态搜索
        query.must(QueryBuilders.matchQuery("state", esSearchArg.getStatus()));

        builder.query(query);

        searchRequest.source(builder);

        return getEsSource(client, searchRequest);

    }

    private List<Map<String, Object>> getEsSource(RestHighLevelClient client, SearchRequest searchRequest) {

        try {
            SearchResponse response = client.search(searchRequest);
            esConfiguration.close(client);
            List<Map<String, Object>> rs = new ArrayList<>();
            for (SearchHit hit : response.getHits().getHits()) {
                rs.add(hit.getSourceAsMap());
            }
            return rs;

        } catch (Exception e) {
            log.info("[es] es search err,the err msg is: {}", e.getMessage());
            esConfiguration.close(client);
            return Collections.emptyList();
        }
    }

    @Override
    public CollectionRs getPresentTask(String id, EsSearchArg esSearchArg) {

        RestHighLevelClient client = esConfiguration.conn();

        SearchRequest searchRequest = new SearchRequest(ES_PREFIX + id);
        searchRequest.types("doc");

        SearchSourceBuilder builder = new SearchSourceBuilder();

        builder.query(QueryBuilders.boolQuery()
                .should(QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("executor", id))))
                .sort("time", SortOrder.DESC);

        builder.size(esSearchArg.getSize());
        builder.from(esSearchArg.getSize() * esSearchArg.getPage());

        searchRequest.source(builder);

        return getSearchTotalAndSource(client, searchRequest);

    }

    @Override
    public CollectionRs getMeTask(String id, EsSearchArg esSearchArg) {
        RestHighLevelClient client = esConfiguration.conn();

        SearchRequest searchRequest = new SearchRequest(ES_PREFIX + id);
        searchRequest.types("doc");

        SearchSourceBuilder builder = new SearchSourceBuilder();

        builder.query(QueryBuilders.boolQuery()
                .should(QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("workAttribution",1))))
                .sort("time", SortOrder.DESC);

        builder.size(esSearchArg.getSize());
        builder.from(esSearchArg.getSize() * esSearchArg.getPage());

        searchRequest.source(builder);

        return getSearchTotalAndSource(client, searchRequest);
    }

    @Override
    public CollectionRs getMeReceiveTask(String id, EsSearchArg esSearchArg) {
        RestHighLevelClient client = esConfiguration.conn();

        SearchRequest searchRequest = new SearchRequest(ES_PREFIX + id);
        searchRequest.types("doc");

        SearchSourceBuilder builder = new SearchSourceBuilder();

        builder.query(QueryBuilders.boolQuery()
                .should(QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("workAttribution", 2))))
                .sort("time", SortOrder.DESC);

        builder.size(esSearchArg.getSize());
        builder.from(esSearchArg.getSize() * esSearchArg.getPage());

        searchRequest.source(builder);

        return getSearchTotalAndSource(client, searchRequest);
    }

    @Override
    public CollectionRs getMeResolveTask(String id, EsSearchArg esSearchArg) {
        RestHighLevelClient client = esConfiguration.conn();

        SearchRequest searchRequest = new SearchRequest(ES_PREFIX + id);
        searchRequest.types("doc");

        SearchSourceBuilder builder = new SearchSourceBuilder();

        builder.query(QueryBuilders.boolQuery()
                .should(QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("workAttribution", 3))))
                .sort("time", SortOrder.DESC);

        builder.size(esSearchArg.getSize());
        builder.from(esSearchArg.getSize() * esSearchArg.getPage());

        searchRequest.source(builder);

        return getSearchTotalAndSource(client, searchRequest);
    }

    @Override
    public CollectionRs getMeReleaseTask(String id, EsSearchArg esSearchArg) {
        RestHighLevelClient client = esConfiguration.conn();

        SearchRequest searchRequest = new SearchRequest(ES_PREFIX + id);
        searchRequest.types("doc");

        SearchSourceBuilder builder = new SearchSourceBuilder();

        builder.query(QueryBuilders.boolQuery()
                .should(QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("workAttribution", 4)))
        .mustNot(QueryBuilders.matchQuery("superiorId","")))
                .sort("time", SortOrder.DESC);

        builder.size(esSearchArg.getSize());
        builder.from(esSearchArg.getSize() * esSearchArg.getPage());

        searchRequest.source(builder);

        return getSearchTotalAndSource(client, searchRequest);
    }

    @Override
    public CollectionRs getApplyTask(String id, EsSearchArg esSearchArg) {

        RestHighLevelClient client = esConfiguration.conn();

        SearchRequest searchRequest = new SearchRequest(ES_PREFIX + id);
        searchRequest.types("doc");

        SearchSourceBuilder builder = new SearchSourceBuilder();

        builder.query(QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("executor", id))
                .must(QueryBuilders.matchQuery("state", "committed")));

        builder.size(esSearchArg.getSize())
                .from(esSearchArg.getSize() * esSearchArg.getPage())
                .sort("updateTime", SortOrder.DESC);

        searchRequest.source(builder);

        return getSearchTotalAndSource(client, searchRequest);
    }

    @Override
    public CollectionRs getContactTask(String id, EsSearchArg esSearchArg) {

        RestHighLevelClient client = esConfiguration.conn();

        SearchRequest searchRequest = new SearchRequest(ES_PREFIX + id);
        searchRequest.types("doc");

        SearchSourceBuilder builder = new SearchSourceBuilder();

        builder.query(QueryBuilders.boolQuery()
                .should(QueryBuilders.boolQuery()
                        .must(QueryBuilders.matchQuery("publisher", id))
                        .mustNot(QueryBuilders.matchQuery("state", "finished")))
                .should(QueryBuilders.boolQuery()
                        .must(QueryBuilders.matchQuery("viewer", id))
                        .mustNot(QueryBuilders.matchQuery("state", "finished")))
                .should(QueryBuilders.boolQuery()
                        .must(QueryBuilders.matchQuery("inspector", id))
                        .mustNot(QueryBuilders.matchQuery("state", "finished"))
                        .mustNot(QueryBuilders.matchQuery("state", "committed")))
        );

        builder.size(esSearchArg.getSize())
                .from(esSearchArg.getSize() * esSearchArg.getPage())
                .sort("updateTime", SortOrder.DESC);

        searchRequest.source(builder);

        return getSearchTotalAndSource(client, searchRequest);
    }

    @Override
    public CollectionRs getCollection(String id, int page, int size) {

        RestHighLevelClient client = esConfiguration.conn();

        SearchRequest searchRequest = new SearchRequest(ES_PREFIX + id);
        searchRequest.types("doc");

        SearchSourceBuilder builder = new SearchSourceBuilder();

        builder.query(QueryBuilders.boolQuery()
                .must(QueryBuilders.matchQuery("collection", 1))
                .mustNot(QueryBuilders.matchQuery("state", "finished")));
        builder.size(size)
                .from(size * page)
                .sort("updateTime", SortOrder.DESC);

        searchRequest.source(builder);

        return getSearchTotalAndSource(client, searchRequest);
    }

    @Override
    public CollectionRs getTaskByTitle(String id, String title, int page, int size) {
        RestHighLevelClient client = esConfiguration.conn();

        SearchRequest searchRequest = new SearchRequest(ES_PREFIX + id);
        searchRequest.types("doc");

        SearchSourceBuilder builder = new SearchSourceBuilder();

        builder.query(QueryBuilders.boolQuery()
                .must(QueryBuilders.matchQuery("title", title))
                );
        builder.size(size)
                .from(size * page)
                .sort("updateTime", SortOrder.DESC);

        searchRequest.source(builder);

        return getSearchTotalAndSource(client, searchRequest);
    }

    private CollectionRs getSearchTotalAndSource(RestHighLevelClient client, SearchRequest searchRequest) {
        try {
            SearchResponse response = client.search(searchRequest);
            esConfiguration.close(client);
            List<Map<String, Object>> rs = new ArrayList<>();
            for (SearchHit hit : response.getHits().getHits()) {
                rs.add(hit.getSourceAsMap());
            }
            return new CollectionRs(response.getHits().getTotalHits(), rs);

        } catch (Exception e) {
            log.info("[es] es search err,the err msg is: {}", e.getMessage());
            esConfiguration.close(client);
            return new CollectionRs(0L, Collections.emptyList());
        }
    }

    @Override
    public List<Map<String, Object>> findByIds(String indexName) {

        RestHighLevelClient client = esConfiguration.conn();

        SearchRequest searchRequest = new SearchRequest(indexName);
        searchRequest.types("doc");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.matchAllQuery()).size((int) getTotal(indexName));
        searchRequest.source(searchSourceBuilder);

        try {
            SearchResponse response = client.search(searchRequest);
            esConfiguration.close(client);
            SearchHit[] hits = response.getHits().getHits();
            List<Map<String, Object>> rs = new ArrayList<>();
            for (SearchHit hit : hits) {
                Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                rs.add(sourceAsMap);
            }

            return rs;

        } catch (IOException e) {
            log.error("[es] find by id err, the err msg is : {}", e.getMessage());
            esConfiguration.close(client);

            return Collections.emptyList();
        }

    }

    @Override
    public Map<String, Object> findOne(String indexName, String id) {

        RestHighLevelClient client = esConfiguration.conn();

        GetRequest getRequest = new GetRequest(indexName, "doc", id);

        try {
            GetResponse response = client.get(getRequest);
            Map<String, Object> rs = response.getSource();
            esConfiguration.close(client);
            return rs;
        } catch (IOException e) {
            esConfiguration.close(client);
            log.error("[es] find by id err, the err msg is: {}", e.getMessage());
            return Collections.emptyMap();
        }
    }

    @Override
    public void delete(String indexName, String id) {

        RestHighLevelClient client = esConfiguration.conn();

        DeleteRequest deleteRequest = new DeleteRequest(indexName, "doc", id);

        try {
            client.delete(deleteRequest);
            esConfiguration.close(client);

        } catch (IOException e) {
            esConfiguration.close(client);
            log.error("[es] delete by id err, the err msg is : {}", e.getMessage());
        }

    }

    @Override
    public long getTotal(String indexName) {
        RestHighLevelClient client = esConfiguration.conn();

        SearchRequest searchRequest = new SearchRequest(indexName);
        searchRequest.types("doc");

        SearchSourceBuilder builder = new SearchSourceBuilder();
        builder.query(QueryBuilders.matchAllQuery());

        searchRequest.source(builder);

        SearchResponse response = null;
        try {
            response = client.search(searchRequest);
        } catch (IOException e) {
            log.error("[es] es get num err,index name is: {},err msg is: {}", indexName, e.getMessage());
        }

        return response != null ? response.getHits().getTotalHits() : 0;
    }
}
