package com.search.supersearch.service;

import cn.hutool.core.collection.CollUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.TextQueryType;
import co.elastic.clients.elasticsearch.core.CountRequest;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.TrackHits;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author 喜欢番茄火锅
 * @date 2023/10/26 18:10
 */
@Service
@Slf4j
public class DocsService {
    private static String regex = "\\s+|\\t";
    @Resource
    private ElasticsearchClient elasticsearchClient;

    public SearchResponse<Map> searchByCommonId(List<String> commonId, List<String> indexs) {

        SearchResponse<Map> search = null;
        ArrayList<@Nullable FieldValue> es_index_name = Lists.newArrayList();

        for (String index : indexs) {
            es_index_name.add(FieldValue.of(index));
        }

        try {
            search = elasticsearchClient.search(s -> {
                        SearchRequest.Builder searchRequestBuilder = s.index(indexs).trackTotalHits(TrackHits.of(o -> o.enabled(true)));

                        //组装查询条件
                        searchRequestBuilder.query(q -> q.bool(b -> {

                                    b.minimumShouldMatch(commonId.size() + "");
                                    for (String keyword : commonId) {
                                        b.should(s1 -> s1
                                                .multiMatch(mm -> mm
                                                        .query(keyword)
                                                        .type(TextQueryType.Phrase)
                                                        .fields("data.commonId")));
                                    }

                                    b.filter(must -> must
                                            .terms(t -> t
                                                    .field("es_index_name")
                                                    .terms(tt -> tt.value(es_index_name))));
                                    return b;
                                }
                        ));
                        //不展示大字段内容
                        searchRequestBuilder.source(source -> source.filter(f -> f.excludes("es_data_content")));
                        //组装排序条件
                        assemblySortingCriteria(0, searchRequestBuilder);
                        //组装分页条件
                        return assemblyPagingCriteria(null, searchRequestBuilder);
                    }
                    ,
                    Map.class);
            log.info("成功查询={}", indexs);
        } catch (Exception e) {
            log.info("失败查询={}", indexs);
            e.printStackTrace();
        }
        return search;
    }

    public SearchResponse<Map> searchHitv2(String keyword, Integer match, Integer sort, List<String> index, Integer size, List<String> searchAfter, Map<String, List<String>> esOrdId_map) {
        SearchResponse<Map> search = null;
        String[] keywords = keyword.split(regex);
        if (index == null || index.size() < 1) {
            return null;
        }
        if (match == null) {
            match = 0;
        }
        if (sort == null) {
            sort = 0;
        }
        try {
            Integer finalMatch = match;
            Integer finalSort = sort;
            search = elasticsearchClient.search(s -> {
                        SearchRequest.Builder searchRequestBuilder = s.index(index).trackTotalHits(TrackHits.of(o -> o.enabled(true)));
                        //组装查询条件
                        assemblySearchCriteria(finalMatch, index, esOrdId_map, keywords, searchRequestBuilder);
                        //不展示大字段内容
                        searchRequestBuilder.source(source -> source.filter(f -> f.excludes("es_data_content")));
                        //组装排序条件
                        assemblySortingCriteria(finalSort, searchRequestBuilder);
                        //组装分页条件
                        assemblyPagingCriteria(searchAfter, searchRequestBuilder);
                        //组装高亮条件
                        return assemblyhighlightCriteria(size, searchRequestBuilder);
                    }
                    ,
                    Map.class);
            log.info("成功查询={}", index);
        } catch (Exception e) {
            log.info("失败查询={}", index);
            e.printStackTrace();
        }
        return search;
    }

    private SearchRequest.Builder assemblyhighlightCriteria(Integer size, SearchRequest.Builder searchRequestBuilder) {
        return searchRequestBuilder.highlight(h -> h
                        .fields("es_data_content", hf -> hf)
                        .fields("es_data_title", hf -> hf))
                .size(size);
    }

    private SearchRequest.Builder assemblyPagingCriteria(List<String> searchAfter, SearchRequest.Builder searchRequestBuilder) {
        if (searchAfter != null && searchAfter.size() != 0) {
//            searchRequestBuilder.searchAfter(sa -> sa.stringValue(searchAfter.get(0)));
            List<FieldValue> collect = searchAfter.stream().map(FieldValue::of).collect(Collectors.toList());
            searchRequestBuilder.searchAfter(collect);
        }
        return searchRequestBuilder;
    }

    private void assemblySortingCriteria(Integer sort, SearchRequest.Builder searchRequestBuilder) {
        if (sort == 0) {
            searchRequestBuilder.sort(searchRequestBuilderSort -> searchRequestBuilderSort.field(f -> f.field("es_order_time").order(SortOrder.Desc)));
            searchRequestBuilder.sort(searchRequestBuilderSort -> searchRequestBuilderSort.field(f -> f.field("_score").order(SortOrder.Desc)));
            searchRequestBuilder.sort(searchRequestBuilderSort -> searchRequestBuilderSort.field(f -> f.field("es_index_creat_date").order(SortOrder.Desc)));
        }
        if (sort == 1) {
            searchRequestBuilder.sort(searchRequestBuilderSort -> searchRequestBuilderSort.field(f -> f.field("_score").order(SortOrder.Desc)));
            searchRequestBuilder.sort(searchRequestBuilderSort -> searchRequestBuilderSort.field(f -> f.field("es_order_time").order(SortOrder.Desc)));
            searchRequestBuilder.sort(searchRequestBuilderSort -> searchRequestBuilderSort.field(f -> f.field("es_index_creat_date").order(SortOrder.Desc)));
        }
    }

    private void assemblySearchCriteria(Integer match, List<String> index, Map<String, List<String>> esOrdId_map, String[] keywords, SearchRequest.Builder searchRequestBuilder) {
        searchRequestBuilder.query(q -> q.bool(b -> {
                    assemblyKeywords(match, keywords, b);
                    return assemblyPermissions(b, index, esOrdId_map);
                }
        ));
    }

    public void matchCriteriaCountRequest(Integer match, List<String> index, Map<String, List<String>> esOrdId_map, String keyword, CountRequest.Builder countRequestBuilder) {
        String[] keywords = keyword.split(regex);
        countRequestBuilder.query(q -> q.bool(b -> {
                    assemblyKeywords(match, keywords, b);
                    return assemblyPermissions(b, index, esOrdId_map);
                }
        ));
    }

    private BoolQuery.Builder assemblyKeywords(Integer match, String[] keywords, BoolQuery.Builder b) {
        if (match == 0) {
            b.minimumShouldMatch("1");
            for (String keyword : keywords) {
                b.should(s1 -> s1
                        .multiMatch(mm -> mm
                                .query(keyword)
                                .type(TextQueryType.MostFields)
                                .fields("es_data_content", "es_data_title")));
            }

        }
        if (match == 1) {
            b.minimumShouldMatch("1");
            for (String keyword : keywords) {

                b.should(s1 -> s1
                        .multiMatch(mm -> mm
                                .query(keyword)
                                .type(TextQueryType.Phrase)
                                .fields("es_data_content", "es_data_title")));
            }
        }
        if (match == 2) {
            b.minimumShouldMatch(keywords.length + "");
            for (String keyword : keywords) {
                b.should(s1 -> s1
                        .multiMatch(mm -> mm
                                .query(keyword)
                                .type(TextQueryType.Phrase)
                                .fields("es_data_content", "es_data_title")));
            }
        }
        return b;

    }

    private BoolQuery.Builder assemblyPermissions(BoolQuery.Builder b, List<String> searchType, Map<String, List<String>> map) {
        ArrayList<@Nullable FieldValue> es_index_name = Lists.newArrayList();
        ArrayList<@Nullable FieldValue> es_ord_id = Lists.newArrayList();

        for (String index : searchType) {
            es_index_name.add(FieldValue.of(index));
            List<String> strings = map.get(index);
            if (CollUtil.isEmpty(strings)) {
                continue;
            }
            es_ord_id.addAll(strings.stream().map(FieldValue::of).collect(Collectors.toList()));
        }

        b.filter(must -> must
                .terms(t -> t
                        .field("es_index_name")
                        .terms(tt -> tt.value(es_index_name))));
        if (CollUtil.isNotEmpty(es_ord_id)) {
            b.filter(must -> must
                    .terms(t -> t
                            .field("es_ord_id")
                            .terms(tt -> tt.value(es_ord_id))));
        }


        return b;
    }

}
