package com.bawei.es.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bawei.common.core.domain.R;
import com.bawei.common.core.exception.ServiceException;
import com.bawei.common.core.utils.StringUtils;
import com.bawei.common.core.web.domain.AjaxResult;
import com.bawei.common.domain.ConmtMap;
import com.bawei.es.service.EsDocService;
import lombok.extern.log4j.Log4j2;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.xcontent.XContentType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.elasticsearch.search.sort.SortOrder;

import javax.swing.text.Highlighter;
import java.io.IOException;
import java.util.*;

@Service
@Log4j2
public class EsDocServiceImpl implements EsDocService {
    @Autowired
    private RestHighLevelClient restHighLevelClient;
    /**
     * 批量添加文档
     * @param indexName
     * @param docMsg
     */
    @Override
    public void batchAddDoc(String indexName, Map<String, Object> docMsg) {
        BulkRequest bulkRequest = new BulkRequest();
        for (Map.Entry<String, Object> entry : docMsg.entrySet()){
            String id = entry.getKey();
            Map<String, Object> value = (Map<String, Object>) entry.getValue();
            IndexRequest source = new IndexRequest(indexName).id(id).source(value, XContentType.JSON);
            bulkRequest.add(source);
        }

        try {
            BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
            if(bulk.hasFailures()){
                throw new RuntimeException("Bulk operation failed: "+ bulk.buildFailureMessage());
            }
        } catch (IOException e) {
            throw new ServiceException("批量操作失败: "+e.getMessage());
        }
    }

    /**
     * 批量删除
     * @param indexName
     * @param ids
     */
    @Override
    public void bathDeleteDoc(String indexName, List<String> ids) {
        BulkRequest bulkRequest = new BulkRequest();

        for (String id : ids){
            DeleteRequest deleteRequest = new DeleteRequest(indexName, String.valueOf(id));
            bulkRequest.add(deleteRequest);
        }

        try {
            BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
            if(bulk.hasFailures()){
                throw new RuntimeException("批量删除失败: "+ bulk.buildFailureMessage());
            }
        } catch (IOException e) {
            throw new RuntimeException("批量删除失败:"+ e.getMessage());
        }


    }

    /**
     * 搜索
     * @param indexName
     * @param docMsg
     * @return
     */
    @Override
    public R queryList(String indexName, Map<String, Object> docMsg) {
        // 验证索引名称
        checkedIndexName(indexName);
        // 构造搜索请求
        SearchRequest searchRequest = builderRequest(indexName,docMsg);
        // 执行搜索
        SearchResponse searchResponse;
        try {
            searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            log.error("ES 搜索 异常: {}",e.getMessage());
            return R.fail("ES 搜索 异常: "+e.getMessage());
        }
        // 处理搜索结果
        ConmtMap response = processSearchResponse(searchResponse);

        return R.ok(response);
    }

    /**
     * 搜索请求
     * @param searchResponse
     * @return
     */
    private ConmtMap processSearchResponse(SearchResponse searchResponse) {
        ConmtMap conmtMap = new ConmtMap();
        long total = searchResponse.getHits().getTotalHits().value;
        conmtMap.setTotal(total);

        LinkedHashMap<String, Object> stringObjectLinkedHashMap = new LinkedHashMap<>();
        for (SearchHit hit : searchResponse.getHits().getHits()) {
            String sourceAsString = hit.getSourceAsString();
            HashMap<String,Object> map = JSONObject.parseObject(sourceAsString, HashMap.class);
            String id = hit.getId();
            // 添加高亮信息到最后解析的文档
            addHighlightedFields(hit.getHighlightFields(),map);

            stringObjectLinkedHashMap.put(id,map);
            log.info("查询出来的数据是: {}",JSONObject.toJSONString(map));
        }
        log.info("查询出来的数据是: {}",JSONObject.toJSONString(stringObjectLinkedHashMap));
        conmtMap.setObjectMap(stringObjectLinkedHashMap);
        return conmtMap;
    }

    /**
     * 高亮请求
     * @param highlightFields
     * @param parsedObject
     */
    private void addHighlightedFields(Map<String, HighlightField> highlightFields, Map<String, Object> parsedObject) {
        if (!highlightFields.isEmpty()) {
            for (Map.Entry<String, HighlightField> entry : highlightFields.entrySet()) {
                String fieldName = entry.getKey();
                Text[] fragments = entry.getValue().fragments();
                StringBuilder highlightedValue = new StringBuilder();
                for (Text fragment : fragments) {
                    highlightedValue.append(fragment);
                }
                parsedObject.put(fieldName, highlightedValue);
            }
        }
    }

    private SearchRequest builderRequest(String indexName,Map<String, Object> docMsg) {
        SearchRequest request = new SearchRequest(indexName);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        // 构造查询条件
        QueryBuilder queryBuilder = builderQuery(docMsg);
        sourceBuilder.query(queryBuilder);

        // 设置分页参数
        setPaginationParameters(docMsg,sourceBuilder);

        // 设置排序参数
        setSortParameters(docMsg,sourceBuilder);
        //精确查找
        setFuzzyParameters(docMsg,sourceBuilder);
        // 设置高亮参数
        setHighlightParameters(docMsg,sourceBuilder);

        request.source(sourceBuilder);

        // 打印请求参数
        log.debug("ES 搜索 请求参数: {}",sourceBuilder.toString());
        return request;
    }

    private void setFuzzyParameters(Map<String, Object> docMsg, SearchSourceBuilder sourceBuilder) {

        Map<String, Object> fuzzy = (Map<String, Object>) docMsg.get("fuzzy");
        if (fuzzy!=null){
            BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
            boolQueryBuilder.must(QueryBuilders.termQuery((String) fuzzy.get("field"),(String) fuzzy.get("value")));
            sourceBuilder.query(boolQueryBuilder);
        }

    }

    private void setHighlightParameters(Map<String, Object> docMsg, SearchSourceBuilder sourceBuilder) {
        // 获取高亮参数
        Map<String,Object> highlight = (Map<String, Object>) docMsg.get("highlight");
        // 判断是否需要高亮
        if(highlight != null){
            // 创建高亮构建器
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            // 获取高亮字段
            List<String> fields = (List<String>) highlight.getOrDefault("fields", Collections.emptyList());
            // 获取高亮前缀
            String preTag = (String) highlight.getOrDefault("preTag", "<em>");
            // 获取高亮后缀
            String postTag = (String) highlight.getOrDefault("postTag", "</em>");
            // 循环设置高亮字段
            for (String field : fields) {
                highlightBuilder.field(field);
            }
            // 设置高亮包裹
            highlightBuilder.preTags(preTag).postTags(postTag);
            // 设置高亮
            sourceBuilder.highlighter(highlightBuilder);
        }
    }

    private void setSortParameters(Map<String, Object> docMsg, SearchSourceBuilder sourceBuilder) {
        Map sort = (Map) docMsg.get("sort");
        if(sort != null){
            Object sortOrderObject = sort.get("sortOrder");
            Object fieldObject = sort.get("field");

            if(sortOrderObject instanceof String && fieldObject instanceof String){
                String sortOrder = (String) sortOrderObject;
                String field = (String) fieldObject;

                if("asc".equalsIgnoreCase(sortOrder) || "desc".equalsIgnoreCase(sortOrder)){
                    SortOrder sortOrderEnum = "asc".equalsIgnoreCase(sortOrder) ? SortOrder.ASC : SortOrder.DESC;
                    log.info("排序字段："+field+",排序方式："+sortOrderEnum);
                    sourceBuilder.sort(field,sortOrderEnum);
                }else {
                    SortOrder sortOrderEnum = SortOrder.ASC;
                    log.info("排序字段："+field+",排序方式："+sortOrderEnum);
                    sourceBuilder.sort(field,sortOrderEnum);
                }
            }else {
                throw new IllegalArgumentException("排序参数有误");
            }
        }
    }

    private void setPaginationParameters(Map<String, Object> docMsg, SearchSourceBuilder sourceBuilder) {
        // 从条件中获取分页信息
        Map<String,Object> pagination = (Map<String, Object>) docMsg.get("pagination");
        // 如果没有分页信息，则不设置分页参数
        if(pagination == null){
            return;
        }
        // 获取分页参数
        int page;
        int limit;
        try {
            // 获取分页参数
            page = Integer.parseInt(pagination.getOrDefault("page","1").toString());
            limit = Integer.parseInt(pagination.getOrDefault("limit","10").toString());
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException("分页参数有误", e);
        }
        // 计算分页参数
        int form = (page - 1) * limit;
        // 设置分页参数
        sourceBuilder.from(form).size(limit);
        // 打印分页参数
        log.debug("Pagination parameters set: page={}, limit={}, from={}" , page, limit, form);
    }


    private QueryBuilder builderQuery(Map<String, Object> docMsg) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        if(docMsg != null && !docMsg.isEmpty()){
            for (Map.Entry<String, Object> entry : docMsg.entrySet()) {
                String key = entry.getKey();
                Object value = entry.getValue();

                if(value instanceof Map && ((Map<?, ?>) value).containsKey("range")){
                    Map<String, Object> rangeParms = (Map<String, Object>) value;
                    RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(key);

                    String gt = (String) rangeParms.get("gt");

                    if(StringUtils.isNotBlank(gt)){
                        rangeQueryBuilder.gt(gt);
                    }

                    String gte = (String) rangeParms.get("gte");
                    if(StringUtils.isNotBlank(gte)){
                        rangeQueryBuilder.gte(gte);
                    }

                    String lt = (String) rangeParms.get("lt");
                    if(StringUtils.isNotBlank(lt)){
                        rangeQueryBuilder.lt(lt);
                    }

                    String lte = (String) rangeParms.get("lte");
                    if(StringUtils.isNotBlank(lte)){
                        rangeQueryBuilder.lte(lte);
                    }

                    boolQueryBuilder.filter(rangeQueryBuilder);
                }else if(value instanceof String) {
                    boolQueryBuilder.filter(QueryBuilders.matchQuery(key,value));
                }
            }
        }
        return boolQueryBuilder;
    }

    public void checkedIndexName(String indexName){
        if(StringUtils.isBlank(indexName)){
            throw new ServiceException("索引名称为空,请重试");
        }
    }
}
