package cn.reghao.tnb.search.esapp.es;

import cn.reghao.tnb.search.esapp.model.vo.ElasticQuery;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch._types.query_dsl.QueryStringQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.TermQuery;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Highlight;
import co.elastic.clients.elasticsearch.core.search.HighlightField;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author reghao
 * @date 2025-03-12 11:16:48
 */
@Slf4j
@Service
public class QueryService<T> {
    private final ElasticsearchClient esClient;

    public QueryService(ElasticService elasticService) {
        this.esClient = elasticService.getElasticsearchClient();
    }

    public Page<T> queryWithHighlight(ElasticQuery elasticQuery, Class<T> clazz) {
        int pageSize = elasticQuery.getPageSize();
        int pageNumber = elasticQuery.getPageNumber();
        String indexName = elasticQuery.getIndexName();
        List<String> queryFiledNames = elasticQuery.getQueryFiledNames();
        List<String> highlightFieldNames = elasticQuery.getHighlightFiledNames();

        String queryString = elasticQuery.getQueryString();

        // 1.构建查询的对象
        QueryStringQuery stringQuery = new QueryStringQuery.Builder()
                // 查询的字段
                .fields(queryFiledNames)
                .query(queryString)
                .build();
        Query query = new Query.Builder()
                .queryString(stringQuery)
                .build();

        Query query1 = new Query.Builder()
                .bool(BoolQuery.of(b -> b
                        .must(m -> m.term(t -> t.field("vip").value(FieldValue.of(""))))
                        .must(m -> m.queryString(stringQuery))))
                .build();

        String highlightFieldName = highlightFieldNames.get(0);
        // 2.高亮显示
        HighlightField highlightField = new HighlightField.Builder()
                .matchedFields(highlightFieldName)
                .preTags("<span style=\"color:red\">")
                .postTags("</span>")
                .build();
        Highlight highlight = new Highlight.Builder()
                .fields(highlightFieldName, highlightField)
                .requireFieldMatch(false)
                .build();

        // 3.搜索请求
        int start = (pageNumber-1)*pageSize;
        SearchRequest searchRequest = new SearchRequest.Builder()
                .index(indexName)
                .from(start)
                .size(pageSize)
                .query(query)
                .highlight(highlight)
                .build();
        try {
            SearchResponse<T> searchResponse = esClient.search(searchRequest, clazz);
            HitsMetadata<T> hitsMetadata = searchResponse.hits();
            long total = hitsMetadata.total().value();
            List<T> list = hitsMetadata.hits().stream().map(mapper -> {
                Map<String, List<String>> highlightMap = mapper.highlight();
                String highlightStr = "";
                if (!highlightMap.isEmpty()) {
                    highlightStr = mapper.highlight()
                            .get(highlightFieldName)
                            .get(0);
                }

                T t = mapper.source();
                try {
                    Field field = clazz.getDeclaredField(highlightFieldName);
                    field.setAccessible(true);

                    if (t == null) {
                        Object object = clazz.getDeclaredConstructors()[0].newInstance();
                        field.set(object, highlightStr);
                    } else {
                        field.set(t, highlightStr);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return mapper.source();
            }).collect(Collectors.toList());

            //return list;
            PageRequest pageRequest = PageRequest.of(pageNumber-1, pageSize);
            return new PageImpl<>(list, pageRequest, total);
        } catch (IOException e) {
            log.error("search By Query Highlight error", e);
        }

        return Page.empty();
    }

    public Page<T> queryByPage(String index, String queryString, int pn, int ps, Class<T> clazz) {
        String highlightFieldName = "caseName";
        List<String> otherFiledNames = List.of("caseName", "cause", "parties");

        // 1.构建查询的对象
        QueryStringQuery stringQuery = new QueryStringQuery.Builder()
                // 查询的字段
                .fields(highlightFieldName, otherFiledNames.toArray(new String[0]))
                .query(queryString)
                .build();
        Query query = new Query.Builder()
                .queryString(stringQuery)
                .build();

        // 2.高亮显示
        HighlightField highlightField = new HighlightField.Builder()
                .matchedFields(highlightFieldName)
                .preTags("<span style=\"color:red\">")
                .postTags("</span>")
                .build();
        Highlight highlight = new Highlight.Builder()
                .fields(highlightFieldName, highlightField)
                .requireFieldMatch(false)
                .build();

        // 3.搜索请求
        int start = (pn-1)*ps;
        SearchRequest searchRequest = new SearchRequest.Builder()
                .index(index)
                .from(start)
                .size(ps)
                //.query(query)
                //.highlight(highlight)
                .build();
        try {
            SearchResponse<T> searchResponse = esClient.search(searchRequest, clazz);
            HitsMetadata<T> hitsMetadata = searchResponse.hits();
            long total = hitsMetadata.total().value();
            List<T> list = hitsMetadata.hits().stream().map(mapper -> {
                Map<String, List<String>> highlightMap = mapper.highlight();
                String highlightStr = "";
                if (!highlightMap.isEmpty()) {
                    highlightStr = mapper.highlight()
                            .get(highlightFieldName)
                            .get(0);
                }

                T t = mapper.source();
                try {
                    Field field = clazz.getDeclaredField(highlightFieldName);
                    field.setAccessible(true);

                    if (t == null) {
                        Object object = clazz.getDeclaredConstructors()[0].newInstance();
                        field.set(object, highlightStr);
                    } else {
                        field.set(t, highlightStr);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return mapper.source();
            }).collect(Collectors.toList());

            //return list;
            PageRequest pageRequest = PageRequest.of(pn-1, ps);
            return new PageImpl<>(list, pageRequest, total);
        } catch (IOException e) {
            log.error("search By Query Highlight error", e);
        }

        return Page.empty();
    }

    public Page<T> queryWithHighlight(String indexName, Map<String, String> map, String fieldName, String fieldValue, int pn, int ps, Class<T> clazz) {
        String scopeField = "scope";

        QueryStringQuery.Builder builder0 = new QueryStringQuery.Builder();
        map.forEach((fieldName0, fieldValue0) -> {
            builder0.fields(fieldName0).query(fieldValue0);
        });
        QueryStringQuery stringQuery = builder0.build();

        List<Integer> scopeValues = List.of(1, 2, 3);
        BoolQuery.Builder builder = new BoolQuery.Builder();
        for (int scope : scopeValues) {
            builder.should(m -> m.term(t -> t.field(scopeField).value(FieldValue.of(scope))));
        }
        builder.must(m -> m.queryString(stringQuery));
        BoolQuery boolQuery = builder.build();
        Query query = new Query.Builder()
                .bool(boolQuery)
                .build();

        int scopeValue = 1;
        Query query1 = new Query.Builder()
                .bool(BoolQuery.of(b -> b
                        .must(m -> m.term(t -> t.field(scopeField).value(FieldValue.of(scopeValue))))
                        .must(m -> m.queryString(stringQuery))))
                .build();

        String highlightFieldName = fieldName;
        // 1.构建查询的对象
        // 2.高亮显示
        HighlightField highlightField = new HighlightField.Builder()
                .matchedFields(highlightFieldName)
                .preTags("<span style=\"color:red\">")
                .postTags("</span>")
                .build();
        Highlight highlight = new Highlight.Builder()
                .fields(highlightFieldName, highlightField)
                .requireFieldMatch(false)
                .build();

        // 3.搜索请求
        int start = (pn-1)*ps;
        SearchRequest searchRequest = new SearchRequest.Builder()
                .index(indexName)
                .from(start)
                .size(ps)
                .query(query)
                .highlight(highlight)
                .build();
        try {
            SearchResponse<T> searchResponse = esClient.search(searchRequest, clazz);
            HitsMetadata<T> hitsMetadata = searchResponse.hits();
            long total = hitsMetadata.total().value();
            List<T> list = hitsMetadata.hits().stream().map(mapper -> {
                Map<String, List<String>> highlightMap = mapper.highlight();
                String highlightStr = "";
                if (!highlightMap.isEmpty()) {
                    highlightStr = mapper.highlight()
                            .get(highlightFieldName)
                            .get(0);
                }

                T t = mapper.source();
                try {
                    Field field = clazz.getDeclaredField(highlightFieldName);
                    field.setAccessible(true);

                    if (t == null) {
                        Object object = clazz.getDeclaredConstructors()[0].newInstance();
                        field.set(object, highlightStr);
                    } else {
                        field.set(t, highlightStr);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return mapper.source();
            }).collect(Collectors.toList());

            //return list;
            PageRequest pageRequest = PageRequest.of(pn-1, ps);
            return new PageImpl<>(list, pageRequest, total);
        } catch (IOException e) {
            log.error("search By Query Highlight error", e);
        }

        return Page.empty();
    }

    public T queryById(String indexName, String id, Class<T> clazz) {
        Query query = TermQuery.of(t -> t
                .field("id").value(FieldValue.of(id))
        )._toQuery();

        SearchRequest searchRequest = SearchRequest.of(s -> s
                .index(indexName)
                .query(query)
        );

        try {
            SearchResponse<T> searchResponse = esClient.search(searchRequest, clazz);
            List<Hit<T>> hits = searchResponse.hits().hits();
            if (!hits.isEmpty()) {
                return hits.get(0).source();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }
}
