package com.cf.imes.framework.es.core.service;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import co.elastic.clients.elasticsearch.ElasticsearchAsyncClient;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.Result;
import co.elastic.clients.elasticsearch.core.*;
import com.cf.imes.framework.es.core.dal.ESDocument;
import com.cf.imes.framework.security.core.LoginUser;
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;

import java.io.IOException;
import java.io.StringReader;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;

/**
 * @author there
 */
public class ESDocumentServiceImpl implements ESDocumentService {
    //同步客户端
    private final ElasticsearchClient elasticsearchClient;

    // 异步客户端
    private final ElasticsearchAsyncClient elasticsearchAsyncClient;

    private Snowflake snowflake = IdUtil.getSnowflake();

    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public ESDocumentServiceImpl(ElasticsearchClient elasticsearchClient, ElasticsearchAsyncClient elasticsearchAsyncClient) {
        this.elasticsearchClient = elasticsearchClient;
        this.elasticsearchAsyncClient = elasticsearchAsyncClient;
    }

    @Override
    public <T> IndexResponse createByFluentDSL(String idxName, String idxId, ESDocument document) throws Exception {
        Date date = new Date();
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        document.setCreator(loginUser.getNickname());
        document.setUpdater(loginUser.getNickname());
        document.setOrganId(loginUser.getOrganId());
        document.setCreateTime(simpleDateFormat.format(date));
        document.setUpdateTime(simpleDateFormat.format(date));

        if (StrUtil.isBlank(document.getId())) {
            document.setId(snowflake.nextIdStr());
        }
        return elasticsearchClient.index(idx -> idx
                .index(idxName)
                .id(document.getId())
                .document(document));
    }

    /**
     * BuilderPattern 方式创建文档
     *
     * @param idxName  索引名
     * @param idxId    索引id
     * @param document 文档对象
     */
    @Override
    public <T> IndexResponse createByBuilderPattern(String idxName, String idxId, ESDocument document) throws Exception {
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        Date date = new Date();
        document.setCreator(loginUser.getNickname());
        document.setUpdater(loginUser.getNickname());
        document.setOrganId(loginUser.getOrganId());
        document.setCreateTime(simpleDateFormat.format(date));
        document.setUpdateTime(simpleDateFormat.format(date));
        IndexRequest.Builder<Object> indexReqBuilder = new IndexRequest.Builder<>();
        indexReqBuilder.index(idxName);
        if (StrUtil.isBlank(idxId)) {
            idxId = snowflake.nextIdStr();
        }
        indexReqBuilder.id(idxId);
        indexReqBuilder.document(document);
        return elasticsearchClient.index(indexReqBuilder.build());
    }

    /**
     * json方式创建文档
     *
     * @param idxName     索引名
     * @param idxId       索引id
     * @param jsonContent json字符串
     */
    @Override
    public IndexResponse createByJson(String idxName, String idxId, String jsonContent) throws Exception {
        if (StrUtil.isBlank(idxId)) {
            idxId = snowflake.nextIdStr();
        }
        String finalIdxId = idxId;
        return elasticsearchClient.index(i -> i
                .index(idxName)
                .id(finalIdxId)
                .withJson(new StringReader(jsonContent))
        );
    }

    /**
     * 异步方式创建文档
     *
     * @param idxName  索引名
     * @param idxId    索引id
     * @param document 文档
     * @param action   操作
     */
    @Override
    public <T> void createAsync(String idxName, String idxId, T document, BiConsumer<IndexResponse, Throwable> action) {
        elasticsearchAsyncClient.index(idx -> idx
                .index(idxName)
                .id(idxId)
                .document(document)
        ).whenComplete(action);
    }

    /**
     * 批量方式创建文档
     *
     * @param idxName   索引名
     * @param documents 要增加的对象集合
     */

    @Override
    public <T> BulkResponse bulkCreate(String idxName, List<? extends ESDocument> documents) throws Exception {
        BulkRequest.Builder br = new BulkRequest.Builder();
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        Date date = new Date();
        LocalDateTime now = LocalDateTime.now();
        documents.forEach(esDocument -> {
            if (StrUtil.isBlank(esDocument.getId())) {
                esDocument.setId(snowflake.nextIdStr());
            }
            esDocument.setCreator(loginUser.getNickname());
            esDocument.setCreateTime(simpleDateFormat.format(date));
            esDocument.setUpdater(loginUser.getNickname());

            esDocument.setUpdateTime(simpleDateFormat.format(date));
            br.operations(op -> op.index(idx -> idx
                    .index(idxName)
                    .id(esDocument.getId())
                    .document(esDocument)));
        });
            return elasticsearchClient.bulk(br.build());
    }




    /**
     * 批量方式更新文档
     *
     * @param idxName   索引名
     * @param documents 要更新的对象集合
     */
    @Override
    public <T> BulkResponse bulkUpdate(String idxName, List<? extends ESDocument> documents) throws Exception {
        BulkRequest.Builder br = new BulkRequest.Builder();
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        Date date = new Date();
        documents.forEach(esDocument -> {
            // 假设esDocument有一个ID集。如果没有，则应该抛出异常或进行相应的处理。
            if (StrUtil.isBlank(esDocument.getId())) {
                throw new IllegalArgumentException("更新操作的文档ID不能为空");
            }
            esDocument.setUpdater(loginUser.getNickname());
            esDocument.setUpdateTime(simpleDateFormat.format(date));

            br.operations(op -> op.update(u -> u
                    .index(idxName)
                    .id(esDocument.getId())
                    .action(a -> a
                            .doc(esDocument))));
        });
        return elasticsearchClient.bulk(br.build());
    }




    /**
     * @param idxName 索引名称
     * @param docId   文档id
     * @param tClass  返回的类型
     * @param map     修改内容的map
     *                Map<String, Object> map = new HashMap<>();
     *                map.put("age", 35);
     *                把年龄改成35
     */
    @Override
    public <T> Result updateById(String idxName, String docId, Class<T> tClass, Map<String, Object> map) throws IOException {
        UpdateResponse<T> response = elasticsearchClient.update(e -> e.index(idxName).id(docId).doc(map), tClass);
        return response.result();
    }

    /**
     * 文档id查询信息
     *
     * @param idxName 索引名
     * @param docId   文档id
     */
    @Override
    public <T> T getById(String idxName, String docId, Class<T> tClass) throws IOException {
        GetResponse<T> response = elasticsearchClient.get(g -> g
                        .index(idxName)
                        .id(docId),
                tClass);
        return response.found() ? response.source() : null;
    }

    /**
     * 根据索引名称和文档id查询ObjectNode
     *
     * @param idxName 索引名
     * @param docId   文档id
     */
    @Override
    public JSONObject getObjectNodeById(String idxName, String docId) throws IOException {
        GetResponse<JSONObject> response = elasticsearchClient.get(g -> g
                        .index(idxName)
                        .id(docId),
                JSONObject.class);

        return response.found() ? response.source() : null;
    }

    /**
     * 单条输出
     *
     * @param idxName 索引名
     * @param docId   文档id
     */
    @Override
    public Boolean deleteById(String idxName, String docId) throws IOException {
        DeleteResponse delete = elasticsearchClient.delete(d -> d
                .index(idxName)
                .id(docId));
        return delete.forcedRefresh();
    }

    /**
     * 批量删除
     *
     * @param idxName 索引名
     * @param docIds  要删除的文档id集合
     */
    @Override
    public BulkResponse bulkDeleteByIds(String idxName, List<String> docIds) throws Exception {
        BulkRequest.Builder br = new BulkRequest.Builder();
        // 将每一个对象都放入builder中
        docIds.forEach(id -> br
                .operations(op -> op
                        .delete(d -> d
                                .index(idxName)
                                .id(id))));
        return elasticsearchClient.bulk(br.build());
    }

}
