package cn.estsh.i3plus.ext.mes.pcn.apiservice.serviceimpl.busi;

import cn.estsh.i3plus.ext.mes.pcn.api.base.IMesContainerSnService;
import cn.estsh.i3plus.ext.mes.pcn.api.busi.IMesContainerSnBindService;
import cn.estsh.i3plus.ext.mes.pcn.apiservice.serviceimpl.rulematch.MesNumberRuleMatchRegularExpressionService;
import cn.estsh.i3plus.ext.mes.pcn.apiservice.serviceimpl.rulematch.containerbind.ContainerBindManager;
import cn.estsh.i3plus.ext.mes.pcn.apiservice.serviceimpl.rulematch.containerbind.checkqty.MesAbstractContainerBindCheckQtyService;
import cn.estsh.i3plus.ext.mes.pcn.apiservice.util.MesPcnException;
import cn.estsh.i3plus.ext.mes.pcn.pojo.model.MesContainerPartsModel;
import cn.estsh.i3plus.ext.mes.pcn.pojo.model.MesContainerSnBindModel;
import cn.estsh.i3plus.ext.mes.pcn.pojo.util.MesPcnExtConstWords;
import cn.estsh.i3plus.ext.mes.pcn.pojo.util.OverwriteStringJoin;
import cn.estsh.i3plus.platform.common.convert.ConvertBean;
import cn.estsh.i3plus.platform.common.tool.TimeTool;
import cn.estsh.i3plus.platform.common.util.MesConstWords;
import cn.estsh.i3plus.pojo.base.bean.DdlPackBean;
import cn.estsh.i3plus.pojo.base.codemaker.SnowflakeIdMaker;
import cn.estsh.i3plus.pojo.base.enumutil.CommonEnumUtil;
import cn.estsh.i3plus.pojo.base.tool.DdlPreparedPack;
import cn.estsh.i3plus.pojo.mes.bean.*;
import cn.estsh.i3plus.pojo.mes.repository.*;
import cn.estsh.i3plus.pojo.mes.util.MesExtEnumUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class MesContainerSnBindServiceImpl implements IMesContainerSnBindService {
    @Autowired
    private IMesContainerSnService containerSnService;
    @Autowired
    private MesContainerTypeRepository containerTypeRDao;
    @Autowired
    private MesContainerTypePartRepository containerTypePartRDao;
    @Autowired
    private MesContainerSnPartRepository containerSnPartRDao;
    @Autowired
    private MesContainerPackageRepository containerPackageRDao;
    @Autowired
    private MesContainerPackageDetailRepository containerPackageDetailRDao;
    @Autowired
    private MesProduceSnRepository produceSnRDao;
    @Autowired
    private MesContainerPackageDetailBindingRepository detailBindingRDao;
    @Autowired
    private MesNumberRuleMatchRegularExpressionService matchRegularService;
    @Autowired
    private MesContainerSnStationRepository containerSnStationRDao;
    @Autowired
    private EntityManager entityManager;
    @Autowired
    private SnowflakeIdMaker snowflakeIdMaker;
    @Autowired
    protected MesContainerPackageDetailFeedingRepository packageDetailFeedingRDao;

    private MesContainerPackage getContainerPackage(MesContainerSnBindModel model, String containerSn) {
        MesContainerPackage containerPackage = containerPackageRDao.getByProperty(
                new String[]{MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_DELETED, MesPcnExtConstWords.IS_VALID, "containerSn"},
                new Object[]{model.getOrganizeCode(), CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), CommonEnumUtil.IS_VAILD.VAILD.getValue(), containerSn});
        return containerPackage;
    }

    private List<MesContainerPackageDetail> getContainerPackageDetail(MesContainerSnBindModel model, MesContainerSn containerSn) {
        List<MesContainerPackageDetail> packageDetails = containerPackageDetailRDao.findByProperty(
                new String[]{MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_DELETED, MesPcnExtConstWords.IS_VALID, "containerSn"},
                new Object[]{model.getOrganizeCode(), CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), CommonEnumUtil.IS_VAILD.VAILD.getValue(), containerSn.getContainerSn()});
        return packageDetails;
    }

    private List<MesContainerPartsModel> getContainerParts(String organizeCode, MesContainerType containerType, String containerSn) {
        List<MesContainerPartsModel> result = new ArrayList<>();
        Set<String> partNoSet = new HashSet<>();
        List<MesContainerSnPart> snPartList = containerSnPartRDao.findByProperty(
                new String[]{MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_DELETED, MesPcnExtConstWords.IS_VALID, "containerTypeCode", "containerSn"},
                new Object[]{organizeCode, CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), CommonEnumUtil.IS_VAILD.VAILD.getValue(), containerType.getContainerTypeCode(), containerSn});
        if (!CollectionUtils.isEmpty(snPartList)) {
            for (MesContainerSnPart snPart : snPartList) {
                if (StringUtils.isEmpty(snPart.getPartNo())) {
                    continue;
                }

                partNoSet.add(snPart.getPartNo());
                MesContainerPartsModel model = MesContainerPartsModel.builder()
                        .containerType(containerType.getContainerType())
                        .partNo(snPart.getPartNo())
                        .partName(snPart.getPartName())
                        .matchType(snPart.getMatchType())
                        .matchRule(snPart.getMatchRule())
                        .qty(snPart.getQty())
                        .build();
                result.add(model);
            }
        }

        List<MesContainerTypePart> partList = containerTypePartRDao.findByProperty(
                new String[]{MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_DELETED, MesPcnExtConstWords.IS_VALID, "containerTypeCode"},
                new Object[]{organizeCode, CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), CommonEnumUtil.IS_VAILD.VAILD.getValue(), containerType.getContainerTypeCode()});
        if (!CollectionUtils.isEmpty(partList)) {
            for (MesContainerTypePart partPart : partList) {
                if (StringUtils.isEmpty(partPart.getPartNo())) {
                    continue;
                }

                if (partNoSet.contains(partPart.getPartNo())) {
                    continue;
                }
                MesContainerPartsModel model = MesContainerPartsModel.builder()
                        .containerType(containerType.getContainerType())
                        .partNo(partPart.getPartNo())
                        .partName(partPart.getPartName())
                        .matchType(partPart.getMatchType())
                        .matchRule(partPart.getMatchRule())
                        .qty(partPart.getQty())
                        .build();
                result.add(model);
            }
        }
        return result;
    }

    private MesContainerPartsModel getContainerPartsByPartNo(String organizeCode, String containerTypeCode, String containerSn, String partNo) {
        MesContainerPartsModel model = null;
        MesContainerSnPart snPart = containerSnPartRDao.getByProperty(
                new String[]{MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_DELETED, MesPcnExtConstWords.IS_VALID, "containerTypeCode", "containerSn", "partNo"},
                new Object[]{organizeCode, CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), CommonEnumUtil.IS_VAILD.VAILD.getValue(), containerTypeCode, containerSn, partNo});
        if (snPart != null) {
             model = MesContainerPartsModel.builder()
                    .partNo(snPart.getPartNo())
                   .partName(snPart.getPartName())
                    .matchType(snPart.getMatchType())
                    .matchRule(snPart.getMatchRule())
                    .qty(snPart.getQty())
                    .build();
        } else {
            MesContainerTypePart typePart = containerTypePartRDao.getByProperty(
                    new String[]{MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_DELETED, MesPcnExtConstWords.IS_VALID, "containerTypeCode", "partNo"},
                    new Object[]{organizeCode, CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), CommonEnumUtil.IS_VAILD.VAILD.getValue(), containerTypeCode, partNo});
            if (typePart != null) {
                model = MesContainerPartsModel.builder()
                        .partNo(typePart.getPartNo())
                        .partName(typePart.getPartName())
                        .matchType(typePart.getMatchType())
                        .matchRule(typePart.getMatchRule())
                        .qty(typePart.getQty())
                        .build();
            }
        }
        return model;
    }

    private MesContainerPartsModel getContainerPartsByBarcode(String organizeCode, String containerTypeCode, String containerSn, String barcode) {
        MesContainerPartsModel model = null;
        List<MesContainerSnPart> snPartList = containerSnPartRDao.findByProperty(
                new String[]{MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_DELETED, MesPcnExtConstWords.IS_VALID, "containerTypeCode", "containerSn"},
                new Object[]{organizeCode, CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), CommonEnumUtil.IS_VAILD.VAILD.getValue(), containerTypeCode, containerSn});
        if (!CollectionUtils.isEmpty(snPartList)) {
            for (MesContainerSnPart snPart : snPartList) {
                if (StringUtils.isEmpty(snPart.getMatchRule())) {
                    continue;
                }

                Map<String, Object> result = matchRegularService.matchNumberRule(organizeCode, barcode, "", snPart.getMatchRule());
                if (result != null && Objects.equals(result.get(MesPcnExtConstWords.RESULT), true)) {
                    model = MesContainerPartsModel.builder()
                            .partNo(snPart.getPartNo())
                            .partName(snPart.getPartName())
                            .matchType(snPart.getMatchType())
                            .matchRule(snPart.getMatchRule())
                            .qty(snPart.getQty())
                            .build();
                    break;
                }
            }
        }
        if (model == null) {
            List<MesContainerTypePart> partList = containerTypePartRDao.findByProperty(
                    new String[]{MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_DELETED, MesPcnExtConstWords.IS_VALID, "containerTypeCode"},
                    new Object[]{organizeCode, CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), CommonEnumUtil.IS_VAILD.VAILD.getValue(), containerTypeCode});
            if (!CollectionUtils.isEmpty(partList)) {
                for (MesContainerTypePart typePart : partList) {
                    if (StringUtils.isEmpty(typePart.getMatchRule())) {
                        continue;
                    }

                    Map<String, Object> reulst = matchRegularService.matchNumberRule(organizeCode, barcode, "", typePart.getMatchRule());
                    if (reulst != null && Objects.equals(reulst.get(MesPcnExtConstWords.RESULT), true)) {
                        model = MesContainerPartsModel.builder()
                                .partNo(typePart.getPartNo())
                                .partName(typePart.getPartName())
                                .matchType(typePart.getMatchType())
                                .matchRule(typePart.getMatchRule())
                                .qty(typePart.getQty())
                                .build();
                        break;
                    }
                }
            }
        }
        return model;
    }

    private boolean isEmptyBindSn(String organizeCode, String containerSn) {
        return !containerPackageDetailRDao.isExitByProperty(new String[]{MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_DELETED, MesPcnExtConstWords.IS_VALID, "containerSn"},
                new Object[]{organizeCode, CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), CommonEnumUtil.IS_VAILD.VAILD.getValue(), containerSn});
    }

    @Override
    public MesContainerSnBindModel doScanContainer(MesContainerSnBindModel model) {
        //校验容器条码
        MesContainerSn containerSn = containerSnService.checkContainerSn(model.getOrganizeCode(), model.getContainerSn());
        MesContainerType containerType = containerTypeRDao.getByProperty(
                new String[]{MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_DELETED, MesPcnExtConstWords.IS_VALID, "containerTypeCode"},
                new Object[]{model.getOrganizeCode(), CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), CommonEnumUtil.IS_VAILD.VAILD.getValue(), containerSn.getContainerTypeCode()});
        if (containerType == null) {
            MesPcnException.throwMesBusiException("容器类型代码[%s]信息不存在，请检查容器类型主数据", containerSn.getContainerTypeCode());
        }

        model.setBindIsChoosePart(CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue());
        if (Objects.equals(containerType.getBindIsChoosePart(), CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue())) {
            List<MesContainerPartsModel> partsModelList = getContainerParts(model.getOrganizeCode(), containerType, containerSn.getContainerSn());
            if (CollectionUtils.isEmpty(partsModelList)) {
                MesPcnException.throwMesBusiException("容器条码[%s]或容器类型代码[%s]未维护与容器关系，请检查数据", containerSn.getContainerSn(), containerType.getContainerTypeCode());
            }
            if (partsModelList.size() > 1 && isEmptyBindSn(model.getOrganizeCode(), model.getContainerSn())) {
                model.setContainerParts(partsModelList);
                model.setBindIsChoosePart(CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue());
            }
        }
        autoOpenContainerSn(model.getOrganizeCode(), containerSn.getContainerSn(), false, model.getUserName());

        MesContainerPackage containerPackage = containerPackageRDao.getByProperty(
                new String[]{MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_DELETED, MesPcnExtConstWords.IS_VALID, "containerSn"},
                new Object[]{model.getOrganizeCode(), CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), CommonEnumUtil.IS_VAILD.VAILD.getValue(), containerSn.getContainerSn()});
        if (containerPackage != null) {
            List<MesContainerPackageDetail> packageDetails = containerPackageDetailRDao.findByProperty(
                    new String[]{MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_DELETED, MesPcnExtConstWords.IS_VALID, "pid", "snStatus"},
                    new Object[]{model.getOrganizeCode(), CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), CommonEnumUtil.IS_VAILD.VAILD.getValue(), containerPackage.getId(), MesExtEnumUtil.CONTAINER_BARCODE_STATUS.STATUS_10.getValue()});
            for (MesContainerPackageDetail detail : packageDetails) {
                if (detail.getQty() == null) {
                    detail.setQty(0.0);
                }
                MesContainerPackageDetailFeeding feeding = packageDetailFeedingRDao.getByProperty(new String[]{MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_DELETED, MesPcnExtConstWords.IS_VALID, "pid", "sourceId"},
                        new Object[]{model.getOrganizeCode(), CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), CommonEnumUtil.IS_VAILD.VAILD.getValue(), detail.getPid(), detail.getId()});
                if (feeding != null && feeding.getQty() != null) {
                    detail.setQty(detail.getQty() - feeding.getQty());
                }
            }
            model.setPackageDetails(packageDetails);
        }

        model.setContainerPackage(containerPackage);
        return model;
    }

    private String getTopContainerSn(String organizeCode, String containerSn) {
        MesContainerPackageDetail packageDetail = containerPackageDetailRDao.getByProperty(new String[]{MesConstWords.ORGANIZE_CODE, MesConstWords.IS_VALID, MesConstWords.IS_DELETED, "barCode"},
                new Object[]{organizeCode, CommonEnumUtil.IS_VAILD.VAILD.getValue(), CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), containerSn});
        if (packageDetail != null) {
            return getTopContainerSn(organizeCode, packageDetail.getContainerSn());
        }
        return containerSn;
    }

    private Boolean getBottomPackageDetails(String organizeCode, MesContainerPackage containerPackage, List<MesContainerPackageDetail> bottomPackageDetails) {
        if (containerPackage == null) {
            return true;
        }
        List<MesContainerPackageDetail> packageDetailList = containerPackageDetailRDao.findByProperty(new String[]{MesConstWords.ORGANIZE_CODE, MesConstWords.IS_VALID, MesConstWords.IS_DELETED, "pid"},
                new Object[]{organizeCode, CommonEnumUtil.IS_VAILD.VAILD.getValue(), CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), containerPackage.getId()});
        if (CollectionUtils.isEmpty(packageDetailList)) {
            return true;
        }
        for (MesContainerPackageDetail packageDetail : packageDetailList) {
            MesContainerPackage childContainerPackage = containerPackageRDao.getByProperty(
                    new String[]{MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_DELETED, MesPcnExtConstWords.IS_VALID, "containerSn"},
                    new Object[]{organizeCode, CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), CommonEnumUtil.IS_VAILD.VAILD.getValue(), packageDetail.getBarCode()});
            if (childContainerPackage == null || getBottomPackageDetails(organizeCode, childContainerPackage, bottomPackageDetails)) {
                bottomPackageDetails.add(packageDetail);
            }
        }

        return false;
    }

    /**
     * 校验扫描的条码是否可以进行上料操作
     * @param organizeCode 工厂代码
     * @param barcode 扫描的条码（零件条码/容器条码）
     */
    private List<MesContainerPackageDetail> checkContainerBarcode(String organizeCode, String barcode, List<MesContainerPackageDetail> oldPackageDetails) {
        MesContainerPackage containerPackage = containerPackageRDao.getByProperty(
                new String[]{MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_DELETED, MesPcnExtConstWords.IS_VALID, "containerSn"},
                new Object[]{organizeCode, CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), CommonEnumUtil.IS_VAILD.VAILD.getValue(), barcode});
        if (containerPackage == null || !Objects.equals(containerPackage.getPackageStatus(),MesExtEnumUtil.CONTAINER_PACKAGE_STATUS.STATUS_20.getValue())) {
            MesPcnException.throwMesBusiException("容器条码[%s]未关箱，不能进行上料操作！", barcode);
        }

        List<MesContainerPackageDetail> bottomPackageDetails = new ArrayList<>();
        getBottomPackageDetails(organizeCode, containerPackage, bottomPackageDetails);
        if (CollectionUtils.isEmpty(bottomPackageDetails)) {
            return new ArrayList<>();
        }

        List<MesContainerPackageDetail> combPackageDetails = new ArrayList<>(oldPackageDetails);
        combPackageDetails.addAll(bottomPackageDetails);
        final Set<String> containerSnSet = combPackageDetails.stream().map(MesContainerPackageDetail::getContainerSn).collect(Collectors.toSet());
        StringBuilder hql = new StringBuilder();
        hql.append(" select count(DISTINCT mss.top_container_sn) from mes_container_sn_station mss where mss.organize_code=:organizeCode and mss.is_deleted=:isDeleted and mss.is_valid=:isValid");
        if (containerSnSet.size() == 1) {
            hql.append(" and mss.container_sn = '").append(containerSnSet.iterator().next()).append("'");
        } else {
            hql.append(" and mss.container_sn in (").append(OverwriteStringJoin.join(",", containerSnSet)).append(")");
        }
        Query countQuery = entityManager.createNativeQuery(hql.toString());
        countQuery.setParameter("organizeCode", organizeCode)
                .setParameter("isValid", CommonEnumUtil.VALID)
                .setParameter("isDeleted", CommonEnumUtil.FALSE);
        //本次查询满足条件的总数
        int count = countQuery.getResultList().size();
        if (count > 1) {
            MesPcnException.throwMesBusiException("容器条码【%s】在多个站点进行过绑定！", barcode);
        }
        return bottomPackageDetails;
    }

    /**
     * 判断扫描的容器下所有零件是否为扣减完成，表示该容器为空箱，需要将该容器条码与其他容器的绑定关系解除
     * 同时记录解绑记录。
     * @param organizeCode 工厂代码
     * @param containerSn 扫描的容器条码
     * @param bStep 是否为工步上料
     * @param username 用户名
     */
    private MesContainerPackage autoOpenContainerSn(String organizeCode, String containerSn, boolean bStep, String username) {
        MesContainerPackage containerPackage = containerPackageRDao.getByProperty(
                new String[]{MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_DELETED, MesPcnExtConstWords.IS_VALID, "containerSn"},
                new Object[]{organizeCode, CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), CommonEnumUtil.IS_VAILD.VAILD.getValue(), containerSn});
        if (containerPackage == null) {
            return null;
        }

        List<MesContainerPackageDetail> bottomPackageDetails = new ArrayList<>();
        getBottomPackageDetails(organizeCode, containerPackage, bottomPackageDetails);
        List<MesContainerPackageDetail> deductionDetails = new ArrayList<>();
        for (MesContainerPackageDetail packageDetail : bottomPackageDetails) {
            if (Objects.equals(packageDetail.getSnStatus(), MesExtEnumUtil.CONTAINER_BARCODE_STATUS.STATUS_20.getValue())) {
                deductionDetails.add(packageDetail);
            }
        }
        if (!deductionDetails.isEmpty()) {
            // 扣减完成的条码与容器进行解绑
            for (MesContainerPackageDetail detail : deductionDetails) {
                if (detailBindingRDao.isExitByProperty(new String[]{MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_DELETED, MesPcnExtConstWords.IS_VALID, "pid", "barCode", "snStatus"},
                        new Object[]{organizeCode, CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), CommonEnumUtil.IS_VAILD.VAILD.getValue(), containerPackage.getId(), detail.getBarCode(), MesExtEnumUtil.CONTAINER_RAW_SN_BINDING_STATUS.UN_BINDING.getValue()})) {
                    continue;
                }
                MesContainerPackageDetailBinding binding = createContainerPackageDetailBinding(detail, MesExtEnumUtil.CONTAINER_RAW_SN_BINDING_STATUS.UN_BINDING, username);
                detailBindingRDao.insert(binding);
                DdlPackBean ddlPackBean = DdlPackBean.getDdlPackBean(organizeCode);
                DdlPreparedPack.getStringEqualPack(detail.getBarCode(), "containerSn", ddlPackBean);
                containerSnStationRDao.updateByProperties(
                        new String[]{"modifyUser", "modifyDatetime", "isDeleted"},
                        new Object[]{username, TimeTool.getNowTime(true), CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue()},
                        ddlPackBean);
            }
            // 容器置为拆箱状态。
            containerPackage.setPackageStatus(MesExtEnumUtil.CONTAINER_PACKAGE_STATUS.STATUS_30.getValue());
            containerPackageRDao.update(containerPackage);
        }
        // 全部扣减完成
        if (!deductionDetails.isEmpty() && deductionDetails.size() == bottomPackageDetails.size()) {
            containerPackageRDao.deleteWeaklyById(containerPackage.getId(), username);
            // 非工步上料，需要将容器与站点解绑！
            if (!bStep) {
                DdlPackBean ddlPackBean = DdlPackBean.getDdlPackBean(organizeCode);
                DdlPreparedPack.getStringEqualPack(containerSn, "containerSn", ddlPackBean);
                containerSnStationRDao.updateByProperties(
                        new String[]{"modifyUser", "modifyDatetime", "isDeleted"},
                        new Object[]{username, TimeTool.getNowTime(true), CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue()},
                        ddlPackBean);
            }
            return null;
        }
        return containerPackage;
    }

    @Override
    public MesContainerSnBindModel doScanBarcode(MesContainerSnBindModel model, boolean bStep) {
        final String organizeCode = model.getOrganizeCode();
        String barcode = model.getBarCode();
        //校验容器条码
        MesContainerSn containerSn = containerSnService.checkContainerSn(organizeCode, model.getContainerSn());
        MesContainerType containerType = containerTypeRDao.getByProperty(
                new String[]{MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_DELETED, MesPcnExtConstWords.IS_VALID, "containerTypeCode"},
                new Object[]{organizeCode, CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), CommonEnumUtil.IS_VAILD.VAILD.getValue(), containerSn.getContainerTypeCode()});
        if (containerType == null) {
            MesPcnException.throwMesBusiException("容器类型代码[%s]信息不存在，请检查容器类型主数据", containerSn.getContainerTypeCode());
        }
        if (containerType.getMixType() == null) {
            MesPcnException.throwMesBusiException("容器类型代码[%s]未配置混包类型，请检查容器类型主数据", containerSn.getContainerTypeCode());
        }
        MesContainerPackage containerPackage = autoOpenContainerSn(organizeCode, containerSn.getContainerSn(), bStep, model.getUserName());

        List<MesContainerPackageDetail> packageDetails;
        if (containerPackage == null) {
            containerPackage = createContainerPackage(model, containerType);
            packageDetails = new ArrayList<>();
        } else {
            if (Objects.equals(containerPackage.getPackageStatus(), MesExtEnumUtil.CONTAINER_PACKAGE_STATUS.STATUS_20.getValue())) {
                MesPcnException.throwMesBusiException("容器条码[%s]已关箱，不能再进行上料", containerSn.getContainerSn());
            }
            packageDetails = containerPackageDetailRDao.findByProperty(
                    new String[]{MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_DELETED, MesPcnExtConstWords.IS_VALID, "pid", "snStatus"},
                    new Object[]{organizeCode, CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), CommonEnumUtil.IS_VAILD.VAILD.getValue(), containerPackage.getId(), MesExtEnumUtil.CONTAINER_BARCODE_STATUS.STATUS_10.getValue()});
        }
        MesContainerPackageDetail barcodePackageDetail = containerPackageDetailRDao.getByProperty(new String[]{MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_DELETED, MesPcnExtConstWords.IS_VALID, "barCode"},
                new Object[]{organizeCode, CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), CommonEnumUtil.IS_VAILD.VAILD.getValue(), model.getBarCode()});
        if (barcodePackageDetail != null) {
            MesPcnException.throwMesBusiException("条码[%s]已被容器[%s]绑定，请检查数据！", model.getBarCode(), barcodePackageDetail.getContainerSn());
        }
        List<MesContainerPackageDetail> oldPackageDetails = new ArrayList<>();
        getBottomPackageDetails(organizeCode, containerPackage, oldPackageDetails);

        // 判断扫描的是否为容器条码
        List<MesContainerPackageDetail> bottomPackageDetails;
        MesContainerSn childContainerSn = containerSnService.getContainerSn(organizeCode, model.getBarCode());
        if (childContainerSn != null) {
            if (!Objects.equals(CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue(), containerType.getIsAllowRecursion())) {
                MesPcnException.throwBusiException("容器条码[%s]不能绑定容器，请重新扫描！", containerSn.getContainerSn());
            }
            bottomPackageDetails = checkContainerBarcode(organizeCode, model.getBarCode(), oldPackageDetails);
        } else {
            if (Objects.equals(CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue(), containerType.getIsAllowRecursion())) {
                MesPcnException.throwBusiException("容器条码[%s]不能扫描原料条码，请重新扫描！", containerSn.getContainerSn());
            }
            bottomPackageDetails = new ArrayList<>();
        }
        if (!CollectionUtils.isEmpty(bottomPackageDetails)) {
            barcode = bottomPackageDetails.get(0).getBarCode();
        }

        MesContainerPartsModel containerPart = null;
        Map<String, Object> matchResult = null;
        List<MesContainerPartsModel> containerPartsModels = getContainerParts(model.getOrganizeCode(), containerType, containerSn.getContainerSn());
        for (MesContainerPartsModel containerPartsModel : containerPartsModels) {
            // 调用策略校验条码与容器是否可以绑定
            matchResult = ContainerBindManager.INSTANCE.matchNumberRule(model.getOrganizeCode(), barcode, containerPartsModel);
            assert matchResult != null;
            if (Objects.equals(matchResult.get(MesPcnExtConstWords.RESULT), true)) {
                containerPart = containerPartsModel;
                break;
            }
        }
        if (containerPart == null) {
            MesPcnException.throwMesBusiException("容器条码[%s]或容器类型代码[%s]未维护与零件关系，请检查数据", containerSn.getContainerSn(), containerSn.getContainerTypeCode());
        }
        if (!StringUtils.isEmpty(model.getPartNo()) && !Objects.equals(model.getPartNo(), containerPart.getPartNo())) {
            MesPcnException.throwMesBusiException("条码[%s]的零件号【%s】与选择的零件不匹配，请检查数据", model.getBarCode(), model.getPartNo());
        }

        containerPart.setContainerType(containerType.getContainerType());
        containerPart.setContainerPackage(containerPackage);

        MesContainerPackageDetail packageDetail = createContainerPackageDetail(model, containerPackage, containerPart, matchResult);
        List<MesContainerPackageDetail> combPackageDetails = new ArrayList<>(oldPackageDetails);
        if (CollectionUtils.isEmpty(bottomPackageDetails)) {
            combPackageDetails.add(packageDetail);
        } else {
            packageDetail.setDataType(MesExtEnumUtil.CONTAINER_BARCODE_DATA_TYPE.STATUS_30.getValue());
            packageDetail.setQty((double)bottomPackageDetails.size());
            combPackageDetails.addAll(bottomPackageDetails);
        }

        MesAbstractContainerBindCheckQtyService checkQtyService = ContainerBindManager.INSTANCE.getCheckQtyService(MesExtEnumUtil.CONTAINER_TYPE_MIX_TYPE.getByValue(containerType.getMixType()));
        Map<String, Object> checkQtyResult = checkQtyService.doCheck(model.getOrganizeCode(), containerPackage, combPackageDetails, containerPartsModels);
        if (checkQtyResult != null && Objects.equals(checkQtyResult.get(MesPcnExtConstWords.RESULT), false)) {
            MesPcnException.throwMesBusiException(checkQtyResult.get(MesPcnExtConstWords.MESSAGE).toString());
        }

        MesContainerPackageDetailBinding binding = createContainerPackageDetailBinding(packageDetail, MesExtEnumUtil.CONTAINER_RAW_SN_BINDING_STATUS.BINDING, model.getUserName());
        bindContainerSnStation(organizeCode, model.getContainerSn(), model.getBarCode(), model.getUserName());

        packageDetails.add(packageDetail);
        model.setContainerPackage(containerPackage);
        model.setPackageDetails(packageDetails);
        model.addPackageDetailBinding(binding);
        model.setScanPartNo(containerPart.getPartNo());
        model.setScanPartName(containerPart.getPartName());
        saveContainerPackage(model);
        return model;
    }

    private void bindContainerSnStation(String organizeCode, String containerSn, String barCode, String userName) {
        MesContainerPackage barcodePackage = containerPackageRDao.getByProperty(
                new String[]{MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_DELETED, MesPcnExtConstWords.IS_VALID, "containerSn"},
                new Object[]{organizeCode, CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), CommonEnumUtil.IS_VAILD.VAILD.getValue(), barCode});
        if (barcodePackage == null) {
            return;
        }

        String topContainerSn = getTopContainerSn(organizeCode, containerSn);
        MesContainerSnStation containerSnStation = containerSnStationRDao.getByProperty(new String[]{MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_DELETED, MesPcnExtConstWords.IS_VALID, "topContainerSn"},
                new Object[]{organizeCode, CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), CommonEnumUtil.IS_VAILD.VAILD.getValue(), topContainerSn});
        if (containerSnStation == null) {
            return;
        }
        List<MesContainerPackageDetail> bottomPackageDetailList = new ArrayList<>();
        getBottomPackageDetails(organizeCode, barcodePackage, bottomPackageDetailList);
        final Set<String> containerSnSet = bottomPackageDetailList.stream().map(MesContainerPackageDetail::getContainerSn).collect(Collectors.toSet());
        for (String bottomContainerSn : containerSnSet) {
            MesContainerSnStation snStation = new MesContainerSnStation();
            snStation.setOrganizeCode(organizeCode);
            snStation.setStation(containerSnStation.getStation());
            snStation.setContainerSn(bottomContainerSn);
            snStation.setTopContainerSn(topContainerSn);
            snStation.setSeq(1);
            ConvertBean.serviceModelInitialize(snStation, userName);
            containerSnStationRDao.insert(snStation);
        }
    }

    private void saveContainerPackage(MesContainerSnBindModel model) {
        MesContainerPackage containerPackage = model.getContainerPackage();
        if (containerPackage != null) {
            containerPackageRDao.save(containerPackage);
        }
        if (!CollectionUtils.isEmpty(model.getPackageDetails())) {
            containerPackageDetailRDao.saveAll(model.getPackageDetails());
        }
        if (!CollectionUtils.isEmpty(model.getBindingList())) {
            detailBindingRDao.saveAll(model.getBindingList());
        }
    }

    @Override
    public MesContainerSnBindModel doClear(MesContainerSnBindModel model) {
        //校验容器条码
        MesContainerSn containerSn = containerSnService.checkContainerSn(model.getOrganizeCode(), model.getContainerSn());
        MesContainerPackage containerPackage = getContainerPackage(model, containerSn.getContainerSn());
        if (containerPackage == null) {
            MesPcnException.throwMesBusiException("容器条码[%s]未生成容器条码上料主表，请检查数据", containerSn.getContainerSn());
        }
        List<MesContainerPackageDetail> packageDetails = getContainerPackageDetail(model, containerSn);
        if (CollectionUtils.isEmpty(packageDetails)) {
            MesPcnException.throwMesBusiException("容器条码[%s]不包含上料明细数据，请检查数据", containerSn.getContainerSn());
        }

        DdlPackBean ddlPackBean = DdlPackBean.getDdlPackBean(model.getOrganizeCode());
        DdlPreparedPack.getStringEqualPack(containerSn.getContainerSn(),"containerSn",ddlPackBean);
        containerPackageDetailRDao.updateByProperties(new String[]{"modifyUser", "modifyDatetime", "isDeleted"},
                new Object[]{model.getUserName(), TimeTool.getNowTime(true), CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue()}, ddlPackBean);

        containerPackage.setPackageStatus(MesExtEnumUtil.CONTAINER_PACKAGE_STATUS.STATUS_30.getValue());
        ConvertBean.serviceModelUpdate(containerPackage, model.getUserName());
        containerPackageRDao.update(containerPackage);

        for (MesContainerPackageDetail packageDetail : packageDetails) {
            MesContainerPackageDetailBinding binding = createContainerPackageDetailBinding(packageDetail, MesExtEnumUtil.CONTAINER_RAW_SN_BINDING_STATUS.UN_BINDING, model.getUserName());
            detailBindingRDao.insert(binding);
        }
        deleteContainerSnStation(containerPackage, model.getUserName());
        model.setPackageDetails(new ArrayList<>());
        return model;
    }

    @Override
    public MesContainerSnBindModel doDeleteDetails(MesContainerSnBindModel model) {
        //校验容器条码
        MesContainerSn containerSn = containerSnService.checkContainerSn(model.getOrganizeCode(), model.getContainerSn());
        MesContainerPackage containerPackage = getContainerPackage(model, containerSn.getContainerSn());
        if (containerPackage == null) {
            MesPcnException.throwMesBusiException("容器条码[%s]未生成容器条码上料主表，请检查数据", containerSn.getContainerSn());
        }
        List<Long> deleteDetailIds = new ArrayList<>();
        for (MesContainerPackageDetail detail : model.getPackageDetails()) {
            MesContainerPackageDetailBinding binding = createContainerPackageDetailBinding(detail, MesExtEnumUtil.CONTAINER_RAW_SN_BINDING_STATUS.UN_BINDING, model.getUserName());
            detailBindingRDao.insert(binding);

            deleteDetailIds.add(detail.getId());

            DdlPackBean ddlPackBean = DdlPackBean.getDdlPackBean(model.getOrganizeCode());
            DdlPreparedPack.getStringEqualPack(detail.getBarCode(), "containerSn", ddlPackBean);
            containerSnStationRDao.updateByProperties(
                    new String[]{"modifyUser", "modifyDatetime", "isDeleted"},
                    new Object[]{model.getUserName(), TimeTool.getNowTime(true), CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue()},
                    ddlPackBean);
            MesContainerPackage detailPackage = getContainerPackage(model, detail.getBarCode());
            if (detailPackage != null) {
                deleteContainerSnStation(detailPackage, model.getUserName());
            }
        }
        containerPackageDetailRDao.deleteWeaklyByIds(deleteDetailIds.toArray(new Long[0]), model.getUserName());

        containerPackage.setPackageStatus(MesExtEnumUtil.CONTAINER_PACKAGE_STATUS.STATUS_30.getValue());
        ConvertBean.serviceModelUpdate(containerPackage, model.getUserName());
        containerPackageRDao.update(containerPackage);

        model.setPackageDetails(getContainerPackageDetail(model, containerSn));
        return model;
    }

    @Override
    public MesContainerSnBindModel doClosePackage(MesContainerSnBindModel model) {
        //校验容器条码
        MesContainerSn containerSn = containerSnService.checkContainerSn(model.getOrganizeCode(), model.getContainerSn());
        MesContainerPackage containerPackage = getContainerPackage(model, containerSn.getContainerSn());
        if (containerPackage == null) {
            MesPcnException.throwMesBusiException("容器条码[%s]未生成容器条码上料主表，请检查数据", containerSn.getContainerSn());
        }
        if (Objects.equals(containerPackage.getPackageStatus(), MesExtEnumUtil.CONTAINER_PACKAGE_STATUS.STATUS_20.getValue())) {
            MesPcnException.throwMesBusiException("容器条码[%s]已关箱，请务重复操作！", containerSn.getContainerSn());
        }
        List<MesContainerPackageDetail> packageDetails = containerPackageDetailRDao.findByProperty(
                new String[]{MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_DELETED, MesPcnExtConstWords.IS_VALID, "pid"},
                new Object[]{model.getOrganizeCode(), CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), CommonEnumUtil.IS_VAILD.VAILD.getValue(), containerPackage.getId()});
        for (MesContainerPackageDetail detail : packageDetails) {
            MesContainerPackageDetailBinding binding = createContainerPackageDetailBinding(detail, MesExtEnumUtil.CONTAINER_RAW_SN_BINDING_STATUS.CLOSE, model.getUserName());
            detailBindingRDao.insert(binding);
        }

        containerPackage.setPackageStatus(MesExtEnumUtil.CONTAINER_PACKAGE_STATUS.STATUS_20.getValue());
        ConvertBean.serviceModelUpdate(containerPackage, model.getUserName());
        containerPackageRDao.update(containerPackage);

        model.setPackageDetails(packageDetails);
        return model;
    }

    @Override
    public MesContainerSnBindModel doOpenPackage(MesContainerSnBindModel model) {
        //校验容器条码
        MesContainerSn containerSn = containerSnService.checkContainerSn(model.getOrganizeCode(), model.getContainerSn());
        MesContainerPackage containerPackage = getContainerPackage(model, containerSn.getContainerSn());
        if (containerPackage == null) {
            MesPcnException.throwMesBusiException("容器条码[%s]未生成容器条码上料主表，请检查数据", containerSn.getContainerSn());
        }
        if (!Objects.equals(containerPackage.getPackageStatus(), MesExtEnumUtil.CONTAINER_PACKAGE_STATUS.STATUS_20.getValue())) {
            MesPcnException.throwMesBusiException("容器条码[%s]未关箱，不能执行开箱操作！", containerSn.getContainerSn());
        }
        MesContainerPackageDetail packageDetail = containerPackageDetailRDao.getByProperty(new String[]{MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_DELETED, MesPcnExtConstWords.IS_VALID, "barCode"},
                new Object[]{model.getOrganizeCode(), CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), CommonEnumUtil.IS_VAILD.VAILD.getValue(), containerSn.getContainerSn()});
        if (packageDetail != null) {
            MesPcnException.throwMesBusiException("容器条码[%s]已被容器[%s]绑定，请先解绑再执行开箱操作！", containerSn.getContainerSn(), packageDetail.getContainerSn());
        }

        containerPackage.setPackageStatus(MesExtEnumUtil.CONTAINER_PACKAGE_STATUS.STATUS_10.getValue());
        ConvertBean.serviceModelUpdate(containerPackage, model.getUserName());
        containerPackageRDao.update(containerPackage);
        return model;
    }

    private MesContainerPartsModel checkProduceSn(String organizeCode, MesContainerSn containerSn, String barCode, String selectPartNo) {
        MesContainerPartsModel model;
        MesProduceSn produceSn = produceSnRDao.getByProperty(
                new String[]{MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_DELETED, MesPcnExtConstWords.IS_VALID, "productSn"},
                new Object[]{organizeCode, CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), CommonEnumUtil.IS_VAILD.VAILD.getValue(), barCode});
        if (produceSn != null) {
            // 自制件条码
            model = getContainerPartsByPartNo(organizeCode, containerSn.getContainerTypeCode(), containerSn.getContainerSn(), produceSn.getPartNo());
            if (model != null) {
                model.setBarcode(barCode);
            }
        } else {
            model = getContainerPartsByBarcode(organizeCode, containerSn.getContainerTypeCode(), containerSn.getContainerSn(), barCode);
            if (model != null) {
                model.setBarcode(barCode);
            }
        }
        if (model == null) {
            MesPcnException.throwMesBusiException("容器条码[%s]或容器类型代码[%s]未维护与零件关系，请检查数据", containerSn.getContainerSn(), containerSn.getContainerTypeCode());
        }
        if (!StringUtils.isEmpty(selectPartNo) && !Objects.equals(selectPartNo, model.getPartNo())) {
            MesPcnException.throwMesBusiException("条码[%s]的零件号【%s】与选择的零件不匹配，请检查数据", barCode, model.getPartNo());
        }

        return model;
    }

    private MesContainerPackage createContainerPackage(MesContainerSnBindModel model, MesContainerType containerType) {
        MesContainerPackage containerPackage = new MesContainerPackage();
        containerPackage.setOrganizeCode(model.getOrganizeCode());
        containerPackage.setContainerSn(model.getContainerSn());
        containerPackage.setPackageStatus(MesExtEnumUtil.CONTAINER_PACKAGE_STATUS.STATUS_10.getValue());
        ConvertBean.serviceModelInitialize(containerPackage, model.getUserName());
        containerPackageRDao.insert(containerPackage);
        return containerPackage;
    }

    private MesContainerPackageDetail createContainerPackageDetail(MesContainerSnBindModel model, MesContainerPackage containerPackage, MesContainerPartsModel containerPart, Map<String, Object> matchResult) {
        Object qty = matchResult.get(MesPcnExtConstWords.QTY);
        Object dataType = matchResult.get(MesPcnExtConstWords.DATA_TYPE);
        MesContainerPackageDetail detail = new MesContainerPackageDetail();
        detail.setOrganizeCode(containerPackage.getOrganizeCode());
        detail.setPid(containerPackage.getId());
        detail.setContainerSn(containerPackage.getContainerSn());
        detail.setPartNo(containerPart.getPartNo());
        detail.setPartName(containerPart.getPartName());
        detail.setBarCode(model.getBarCode());
        detail.setQty(qty != null ? (double)qty : 1);
        detail.setLoadPartTime(TimeTool.getNowTime(true));
        detail.setLotNo(StringUtils.isEmpty(containerPart.getLotNo()) ? TimeTool.getNowTime("yyyy-MM-dd") : containerPart.getLotNo());
        detail.setMatchType(containerPart.getMatchType());
        detail.setDataType(dataType != null ? (int)dataType : null);
        detail.setSnStatus(MesExtEnumUtil.CONTAINER_BARCODE_STATUS.STATUS_10.getValue());

        ConvertBean.serviceModelInitialize(detail, model.getUserName());
        return detail;
    }

    private MesContainerPackageDetailBinding createContainerPackageDetailBinding(MesContainerPackageDetail packageDetail, MesExtEnumUtil.CONTAINER_RAW_SN_BINDING_STATUS snBindingStatus, String userName) {
        MesContainerPackageDetailBinding binding = new MesContainerPackageDetailBinding();
        binding.setOrganizeCode(packageDetail.getOrganizeCode());
        binding.setPid(packageDetail.getPid());
        binding.setSourceId(packageDetail.getId());
        binding.setContainerSn(packageDetail.getContainerSn());
        binding.setBarCode(packageDetail.getBarCode());
        binding.setQty(packageDetail.getQty());
        binding.setSnStatus(snBindingStatus.getValue());
        binding.setMatchType(packageDetail.getMatchType());
        ConvertBean.serviceModelInitialize(binding, userName);
        return binding;
    }

    private void deleteContainerSnStation(MesContainerPackage containerPackage, String userName) {
        final String organizeCode = containerPackage.getOrganizeCode();
        List<MesContainerPackageDetail> bottomPackageDetailList = new ArrayList<>();
        getBottomPackageDetails(organizeCode, containerPackage, bottomPackageDetailList);
        final Set<String> containerSnSet = bottomPackageDetailList.stream().map(MesContainerPackageDetail::getContainerSn).collect(Collectors.toSet());

        DdlPackBean ddlPackBean = DdlPackBean.getDdlPackBean(organizeCode);
        DdlPreparedPack.getInPackList(new ArrayList<>(containerSnSet), "containerSn", ddlPackBean);
        containerSnStationRDao.updateByProperties(
                new String[]{"modifyUser", "modifyDatetime", "isDeleted"},
                new Object[]{userName, TimeTool.getNowTime(true), CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue()},
                ddlPackBean);
    }
}
