package com.cf.imes.module.executor.service.optimizeplan;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import com.cf.imes.framework.common.enums.DeletedCodeEnum;
import com.cf.imes.framework.common.enums.OrderPlateCutStatusEnum;
import com.cf.imes.framework.common.enums.OrderStatusEnum;
import com.cf.imes.framework.common.enums.PlanStatusEnum;
import com.cf.imes.framework.common.enums.PlanTypeEnum;
import com.cf.imes.framework.common.exception.ServiceException;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.common.util.Assert.AssertUtils;
import com.cf.imes.framework.common.util.object.BeanUtils;
import com.cf.imes.framework.mybatis.core.query.LambdaUpdateWrapperX;
import com.cf.imes.framework.mybatis.core.query.QueryWrapperX;
import com.cf.imes.module.executor.controller.admin.plan.bo.OrderIds;
import com.cf.imes.module.executor.controller.admin.plan.bo.OrderSource;
import com.cf.imes.module.executor.controller.admin.plan.bo.PlanProcessSchemeList;
import com.cf.imes.module.executor.controller.admin.plan.component.BoardLayoutResultSummary;
import com.cf.imes.module.executor.controller.admin.plan.component.FlowSessionSavedContent;
import com.cf.imes.module.executor.controller.admin.plan.component.OptimizationResultsReqVO;
import com.cf.imes.module.executor.controller.admin.plan.component.OrderBoardDosageResultsReqVO;
import com.cf.imes.module.executor.controller.admin.plan.saveOptimize.*;
import com.cf.imes.module.executor.controller.admin.plan.vo.*;
import com.cf.imes.module.executor.dal.dataobject.goods.OptimizeBoardModelDO;
import com.cf.imes.module.executor.dal.dataobject.goods.PlanActualGoodsModelDO;
import com.cf.imes.module.executor.dal.dataobject.order.OrderDO;
import com.cf.imes.module.executor.dal.dataobject.orderGroup.OrderGroupDO;
import com.cf.imes.module.executor.dal.dataobject.ordermodel.OrderModelDO;
import com.cf.imes.module.executor.dal.dataobject.ordermodel.OrderPlateModel;
import com.cf.imes.module.executor.dal.dataobject.plan.PlanDO;
import com.cf.imes.module.executor.dal.dataobject.plate.PlateDO;
import com.cf.imes.module.executor.dal.dataobject.plate.PlateDetailDO;
import com.cf.imes.module.executor.dal.dataobject.plate.PlateGoodDO;
import com.cf.imes.module.executor.dal.dataobject.remainplaten.RemainPlateDO;
import com.cf.imes.module.executor.dal.mysql.goods.GoodsMapper;
import com.cf.imes.module.executor.dal.mysql.order.OrderMapper;
import com.cf.imes.module.executor.dal.mysql.orderGroup.OrderGroupMapper;
import com.cf.imes.module.executor.dal.mysql.plan.PlanMapper;
import com.cf.imes.module.executor.dal.mysql.planitem.PlanItemMapper;
import com.cf.imes.module.executor.dal.mysql.plate.PlateGoodMapper;
import com.cf.imes.module.executor.dal.mysql.plate.PlateMapper;
import com.cf.imes.module.executor.dal.mysql.remainplaten.RemainPlateMapper;
import com.cf.imes.module.executor.enums.plan.FlowProcessStatus;
import com.cf.imes.module.executor.service.plan.PlanService;
import com.cf.imes.module.system.api.dataSource.DataSourceApi;
import com.cf.imes.module.system.api.systemconfig.SystemConfigApi;
import com.cf.imes.module.system.api.systemconfig.dto.ProcessSchemeDTO;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import jakarta.annotation.Resource;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.cf.imes.framework.common.exception.enums.GlobalErrorCodeConstants.ES_DATA_ERROR;
import static com.cf.imes.framework.common.util.json.JsonUtils.*;
import static com.cf.imes.framework.security.core.util.SecurityFrameworkUtils.getUserOrganId;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.*;
import static com.cf.imes.module.executor.enums.EsIndexEnum.*;

/**
 * @author there
 */
@Service
@Slf4j
@RefreshScope
public class OptimizePlanServiceImpl implements OptimizePlanService {

    @Resource
    private PlanMapper planMapper;

    @Resource
    private RemainPlateMapper remainPlateMapper;

    @Resource
    private PlateMapper plateMapper;

    @Resource
    private GoodsMapper goodsMapper;

    @Resource
    private DataSourceApi dataSourceApi;

    @Resource
    private OrderMapper orderMapper;

    @Resource
    private SystemConfigApi systemConfigApi;

    @Resource
    private OrderGroupMapper orderGroupMapper;

    @Resource
    private PlanItemMapper planItemMapper;

    @Resource
    private PlateGoodMapper plateGoodMapper;

    @Resource
    private PlanService planService;

    @Value("${chenfeng.plan.create.plate-threshold:20000}")
    private int planCreatePlateThreshold;

    private static final String FIELD_ORDER_ID = "orderId";
    private static final String FIELD_PLATE_ID = "plateId";
    private static final String FIELD_PLAN_ID = "planId";
    private static final String FIELD_GOODS_ID = "goodsId";
    private static final String FIELD_SORT = " order by createTime desc ";
    private static final String FIELD_IS_OPTIMIZED = " op.is_optimized";
    private static final String FIELD_PLAN_CONFIG_ID = "planConfigId";
    private static final String FIELD_UPDATETIME = "updateTime";

    private static final String ES_ORDER_MODEL_DATA_ERROR = "订单板材造型信息ES文档查询数据异常: ";

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

    @Override
    public List<PlateOptimize> getPlateListByPlanId(Long planId) {
        return planMapper.selectPlateListByPlanId(planId,getUserOrganId());
    }

    @Override
    public Boolean addRemain(AddRemainReqVO vo) {
        PlanDO planDO = planMapper.selectById(vo.getPlanId());
        if (Objects.isNull(planDO)) {
            throw new ServiceException(PLAN_NOT_EXISTS);
        }
        RemainPlateDO remainPlateDO = RemainPlateDO.builder()
                .width(vo.getWidth())
                .length(vo.getLength())
                .planId(vo.getPlanId())
                .initPlanId(vo.getPlanId())
                .goodsId(vo.getGoodsId())
                .name(vo.getGoodsName())
                .material(vo.getMaterial())
                .color(vo.getColor())
                .count(vo.getCount())
                .build();
        remainPlateMapper.insert(remainPlateDO);
        return Boolean.TRUE;
    }




    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateCuttingStatus(CuttingStatusRespVO respVO) {

        Long planId = respVO.getPlanId();
        List<String> goodsIds = respVO.getGoodsId();
        List<Integer> goodsNo = respVO.getGoodsNo();
        Integer cutedType = respVO.getCutedType();
        Long planConfigId = respVO.getPlanConfigId();

        if(goodsIds.isEmpty() || goodsNo.isEmpty()){
            return;
        }

        AssertUtils.notEmpty(planConfigId,PLAN_PLATE_OPTIMIZE_DATA_ERROR);

        PlanDO planDO = validatePlanExists(planId);


        // 校验订单信息和获取 orderId
        String orderNos = Optional.ofNullable(planDO.getOrderNos()).orElseThrow(() -> new ServiceException(ORDER_PLAN_ORDERS_EMPTY));

        List<Long> orderIds = new ArrayList<>(JSON.parseArray(orderNos).toJavaList(Long.class).stream().distinct().toList());


        // 查询优化信息
        List<OptimizeBoardModelDO> optimizeBoardModelDOS = new ArrayList<>();

        AssertUtils.notEmpty(optimizeBoardModelDOS,ORDER_PLAN_OPTIMIZE_ERROR);

        OptimizeBoardModelDO optimizeBoardModelDO = optimizeBoardModelDOS.get(0);

        // 校验信息
        String remainBoardInfo = optimizeBoardModelDO.getRemainBoardInfo();
        AssertUtils.notEmpty(remainBoardInfo,ORDER_PLAN_OPTIMIZE_ERROR);

        Integer boardCount = optimizeBoardModelDO.getBoardCount();
        AssertUtils.notEmpty(boardCount,ORDER_PLAN_OPTIMIZE_ERROR);

        List<RemainBoardInfo> remainBoardInfos;
        try {

            remainBoardInfos = new ArrayList<>(parseArray(remainBoardInfo, RemainBoardInfo.class));

        }catch (Exception e){
            log.error(e.getMessage());
            throw new ServiceException(PLAN_PLATE_OPTIMIZE_DATA_ERROR);
        }

        AssertUtils.notEmpty(remainBoardInfos,ORDER_PLAN_OPTIMIZE_ERROR);

        Long goodsNum = updatePlanOptimizeData(remainBoardInfos, optimizeBoardModelDO, orderIds, goodsIds, goodsNo, cutedType);


        // 根据开料状态修改排单和订单的状态
        OptimizePlanServiceImpl o = (OptimizePlanServiceImpl) AopContext.currentProxy();
        o.updateOrderAndPlanByCutStatus(planDO,goodsNum,cutedType,orderIds,boardCount);


    }



    @Override
    public Map<String, Object> getLabelDataSourceValue(GetSourceDataReq req) {
        String sqlStr = dataSourceApi.getSqlById(req.getDataSourceId()).getCheckedData();
        String sql = sqlStr.replace("#{orderId}", req.getOrderId() + "");
        if (!Objects.isNull(req.getPackageId())) {
            sql = sql.replace("#{packageId}", req.getPackageId() + "");
        }
        if (!Objects.isNull(req.getPlanId())) {
            sql = sql.replace("#{planId}", req.getPlanId() + "");
        }
        return orderMapper.selectDynamicSqlString(sql);
    }

    @Override
    public OrderSource getOrderSource(Long orderId, Long planId, String val) {
        if (!Objects.isNull(orderId) && !Objects.isNull(planId)) {
            throw new ServiceException(ORDER_PLAN_ID_IS_ONE);
        }
        OrderSource orderSource;
        if (ObjectUtil.isNotNull(orderId)) {
            orderSource = getOrderSourceByOrderId(orderId);
            return orderSource;
        }
        if (ObjectUtil.isNotNull(planId)) {
            // 获取排单锁
            planService.getPlanLock(new OrderPlanLockReqVO().setPlanId(planId).setVal(val));
            orderSource = getOrderSourceByPlanId(planId);
            return orderSource;
        }

        throw new ServiceException(SOURCE_NOT_EXITS_ERROR);

    }

    @Override
    public OrderSource getOrderSourceByOrderId(Long orderId) {
        OrderDO orderDO = orderMapper.selectById(orderId);

        AssertUtils.notEmpty(orderDO,ORDER_NOT_EXISTS);

        List<GoodsReqVO> goodsDOS = goodsMapper.selectGoodsList(orderId,getUserOrganId());

        AssertUtils.notEmpty(goodsDOS, ORDER_PLATE_EMPTY);

        List<PlateDetailDO> plateDOS = plateMapper.selectPlateListByGoodsIds(orderId, getUserOrganId());

        AssertUtils.notEmpty(plateDOS, ORDER_PLATE_EMPTY);

        List<OrderModelDO> models =
                plateDOS.stream()
                        .map(PlateDetailDO::getModelData)
                        .filter(StringUtils::isNotBlank)
                        .map(json -> parseObject(json, OrderModelDO.class))
                        .toList();

        AssertUtils.notEmpty(models, ORDER_PLATE_MODEL_DATE_ERROR);

        // 查询订单对应的配件信息
//        List<PrintOrderPartsRespVO> printOrderPartsRespVOS = orderPartsMapper.selectPartList(Collections.singletonList(orderId), getUserOrganId());


//        // 查询订单对应的包裹信息
//        List<PrintOrderPackReqVO> printOrderPackReqVOS = plateMapper.selectOrderPackList(orderId,getUserOrganId());
//
//
//        if(CollUtil.isNotEmpty(printOrderPackReqVOS)) {
//            // 统计每个订单的包装数量
//            printOrderPackReqVOS
//                    .stream()
//                    .filter(f -> f
//                            .getOrderId().equals(orderId))
//                    .forEach(f -> f.setPackNum(printOrderPackReqVOS.stream().filter(e -> e.getOrderId().equals(orderId)).map(PrintOrderPackReqVO::getPackId).toList().size()));
//        }

        return OrderSource.builder()
                .orderList(Collections.singletonList(orderDO))
                .goodsList(goodsDOS)
                .plateList(BeanUtil.copyToList(plateDOS, PlateDetailRespVO.class))
                .plateModels(models)
//                .orderPartsRespVOS(printOrderPartsRespVOS)
                .orderPartsRemarks(null)
//                .orderPackReqVOS(printOrderPackReqVOS)
                .build();

    }



    @Override
    public OrderSource getOrderSourceByPlanId(Long planId) {
        PlanDO planDO = planMapper.selectById(planId);

        AssertUtils.notEmpty(planDO,PLAN_NOT_EXISTS);

        // 查询排单对应的大板信息
        List<GoodsReqVO> goodsDOS = goodsMapper.selectGoodsListByOrderIds(planId, getUserOrganId());

        AssertUtils.notEmpty(goodsDOS, PLAN_GOODS_DATA_ERROR);

        String orderNos = Optional.ofNullable(planDO.getOrderNos()).orElse("[]");

        List<Long> orderIds = JSON.parseArray(orderNos).toJavaList(Long.class).stream().distinct().toList();

        // 查询排单对应的小板信息
        List<PlateDetailDO> plateDetailRespVOS = planItemMapper.selectPlanPlateDetailList(planId, orderIds, getUserOrganId());

        AssertUtils.notEmpty(plateDetailRespVOS, ORDER_PLAN_PLATE_ERROR);

        List<OrderDO> orderDOS = orderMapper.selectBatchIds(orderIds);

        AssertUtils.notEmpty(orderDOS, ORDER_PLAN_ORDERS_EMPTY);

        List<OrderModelDO> models =
                plateDetailRespVOS.stream()
                        .map(PlateDetailDO::getModelData)
                        .filter(StringUtils::isNotBlank)
                        .map(json -> parseObject(json, OrderModelDO.class))
                        .toList();

        AssertUtils.notEmpty(models, ORDER_PLAN_PLATE_MODEL_DATE_ERROR);


//        List<RemainPlateDO> remainPlateDOS = remainPlateMapper.selectByPlanId(planId,getUserOrganId());


//        List<OptimizeRemainPlate> optimizeRemainPlates = BeanUtils.toBean(remainPlateDOS, OptimizeRemainPlate.class);

        // 查询混单实际请求大板信息
        PlanActualGoodsModelDO planActualGoodsModelDO = null;
        if (planDO.getType().equals(PlanTypeEnum.MIXEDORDERPLAN.getType())) {
            List<PlateGoodDO> plateGoodDOS = plateGoodMapper.selectGoodsByGoodsId(List.of(planDO.getGoodsId()), getUserOrganId());
            if (CollUtil.isNotEmpty(plateGoodDOS)) {
                planActualGoodsModelDO = BeanUtils.toBean(plateGoodDOS.get(0), PlanActualGoodsModelDO.class);
                planActualGoodsModelDO.setPlanId(planId);
            }
        }

        // 查询订单对应的配件信息
//        List<PrintOrderPartsRespVO> printOrderPartsRespVOS = orderPartsMapper.selectPartList(orderIds, getUserOrganId());

//        // 查询订单对应的包裹信息
//        List<PrintOrderPackReqVO> printOrderPackReqVOS = plateMapper.selectPlanPackList(ids,getUserOrganId());
//
//
//        if(CollUtil.isNotEmpty(printOrderPackReqVOS)) {
//            for (Long orderId : orderIds) {
//                // 统计每个订单的包装数量
//                printOrderPackReqVOS
//                        .stream()
//                        .filter(f -> f.getOrderId().equals(orderId))
//                        .forEach(f -> f.setPackNum(printOrderPackReqVOS.stream()
//                                .filter(e -> e.getOrderId().equals(orderId))
//                                .map(PrintOrderPackReqVO::getPackId).toList()
//                                .size()));
//            }
//        }

        return OrderSource.builder()
                .orderList(orderDOS)
                .plan(planDO)
                .goodsList(goodsDOS)
                .plateList(BeanUtil.copyToList(plateDetailRespVOS, PlateDetailRespVO.class))
                .plateModels(models)
                .planActualGoodsModelDO(planActualGoodsModelDO)
//                .optimizeRemainPlates(optimizeRemainPlates)
//                .orderPartsRespVOS(printOrderPartsRespVOS)
                .orderPartsRemarks(null)
//                .orderPackReqVOS(printOrderPackReqVOS)
                .build();
    }



    // 保存优化后的数据
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveOptimizationResults(SavePlanPlateResultReqVO req) {

        OptimizeBoardModelDO boardModelDO = req.getOptimizeBoardModelDOS().get(0);

        Long planId = boardModelDO.getPlanId();

        Long processId = 1L;


//        // 延长操作用户的缓存时间
//        redisInsertPlanCreator(planId);
//
//        // 校验是否当前登陆的用户操作排单
//        validatePlanCreator(planId);


        PlanDO planDO = planMapper.selectById(planId);

        AssertUtils.notEmpty(planDO, ORDER_PLAN_DATA_ERROR);

        if(planDO.getStatus().equals(PlanStatusEnum.OPENED.getStatus())){
            throw new ServiceException(PLAN_OPTIMIZE_DATA_ALL_OPENED);
        }


        // 兼容数据，后期可删除
        List<OptimizeBoardModelDO> document = new ArrayList<>();
        if(CollUtil.isNotEmpty(document)){
            OptimizeBoardModelDO optimizeBoardModelDO = document.get(0);
            processId = optimizeBoardModelDO.getProcessId();
        }

        // 更新排单对应小板优化属性
        plateMapper.update(new LambdaUpdateWrapper<PlateDO>()
                .eq(PlateDO::getPlanId, planId)
                .eq(PlateDO::getOrganId, getUserOrganId())
                .set(PlateDO::getIsOptimized, true));

        if(req.getProcessId() != null){

            boardModelDO.setProcessId(req.getProcessId());

            if(!planDO.getProcessId().equals(req.getProcessId())) {

                planDO.setProcessId(req.getProcessId());
                planMapper.updateById(planDO);

                // 保存排单对应的方案组的配置信息
                getPlanProcessSchemeConfig(planDO.getProcessId(),planId);


            }else {

            }


        }else {

            boardModelDO.setProcessId(planDO.getProcessId());

        }


        if(req.getPlanConfigId() != null && req.getProcessId() == null){
        }

        boardModelDO.setIsNewInsertPlate(Boolean.FALSE);

        if (processId == null) {

        }

    }





    @Override
    public void saveProcessSchemeResults(SaveProcessSchemeReqVO saveProcessSchemeReqVO) {

        Long processId = saveProcessSchemeReqVO.getProcessId();

        List<SaveProcessSchemeModelDO> modelDOList = saveProcessSchemeReqVO.getModelDOList();

        List<SaveProcessSchemeModelDO> schemeModelDOS = new ArrayList<>(modelDOList.stream().filter(Objects::nonNull).toList());

        SaveProcessSchemeModelDO saveProcessSchemeModelDO = Optional.ofNullable(schemeModelDOS.get(0)).orElseThrow(() -> new ServiceException(PLAN_SCHEME_DATA_ERROR));

        Long planId = Optional.ofNullable(saveProcessSchemeModelDO.getPlanId()).orElseThrow(() -> new ServiceException(PLAN_SCHEME_DATA_ERROR));


        PlanDO planDO = planMapper.selectById(planId);

        AssertUtils.notEmpty(planDO,PLAN_NOT_EXISTS);

        List<Long> planConfigIds = schemeModelDOS.stream().map(SaveProcessSchemeModelDO::getPlanConfigId).distinct().toList();

        // 更新排单对应小板优化属性
        plateMapper.update(new LambdaUpdateWrapper<PlateDO>()
                .eq(PlateDO::getPlanId, planId)
                .eq(PlateDO::getOrganId, getUserOrganId())
                .set(PlateDO::getIsOptimized, true));


        // 如果方案组ID不相等，就删除旧的方案组信息,再保存新的加工方案组ID
        if(!planDO.getProcessId().equals(processId)){

            if(planDO.getStatus().equals(PlanStatusEnum.OPENING.getStatus()) || planDO.getStatus().equals(PlanStatusEnum.OPENED.getStatus())){
                throw new ServiceException(PLAN_PLATE_IS_CUT);
            }

            // 保存排单对应的加工方案组的配置信息
            getPlanProcessSchemeConfig(processId,planId);

            planMapper.updateById(planDO.setProcessId(processId));

            return;

        }

        List<ProcessSchemeModelDO> processSchemeModelDOS = new ArrayList<>();

        // 保存排单对应的加工方案组的配置信息
        if(CollUtil.isEmpty(processSchemeModelDOS)){

            getPlanProcessSchemeConfig(processId,planId);

        }

    }






    @Override
    public PlanProcessSchemeList getProcessSchemeConfig(Long processId,Long planId) {

        PlanProcessSchemeList planProcessSchemeList = new PlanProcessSchemeList();

        PlanDO planDO = validatePlanExists(planId);

//        List<ProcessSchemeDTO> schemeDTOList = systemConfigApi.getProcessById(processId).getCheckedData();


        List<ProcessSchemeDTO> schemeDTOList;

        // 获取排单对应的方案组的配置信息
        List<ProcessSchemeModelDO> schemeModelDOS = new ArrayList<>();

        if(CollUtil.isNotEmpty(schemeModelDOS)){

            if(!planDO.getProcessId().equals(processId)){
                schemeDTOList = systemConfigApi.getProcessById(processId).getCheckedData();

            }else {

                List<ProcessSchemeDTO> schemeDTOS = BeanUtils.toBean(schemeModelDOS, ProcessSchemeDTO.class);
                schemeDTOList = systemConfigApi.getPlanProcessSchemeConfig(schemeDTOS).getCheckedData();
            }

        }else {

            schemeDTOList = systemConfigApi.getProcessById(processId).getCheckedData();

        }


        planProcessSchemeList.setProcessSchemeList(schemeDTOList);


        // 查询排单对应的方案组信息
        List<SaveProcessSchemeModelDO> saveProcessSchemeReqVOS = new ArrayList<>();


        if(CollUtil.isNotEmpty(saveProcessSchemeReqVOS)){

            // 查询排单对应的优化信息
            List<OptimizeBoardModelDO> optimizeBoardModelDOS = new ArrayList<>();

            if(CollUtil.isNotEmpty(optimizeBoardModelDOS)){

                saveProcessSchemeReqVOS.forEach(f -> {

                    List<OptimizeBoardModelDO> modelDOList = optimizeBoardModelDOS.stream().filter(o -> o.getPlanConfigId().equals(f.getPlanConfigId())).toList();
                    if (CollUtil.isNotEmpty(modelDOList)) {

                        OptimizeBoardModelDO optimizeBoardModelDO = modelDOList.get(0);

                        String jsonString = toJsonString(optimizeBoardModelDO);

                        f.setPlanOptimize(jsonString);
                        f.setGoodsNum(optimizeBoardModelDO.getBoardCount());
                    }
                });

            }

        }

        planProcessSchemeList.setSaveProcessSchemeReqVOS(saveProcessSchemeReqVOS);


        return planProcessSchemeList;
    }



    //
//        // 查询排单对应的优化信息
//        List<OptimizeBoardModelDO> optimizeBoardModelDOS = esUtils.getEsDocument(FIELD_PLAN_ID, planId, schemeDTOList.size(), ORDER_OPTIMIZE_PLATE_MODEL, OptimizeBoardModelDO.class);
//
//
//        if (CollUtil.isNotEmpty(optimizeBoardModelDOS)) {
//
//
//            if (CollUtil.isNotEmpty(saveProcessSchemeReqVOS)) {
//
//                saveProcessSchemeReqVOS.forEach(f -> {
//
//                    List<OptimizeBoardModelDO> modelDOList = optimizeBoardModelDOS.stream().filter(o -> o.getPlanConfigId().equals(f.getPlanConfigId())).toList();
//                    if (CollUtil.isNotEmpty(modelDOList)) {
//
//                        String jsonString = toJsonString(modelDOList.get(0));
//
//                        f.setPlanOptimize(jsonString);
//                    }
//                });
//            } else {
//                optimizeBoardModelDOS.forEach(f -> {
//
//                    saveProcessSchemeReqVOS.add(SaveProcessSchemeModelDO.builder()
//                            .planConfigId(f.getPlanConfigId())
//                            .planName(f.getPlanConfigName())
//                            .planId(f.getPlanId())
//                            .plateNum(f.getPlaceBlockNumber())
//                            .goodsNum(f.getBoardCount())
//                            .planOptimize(toJsonString(f))
//                            .build());
//
//                });
//            }
//        }



    @Override
    public void updateProcessScheme(PlanProcessSchemeReqVO reqVO) {

        validatePlanExists(reqVO.getPlanId());

        updatePlanProcessScheme(reqVO.getPlanId(),reqVO.getPlanConfigId(),reqVO.getIsLock());


    }




    @Override
    public List<OrderModelDO> getPlateModel(Long orderId) {

        return new ArrayList<>();

    }






    @Override
    public OrderSource getNoPlanPlateData(NoPlanFilterPlateReqVO reqVO) {

        List<Long> orderIds = reqVO.getOrderIds();

        List<String> goodsIdList = reqVO.getGoodsIdList();

        List<GoodsReqVO> goodsReqVOS = goodsMapper.selectGoodsAndRemainPlate(orderIds,goodsIdList, getUserOrganId());

        AssertUtils.notEmpty(goodsReqVOS, ORDER_GOODS_DATA_ERROR);

        List<Long> orderGoodsIds = goodsReqVOS.stream().map(GoodsReqVO::getId).toList();

        List<OrderDO> orderDOS = orderMapper.selectBatchIds(orderIds);

        AssertUtils.notEmpty(orderDOS, ORDER_PLAN_ORDERS_EMPTY);

        // 查询未排单的小板数量
        Long unplanPlateNum = plateMapper.selectCount(new LambdaQueryWrapper<PlateDO>()
                .in(PlateDO::getOrderId, orderIds)
                .in(PlateDO::getGoodsId, orderGoodsIds)
                .eq(PlateDO::getPlanId, 0)
                .eq(PlateDO::getDeleted, false));
        if (planCreatePlateThreshold < unplanPlateNum) {
            throw new ServiceException(ORDER_PLAN_CREATE_PLATE_NUM_REACH_THRESHOLD_ERROR, planCreatePlateThreshold);
        }

        // 查询未排单小板
        List<PlateDetailDO> plateDetailRespVOS = plateMapper.selectNoPlanPlate(
                orderIds, orderGoodsIds, reqVO.getRoomIds(), reqVO.getBodyIds(), getUserOrganId());

        AssertUtils.notEmpty(plateDetailRespVOS, ORDER_PLATE_EMPTY);

        List<PlateDetailDO> resultPlateList = mergeGroupAndComponentName(plateDetailRespVOS);

        List<OrderModelDO> models =
                resultPlateList.stream()
                        .map(PlateDetailDO::getModelData)
                        .filter(StringUtils::isNotBlank)
                        .map(json -> parseObject(json, OrderModelDO.class))
                        .toList();

        AssertUtils.notEmpty(plateDetailRespVOS, ORDER_PLATE_MODEL_DATE_ERROR);

        return OrderSource.builder()
                .orderList(buildTree(orderDOS))
                .goodsList(goodsReqVOS)
                .plateList(BeanUtil.copyToList(resultPlateList, PlateDetailRespVO.class))
                .plateModels(models)
                .build();

    }

    /**
     * 合并加工组、部件名称
     *
     * @param list
     */
    public List<PlateDetailDO> mergeGroupAndComponentName(List<PlateDetailDO> list) {

        if (list == null || list.isEmpty()) {
            return Collections.emptyList();
        }

        // plateId -> (groupName + 代表对象)
        Map<Long, PlateDetailDO> resultMap = new LinkedHashMap<>();
        Map<Long, Set<String>> groupNameMap = new HashMap<>();
        Map<Long, Set<String>> componentNameMap = new HashMap<>();

        for (PlateDetailDO item : list) {

            Long plateId = item.getPlateId();
            if (plateId == null) {
                continue;
            }

            // 保留第一条作为代表对象（或你也可以自定义取最新）
            resultMap.putIfAbsent(plateId, item);

            if (StringUtils.isNotBlank(item.getProcessGroupName())) {
                groupNameMap
                        .computeIfAbsent(plateId, k -> new LinkedHashSet<>())
                        .add(item.getProcessGroupName());
            }
            if (StringUtils.isNotBlank(item.getComponentName())) {
                componentNameMap
                        .computeIfAbsent(plateId, k -> new LinkedHashSet<>())
                        .add(item.getComponentName());
            }
        }

        // 组装最终结果
        List<PlateDetailDO> result = new ArrayList<>(resultMap.size());

        for (Map.Entry<Long, PlateDetailDO> entry : resultMap.entrySet()) {

            Long plateId = entry.getKey();
            PlateDetailDO item = entry.getValue();

            Set<String> groupNames = groupNameMap.get(plateId);
            if (groupNames != null && !groupNames.isEmpty()) {
                item.setProcessGroupName(String.join(",", groupNames));
            }
            Set<String> componentNames = componentNameMap.get(plateId);
            if (CollUtil.isNotEmpty(componentNames)) {
                item.setComponentName(String.join(",", componentNames));
            }

            result.add(item);
        }

        return result;
    }

    /**
     * 将订单列表整理成父子结构
     * 规则：
     * 1. parentId = 0 的为父级
     * 2. 子级通过 parentId 关联父级 id
     * 3. 只有一层父子关系
     */
    public List<OrderDO> buildTree(List<OrderDO> orderList) {

        if (orderList == null || orderList.isEmpty()) {
            return Collections.emptyList();
        }

        // 父级Map
        Map<Long, OrderDO> parentMap = orderList.stream()
                .filter(order -> order.getParentId() == 0)
                .collect(Collectors.toMap(OrderDO::getId, Function.identity()));

        // 初始化child
        parentMap.values().forEach(order -> order.setChild(new ArrayList<>()));

        List<OrderDO> result = new ArrayList<>();

        for (OrderDO order : orderList) {

            // 顶级
            if (order.getParentId() == 0) {
                result.add(order);
                continue;
            }

            // 子级挂到父级
            OrderDO parent = parentMap.get(order.getParentId());
            if (parent != null) {
                parent.getChild().add(order);
            }
        }

        return result;
    }


    @Override
    public PageResult<OrderRespVOCopy> getOrderPlateDataFilter(FilterDataPageReqVO pageReqVO) {

        PageDTO<OrderRespVOCopy> page = new PageDTO<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());

        IPage<OrderRespVOCopy> orderRespVOCopyIPage = new PageDTO<>();

        AdvancedScreeningConfig filterConfigs = parseObject(pageReqVO.getConfig(), AdvancedScreeningConfig.class);

        AssertUtils.notEmpty(filterConfigs, ADVANCED_SCREENING_CONFIG_ERROR);


        boolean isPlateFilter = CharSequenceUtil.isNotBlank(filterConfigs.getPlateDataFilter());

        // Mysql 的筛选条件
        String plateDataFilter = isPlateFilter ? filterConfigs.getPlateDataFilter() :  "" ;

        // ES 的筛选条件
        String modelDataFilter = filterConfigs.getModelDataFilter();

        // 连接中间运算符
        String logicalOperator = filterConfigs.getLogicalOperator();

        List<Long> orderIdLists = Optional.ofNullable(pageReqVO.getOrderIds()).orElse(new ArrayList<>());



            // 如果有加工组的字段，SQL查询走加工组表
            if(plateDataFilter.contains("og.name")){

                QueryWrapperX<OrderGroupDO> queryWrapperX = new QueryWrapperX<>();
                queryWrapperX
                        .eq("og.organ_id",getUserOrganId())
                        .eq("og.deleted",false)
                        .eq("ogs.plan_id",0)
                        .eq(FIELD_IS_OPTIMIZED,false)
                        .inIfPresent("ogs.good_id",pageReqVO.getGoodsIds())
                        .apply(plateDataFilter);

                if(CharSequenceUtil.isBlank(modelDataFilter)) {
                    queryWrapperX.orderByDesc("og.order_id");
                    orderRespVOCopyIPage = orderGroupMapper.selectFilterOrderPagePlate(page, queryWrapperX);
                    List<Long> orderIdList = orderRespVOCopyIPage.getRecords().stream().map(OrderRespVOCopy::getOrderId).toList();
                    List<OrderRespVOCopy> orderRespVOCopies = orderMapper.selectOrderListByIds(orderIdList, getUserOrganId());
                    orderRespVOCopyIPage.setRecords(orderRespVOCopies);

                    return new PageResult<>(orderRespVOCopyIPage.getRecords(), orderRespVOCopyIPage.getTotal());
                }

                orderIdLists.addAll(orderGroupMapper.selectFilterOrderPlateList(queryWrapperX));


            }else {

                QueryWrapperX<OrderDO> queryWrapperX = new QueryWrapperX<>();
                queryWrapperX
                        .eq("o.organ_id", getUserOrganId())
                        .eq("o.deleted", false)
                        .eq("og.plan_id", 0)
                        .inIfPresent("og.good_id",pageReqVO.getGoodsIds())
                        .apply(isPlateFilter,plateDataFilter);

                if (CharSequenceUtil.isBlank(modelDataFilter)) {
                    queryWrapperX.orderByDesc("o.id");
                    IPage<OrderRespVOCopy> orderPlateList = orderMapper.selectFilterOrderPagePlate(page, queryWrapperX);
                    return new PageResult<>(orderPlateList.getRecords(), orderPlateList.getTotal());
                }

                List<Long> orderIds = orderMapper.selectFilterOrderPlateList(queryWrapperX);

                orderIdLists.addAll(orderIds);

            }



        if(CharSequenceUtil.isNotBlank(modelDataFilter)){

            Map<Long, List<OrderPlateModel>> orderPlateModelMap = getOrderPlateModelList(conversion(orderIdLists.toString()), null, logicalOperator, modelDataFilter, pageReqVO,FIELD_ORDER_ID);
            if(orderPlateModelMap.isEmpty()){
                return null;
            }
            for (Map.Entry<Long, List<OrderPlateModel>> entry : orderPlateModelMap.entrySet()) {

                List<OrderRespVOCopy> orderRespVOCopies = orderMapper.selectOrderListByIds(entry.getValue().stream().map(OrderPlateModel::getOrderId).toList(), getUserOrganId());

                orderRespVOCopyIPage.setRecords(orderRespVOCopies);
                orderRespVOCopyIPage.setTotal(entry.getKey());
            }
        }

        return new PageResult<>(orderRespVOCopyIPage.getRecords(), orderRespVOCopyIPage.getTotal());

    }



    @Override
    public Boolean getNewPlanPlate(Long planId) {

        List<OptimizeBoardModelDO> optimizeBoardModelDOS = new ArrayList<>();

        if(CollUtil.isNotEmpty(optimizeBoardModelDOS)){
            List<OptimizeBoardModelDO> modelDOS = optimizeBoardModelDOS.stream().filter(f ->Boolean.TRUE.equals(f.getIsNewInsertPlate())).toList();
            return modelDOS.isEmpty() ? Boolean.FALSE : Boolean.TRUE;
        }

        return Boolean.FALSE;

    }




    @Override
    public void updateProcessSchemeConfig(PlanSchemeConfigReqVO reqVO) {

    }




    @Override
    public PageResult<OrderGoodsResp> getGoodsPlateDataFilter(FilterDataPageReqVO pageReqVO) {

        PageDTO<OrderGoodsResp> page = new PageDTO<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());

        IPage<OrderGoodsResp> orderRespVOCopyIPage = new PageDTO<>();

        AdvancedScreeningConfig filterConfigs = parseObject(pageReqVO.getConfig(), AdvancedScreeningConfig.class);

        AssertUtils.notEmpty(filterConfigs,ADVANCED_SCREENING_CONFIG_ERROR);

        // MySql 筛选条件非空判断，方便判断是否进行 SQL 拼接
        boolean isPlateFilter = CharSequenceUtil.isNotBlank(filterConfigs.getPlateDataFilter());

        // Mysql 的筛选条件
        String plateDataFilter = isPlateFilter ? filterConfigs.getPlateDataFilter() : "" ;

        // ES 的筛选条件
        String modelDataFilter = filterConfigs.getModelDataFilter();

        // 连接中间运算符
        String logicalOperator = filterConfigs.getLogicalOperator();


        List<Long> orderIdList = Optional.ofNullable(pageReqVO.getOrderIds()).orElse(new ArrayList<>());

        List<String> goodsIdList = Optional.ofNullable(pageReqVO.getGoodsIds()).orElse(new ArrayList<>());

        // 判断 MySql 的查询条件是否为空

        // 如果有加工组的字段，SQL查询走加工组表
        if (plateDataFilter.contains("og.name")) {

            QueryWrapperX<OrderGroupDO> queryWrapperX = new QueryWrapperX<>();
            queryWrapperX
                    .eq("og.organ_id", getUserOrganId())
                    .eq("og.deleted", false)
                    .eq("ogs.plan_id", 0)
                    .eq(FIELD_IS_OPTIMIZED, false)
                    .inIfPresent("og.id", pageReqVO.getOrderIds())
                    .apply(plateDataFilter);

            if (CharSequenceUtil.isBlank(modelDataFilter)) {
                orderRespVOCopyIPage = orderGroupMapper.selectFilterGoodsPagePlate(page, queryWrapperX);
                List<String> goodsIds = orderRespVOCopyIPage.getRecords().stream().map(OrderGoodsResp::getGoodsId).toList();

                orderRespVOCopyIPage.setRecords(BeanUtils.toBean(goodsMapper.selectListByGoodsIdList(goodsIds, getUserOrganId()), OrderGoodsResp.class));

                return new PageResult<>(orderRespVOCopyIPage.getRecords(), orderRespVOCopyIPage.getTotal());
            }

            List<OrderIds> orderIds = orderGroupMapper.selectFilterGoodsPlateList(queryWrapperX);

            orderIdList.addAll(orderIds.stream().map(OrderIds::getOrderId).distinct().toList());

            goodsIdList.addAll(orderIds.stream().map(OrderIds::getGoodsId).distinct().toList());

        } else {

            QueryWrapperX<OrderDO> queryWrapperX = new QueryWrapperX<>();
            queryWrapperX
                    .eq("o.organ_id", getUserOrganId())
                    .eq("o.deleted", false)
                    .eq("og.plan_id", 0)
                    .eq(FIELD_IS_OPTIMIZED, false)
                    .inIfPresent("o.id", pageReqVO.getOrderIds())
                    .apply(isPlateFilter, plateDataFilter);

            // ES筛选条件为空，查询后直接返回
            if (CharSequenceUtil.isBlank(modelDataFilter)) {
                IPage<OrderGoodsResp> orderGoodsRespIPage = orderMapper.selectFilterGoodsPlatePage(page, queryWrapperX);
                return new PageResult<>(orderGoodsRespIPage.getRecords(), orderGoodsRespIPage.getTotal());
            }

            List<OrderIds> orderIds = orderMapper.selectFilterGoodsPlateList(queryWrapperX);

            orderIdList.addAll(orderIds.stream().map(OrderIds::getOrderId).distinct().toList());

            goodsIdList.addAll(orderIds.stream().map(OrderIds::getGoodsId).distinct().toList());

        }


        // 判断 ES 是否为空
        if(CharSequenceUtil.isNotBlank(modelDataFilter)){

            Map<Long, List<OrderPlateModel>> orderModelMap = getOrderPlateModelList(conversion(orderIdList.toString()), conversion(goodsIdList.toString()), logicalOperator, modelDataFilter, pageReqVO,FIELD_GOODS_ID);
            if(orderModelMap.isEmpty()){
                return null;
            }
            for (Map.Entry<Long, List<OrderPlateModel>> entry : orderModelMap.entrySet()) {

                List<String> goodsIds = entry.getValue().stream().map(OrderPlateModel::getGoodsId).toList();

                List<OrderGoodsResp> orderGoodsRests = BeanUtils.toBean(goodsMapper.selectListByGoodsIdList(goodsIds, getUserOrganId()), OrderGoodsResp.class);

                orderRespVOCopyIPage.setRecords(orderGoodsRests);
                orderRespVOCopyIPage.setTotal(entry.getKey());
            }
        }

        return new PageResult<>(orderRespVOCopyIPage.getRecords(), orderRespVOCopyIPage.getTotal());
    }




    private Map<Long,List<OrderPlateModel>> getOrderPlateModelList(String orderIdList,
                                                    String goodsIdList,
                                                    String logicalOperator,
                                                    String config,
                                                    FilterDataPageReqVO pageReqVO, String field) {

            // < 去重后查询到的总数量 ， 查询到的板材造型数据 >
            return new HashMap<>();
    }

    private String conversion(String filed) {

        // 去除字段中的 【 】
        return filed.replaceAll("\\[|\\]", "");

    }



    private String generateSql(String orderIdList,String goodsIdList,String logicalOperator,String config){

        String filterSql;

        // 因生成 ES DSL 语句规则限制，无法赋值为空，只能赋值为 0
        String orderIds =  CharSequenceUtil.isNotBlank(orderIdList) ? orderIdList : "0";

        if(CharSequenceUtil.isNotBlank(logicalOperator)){

            if(CharSequenceUtil.isNotBlank(goodsIdList)){

                filterSql = " SELECT orderId,goodsId FROM " + ORDER_PLATE_MODEL.getIndex()
                        + " WHERE isOptimized = false " +
                        "and ( orderId in ( " + orderIds + " ) and goodsId in ( " + goodsIdList + " ) )"
                        + logicalOperator
                        + config
                        + FIELD_SORT;

            }else {

                filterSql = "SELECT orderId,goodsId FROM " + ORDER_PLATE_MODEL.getIndex()
                        + " WHERE ( orderId in ( " + orderIds + "))"
                        + logicalOperator
                        + config
                        + FIELD_SORT;

            }
        }else {

            filterSql = "SELECT orderId,goodsId FROM " + ORDER_PLATE_MODEL.getIndex()
                    + " WHERE isOptimized = false and "
                    + config
                    + FIELD_SORT;

        }

        return filterSql;
    }



    private List<PlateDO> getPlanGoodsPlateList(List<Long> goodsNo,PlanDO planDO,List<OptimizeBoardModelDO> optimizeBoardModelDOS){


        List<Long> orderIdList = JSON.parseArray(planDO.getOrderNos()).toJavaList(Long.class).stream().distinct().toList();


        AssertUtils.notEmpty(optimizeBoardModelDOS,ORDER_PLAN_OPTIMIZE_ERROR);

        OptimizeBoardModelDO optimizeBoardModelDO = optimizeBoardModelDOS.get(0);

        Boolean isOptimized = optimizeBoardModelDO.getIsOptimized();

        if(Boolean.FALSE.equals(isOptimized)){
            throw new ServiceException(ORDER_PLAN_NO_OPTIMIZE_UPDATE_ERROR);
        }


        List<String> plateNo = new ArrayList<>();


        // 创建一个列表存储 BlockPlaceInfo 对象,将优化的小板信息拿出，获取到小板编号，查询订单，修改订单的状态
        List<BlockPlaceInfo> outputList = new ArrayList<>();


        String blockPlaceInfo = optimizeBoardModelDO.getBlockPlaceInfo();


        AssertUtils.notEmpty(blockPlaceInfo, ORDER_PLAN_OPTIMIZE_ERROR);


        JsonNode jsonArray = parseTree(blockPlaceInfo);


        boolean isArray = jsonArray.isArray();


        if (Boolean.FALSE.equals(isArray)) {
            throw new ServiceException(ORDER_PLAN_OPTIMIZE_ERROR);
        }


        for (JsonNode jsonNode : jsonArray) {
            Long bi = jsonNode.get("bi").asLong();
            String bo = jsonNode.get("bo").asText();
            outputList.add(new BlockPlaceInfo(bi,bo));
        }


        goodsNo.forEach(g->
                plateNo.addAll(outputList.stream().filter(f -> f.getBi().equals(g)).map(BlockPlaceInfo::getBo).distinct().toList())
        );


        List<PlateDO> plateDOS = plateMapper.selectPlateByPlateNo(orderIdList,plateNo.stream().distinct().toList(), getUserOrganId());

        AssertUtils.notEmpty(plateDOS,ORDER_PLAN_OPTIMIZE_ERROR);

        return plateDOS;


    }



    private PlanDO validatePlanExists(Long id ) {
        PlanDO planDO = planMapper.selectById(id);

        AssertUtils.notEmpty(planDO,PLAN_NOT_EXISTS);
        return planDO;
    }




    private void updatePlanProcessScheme(Long planId,Long planConfigId,Boolean isLock) {

    }






    private Long updatePlanOptimizeData(List<RemainBoardInfo> remainBoardInfos,
                                        OptimizeBoardModelDO optimizeBoardModelDO,
                                        List<Long> orderIds,
                                        List<String> goodsIds,
                                        List<Integer> goodsNo,
                                        Integer cutedType) {


        Date currentDate = new Date();
        AtomicLong goodsCount = new AtomicLong(0L);
        List<String> planNos = new ArrayList<>();

        // 大板开料数量
        long cutedBoardNumber = Optional.ofNullable(optimizeBoardModelDO.getCutedBoardNumber()).orElse(0L);


        // 更新板材状态并统计数量
        updateRemainBoardInfo(remainBoardInfos, goodsIds, goodsNo, cutedType, goodsCount, planNos,cutedBoardNumber);

        // 更新优化数据
        long goodsNum = updateOptimizeData(optimizeBoardModelDO, goodsCount.get(), planNos.size(), cutedType);

        // 同步数据到 ES
        syncWithElasticsearch(remainBoardInfos, optimizeBoardModelDO, currentDate);

        // 更新数据库中的板材信息
        updatePlateData(orderIds, planNos, cutedType);

        return goodsNum;
    }




    private void updateRemainBoardInfo(List<RemainBoardInfo> remainBoardInfos,
                                       List<String> goodsIds,
                                       List<Integer> goodsNo,
                                       Integer cutedType,
                                       AtomicLong goodsCount,
                                       List<String> planNos,
                                       Long cutedBoardNumber) {

        remainBoardInfos.stream()
                .filter(remainBoard -> goodsIds.contains(remainBoard.getGoodsId()))
                .flatMap(remainBoard -> remainBoard.getPlaceBoardList().stream())
                .filter(board -> goodsNo.contains(board.getBoardId()) && !board.getCutedType().equals(cutedType) &&
                        (!cutedType.equals(OrderPlateCutStatusEnum.OPENING.getStatus()) || !board.getCutedType().equals(OrderPlateCutStatusEnum.OPENED.getStatus())))
                .forEach(board -> {
                    if(cutedType.equals(OrderPlateCutStatusEnum.NOCUTTING.getStatus()) ) {
                        if(board.getCutedType().equals(OrderPlateCutStatusEnum.OPENED.getStatus())) {
                            goodsCount.incrementAndGet();
                        }
                        planNos.addAll(board.getBlocks().stream()
                                .map(RemainBoardInfo.Blocks::getBlockId)
                                .toList());
                        board.setCutedType(cutedType);
                        return;
                    }

                    goodsCount.incrementAndGet();
                    board.setCutedType(cutedType);
                    planNos.addAll(board.getBlocks().stream()
                            .map(RemainBoardInfo.Blocks::getBlockId)
                            .toList());

                });


        if(cutedBoardNumber == 0) {

            remainBoardInfos.stream()
                    .filter(remainBoard -> goodsIds.contains(remainBoard.getGoodsId()))
                    .flatMap(remainBoard -> remainBoard.getPlaceBoardList().stream())
                    .filter(board -> goodsNo.contains(board.getBoardId()) && cutedType.equals(OrderPlateCutStatusEnum.OPENED.getStatus()) && board.getCutedType().equals(OrderPlateCutStatusEnum.OPENED.getStatus()))

                    .forEach(f -> goodsCount.incrementAndGet());

        }


    }





    private long updateOptimizeData(OptimizeBoardModelDO optimizeBoardModelDO,
                                    long goodsCount,
                                    int blockCount,
                                    Integer cutedType) {

        long cutedBoardNumber = Optional.ofNullable(optimizeBoardModelDO.getCutedBoardNumber()).orElse(0L);
        long cutedBlockNumber = Optional.ofNullable(optimizeBoardModelDO.getCutedBlockNumber()).orElse(0L);

        if (OrderPlateCutStatusEnum.NOCUTTING.getStatus().equals(cutedType)) {
            cutedBoardNumber -= goodsCount;
            cutedBlockNumber -= blockCount;
        } else if (OrderPlateCutStatusEnum.OPENED.getStatus().equals(cutedType)) {
            cutedBoardNumber += goodsCount;
            cutedBlockNumber += blockCount;
        }

        cutedBoardNumber = cutedBoardNumber < 0 ?  0 : cutedBoardNumber;

        cutedBlockNumber = cutedBlockNumber < 0 ?  0 : cutedBlockNumber;

        optimizeBoardModelDO.setCutedBoardNumber(cutedBoardNumber);
        optimizeBoardModelDO.setCutedBlockNumber(cutedBlockNumber);

        return cutedBoardNumber;
    }

    private void syncWithElasticsearch(List<RemainBoardInfo> remainBoardInfos,
                                       OptimizeBoardModelDO optimizeBoardModelDO,
                                       Date date) {

        remainBoardInfos.forEach(f->{
            f.setPlaceBoardJSON(toJsonString(f.getPlaceBoardList()));
        });

        Map<String, Object> updateData = new HashMap<>();
        updateData.put("cutedBoardNumber", optimizeBoardModelDO.getCutedBoardNumber());
        updateData.put("cutedBlockNumber", optimizeBoardModelDO.getCutedBlockNumber());
        updateData.put("remainBoardInfo", toJsonString(remainBoardInfos));
        updateData.put(FIELD_UPDATETIME, simpleDateFormat.format(date));
    }



    private void updatePlateData(List<Long> orderIds, List<String> planNo, Integer cutedType) {

        if(CollUtil.isEmpty(planNo)){
            return;
        }

        List<PlateDO> plateDOS = plateMapper.selectPlateByPlateNo(orderIds, planNo, getUserOrganId());
        AssertUtils.notEmpty(plateDOS, ORDER_PLAN_PLATE_DATA_EMPTY);

        if(OrderPlateCutStatusEnum.OPENING.getStatus().equals(cutedType)){

            plateDOS.forEach(plate -> {
                if( !plate.getIsCutted().equals(cutedType) && !plate.getIsCutted().equals(OrderPlateCutStatusEnum.OPENED.getStatus())){
                    plate.setIsCutted(cutedType);
                }
            });

        }else {
            plateDOS.forEach(plate -> {
                if(!plate.getIsCutted().equals(cutedType)){
                    plate.setIsCutted(cutedType);
                }

            });
        }
        plateMapper.updateBatch(plateDOS);
    }







    @Transactional(rollbackFor = Exception.class)
    public void updateOrderAndPlanByCutStatus(PlanDO planDO,Long goodsNum,Integer cutedType,List<Long> orderIds,Integer boardCount){


        orderMapper.update(new LambdaUpdateWrapper<OrderDO>()
                .eq(OrderDO::getOrganId, getUserOrganId())
                .eq(OrderDO::getDeleted,false)
                .in(OrderDO::getId,orderIds)
                .set(OrderDO::getStatus,OrderStatusEnum.IN_PRODUCTION.getStatus()));


        // 未开料
        if(OrderPlateCutStatusEnum.NOCUTTING.getStatus().equals(cutedType)){

            OptimizePlanServiceImpl o = (OptimizePlanServiceImpl) AopContext.currentProxy();
            o.updateOrderAndPlanByCutStatusIsNoCutting(planDO,goodsNum,orderIds);

            return;

        }



        // 开料中
        if(OrderPlateCutStatusEnum.OPENING.getStatus().equals(cutedType) && !planDO.getStatus().equals(OrderPlateCutStatusEnum.OPENED.getStatus())){

            planDO.setStatus(PlanStatusEnum.OPENING.getStatus());
            planDO.setProduceTime(null);

            List<OrderDO> orderDOS = orderMapper.selectBatchIds(orderIds);
            AssertUtils.notEmpty(orderDOS, ORDER_PLAN_ORDERS_EMPTY);

            orderDOS.forEach(f->f.setStatus(OrderStatusEnum.IN_PRODUCTION.getStatus()));

            planMapper.updatePlanStatus(planDO.getId(),planDO.getStatus(),getUserOrganId());

            orderMapper.updateBatch(orderDOS);
            return;

        }


        // 已开料
        if(OrderPlateCutStatusEnum.OPENED.getStatus().equals(cutedType)){

            planDO.setStatus(PlanStatusEnum.OPENING.getStatus());

            List<PlateDO> plateDOList = plateMapper.selectNoCutPlateByPlateId(orderIds, getUserOrganId());

            // 订单的小板已全部开料
            List<Long> orderIdList = plateDOList.stream().map(PlateDO::getOrderId).distinct().toList();

            orderIds.removeAll(orderIdList);

            if(CollUtil.isNotEmpty(orderIds)){

                // 查询订单的打包状态是否为已打包
                List<OrderDO> orderDOS = orderMapper.selectOrderPackList(orderIds, getUserOrganId());
                if (CollUtil.isNotEmpty(orderDOS)) {
                    orderDOS.forEach(f -> {
                        f.setStatus(OrderStatusEnum.FINISH_PRODUCTION.getStatus());
                        f.setFinishTime(LocalDateTime.now());
                    });

                    orderMapper.updateBatch(orderDOS);
                }
            }

            if(goodsNum.equals(Long.valueOf(boardCount))) {

                planDO.setStatus(PlanStatusEnum.OPENED.getStatus());
                planDO.setProduceTime(LocalDateTime.now());

            }

            planMapper.updateById(planDO);

        }

    }



    @Transactional(rollbackFor = Exception.class)
    public void updateOrderAndPlanByCutStatusIsNoCutting(PlanDO planDO,Long goodsNum,List<Long> orderIds){

        planDO.setStatus(PlanStatusEnum.OPENING.getStatus());
        planDO.setProduceTime(null);

        if(goodsNum.equals(0L)){

            Long plateId = planItemMapper.selectIsCuttingPlate(planDO.getId(), getUserOrganId());

            if(plateId == null){
                planDO.setStatus(PlanStatusEnum.NOCUTTING.getStatus());
            }

        }

        planMapper.updatePlanStatus(planDO.getId(),planDO.getStatus(),getUserOrganId());


        List<PlateDO> plateDOS = plateMapper.selectPlateIsCuttingStatus(orderIds, getUserOrganId());
        List<Long> orderIdList = plateDOS.stream().map(PlateDO::getOrderId).distinct().toList();

        orderIds.removeAll(orderIdList);

        if(CollUtil.isNotEmpty(orderIds)){

            List<OrderDO> orderDOS = orderMapper.selectBatchIds(orderIds);
            AssertUtils.notEmpty(orderDOS, ORDER_PLAN_ORDERS_EMPTY);

            orderDOS.forEach(f->f.setStatus(OrderStatusEnum.SORTED.getStatus()));
            orderMapper.updateBatch(orderDOS);
        }
    }



    // 查询排单对应的加工方案组的配置信息并保存
    private void getPlanProcessSchemeConfig(Long processId,Long planId){

        List<ProcessSchemeDTO> schemeDTOS = systemConfigApi.getProcessSchemeConfig(processId).getCheckedData();

        List<ProcessSchemeModelDO> modelDOS = BeanUtils.toBean(schemeDTOS, ProcessSchemeModelDO.class);

        modelDOS.forEach(f->{
            f.setProcessId(processId);
            f.setPlanId(planId);
        });
    }

    /**
     * 重置优化状态
     *
     * @param planId
     * @param orderIds
     */
    private void resetPlanPlateOptimizeStatus(Long planId, List<Long> orderIds) {
        plateMapper.resetPlanPlateOptimizeStatus(planId, orderIds);

        // 重置排单的已优化数量
        planMapper.update(new LambdaUpdateWrapper<PlanDO>()
                .eq(PlanDO::getId, planId)
                .eq(PlanDO::getDeleted, DeletedCodeEnum.NOT_DELETED.getStatus())
                .set(PlanDO::getOptimizedPlateNum, 0)
        );
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveComponentOptimizationResults(OptimizationResultsReqVO reqVO) {
        Long planId = reqVO.getPlanId();
        // todo 判空
        Integer processStatus = reqVO.getFlowSessionSavedContent().getProcessStatus();
        List<BoardLayoutResultSummary> layoutResultSummary = reqVO.getFlowSessionSavedContent().getLayoutResultSummary();
        PlanDO planDO = planMapper.selectById(planId);

        AssertUtils.notEmpty(planDO, ORDER_PLAN_DATA_ERROR);

        // 校验订单信息和获取 orderId
        String orderNos = Optional.ofNullable(planDO.getOrderNos()).orElseThrow(() -> new ServiceException(ORDER_PLAN_ORDERS_EMPTY));
        List<Long> orderIds = new ArrayList<>(JSON.parseArray(orderNos).toJavaList(Long.class).stream().distinct().toList());

        // 完成优化排版
        if (FlowProcessStatus.NOT_STARTED.getStatus().equals(processStatus)) {
            if(planDO.getStatus().equals(PlanStatusEnum.OPENING.getStatus()) || planDO.getStatus().equals(PlanStatusEnum.OPENED.getStatus())){
                throw new ServiceException(PLAN_PLATE_IS_CUT);
            }
            // 重置优化状态
            resetPlanPlateOptimizeStatus(planId, orderIds);
            if (CollUtil.isNotEmpty(layoutResultSummary)) {
                List<String> optimizatePlateNoList = layoutResultSummary.stream().flatMap(summary -> summary.getPlacedBlockIds().stream()).toList();
                BoardLayoutResultSummary boardLayoutResultSummary = layoutResultSummary.get(0);
                // 已排大板数量
                int boardCount = boardLayoutResultSummary.getBoardCount();
                // 优化大板编号
                List<String> materialIds = boardLayoutResultSummary.getMaterialIds();
                String materialId = null;
                if (CollUtil.isNotEmpty(materialIds)) {
                    materialId = materialIds.get(0);
                }

                // 更新参与优化的板材的优化状态
                int updatedCount = plateMapper.updatePlanPlateOptimizeStatus(planId, orderIds, optimizatePlateNoList);;

                if (updatedCount > 0) {
                    planMapper.update(new LambdaUpdateWrapperX<PlanDO>()
                            .eq(PlanDO::getId, planId)
                            .eq(PlanDO::getDeleted, DeletedCodeEnum.NOT_DELETED.getStatus())
                            .setIfPresent(PlanDO::getOptimizeGoodsId, materialId)
                            .set(PlanDO::getOptimizedPlateNum, updatedCount)
                            .set(PlanDO::getStatus, PlanStatusEnum.OPTIMIZED.getStatus())
                            .set(PlanDO::getScheduledBoardNum, boardCount)
                            .set(PlanDO::getOptimizeTime, LocalDateTime.now())
                    );
                }
            }

        }

        // 开始开料
        if (FlowProcessStatus.IN_PROGRESS.getStatus().equals(processStatus)) {
            if (planDO.getStatus().equals(PlanStatusEnum.OPENING.getStatus()) || planDO.getStatus().equals(PlanStatusEnum.OPENED.getStatus())) {
                throw new ServiceException(PLAN_PLATE_IS_CUT);
            }
            List<String> optimizatePlateNoList = layoutResultSummary.stream().flatMap(summary -> summary.getStartCutBlockIds().stream()).toList();

            // 更新开料中状态
            plateMapper.updatePlanPlateCuttingStatus(planId, orderIds, optimizatePlateNoList);

            List<OrderDO> orderDOS = orderMapper.selectByIds(orderIds);
            AssertUtils.notEmpty(orderDOS, ORDER_PLAN_ORDERS_EMPTY);

            orderDOS.forEach(f -> f.setStatus(OrderStatusEnum.IN_PRODUCTION.getStatus()));
            orderMapper.updateBatch(orderDOS);

            // 更新排单状态
            planMapper.update(new LambdaUpdateWrapper<PlanDO>()
                    .eq(PlanDO::getId, planId)
                    .eq(PlanDO::getDeleted, DeletedCodeEnum.NOT_DELETED.getStatus())
                    .set(PlanDO::getStatus, PlanStatusEnum.OPENING.getStatus())
                    .set(PlanDO::getProduceTime, null)
            );

        }


        // 结束开料
        if (FlowProcessStatus.COMPLETED.getStatus().equals(processStatus)) {
            if (planDO.getStatus().equals(PlanStatusEnum.OPENED.getStatus())) {
                throw new ServiceException(PLAN_OPTIMIZE_DATA_ALL_OPENED);
            }
            List<String> optimizatePlateNoList = layoutResultSummary.stream().flatMap(summary -> summary.getEndCutBlockIds().stream()).toList();
            // 累计开料大板数量
            int endCutBoardCount = layoutResultSummary.stream().mapToInt(BoardLayoutResultSummary::getEndCutBoardCount).sum();

            // 更新开料状态
            int updatedCount = plateMapper.updatePlanPlateCutStatus(planId, orderIds, optimizatePlateNoList);

            // 查询已全部开料的订单号
            List<Long> allCuttedOrderIdList = plateMapper.selectPlanAllCuttedOrderIdList(planId, orderIds);

            // 更新已全部开料和全部打包的订单为生产完成
            if (CollUtil.isNotEmpty(allCuttedOrderIdList)) {
                // 查询订单的打包状态是否为已打包
                List<OrderDO> orderDOS = orderMapper.selectOrderPackList(orderIds, getUserOrganId());
                if (CollUtil.isNotEmpty(orderDOS)) {
                    orderDOS.forEach(f -> {
                        f.setStatus(OrderStatusEnum.FINISH_PRODUCTION.getStatus());
                        f.setFinishTime(LocalDateTime.now());
                    });

                    orderMapper.updateBatch(orderDOS);
                }
            }

            // 更新排单状态
            planMapper.update(new LambdaUpdateWrapper<PlanDO>()
                    .eq(PlanDO::getId, planId)
                    .eq(PlanDO::getDeleted, DeletedCodeEnum.NOT_DELETED.getStatus())
                    .set(PlanDO::getStatus, PlanStatusEnum.OPENED.getStatus())
                    .set(PlanDO::getProduceTime, LocalDateTime.now())
                    .set(PlanDO::getCuttedBoardNum, endCutBoardCount)
                    .set(PlanDO::getCuttedPlateNum, updatedCount)
            );
        }
    }

    @Override
    public void saveOrderBoardDosage(OrderBoardDosageResultsReqVO reqVO) {
        Long orderId = reqVO.getOrderId();
        OrderDO orderDO = orderMapper.selectById(orderId);

        AssertUtils.notEmpty(orderDO,ORDER_NOT_EXISTS);

        // 从优化结果中取order_goods_id来获取大板材质
        FlowSessionSavedContent flowSessionSavedContent = reqVO.getFlowSessionSavedContent();
        List<BoardLayoutResultSummary> layoutResultSummary = flowSessionSavedContent.getLayoutResultSummary();
        if (CollUtil.isEmpty(layoutResultSummary)) {
            throw new RuntimeException();
        }

        JSONArray orderBoardDosageJsonArr = new JSONArray();
        for (BoardLayoutResultSummary summary : layoutResultSummary) {
            List<String> materialIds = summary.getMaterialIds();
            if(CollUtil.isEmpty(materialIds)) {
                continue;
            }

            // 提取大板计算数据
            JSONObject orderBoardDosageJSON = new JSONObject();
            orderBoardDosageJSON.put("avgUtilization", summary.getAvgUtilization()); // 平均利用率
            orderBoardDosageJSON.put("avgUtilizationExcludeLast", summary.getAvgUtilizationExcludeLast()); // 排除末张标准大板后的平均利用率
            orderBoardDosageJSON.put("trimAmount", summary.getTrimAmount()); // 最大可执行修边量
            orderBoardDosageJSON.put("boardCount", summary.getBoardCount()); // 排版大板数量
            orderBoardDosageJSON.put("gap", summary.getGap()); // 小板排版间隙
            orderBoardDosageJSON.put(FIELD_GOODS_ID, materialIds.get(0)); // 商品编号
            orderBoardDosageJSON.put("name", summary.getName()); // 大板名称
            orderBoardDosageJSON.put("length", summary.getLength()); // 长
            orderBoardDosageJSON.put("width", summary.getWidth()); // 宽
            orderBoardDosageJSON.put("thickness", summary.getThickness()); // 厚度
            orderBoardDosageJsonArr.add(orderBoardDosageJSON);
        }

        OrderDO update = new OrderDO();
        update.setId(orderId);
        update.setBoardDosageResult(orderBoardDosageJsonArr.toJSONString());
        orderMapper.updateById(update);
    }

    @Override
    public JSONArray getOrderBoardDosage(Long orderId) {
        OrderDO orderDO = orderMapper.selectById(orderId);

        AssertUtils.notEmpty(orderDO, ORDER_NOT_EXISTS);
        return JSON.parseArray(orderDO.getBoardDosageResult());
    }
}
