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

import cn.estsh.i3plus.ext.mes.pcn.api.base.IShiftExtService;
import cn.estsh.i3plus.ext.mes.pcn.api.base.ISxWorkOrderExtService;
import cn.estsh.i3plus.ext.mes.pcn.api.busi.IPackageExtService;
import cn.estsh.i3plus.ext.mes.pcn.api.busi.IProduceSnExtService;
import cn.estsh.i3plus.ext.mes.pcn.api.busi.IProduceSnRepairService;
import cn.estsh.i3plus.ext.mes.pcn.api.busi.ep.IEpQmsOqcManageService;
import cn.estsh.i3plus.ext.mes.pcn.apiservice.dao.ep.IEpProduceSnExtDao;
import cn.estsh.i3plus.ext.mes.pcn.apiservice.dao.ep.IEpProduceSnRepairDao;
import cn.estsh.i3plus.ext.mes.pcn.pojo.bean.MesPackageExt;
import cn.estsh.i3plus.ext.mes.pcn.pojo.bean.MesProduceSnExt;
import cn.estsh.i3plus.ext.mes.pcn.pojo.bean.MesProduceSnRepair;
import cn.estsh.i3plus.ext.mes.pcn.pojo.bean.MesWorkOrderExt;
import cn.estsh.i3plus.ext.mes.pcn.pojo.model.ep.EpProduceSnExtModel;
import cn.estsh.i3plus.ext.mes.pcn.pojo.model.ep.SxEpOqcManageModel;
import cn.estsh.i3plus.ext.mes.pcn.pojo.util.MesPcnExtConstWords;
import cn.estsh.i3plus.ext.mes.pcn.pojo.util.MesPcnExtEnumUtil;
import cn.estsh.i3plus.platform.common.exception.ImppExceptionEnum;
import cn.estsh.i3plus.pojo.base.enumutil.CommonEnumUtil;
import cn.estsh.i3plus.pojo.base.enumutil.MesPcnEnumUtil;
import cn.estsh.i3plus.pojo.mes.bean.MesPackageDetail;
import cn.estsh.i3plus.pojo.mes.bean.MesShift;
import cn.estsh.impp.framework.boot.exception.ImppExceptionBuilder;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author wangjie
 * @version 1.0
 * @date 2021/1/13 9:28
 **/
@Slf4j
@Service
public class EpQmsOqcManageByPackageNoService implements IEpQmsOqcManageService {

    @Autowired
    private IProduceSnExtService produceSnExtService;

    @Autowired
    private IPackageExtService packageExtService;

    @Autowired
    private IProduceSnRepairService produceSnRepairService;

    @Autowired
    private ISxWorkOrderExtService workOrderExtService;

    @Autowired
    private IShiftExtService shiftExtService;

    @Autowired
    private IEpProduceSnExtDao epProduceSnExtDao;

    @Autowired
    private IEpProduceSnRepairDao epProduceSnRepairDao;

    @Override
    public SxEpOqcManageModel queryEpQmsOqcManageData(Map<String, Object> params) {
        String organizeCode = (String) params.get(MesPcnExtConstWords.ORGANIZE_CODE);
        String sn = (String) params.get(MesPcnExtConstWords.SN);
        String dataType = (String) params.get(MesPcnExtConstWords.DATA_TYPE);
        return queryEpQmsOqcManageData(organizeCode, sn, dataType);
    }

    public SxEpOqcManageModel queryEpQmsOqcManageData(String organizeCode, String packageNo, String dataType) {
        log.info("EP-QMS获取出货检验正常检验主数据,参数organizeCode[{}]packageNo[{}]dataType[{}]", organizeCode, packageNo, dataType);

        MesPackageExt packageExt = getPackageNo(organizeCode, packageNo);

        SxEpOqcManageModel result = new SxEpOqcManageModel();

        List<MesPackageDetail> packageDetailList = packageExtService.getPackageDetailListByPackNo(organizeCode, packageExt.getPackageNo());

        checkPackageIsSealed(packageExt, packageDetailList);

        MesWorkOrderExt workOrderExt = getWorkOrder(organizeCode, packageExt);

        packModelByWorkOrder(result, workOrderExt);

        if (CollectionUtils.isEmpty(packageDetailList)) return packModelIsNotExistSerial(result, packageExt, organizeCode, dataType);

        if (packageDetailList.size() == 1) return packModelIsSingleOneSerial(result, packageDetailList.get(0), workOrderExt, organizeCode, dataType);

        return packModelIsManySerial(result, workOrderExt, packageNo, organizeCode, dataType);

    }

    private MesWorkOrderExt getWorkOrder(String organizeCode, MesPackageExt packageExt) {
        MesWorkOrderExt workOrderExt = workOrderExtService.getWorkOrderExtByOrderNo(organizeCode, packageExt.getLastWorkOrderNo());
        if (null == workOrderExt) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION.getCode())
                    .setErrorDetail("包装条码[%s]关联的生产工单[%s]不存在,请重新扫描", packageExt.getPackageNo(), packageExt.getLastWorkOrderNo())
                    .build();
        }
        return workOrderExt;
    }


    private SxEpOqcManageModel packModelIsNotExistSerial(SxEpOqcManageModel result, MesPackageExt packageExtDb, String organizeCode, String dataType) {

        result.setWorkTime(packageExtDb.getWorkTime());
        result.setShiftCode(packageExtDb.getShiftCode());
        packModelByShiftCode(result, organizeCode);
        result.setPackageNo(packageExtDb.getPackageNo());

        log.info("EP-QMS获取出货检验正常检验主数据,结果result[{}]", JSONObject.toJSONString(result));

        if (CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValueStr().equals(dataType)) return result;

        log.info("EP-QMS获取出货检验正常检验条码数据,参数organizeCode[{}]packageNo[{}],没有产品条码情况", organizeCode, result.getPackageNo());

        List<MesPackageExt> packageExtList = packageExtService.getPackageListByWorkOrderNo(organizeCode, result.getWorkOrderNo());
        packageExtList = CollectionUtils.isEmpty(packageExtList) ? null :
                packageExtList.stream().filter(o -> (null != o &&
                        !StringUtils.isEmpty(o.getWorkTime()) && !StringUtils.isEmpty(o.getShiftCode()) &&
                        MesPcnEnumUtil.PACKAGE_IS_SEALED.SEALED.getValue() == o.getIsSealed() &&
                        o.getWorkTime().equals(result.getWorkTime()) && o.getShiftCode().equals(result.getShiftCode()))).collect(Collectors.toList());

        if (!CollectionUtils.isEmpty(packageExtList)) {
            List<Map<String, String>> prodMapList = new ArrayList<>();
            packageExtList = packageExtList.stream().filter(o -> null != o).sorted(Comparator.comparing(MesPackageExt::getCreateDatetime)).collect(Collectors.toList());
            for (MesPackageExt packageExt : packageExtList) {
                if (null == packageExt) {
                    continue;
                }
                Map<String, String> prodMap = new HashMap<>();
                prodMap.put(MesPcnExtConstWords.PACKAGENO, packageExt.getPackageNo());
                prodMap.put(MesPcnExtConstWords.QTY, packageExt.getQty().toString());
                prodMapList.add(prodMap);

                if (packageExt.getPackageNo().equals(result.getPackageNo())) break;

            }

            result.setProdMapList(prodMapList);

        }

        log.info("EP-QMS获取出货检验正常检验条码数据,结果result[{}]", JSONObject.toJSONString(result));

        return result;
    }

    private void packModelByWorkOrder(SxEpOqcManageModel result, MesWorkOrderExt workOrderExt) {
        result.setWorkOrderNo(workOrderExt.getOrderNo());
        result.setPartNo(workOrderExt.getPartNo());
        result.setPartNameRdd(workOrderExt.getPartNameRdd());
        result.setWorkCenterCode(workOrderExt.getWorkCenterCode());
        result.setWorkOrderType(workOrderExt.getWorkOrderType());
        result.setWorkOrderTypeName(MesPcnExtEnumUtil.WORK_ORDER_TYPE.valueOfDescription(workOrderExt.getWorkOrderType()));
        result.setQty(workOrderExt.getQty());
        result.setCompleteQty(workOrderExt.getCompleteQty());
    }

    private void packModelByShiftCode(SxEpOqcManageModel result, String organizeCode) {
        MesShift shiftDb = shiftExtService.getShiftByShiftCodeAndCenterCode(organizeCode, result.getWorkCenterCode(), result.getShiftCode());
        if (null != shiftDb) {
            result.setShiftName(shiftDb.getShiftName());
        }
    }

    private SxEpOqcManageModel packModelIsSingleOneSerial(SxEpOqcManageModel result, MesPackageDetail packageDetail, MesWorkOrderExt workOrderExt, String organizeCode, String dataType) {

        EpProduceSnExtModel produceSn = getProduceSnExtBySn(organizeCode, packageDetail, result);

        if (MesPcnExtEnumUtil.WORK_ORDER_TYPE.REWORK_ORDER.getValue() != workOrderExt.getWorkOrderType()) {
            result.setWorkTime(produceSn.getWorkTime());
            result.setShiftCode(produceSn.getShiftCode());
        } else {
            MesProduceSnRepair produceSnRepair = getProduceSnRepair(organizeCode, produceSn.getSerialNumber(), workOrderExt.getOrderNo(), packageDetail.getPackageNo());
            result.setWorkTime(produceSnRepair.getWorkTime());
            result.setShiftCode(produceSnRepair.getShiftCode());
        }

        result.setPackageNo(packageDetail.getPackageNo());
        result.setSerialNumber(produceSn.getSerialNumber());
        packModelByShiftCode(result, organizeCode);

        log.info("EP-QMS获取出货检验正常检验主数据,结果result[{}]", JSONObject.toJSONString(result));

        if (CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValueStr().equals(dataType)) return result;

        log.info("EP-QMS获取出货检验正常检验条码数据,参数organizeCode[{}]packageNo[{}],包装与产品一对一情况", organizeCode, result.getPackageNo());

        List<EpProduceSnExtModel> produceSnList = getProduceSnList(result, organizeCode);

        packProdMapList(result, produceSnList, result.getSerialNumber());

        log.info("EP-QMS获取出货检验正常检验条码数据,结果result[{}]", JSONObject.toJSONString(result));

        return result;
    }

    private void packProdMapList(SxEpOqcManageModel result, List<EpProduceSnExtModel> produceSnList, String serialNumber) {

        if (!CollectionUtils.isEmpty(produceSnList)) {

            produceSnList = produceSnList.stream().filter(o -> null != o).sorted(Comparator.comparing(EpProduceSnExtModel::getOutWorkCenterTime)).collect(Collectors.toList());

            List<Map<String, String>> prodMapList = new ArrayList<>();

            for (EpProduceSnExtModel produce : produceSnList) {
                if (null == produce) {
                    continue;
                }

                Map<String, String> prodMap = new HashMap<>();
                prodMap.put(MesPcnExtConstWords.PACKAGENO, produce.getPackageNo());
                prodMap.put(MesPcnExtConstWords.SERIAL_NUMBER, produce.getSerialNumber());
                prodMapList.add(prodMap);

                if (produce.getSerialNumber().equals(serialNumber)) break;

            }

            result.setProdMapList(prodMapList);
        }

    }

    private MesProduceSnRepair getProduceSnRepair(String organizeCode, String serialNumber, String workOrderNo, String packageNo) {
        MesProduceSnRepair produceSnRepair = produceSnRepairService.getProduceSnRepairBySnAndOrderNo(organizeCode, serialNumber, workOrderNo);
        if (null == produceSnRepair) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION.getCode())
                    .setErrorDetail("包装条码[%s]对应的产品条码[%s]关联返工工单[%s]的记录不存在,请重新扫描", packageNo, serialNumber, workOrderNo)
                    .build();
        }
        return produceSnRepair;
    }

    private EpProduceSnExtModel getProduceSnExtBySn(String organizeCode, MesPackageDetail packageDetail, SxEpOqcManageModel result) {
        EpProduceSnExtModel produceSn = null;
        MesProduceSnExt produceSnExt = produceSnExtService.getProduceSnExtBySn(organizeCode, packageDetail.getSerialNumber());
        if (null != produceSnExt) {
            produceSn = new EpProduceSnExtModel();
            BeanUtils.copyProperties(produceSnExt, produceSn);
        }
        checkProduceSn(produceSn, packageDetail.getPackageNo());

        if (!produceSn.getLastWorkOrderNo().equals(result.getWorkOrderNo())) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION.getCode())
                    .setErrorDetail("包装条码[%s]关联的产品条码[%s]当前未关联%s[%s],请重新扫描",
                            packageDetail.getPackageNo(), produceSn.getSerialNumber(), MesPcnExtEnumUtil.WORK_ORDER_TYPE.valueOfDescription(result.getWorkOrderType()), result.getWorkOrderNo())
                    .build();
        }
        return produceSn;
    }

    private void checkProduceSn(EpProduceSnExtModel produceSn, String packageNo) {
        if (null == produceSn) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION.getCode())
                    .setErrorDetail("包装条码[%s]关联的产品条码[%s]不存在,请重新扫描", packageNo, produceSn.getSerialNumber())
                    .build();
        }
        if (MesPcnExtEnumUtil.PRODUCE_SN_STATUS.OFFLINE.getValue() != produceSn.getSnStatus() &&
                MesPcnExtEnumUtil.PRODUCE_SN_STATUS.ERR_OFFLINE.getValue() != produceSn.getSnStatus()) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION.getCode())
                    .setErrorDetail("包装条码[%s]关联的产品条码[%s]未打包下线,请重新扫描", packageNo, produceSn.getSerialNumber())
                    .build();
        }
        if (MesPcnExtEnumUtil.PRODUCE_SN_QC_STATUS.QUALIFIED.getValue() != produceSn.getQcStatus()) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION.getCode())
                    .setErrorDetail("包装条码[%s]关联的产品条码[%s]质量状态[%s],请重新扫描",
                            packageNo, produceSn.getSerialNumber(), MesPcnExtEnumUtil.PRODUCE_SN_QC_STATUS.valueOfDescription(produceSn.getQcStatus()))
                    .build();
        }
    }

    private void checkProduceSnList(List<EpProduceSnExtModel> produceSnList, String packageNo) {
        if (CollectionUtils.isEmpty(produceSnList)) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION.getCode())
                    .setErrorDetail("包装条码[%s]关联的多个产品条码信息在产品条码表中不存在,请重新扫描", packageNo)
                    .build();
        }
        for (EpProduceSnExtModel produceSn : produceSnList) {
            if (null == produceSn) {
                continue;
            }
            checkProduceSn(produceSn, packageNo);
        }

        Map<String, List<EpProduceSnExtModel>> workTimeMap = produceSnList.stream()
                .filter(o -> (null != o && !StringUtils.isEmpty(o.getWorkTime()) && !StringUtils.isEmpty(o.getShiftCode())))
                .collect(Collectors.groupingBy(o -> new StringJoiner(MesPcnExtConstWords.AND).add(o.getWorkTime()).add(o.getShiftCode()).toString()));
        if (CollectionUtils.isEmpty(workTimeMap)) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION.getCode())
                    .setErrorDetail("包装条码[%s]关联的所有产品条码信息未关联作业时间与班次代码,请重新扫描", packageNo)
                    .build();
        }
        if (workTimeMap.size() != 1) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION.getCode())
                    .setErrorDetail("包装条码[%s]关联的多个产品条码非同一个批次,请选择[产品条码]扫描模式,重新扫描该包装条码关联的产品条码", packageNo)
                    .build();
        }
    }

    private SxEpOqcManageModel packModelIsManySerial(SxEpOqcManageModel result, MesWorkOrderExt workOrderExt, String packageNo, String organizeCode, String dataType) {
        List<EpProduceSnExtModel> produceSnListByPackNo;

        if (MesPcnExtEnumUtil.WORK_ORDER_TYPE.REWORK_ORDER.getValue() != workOrderExt.getWorkOrderType()) {
            produceSnListByPackNo = epProduceSnExtDao.querySnListByPackNo4Oqc(organizeCode, packageNo);
            produceSnListByPackNo = CollectionUtils.isEmpty(produceSnListByPackNo) ? null :
                    produceSnListByPackNo.stream().filter(o -> (null != o && o.getLastWorkOrderNo().equals(workOrderExt.getOrderNo()))).collect(Collectors.toList());
        } else {
            produceSnListByPackNo = epProduceSnRepairDao.querySnRepairListByPackNo4Oqc(organizeCode, packageNo, workOrderExt.getOrderNo());
        }

        checkProduceSnList(produceSnListByPackNo, packageNo);

        result.setWorkTime(produceSnListByPackNo.get(0).getWorkTime());
        result.setShiftCode(produceSnListByPackNo.get(0).getShiftCode());
        result.setPackageNo(packageNo);
        result.setSerialNumberList(produceSnListByPackNo.stream()
                .filter(o -> (null != o && !StringUtils.isEmpty(o.getSerialNumber()))).map(EpProduceSnExtModel::getSerialNumber).collect(Collectors.toList()));

        log.info("EP-QMS获取出货检验正常检验主数据,结果result[{}]", JSONObject.toJSONString(result));

        if (CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValueStr().equals(dataType)) return result;

        log.info("EP-QMS获取出货检验正常检验条码数据,参数organizeCode[{}]packageNo[{}],包装与产品一对多情况", organizeCode, result.getPackageNo());

        List<EpProduceSnExtModel> produceSnList = getProduceSnList(result, organizeCode);

        produceSnListByPackNo = produceSnListByPackNo.stream().filter(o -> null != o)
                .sorted(Comparator.comparing(EpProduceSnExtModel::getOutWorkCenterTime).reversed()).collect(Collectors.toList());

        packProdMapList(result, produceSnList, produceSnListByPackNo.get(0).getSerialNumber());

        log.info("EP-QMS获取出货检验正常检验条码数据,结果result[{}]", JSONObject.toJSONString(result));

        return result;
    }

    private List<EpProduceSnExtModel> getProduceSnList(SxEpOqcManageModel result, String organizeCode) {
        List<EpProduceSnExtModel> produceSnList;
        if (MesPcnExtEnumUtil.WORK_ORDER_TYPE.REWORK_ORDER.getValue() != result.getWorkOrderType()) {
            produceSnList = epProduceSnExtDao.querySnListByOrderNoAndWorkTime4Oqc(organizeCode, result.getWorkOrderNo(), result.getWorkTime(), result.getShiftCode());
            produceSnList = CollectionUtils.isEmpty(produceSnList) ? null :
                    produceSnList.stream().filter(o -> (null != o && o.getWorkOrderNo().equals(o.getLastWorkOrderNo()))).collect(Collectors.toList());
        } else {
            produceSnList = epProduceSnRepairDao.querySnRepairListByOrderNoAndWorkTime4Oqc(organizeCode, result.getWorkOrderNo(), result.getWorkTime(), result.getShiftCode());
        }
        return produceSnList;
    }

    private MesPackageExt getPackageNo(String organizeCode, String packageNo) {
        MesPackageExt packageExt = packageExtService.getPackageByPackNo(organizeCode, packageNo);
        if (null == packageExt) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION.getCode())
                    .setErrorDetail("包装条码[%s]不存在,请重新扫描", packageNo)
                    .build();
        }
        return packageExt;
    }

    private void checkPackageIsSealed(MesPackageExt packageExt, List<MesPackageDetail> packageDetailList) {
        if (MesPcnEnumUtil.PACKAGE_IS_SEALED.UNSEALED.getValue() == packageExt.getIsSealed()) {
            if (CollectionUtils.isEmpty(packageDetailList)) {
                throw ImppExceptionBuilder.newInstance()
                        .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                        .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION.getCode())
                        .setErrorDetail("包装条码[%s]未封箱,请重新扫描", packageExt.getPackageNo())
                        .build();
            } else {
                throw ImppExceptionBuilder.newInstance()
                        .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                        .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION.getCode())
                        .setErrorDetail("包装条码[%s]未封箱,请选择[产品条码]扫描模式,重新扫描该包装条码关联的产品条码", packageExt.getPackageNo())
                        .build();
            }
        }
    }

}
