package com.rongwei.bscommon.sys.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.rongwei.bscommon.sys.dao.ApsProcessOperationProcessEquDao;
import com.rongwei.bscommon.sys.service.*;
import com.rongwei.bscommon.sys.utils.ApsUtils;
import com.rongwei.bsentity.domain.*;
import com.rongwei.bsentity.vo.*;
import com.rongwei.rwadmincommon.system.vo.SysUserVo;
import com.rongwei.rwcommon.base.R;
import com.rongwei.rwcommon.utils.SecurityUtil;
import com.rongwei.rwcommon.utils.StringUtils;
import com.rongwei.safecommon.utils.CXCommonUtils;
import com.rongwei.safecommon.utils.SaveConstans;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import static com.rongwei.safecommon.utils.SaveConstans.ForcedConflictsDescription.EQUIPMENT_RUN_TIME;
import static com.rongwei.safecommon.utils.SaveConstans.ForcedConflictsDescription.EQ_TIME_CROSS;
import static com.rongwei.safecommon.utils.SaveConstans.JobStatus.*;
import static com.rongwei.safecommon.utils.SaveConstans.LockmarkType.LOCKMARK_N;
import static com.rongwei.safecommon.utils.SaveConstans.LockmarkType.LOCKMARK_Y;
import static com.rongwei.safecommon.utils.SaveConstans.ProductionStatus.*;

/**
 *
 */
@Service
public class ApsProcessOperationProcessEquServiceImpl extends ServiceImpl<ApsProcessOperationProcessEquDao, ApsProcessOperationProcessEquDo>
        implements ApsProcessOperationProcessEquService {
    private final Logger log = LoggerFactory.getLogger(this.getClass().getName());
    @Autowired
    private ApsProcessOperationProcessEquDao apsProcessOperationProcessEquDao;
    @Autowired
    private ApsProcessOperationService apsProcessOperationService;
    @Autowired
    private ApsBlankOrderService apsBlankOrderService;
    @Autowired
    private ApsProductionOrderService apsProductionOrderService;
    @Autowired
    private ApsProductDetailService apsProductDetailService;
    @Autowired
    private ApsEquipmentCalendarServiceImpl apsEquipmentCalendarService;
    @Autowired
    private GanttService ganttService;

    /**
     * 更新工序的待加工批次号信息
     *
     * @param ids
     * @param batchNumn
     */
    public void updateBachmaterialprocessByIds(List<String> ids, String batchNumn,String nextprocessesids) {
        if (StringUtils.isNotBlank(batchNumn)) {
            apsProcessOperationProcessEquDao.updateBachmaterialprocessByIds(ids, batchNumn,
                    StringUtils.isBlank(nextprocessesids)?null:Arrays.asList(nextprocessesids.split(","))
                    );
        }
    }

    /**
     * 更新 工序明细的 实际完工时间 完工状态 作业状态  已检验卷数 未完工卷数
     *
     * @param id                  工序明细表ID aps_process_operation_process_equ
     * @param currentCheckNum     检验卷数
     * @param qualifiedQuantity   合格数量
     * @param unqualifiedQuantity 不合格数量
     * @param previousprocessid   当前工序的上一个工序ID
     * @param processOperationId  当前工序的ID
     */
    public void updateAboutCheckBatchNum(String id, Integer currentCheckNum, Integer qualifiedQuantity,
                                         Integer unqualifiedQuantity, String previousprocessid, String processOperationId) {
        apsProcessOperationProcessEquDao.updateAboutCheckBatchNum(id, currentCheckNum, qualifiedQuantity,
                unqualifiedQuantity, previousprocessid, processOperationId);
    }

    /**
     * 更新后续工序的
     *
     * @param updateIds
     */
    public void updateAboutStateByProcessId(List<ProcessIdRelationVo> updateIds) {
        // 防止sql 过长拆分
        List<List<ProcessIdRelationVo>> partition = Lists.partition(updateIds, 20);
        partition.forEach(data -> {
            apsProcessOperationProcessEquDao.updateAboutStateByProcessId(data);
        });

    }


    @Override
    @Transactional
    public R updateProductionStatus(CommonUpdateProductionStatusReq request) {

        //根据粒度顺序
        //作业明细->工序作业
        if (ObjectUtil.isNotEmpty(request.getProcessOperationEquId())) {
            updateProcess(request.getProcessOperationEquId());
            return R.ok();
        }
        //工序作业->坯料计划
        if (ObjectUtil.isNotEmpty(request.getProcessOperationId())) {
            updateBlank(request.getProcessOperationId());
            return R.ok();
        }
        //坯料计划->订单
        if (ObjectUtil.isNotEmpty(request.getBlankId())) {
            updateOrder(request.getBlankId());
            return R.ok();
        }
        //订单->产品明细
        if (ObjectUtil.isNotEmpty(request.getOrderId())) {
            updateProductDetail(request.getOrderId());
            return R.ok();
        }
        return R.ok();
    }

    public void updateProcess(String processOperationEquId) {
        String processOperationId = updateProcessByProcessEquId(processOperationEquId);

        if (ObjectUtil.isEmpty(processOperationId)) {
            return;
        }

        //更新坯料计划
        updateBlank(processOperationId);
    }

    /**
     * 从作业明细表中的任意一个子表ID开始，自动计算更新工序作业的状态,完成后返回其中一个工序作业的ID（不一定要用）
     *
     * @param processOperationEquId
     */
    public String updateProcessByProcessEquId(String processOperationEquId) {
        //查询所有子表明细的最小生产状态和工序作业ID
        CommonUpdateProductionStatusReq one = this.baseMapper.selectEquMinStatus(processOperationEquId);
        if (ObjectUtil.isEmpty(one)) {
            return "";
        }
        //工序作业ID
        String processId = one.getProcessOperationId();

        //计算工序作业的完工状态
        /**
         * 如果没有作业明细，并且未完工卷数=0，或者有作业明细，并且所有作业明细完工状态=已完工，则=已完工
         * 如果没有作业明细，并且未完工卷数>0，或者有作业明细，并且任一作业明细完工状态=未完工，则=未完工
         */
        ApsProcessOperationDo apsProcessOperationDo = new ApsProcessOperationDo();
        //更新工序作业的完工状态
        apsProcessOperationDo.setClosestatus(one.getProductionStatus());
        //查询工序作业信息
        ApsProcessOperationDo processOperationDo = apsProcessOperationService.getById(processId);
        //已开工卷数
        Integer startingroll = processOperationDo.getStartingroll();
        //计划加工卷数
        Integer planprocessrall = processOperationDo.getPlanprocessrall();
        //未完工卷数
        Integer unfinishroll = planprocessrall - startingroll;
        //计算作业状态
        /**
         * 如果已开工卷数=0，则=待开工；
         * 如果已开工卷数>0，并且完工状态=未完工，则=加工中；（先更新完工状态）
         * 如果未完工卷数=0，并且完工状态=已完工，则=已完工（先更新完工状态）
         */
        if (startingroll <= 0) {
            apsProcessOperationDo.setWorkstatus("待开工");
            //容错修正
            apsProcessOperationDo.setStartingroll(0);
        }
        if (startingroll > 0 && apsProcessOperationDo.getClosestatus().equals("未完工")) {
            apsProcessOperationDo.setWorkstatus("加工中");
        }
        if (unfinishroll <= 0 && apsProcessOperationDo.getClosestatus().equals("已完工")) {
            apsProcessOperationDo.setWorkstatus("已完工");
        }

        //更新工序作业的生产状态
        //开工和报工检验后，生产状态=作业状态（先更新作业状态）
        if (apsProcessOperationDo.getWorkstatus().equals("待开工")) {
            apsProcessOperationDo.setProcessstatus(TO_BE_STARTED);
        }
        if (apsProcessOperationDo.getWorkstatus().equals("加工中")) {
            apsProcessOperationDo.setProcessstatus(PROCESSING);
        }
        if (apsProcessOperationDo.getWorkstatus().equals("已完工")) {
            apsProcessOperationDo.setProcessstatus(COMPLETED);
        }
        apsProcessOperationDo.setId(processId);
        apsProcessOperationService.updateById(apsProcessOperationDo);

        return one.getProcessOperationId();
    }

    /**
     * 换线作业
     *
     * @param changingWiresVos
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R changingWires(List<ChangingWiresVo> changingWiresVos) {
        ApsUtils.checkScheduling(null);
        if (changingWiresVos.isEmpty()) {
            log.error("换线作业的工序作业不为空");
            return R.error("工序作业不能为空");
        }

        // 工序作业明细ID
        List<String> detailIds = changingWiresVos.stream().map(ChangingWiresVo::getId).collect(Collectors.toList());
        if (detailIds.isEmpty()) {
            log.error("需要换线的工序作业id为空");
            return R.error("需要换线的工序作业id为空");
        }
        // 换线作业受影响的工序作业ID
        List<String> affectedIdList = new ArrayList<>(detailIds);
        // 获取工序作业明细
        List<ApsProcessOperationProcessEquDo> apsProcessOperationProcessEquDos = this.baseMapper.selectBatchIds(detailIds);
        if (apsProcessOperationProcessEquDos.isEmpty()) {
            log.error("无法获取到工序作业明细");
            return R.error("无法获取到工序作业明细");
        }
        // 校验是否存在已完工的工序作业
        boolean hasCompletedWork = apsProcessOperationProcessEquDos.stream()
                .anyMatch(info -> JOBSTATUS_TO_BE_END.equals(info.getWorkstatus()));
        if (hasCompletedWork) {
            log.error("存在已完工的工序作业无法换线");
            return R.error("存在已完工的工序作业无法换线");
        }
        // 是否需要清空备份
        AtomicReference<Boolean> emptyBackup = new AtomicReference<>(false);
        List<CheckConflictVo> checkConflictVos = new ArrayList<>();
        List<ApsProcessOperationProcessEquDo> saveList = new ArrayList<>();
        changingWiresVos.forEach(changingWiresVo -> {
            // 工序作业明细
            ApsProcessOperationProcessEquDo processEqus = apsProcessOperationProcessEquDos.stream()
                    .filter(info -> changingWiresVo.getId().equals(info.getId()))
                    .findFirst().orElse(null);

            // 获取该工序作业最早的计划开始时间
            Date planstartdate = processEqus.getPlanstartdate();
            // 获取开工时间间隔=所选作业明细最早计划开工时间-所选加工开始时间
            long timeInterval = planstartdate.getTime() - changingWiresVo.getProcessingTime().getTime();

            // 待开工的直接修改
            if (JOBSTATUS_TO_BE_START.equals(processEqus.getWorkstatus())) {
                // 待开工的工序作业明细 直接更换加工设备 和车间
                processEqus.setProcessworkshopid(changingWiresVo.getWorkshopId());
                processEqus.setProcessworkshop(changingWiresVo.getWorkshopName());
                processEqus.setProcessdeviceid(changingWiresVo.getEquId());
                processEqus.setProcessdevice(changingWiresVo.getEquName());
                // 计计划开工时间=计划开工时间-开工时间间隔
                processEqus.setPlanstartdate(new Date(planstartdate.getTime() - timeInterval));
                // 计划完工时间=计划完工时间-开工时间间隔
                processEqus.setPlanenddate(new Date(processEqus.getPlanenddate().getTime() - timeInterval));
                checkConflictVos.add(new CheckConflictVo(processEqus.getId(), processEqus.getProcessdeviceid(),
                        processEqus.getPlanstartdate(), processEqus.getPlanenddate()));
                // 判断是否存在设备不可用
                inspectionEquipmentIsAvailable(processEqus, processEqus.getProcessdeviceid(), processEqus.getPlanstartdate()
                        , processEqus.getPlanenddate());
                saveList.add(processEqus);
            }
            //加工中的状态 将未加工的改变为重新生成
            if (JOBSTATUS_TO_BE_STARTING.equals(processEqus.getWorkstatus())) {
                emptyBackup.set(true);
                ApsProcessOperationProcessEquDo saveProcessEqu = new ApsProcessOperationProcessEquDo();
                BeanUtils.copyProperties(processEqus, saveProcessEqu);

                // 获取加工时长
                long processingTime = DateUtil.between(processEqus.getPlanenddate(), processEqus.getPlanstartdate(), DateUnit.SECOND, false);
                /************更新换线后工序作业的信息********************/
                saveProcessEqu.setId(SecurityUtil.getUUID());
                affectedIdList.add(saveProcessEqu.getId());
                saveProcessEqu.setProcessworkshopid(changingWiresVo.getWorkshopId());
                saveProcessEqu.setProcessworkshop(changingWiresVo.getWorkshopName());
                saveProcessEqu.setProcessdeviceid(changingWiresVo.getEquId());
                saveProcessEqu.setProcessdevice(changingWiresVo.getEquName());
                // 更新计划完工时间
                saveProcessEqu.setPlanenddate(CalculateCompletionTime(saveProcessEqu.getPlanstartdate(), processingTime,
                        saveProcessEqu.getPlanprocessrall() - saveProcessEqu.getStartingroll(),
                        saveProcessEqu.getPlanprocessrall()));
                //计划加工卷数=计划加工卷数-已开工卷数
                saveProcessEqu.setPlanprocessrall(saveProcessEqu.getPlanprocessrall() - saveProcessEqu.getStartingroll());
                saveProcessEqu.setStartingroll(0);
                saveProcessEqu.setReportroll(0);
                saveProcessEqu.setCheckoutroll(0);
                saveProcessEqu.setUnfinishroll(saveProcessEqu.getPlanprocessrall());
                saveProcessEqu.setWorkstatus(JOBSTATUS_TO_BE_START);
                saveProcessEqu.setClosestatus(SaveConstans.CloseStatus.UNFINISHED);
                checkConflictVos.add(new CheckConflictVo(processEqus.getId(), saveProcessEqu.getProcessdeviceid(),
                        saveProcessEqu.getPlanstartdate(), saveProcessEqu.getPlanenddate()));
                // 判断是否存在设备不可用
                inspectionEquipmentIsAvailable(saveProcessEqu, saveProcessEqu.getProcessdeviceid(), saveProcessEqu.getPlanstartdate()
                        , saveProcessEqu.getPlanenddate());
                saveList.add(saveProcessEqu);
                /************更新原数据的数量信息***********************/
                // 更新计划完工时间
                processEqus.setPlanenddate(CalculateCompletionTime(processEqus.getPlanstartdate(), processingTime,
                        processEqus.getStartingroll(), processEqus.getPlanprocessrall()));
                //更新计划加工卷数
                processEqus.setPlanprocessrall(processEqus.getStartingroll());
                // 更新未完工卷数
                processEqus.setUnfinishroll(processEqus.getPlanprocessrall() - processEqus.getReportroll());
                processEqus.setWorkstatus(processEqus.getUnfinishroll() == 0 ? JOBSTATUS_TO_BE_END : JOBSTATUS_TO_BE_STARTING);
                processEqus.setClosestatus(processEqus.getUnfinishroll() > 0 ? SaveConstans.CloseStatus.UNFINISHED : SaveConstans.CloseStatus.COMPLETED);
                // 判断是否存在设备不可用
                inspectionEquipmentIsAvailable(processEqus, processEqus.getProcessdeviceid(), processEqus.getPlanstartdate()
                        , processEqus.getPlanenddate());
                saveList.add(processEqus);
            }
        });
        // 换线拆单后 需要校验 是否存在冲突 如果存在冲突 提示
        if (!checkConflictVos.isEmpty()) {
            List<String> promptInformation = this.baseMapper.checkConflict(checkConflictVos);
            if (!promptInformation.isEmpty()) {
                log.error("存在冲突的工序作业");
                return R.error(promptInformation.get(0));
            }
        }
        if (emptyBackup.get()) {
            SysUserVo currentUser = CXCommonUtils.getCurrentUser();
            // 删除工序作业备份
            ApsUtils.clearBackup(currentUser);
        }
        this.saveOrUpdateBatch(saveList);

        // 重新获取甘特图数据并返回前段
        return R.ok(ganttService.getListById(affectedIdList));
    }

    /**
     * 计算计划完工时间
     * = 计划开工时间+加工时长*已开工卷数/计划加工卷数
     *
     * @param planStartTime
     * @param processingTime
     * @param startingroll
     * @param planprocessrall
     * @return
     */
    private Date CalculateCompletionTime(Date planStartTime, long processingTime, int startingroll, int planprocessrall) {
        int i = new BigDecimal(processingTime).multiply(new BigDecimal(startingroll)).divide(new BigDecimal(planprocessrall)).intValue();
        return DateUtil.offsetSecond(planStartTime, i);
    }

    /**
     * 根据工序作业明细 获取可选设备
     *
     * @param operationJobDetailsId
     * @return
     */
    @Override
    public R getOptionalEquipment(List<String> operationJobDetailsId) {
        if (operationJobDetailsId.isEmpty()) {
            log.error("工序作业明细必选");
            return R.error("工序作业明细必选");
        }
        log.debug("根据工序作业明细：{},获取可选设备", operationJobDetailsId);
        // 防止工序作业ID重复
        operationJobDetailsId = operationJobDetailsId.stream().distinct().collect(Collectors.toList());
        List<OptionalEquipmentVo> optionalEquipment = this.baseMapper.getOptionalEquipmentById(operationJobDetailsId);
        if (optionalEquipment.isEmpty()) {
            log.error("当前所选作业不存在共同的加工设备");
            return R.error("没有可共用的加工设备,无法合并换线");
        }
        return R.ok(optionalEquipment);
    }

    /**
     * 工序作业明细更新 计划开始和计划结束时间
     *
     * @param changeStartTimeVos
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R changeStartTime(List<ChangeStartTimeVo> changeStartTimeVos) {
        /// 设计到修改的工序作业ID
        ApsUtils.checkScheduling(null);
        List<String> affectedIdList = new ArrayList<>();
        changeStartTimeVos.forEach(changeStartTimeVo -> {
            String id = changeStartTimeVo.getId();
            affectedIdList.add(id);
            ApsProcessOperationProcessEquDo processEquDo = this.getById(id);
            ApsProcessOperationDo operationDo = apsProcessOperationService.getById(processEquDo.getProcessid());
            if (operationDo == null) {
                log.error("无法通过ID:{}获取到坯料计划", processEquDo.getBlankid());
                throw new RuntimeException("无法获取到坯料计划信息");
            }
            if (Arrays.asList(COMPLETED, PROCESSING, TO_BE_STARTED).contains(operationDo.getProcessstatus())) {
                log.error("该作业已发布，不能调整开工时间");
                throw new RuntimeException("该作业已发布，不能调整开工时间");
            }
            // 获取更新之前的存在冲突的工序作业
            List<ApsProcessOperationProcessEquDo> beforeUpdatingConflictId = this.baseMapper.getConflictId(changeStartTimeVo.getEquId(), changeStartTimeVo.getId(),
                    processEquDo.getPlanstartdate(), processEquDo.getPlanenddate());
            affectedIdList.addAll(beforeUpdatingConflictId.stream().map(ApsProcessOperationProcessEquDo::getId).collect(Collectors.toList()));
            // 找到更新时间之后存在冲突的工序作业
            List<ApsProcessOperationProcessEquDo> afterTheUpdateConflictId = this.baseMapper.getConflictId(changeStartTimeVo.getEquId(), changeStartTimeVo.getId(),
                    changeStartTimeVo.getPlanStartTime(), changeStartTimeVo.getPlanEndTime());
            affectedIdList.addAll(afterTheUpdateConflictId.stream().map(ApsProcessOperationProcessEquDo::getId).collect(Collectors.toList()));
            if (!beforeUpdatingConflictId.isEmpty()) {
                updateProcessEquBeforeAddConflictsDesc(beforeUpdatingConflictId, EQ_TIME_CROSS);
            }

            if (!afterTheUpdateConflictId.isEmpty()) {
                processEquDo.setHasconflict(LOCKMARK_Y);
                // 更当前工序作业增加新的冲突
                String newConflictsDesc = addNewConflictsDesc(processEquDo.getConflictdes(), EQ_TIME_CROSS);
                processEquDo.setConflictdes(newConflictsDesc);
                updateProcessEquAfterAddConflictsDesc(afterTheUpdateConflictId, EQ_TIME_CROSS);
            } else {
                String newConflictsDesc = removeConflictsDesc(processEquDo.getConflictdes(), EQ_TIME_CROSS);
                processEquDo.setConflictdes(newConflictsDesc);
                processEquDo.setHasconflict(StringUtils.isBlank(processEquDo.getConflictdes()) &&
                        StringUtils.isBlank(processEquDo.getSoftconflictdes()) ?
                        LOCKMARK_N : LOCKMARK_Y);
            }
            // 判断是否存在设备不可用
            inspectionEquipmentIsAvailable(processEquDo, changeStartTimeVo.getEquId(), changeStartTimeVo.getPlanStartTime()
                    , changeStartTimeVo.getPlanEndTime());

            processEquDo.setPlanstartdate(changeStartTimeVo.getPlanStartTime());
            processEquDo.setPlanenddate(changeStartTimeVo.getPlanEndTime());
            this.updateById(processEquDo);
        });
        // 返回受影响的数据
        return R.ok(this.ganttService.getListById(affectedIdList));
    }


    /**
     * 更新工序作业明细
     *
     * @param jobDetailsVo
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R updateDetails(JobDetailsVo jobDetailsVo) {
        ApsUtils.checkScheduling(null);
        List<String> affectedIdList = new ArrayList<>();
        String id = jobDetailsVo.getId();
        affectedIdList.add(id);
        if (StringUtils.isBlank(id)) {
            log.error("工序作业ID 必传");
            return R.error("参数异常");
        }
        ApsProcessOperationProcessEquDo apsProcessOperationProcessEquDo = this.getById(id);
        if (apsProcessOperationProcessEquDo == null) {
            log.error("无法根据ID:{}获取到工序作业明细", id);
            return R.error("无法获取到工序作业信息");
        }
        if (!JOBSTATUS_TO_BE_START.equals(apsProcessOperationProcessEquDo.getWorkstatus())) {
            log.error("当前工序作业已开工");
            return R.error("当前工序作业已开工无法更新工序作业信息");
        }
        if (StringUtils.isBlank(apsProcessOperationProcessEquDo.getProcessid())) {
            log.error("工序作业明细对应工序作业ID为空");
            return R.error("无法获取作业明细对应的工序作业");
        }
        // 判断工序作业是否待发布
        ApsProcessOperationDo apsProcessOperationDo = apsProcessOperationService.getById(apsProcessOperationProcessEquDo.getProcessid());
        if (apsProcessOperationDo == null) {
            log.error("根据ID：{} 获取工序作业失败", apsProcessOperationProcessEquDo.getProcessid());
        }
        if (!TO_BE_PUBLISHED.equals(apsProcessOperationDo.getProcessstatus())) {
            log.error("工序作业状态：{}异常", apsProcessOperationDo.getProcessstatus());
            return R.error("只有待发布的工序作业才可以修改");
        }
        // 更新开工时间 jobDetailsVo
        String equId = jobDetailsVo.getEquId();
        String equName = jobDetailsVo.getEquName();
        String oldEquId = apsProcessOperationProcessEquDo.getProcessdeviceid();
        if (StringUtils.isNotBlank(equId)) {
            apsProcessOperationProcessEquDo.setProcessdevice(equName);
            apsProcessOperationProcessEquDo.setProcessdeviceid(equId);
            apsProcessOperationProcessEquDo.setProcessworkshop(jobDetailsVo.getWorkshopName());
            apsProcessOperationProcessEquDo.setProcessworkshopid(jobDetailsVo.getWorkshopId());
        }
        // 更新计划开工时间
        Date planStartTime = jobDetailsVo.getPlanStartTime();
        if (planStartTime != null) {
            Date oldPlanstartdate = apsProcessOperationProcessEquDo.getPlanstartdate();
            Date oldPlanenddate = apsProcessOperationProcessEquDo.getPlanenddate();
            // 获取开工时间间隔=所选作业明细最早计划开工时间-所选加工开始时间
            long timeInterval = DateUtil.between(planStartTime, oldPlanstartdate, DateUnit.SECOND, false);
            apsProcessOperationProcessEquDo.setPlanstartdate(planStartTime);
            // 自动计算计划完工时间=原完工时间+新计划开工时间-原计划开工时间
            apsProcessOperationProcessEquDo.setPlanenddate(new Date(apsProcessOperationProcessEquDo.getPlanenddate().getTime() + jobDetailsVo.getPlanStartTime().getTime() - oldPlanstartdate.getTime()));
            // 获取更新之前的存在冲突的工序作业
            List<ApsProcessOperationProcessEquDo> beforeUpdatingConflictId = this.baseMapper.getConflictId(
                    oldEquId, id, oldPlanstartdate, oldPlanenddate);
            affectedIdList.addAll(beforeUpdatingConflictId.stream().map(ApsProcessOperationProcessEquDo::getId).collect(Collectors.toList()));
            // 找到更新时间之后存在冲突的工序作业
            List<ApsProcessOperationProcessEquDo> afterTheUpdateConflictId = this.baseMapper.getConflictId(
                    apsProcessOperationProcessEquDo.getProcessdeviceid(), id,
                    apsProcessOperationProcessEquDo.getPlanstartdate(), apsProcessOperationProcessEquDo.getPlanenddate());
            affectedIdList.addAll(afterTheUpdateConflictId.stream().map(ApsProcessOperationProcessEquDo::getId).collect(Collectors.toList()));
            if (!beforeUpdatingConflictId.isEmpty()) {
                updateProcessEquBeforeAddConflictsDesc(beforeUpdatingConflictId, EQ_TIME_CROSS);
            }

            if (!afterTheUpdateConflictId.isEmpty()) {
                apsProcessOperationProcessEquDo.setHasconflict(LOCKMARK_Y);
                // 更当前工序作业增加新的冲突
                String newConflictsDesc = addNewConflictsDesc(apsProcessOperationProcessEquDo.getConflictdes(), EQ_TIME_CROSS);
                apsProcessOperationProcessEquDo.setConflictdes(newConflictsDesc);
                updateProcessEquAfterAddConflictsDesc(afterTheUpdateConflictId, EQ_TIME_CROSS);
                this.updateBatchById(afterTheUpdateConflictId);
            } else {
                // 更当前工序作业增加新的冲突
                String newConflictsDesc = removeConflictsDesc(apsProcessOperationProcessEquDo.getConflictdes(), EQ_TIME_CROSS);
                apsProcessOperationProcessEquDo.setConflictdes(newConflictsDesc);
                apsProcessOperationProcessEquDo.setHasconflict(StringUtils.isBlank(apsProcessOperationProcessEquDo.getConflictdes()) &&
                        StringUtils.isBlank(apsProcessOperationProcessEquDo.getSoftconflictdes()) ?
                        LOCKMARK_N : LOCKMARK_Y);
            }
        }
        // 判断是否存在设备不可用
        inspectionEquipmentIsAvailable(apsProcessOperationProcessEquDo, apsProcessOperationProcessEquDo.getProcessdeviceid()
                , apsProcessOperationProcessEquDo.getPlanstartdate(), apsProcessOperationProcessEquDo.getPlanenddate());
        this.updateById(apsProcessOperationProcessEquDo);
        return R.ok(this.ganttService.getListById(affectedIdList));
    }

    public void updateBlank(String processOperationId) {
        //查询此工序的所属订单ID
        String orderId = this.baseMapper.selectOrderIdByProcessOperationId(processOperationId);

        //查询此工序所属订单的所有坯料计划的最小生产状态、最大生产状态和坯料计划ID
        List<CommonUpdateProductionStatusReq> twoList = this.baseMapper.selectProcessMinAndmaxStatusList(orderId);
        //根据最小生产状态和最大生产状态，判断当前最新的生产状态，等待更新
        for (CommonUpdateProductionStatusReq commonUpdateProductionStatusReq : twoList) {
            setProductStatusByMinAndMax(commonUpdateProductionStatusReq);
        }


        //查询此坯料计划目前的生产状态信息
        List<ApsBlankOrderDo> list = apsBlankOrderService.list(new QueryWrapper<ApsBlankOrderDo>().lambda().eq(ApsBlankOrderDo::getProductionorderid, orderId));

        if (twoList.size() <= 0 || list.size() <= 0) {
            return;
        }
        List<ApsBlankOrderDo> needUpdateList = new LinkedList<>();
        for (CommonUpdateProductionStatusReq two : twoList) {
            if (ObjectUtil.isNotEmpty(two.getProductionStatus())) {
                for (ApsBlankOrderDo apsBlankOrderDo : list) {
                    //如果不为空，并且状态不同
                    if (ObjectUtil.isNotEmpty(two.getBlankId()) && two.getBlankId().equals(apsBlankOrderDo.getId())
                            && ObjectUtil.isNotEmpty(two.getProductionStatus()) && ObjectUtil.isNotEmpty(apsBlankOrderDo.getProductstatus())
                            && !two.getProductionStatus().equals(apsBlankOrderDo.getProductstatus())) {
                        //更新坯料计划的生产状态
                        ApsBlankOrderDo blankOrderDo = new ApsBlankOrderDo();
                        blankOrderDo.setId(two.getBlankId());
                        blankOrderDo.setProductstatus(two.getProductionStatus());
                        //更新坯料计划的排程状态
                        /**
                         * 如果生产状态=待排程，则排程状态=待排程
                         * 如果生产状态=待发布，则排程状态=待发布
                         * 如果生产状态=待开工或加工中或已完工，则排程状态=已发布
                         */
                        if (two.getProductionStatus().equals(TO_BE_SCHEDULED)) {
                            blankOrderDo.setSchedulingstatus(SaveConstans.SchedulingStatus.TO_BE_SCHEDULED);
                        } else if (two.getProductionStatus().equals(TO_BE_PUBLISHED)) {
                            blankOrderDo.setSchedulingstatus(SaveConstans.SchedulingStatus.TO_BE_PUBLISHED);
                        } else {
                            blankOrderDo.setSchedulingstatus(SaveConstans.SchedulingStatus.PUBLISHED);
                        }
                        needUpdateList.add(blankOrderDo);
                    }
                }
            }
        }
        //更新坯料计划
        if (needUpdateList.size() > 0) {
            apsBlankOrderService.updateBatchById(needUpdateList);
        }

        //更新订单
        updateOrder(twoList.get(0).getBlankId());
    }

    private void setProductStatusByMinAndMax(CommonUpdateProductionStatusReq item) {
        //根据最小生产状态和最大生产状态，判断当前最新的生产状态，等待更新
        String maxProductionStatus = item.getMaxProductionStatus();
        String minProductionStatus = item.getMinProductionStatus();
        if (ObjectUtil.isEmpty(maxProductionStatus) || ObjectUtil.isEmpty(minProductionStatus)) {
            return;
        }
        /*
         * 如果作业最大生产状态=待排程，则生产状态=待排程
         * 如果作业最大生产状态=待发布，则生产状态=待发布
         * 如果作业最大生产状态=待开工，则生产状态=待开工
         * 如果作业最大生产状态=加工中或已完工，并且作业最小生产状态<已完工，则生产状态=加工中
         * 如果作业最大生产状态=作业最小生产状态=已完工，则生产状态=已完工
         */
        //如果作业最大生产状态=待排程，则生产状态=待排程
        if (maxProductionStatus.equals(TO_BE_SCHEDULED)) {
            item.setProductionStatus(TO_BE_SCHEDULED);
        }
        //如果作业最大生产状态=待发布，则生产状态=待发布
        if (maxProductionStatus.equals(TO_BE_PUBLISHED)) {
            item.setProductionStatus(TO_BE_PUBLISHED);
        }
        //如果作业最大生产状态=待开工，则生产状态=待开工
        if (maxProductionStatus.equals(TO_BE_STARTED)) {
            item.setProductionStatus(TO_BE_STARTED);
        }
        //如果作业最大生产状态=加工中或已完工，并且作业最小生产状态<已完工(50)，则生产状态=加工中
        if ((maxProductionStatus.equals(PROCESSING) || maxProductionStatus.equals(COMPLETED)) && Integer.parseInt(minProductionStatus) < Integer.parseInt(COMPLETED)) {
            item.setProductionStatus(PROCESSING);
        }
        //如果作业最大生产状态=作业最小生产状态=已完工，则生产状态=已完工
        if (minProductionStatus.equals(maxProductionStatus) && minProductionStatus.equals(COMPLETED)) {
            item.setProductionStatus(COMPLETED);
        }
    }


    public void updateOrder(String blankId) {
        //查询此坯料计划的所属订单的最小生产状态和订单ID
        CommonUpdateProductionStatusReq three = this.baseMapper.selectBlankMinAndMaxStatus(blankId);
        if (ObjectUtil.isEmpty(three)) {
            return;
        }
        //根据最小生产状态和最大生产状态，判断当前最新的生产状态，等待更新
        setProductStatusByMinAndMax(three);

        if (ObjectUtil.isNotEmpty(three.getProductionStatus())) {
            //更新订单
            ApsProductionOrderDo apsProductionOrderDo = new ApsProductionOrderDo();
            apsProductionOrderDo.setId(three.getOrderId());
            apsProductionOrderDo.setProductionstatus(three.getProductionStatus());
            apsProductionOrderService.updateById(apsProductionOrderDo);
        }

        //更新产品明细的生产状态
        //订单ID
        String orderId = three.getOrderId();
        updateProductDetail(orderId);
    }

    public void updateProductDetail(String orderId) {
        List<ApsProductDetailDo> list = apsProductDetailService.list(new QueryWrapper<ApsProductDetailDo>().lambda().eq(ApsProductDetailDo::getMainid, orderId));
        if (list.size() > 0) {
            List<String> productIdList = list.stream().map(ApsProductDetailDo::getId).collect(Collectors.toList());
            //查找产品的最新坯料状态
            List<CommonUpdateProductionStatusReq> four = apsProcessOperationProcessEquDao.selectNeedUpdate(productIdList);
            //根据最小生产状态和最大生产状态，判断当前最新的生产状态，等待更新
            for (CommonUpdateProductionStatusReq commonUpdateProductionStatusReq : four) {
                setProductStatusByMinAndMax(commonUpdateProductionStatusReq);
            }


            //需要更新的数组
            List<ApsProductDetailDo> needUpdateList = new LinkedList<>();

            for (CommonUpdateProductionStatusReq vo : four) {
                //对比当前生产状态判断是否需要更新
                for (ApsProductDetailDo productDetailDo : list) {
                    //同一产品明细
                    if (vo.getProductDetailId().equals(productDetailDo.getId())) {
                        //比较状态
                        if (vo.getProductionStatus() != null && !vo.getProductionStatus().equals(productDetailDo.getProductstatus())) {
                            //不相等则更新
                            ApsProductDetailDo needUpdate = new ApsProductDetailDo();
                            needUpdate.setId(vo.getProductDetailId());
                            needUpdate.setProductstatus(vo.getProductionStatus());

                            needUpdateList.add(needUpdate);
                        }
                    }
                }
            }

            if (needUpdateList.size() > 0) {
                apsProductDetailService.updateBatchById(needUpdateList);
            }
        }
    }

    /**
     * 更新设备或者时间之后 更新冲突描述
     *
     * @param beforeUpdatingConflictId 当前作业更新设备或者时间之后与该作业存在冲突的工序作业信息
     * @param removeConflictsDesc      需要被删除的冲突描述
     */
    public void updateProcessEquBeforeAddConflictsDesc(List<ApsProcessOperationProcessEquDo> beforeUpdatingConflictId,
                                                       String removeConflictsDesc) {
        if (beforeUpdatingConflictId.isEmpty()) {
            return;
        }
        beforeUpdatingConflictId.forEach(info -> {
            // 判断是否和其他工序作业明细存在从头
            List<ApsProcessOperationProcessEquDo> conflictId = this.baseMapper.getConflictId(info.getProcessdeviceid(), info.getId(), info.getPlanstartdate(), info.getPlanenddate());
            if (!conflictId.isEmpty()) {
                info.setConflictdes(removeConflictsDesc(info.getConflictdes(), removeConflictsDesc));
                info.setHasconflict(StringUtils.isBlank(info.getConflictdes()) &&
                        StringUtils.isBlank(info.getSoftconflictdes()) ?
                        LOCKMARK_N : LOCKMARK_Y);
            }

        });
        this.updateBatchById(beforeUpdatingConflictId);
    }


    /**
     * 更新设备或者时间之后 更新冲突描述
     *
     * @param afterTheUpdateConflictId 当前作业更新设备或者时间之后与该作业存在冲突的工序作业信息
     * @param conflictsDesc            冲突描述
     */
    public void updateProcessEquAfterAddConflictsDesc(List<ApsProcessOperationProcessEquDo> afterTheUpdateConflictId,
                                                      String conflictsDesc) {
        if (afterTheUpdateConflictId.isEmpty()) {
            return;
        }
        afterTheUpdateConflictId.forEach(info -> {
            String newConflictsDesc = addNewConflictsDesc(info.getConflictdes(), conflictsDesc);
            info.setConflictdes(newConflictsDesc);
            info.setHasconflict(StringUtils.isBlank(info.getConflictdes()) && StringUtils.isBlank(info.getSoftconflictdes()) ?
                    LOCKMARK_N : LOCKMARK_Y);
        });
        this.updateBatchById(afterTheUpdateConflictId);
    }

    /**
     * 增加 新的冲突
     *
     * @param oldConflictDesc 工序作业的旧冲突
     * @param newConflictDesc 需要新增的冲突
     * @return
     */
    public String addNewConflictsDesc(String oldConflictDesc, String newConflictDesc) {
        if (StringUtils.isBlank(oldConflictDesc)) {
            return newConflictDesc;
        }
        List<String> conflictdeList = new ArrayList<>(Arrays.asList(oldConflictDesc.split(";")));
        if (!conflictdeList.contains(newConflictDesc)) {
            conflictdeList.add(newConflictDesc);
        }
        return CollUtil.join(conflictdeList, ";");
    }

    /**
     * 删除旧冲突
     *
     * @param oldConflictDesc    工序作业的旧冲突
     * @param removeConflictDesc 需要删除的冲突
     * @return
     */
    public String removeConflictsDesc(String oldConflictDesc, String removeConflictDesc) {
        if (StringUtils.isBlank(oldConflictDesc)) {
            return oldConflictDesc;
        }
        List<String> conflictdeList = new ArrayList<>(Arrays.asList(oldConflictDesc.split(";")));
        conflictdeList.remove(removeConflictDesc);
        return CollUtil.join(conflictdeList, ";");
    }

    /**
     * 校验设备是否可用
     *
     * @param equId
     * @param startDate
     * @param endDate
     */
    public void inspectionEquipmentIsAvailable(ApsProcessOperationProcessEquDo processEquDo, String equId, Date startDate, Date endDate) {
        // 查询当前设备当前时间段是否存在不可用设备
        List<ApsEquipmentCalendarDo> list = apsEquipmentCalendarService.list(new LambdaQueryWrapper<ApsEquipmentCalendarDo>()
                .eq(ApsEquipmentCalendarDo::getProcessdeviceid, equId)
                .and(wapper -> wapper.apply("{0}   BETWEEN sdstarttime AND sdendtime", startDate)
                        .or()
                        .apply("{0}   BETWEEN sdstarttime AND sdendtime", endDate))
        );
        String newConflictdes;
        if (list.isEmpty()) {
            newConflictdes = removeConflictsDesc(processEquDo.getConflictdes(), EQUIPMENT_RUN_TIME);
        } else {
            newConflictdes = addNewConflictsDesc(processEquDo.getConflictdes(), EQUIPMENT_RUN_TIME);
        }
        processEquDo.setConflictdes(newConflictdes);
        processEquDo.setHasconflict(StringUtils.isBlank(newConflictdes) && StringUtils.isBlank(processEquDo.getSoftconflictdes()) ? LOCKMARK_N : LOCKMARK_Y);
    }
}




