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

import cn.reghao.tnb.search.esapp.es.ElasticService;
import cn.reghao.tnb.search.esapp.model.po.VideoText;
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.*;
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 co.elastic.clients.json.JsonData;
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.Collections;
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 VideoTextQuery {
    private final String indexName = "video_text";
    private final int pageSize = 12;
    private final ElasticsearchClient esClient;

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

    public Page<VideoText> queryWithHighlight(String keyword, List<Integer> scopes, int pageNumber) {
        if (pageNumber > 100) {
            pageNumber = 100;
        }

        String fieldName = "title";
        String fieldValue = keyword;
        QueryStringQuery stringQuery = new QueryStringQuery.Builder()
                // 查询的字段
                .fields(fieldName)
                .query(fieldValue)
                .build();

        BoolQuery.Builder builder = new BoolQuery.Builder();
        for (int scope : scopes) {
            // filter 表示 list 中的值全部都要满足
            //builder.filter(m -> m.term(t -> t.field("scope").value(FieldValue.of(scope))));
            builder.should(m -> m.term(t -> t.field("scope").value(FieldValue.of(scope))));
        }
        builder.minimumShouldMatch("1");
        builder.must(m -> m.queryString(stringQuery));
        BoolQuery boolQuery = builder.build();
        Query query = new Query.Builder()
                .bool(boolQuery)
                .build();
        Query query1 = new Query.Builder()
                .bool(BoolQuery.of(b -> b
                        .must(m -> m.term(t -> t.field("scope").value(FieldValue.of(1))))
                        .must(m -> m.queryString(stringQuery))))
                .build();

        // 1.构建查询的对象
        // 2.高亮显示
        String highlightFieldName = fieldName;
        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();

        String sortField = "publishTime";
        // 3.搜索请求
        int start = (pageNumber-1)*pageSize;
        SearchRequest searchRequest = new SearchRequest.Builder()
                .index(indexName)
                .from(start)
                .size(pageSize)
                .query(query)
                .highlight(highlight)
                //.sort(f -> f.field(o -> o.field(sortField).order(SortOrder.Desc)))
                .build();
        try {
            SearchResponse<VideoText> searchResponse = esClient.search(searchRequest, VideoText.class);
            HitsMetadata<VideoText> hitsMetadata = searchResponse.hits();
            long total = hitsMetadata.total().value();
            List<VideoText> list = hitsMetadata.hits().stream().map(hit -> {
                double score = hit.score() != null ? hit.score() : 0.0;
                log.info("score -> {}", score);
                Map<String, List<String>> highlightMap = hit.highlight();
                String highlightStr = "";
                if (!highlightMap.isEmpty()) {
                    highlightStr = hit.highlight()
                            .get(highlightFieldName)
                            .get(0);
                }

                VideoText videoText = hit.source();
                try {
                    Class<?> clazz = videoText.getClass();
                    Field field = clazz.getDeclaredField(highlightFieldName);
                    field.setAccessible(true);
                    field.set(videoText, highlightStr);
                } catch (Exception e) {
                    log.error(e.getMessage());
                }
                return hit.source();
            }).collect(Collectors.toList());

            if (total > pageSize*100L) {
                total = pageSize*100L;
            }

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

        return Page.empty();
    }

    public List<VideoText> queryByPage(int pageNumber) {
        Query rangeQuery = RangeQuery.of(r -> r.field("age").gte(JsonData.of(8)))._toQuery();
        String fieldName = "host";
        String searchText = "api.iquizoo.com";
        Query matchQuery = MatchQuery.of(m -> m.field(fieldName).query(searchText))._toQuery();
        Query matchAllQuery = new Query.Builder()
                .matchAll(new MatchAllQuery.Builder().build())
                .build();

        String sortField = "publishTime";
        int start = (pageNumber-1)*pageSize;
        SearchRequest searchRequest = SearchRequest.of(s -> s
                .index(indexName)
                .query(matchAllQuery)
                .from(start)
                .size(pageSize)
                // 按指定字段降序排列
                .sort(f -> f.field(o -> o.field(sortField).order(SortOrder.Desc)))
        );

        try {
            SearchResponse<VideoText> searchResponse = esClient.search(searchRequest, VideoText.class);
            return searchResponse.hits().hits().stream().map(Hit::source).collect(Collectors.toList());
        } catch (Exception e) {
            log.error("{}", e.getMessage());
        }
        return Collections.emptyList();
    }
}
