package com.cf.imes.module.plan.service.order;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.IdUtil;
import co.elastic.clients.elasticsearch.ElasticsearchAsyncClient;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.ElasticsearchException;
import co.elastic.clients.elasticsearch.core.BulkRequest;
import co.elastic.clients.elasticsearch.core.BulkResponse;
import co.elastic.clients.elasticsearch.core.DeleteByQueryRequest;
import co.elastic.clients.elasticsearch.core.DeleteByQueryResponse;
import com.cf.imes.framework.common.exception.ServiceException;
import com.cf.imes.framework.es.core.dal.ESDocument;
import com.cf.imes.module.plan.dal.dataobject.ordermodel.OrderModelDO;
import com.cf.imes.module.plan.dal.elasticsearch.OrderPartsRemark;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import jakarta.annotation.Resource;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import static com.cf.imes.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR;

/**
 * @author Beal
 * 生产单入库处理器
 */
@Slf4j
@Component
public class OrderInputProcessor {

    @Resource
    private ElasticsearchClient elasticsearchClient;

    @Resource
    private ElasticsearchAsyncClient elasticsearchAsyncClient;

    private Snowflake snowflake = IdUtil.getSnowflake();

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

    public static final String ORDER_PLATE_MODEL = "imes_order_plate_model";

    public static final String ORDER_PARTS_REMARK_MODEL = "imes_order_parts_remark_model";

    private static final String FIELD_ORDER_ID = "orderId";

    private static final String FIELD_BATCH_ID = "batchId";

    private static ObjectMapper objectMapper = new ObjectMapper();

    static {
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 跳过 null 字段
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
        objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false);
    }

    /**
     * 保存生产单造型数据
     *
     * @param orderModelDOs
     */
    public void batchSaveModel(List<OrderModelDO> orderModelDOs) {
        try {
            BulkResponse bulkResponse = bulkCreate(ORDER_PLATE_MODEL, orderModelDOs);
            boolean errors = bulkResponse.errors();
            if (errors) {
                log.error(bulkResponse.items().toString());
                throw new ServiceException(500, "未知异常");
            }
        } catch (IOException | ElasticsearchException e) {
            log.error(e.getMessage());
            throw new RuntimeException(e);
        }

    }

    /**
     * 异步保存生产单造型数据
     *
     * @param orderModelDOs
     */
    public void batchAsyncSaveModel(List<OrderModelDO> orderModelDOs) {
        try {
            bulkAsyncCreate(ORDER_PLATE_MODEL, orderModelDOs);
        } catch (IOException | ElasticsearchException e) {
            log.error(e.getMessage(), e);
            throw new RuntimeException(e);
        }

    }

    /**
     * 按批次删除生产单OrderModels
     *
     * @param orderId 生产单id
     * @param batchId 批次id
     * @param index
     */
    public void deleteByOrderIdBatch(Long orderId, Long batchId, String index) {
        DeleteByQueryRequest.Builder builder = new DeleteByQueryRequest.Builder();
        builder.index(index);
        builder.query(q -> q.bool(b -> b
                .must(must -> must.term(t -> t.field(FIELD_ORDER_ID).value(orderId)))
                .must(must -> must.term(t -> t.field(FIELD_BATCH_ID).value(batchId)))
        ));

        try {
            DeleteByQueryRequest deleteBuild = builder.build();
            // es刚插入有可能无法马上查询删除（默认1s的可见时间），线程每1s删除一次，再删除两次
            DeleteByQueryResponse response = elasticsearchClient.deleteByQuery(deleteBuild);
            if (response.deleted() == 0) {
                for (int i = 0; i < 2; i++) {
                    response = elasticsearchClient.deleteByQuery(deleteBuild);
                    // 删除成功，不再继续
                    if (response.deleted() > 0) {
                        break;
                    }
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        log.error("====================【线程中断异常】====================");
                    }
                }
            }
            log.info("Deleted {} documents with orderId {}", response.deleted(), orderId);
        } catch (IOException | ElasticsearchException e) {
            log.error("Error deleting documents with orderId {}: {}", orderId, e.getMessage());
            throw new ServiceException(INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * 保存生产单配件备注数据
     *
     * @param orderPartsRemarks
     */
    public void batchSaveOrderPartsModel(List<OrderPartsRemark> orderPartsRemarks) {
        try {
            BulkResponse bulkResponse = bulkCreate(ORDER_PARTS_REMARK_MODEL, orderPartsRemarks);
            boolean errors = bulkResponse.errors();
            if (errors) {
                log.error(bulkResponse.items().toString());
                throw new ServiceException(500, "未知异常");
            }
        } catch (IOException | ElasticsearchException e) {
            e.printStackTrace();
            log.error(e.getMessage());
            throw new RuntimeException(e);
        }

    }

    public <T> BulkResponse bulkCreate(String idxName, List<? extends ESDocument> documents) throws IOException, ElasticsearchException {
        BulkRequest.Builder br = new BulkRequest.Builder();
        Date date = new Date();
        documents.forEach(esDocument -> {
            if (CharSequenceUtil.isBlank(esDocument.getId())) {
                esDocument.setId(snowflake.nextIdStr());
            }
            esDocument.setCreateTime(simpleDateFormat.format(date));

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

    public void bulkAsyncCreate(String idxName, List<OrderModelDO> documents) throws IOException, ElasticsearchException {
        BulkRequest.Builder br = new BulkRequest.Builder();
        Date date = new Date();
        // 深拷贝documents
        List<OrderModelDO> deepCopy = objectMapper.readValue(
                objectMapper.writeValueAsString(documents),
                new TypeReference<>() {
                }
        );

        deepCopy.forEach(esDocument -> {
            if (CharSequenceUtil.isBlank(esDocument.getId())) {
                esDocument.setId(snowflake.nextIdStr());
            }
            esDocument.setCreateTime(simpleDateFormat.format(date));

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

        elasticsearchAsyncClient.bulk(br.build())
                .whenComplete((response, exception) -> {
                    deepCopy.clear();

                    if (exception != null) {
                        log.error("ES bulk async failed for index: {}, size: {}, error: {}",
                                idxName, documents.size(), exception.getMessage(), exception);
                    } else if (response.errors()) {
                        log.warn("ES bulk async partially failed for index: {}, size: {}",
                                idxName, documents.size());
                    } else {
                        log.debug("ES bulk async success for index: {}, size: {}",
                                idxName, documents.size());
                    }
                });
    }
}
