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

import cn.estsh.i3plus.ext.mes.api.busi.bu3.ISxWorkCenterNotReachReasonService;
import cn.estsh.i3plus.ext.mes.pojo.bean.MesOutPutStatistics;
import cn.estsh.i3plus.ext.mes.pojo.bean.MesShiftProdCenterRecord;
import cn.estsh.i3plus.ext.mes.pojo.bean.MesWorkCenterExt;
import cn.estsh.i3plus.ext.mes.pojo.bean.MesWorkCenterNotReachReason;
import cn.estsh.i3plus.ext.mes.pojo.repository.MesOutPutStatisticsRepository;
import cn.estsh.i3plus.ext.mes.pojo.repository.MesShiftProdCenterRecordRepository;
import cn.estsh.i3plus.ext.mes.pojo.repository.MesWorkCenterExtRepository;
import cn.estsh.i3plus.ext.mes.pojo.repository.MesWorkCenterNotReachReasonRepository;
import cn.estsh.i3plus.ext.mes.pojo.util.MesExtConstWords;
import cn.estsh.i3plus.ext.mes.pojo.util.MesExtEnumUtil;
import cn.estsh.i3plus.platform.common.convert.ConvertBean;
import cn.estsh.i3plus.platform.common.tool.MathOperation;
import cn.estsh.i3plus.platform.common.tool.TimeTool;
import cn.estsh.i3plus.pojo.base.bean.DdlPackBean;
import cn.estsh.i3plus.pojo.base.bean.ListPager;
import cn.estsh.i3plus.pojo.base.common.Pager;
import cn.estsh.i3plus.pojo.base.common.PagerHelper;
import cn.estsh.i3plus.pojo.base.enumutil.CommonEnumUtil;
import cn.estsh.i3plus.pojo.base.tool.DdlPreparedPack;
import cn.estsh.i3plus.pojo.mes.bean.MesWorkOrder;
import cn.estsh.i3plus.pojo.mes.repository.MesWorkOrderRepository;
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.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @PROJECT_NAME: i3plus-mes-panasonic-dev
 * @DESCRIPTION:
 * @USER: xinwang.yi
 * @DATE: 2023-07-26 18:09
 */
@Service
public class SxWorkCenterNotReachReasonService implements ISxWorkCenterNotReachReasonService {

    @Autowired
    private MesWorkOrderRepository workOrderRepository;

    @Autowired
    private MesOutPutStatisticsRepository outPutStatisticsRepository;

    @Autowired
    private MesWorkCenterNotReachReasonRepository workCenterNotReachReasonRepository;

    @Autowired
    private MesWorkCenterExtRepository workCenterExtRepository;

    @Autowired
    private MesShiftProdCenterRecordRepository shiftProdCenterRecordRepository;

    @Override
    public ListPager<MesWorkCenterNotReachReason> querySxWorkCenterNotReachReasonByPager(MesWorkCenterNotReachReason notReachReason, Pager pager) {

        List<MesWorkCenterNotReachReason> workCenterNotReachReasonList = new ArrayList<>();

        Map<String, MesWorkCenterExt> workCenterMap = getWorkCenterMap(notReachReason.getWorkCenterCode());

        //if (notReachReason.getWorkTime().compareTo(TimeTool.getToday()) < 0) {
        //    workCenterMap.keySet();
        //}

        Map<String, MesWorkCenterNotReachReason> workCenterNotReachReasonMapByWorkCenterCode = getMesWorkCenterNotReachReasonMapByWorkCenterCode(notReachReason.getWorkCenterCode(), notReachReason.getWorkTime(), notReachReason.getOrganizeCode());

        Map<String, List<MesWorkOrder>> workOrderMapByWorkCenterCode = getMesWorkOrderMapByWorkCenterCode(notReachReason.getWorkCenterCode(), notReachReason.getWorkTime(), notReachReason.getOrganizeCode());

        Map<String, List<MesOutPutStatistics>> outPutStatisticsMapByWorkCenterCode = getMesOutPutStatisticsMapByWorkCenterCode(notReachReason.getWorkCenterCode(), notReachReason.getWorkTime(), notReachReason.getOrganizeCode());

        List<String> workCenterCodeList = getWorkCenterCodeList(workOrderMapByWorkCenterCode, outPutStatisticsMapByWorkCenterCode);

        //if (!CollectionUtils.isEmpty(workCenterCodeList)) {
        //    DdlPackBean packBean = DdlPackBean.getDdlPackBean(notReachReason.getOrganizeCode());
        //    DdlPreparedPack.getInPackList(workCenterCodeList, MesExtConstWords.WORK_CENTER_CODE, packBean);
        //    DdlPreparedPack.timeBuilder(notReachReason.getWorkTime(), TimeTool.getToday(), MesExtConstWords.WORK_TIME, packBean, false);
        //    List<MesShiftProdCenterRecord> shiftProdCenterRecordList = shiftProdCenterRecordRepository.findByHqlWhere(packBean);
        //    shiftProdCenterRecordList.stream().collect(Collectors.toMap(MesShiftProdCenterRecord::getWorkCenterCode, Function.identity()));
        //}

        for (String workCenterCode : workCenterCodeList) {

            //if (TimeTool.getToday().compareTo(notReachReason.getWorkTime()) > 1) {
            //    if (workCenterNotReachReasonMapByWorkCenterCode.containsKey(workCenterCode)) workCenterNotReachReasonList.add(workCenterNotReachReasonMapByWorkCenterCode.get(workCenterCode));
            //    continue;
            //}

            if (workCenterMap.containsKey(workCenterCode)) {
                MesWorkCenterExt workCenterExt = workCenterMap.get(workCenterCode);
                // 判定是否查询作业日期是否为昨天 为昨天则 实时统计 反之查产线未达成原因
                if (TimeTool.getToday().compareTo(notReachReason.getWorkTime()) > 1) {
                    if (workCenterNotReachReasonMapByWorkCenterCode.containsKey(workCenterCode)) workCenterNotReachReasonList.add(workCenterNotReachReasonMapByWorkCenterCode.get(workCenterCode));
                } else {
                    addWorkCenterNotReachReasonList(notReachReason.getWorkTime(), workCenterNotReachReasonList, workCenterNotReachReasonMapByWorkCenterCode, workOrderMapByWorkCenterCode, outPutStatisticsMapByWorkCenterCode, workCenterCode);
                }
            } else {
                workCenterNotReachReasonList.add(workCenterNotReachReasonMapByWorkCenterCode.get(workCenterCode));
            }
        }

        pager = PagerHelper.getPager(pager, workCenterNotReachReasonList.size());
        return new ListPager<>(workCenterNotReachReasonList.stream().skip((long) (pager.getCurrentPage() - 1) * pager.getPageSize()).limit(pager.getPageSize()).collect(Collectors.toList()), pager);
    }

    @Override
    public void doStatisticsWorkCenterNotReachReason(String organizeCode, String workTime) {

        List<MesWorkCenterNotReachReason> workCenterNotReachReasonList = new ArrayList<>();

        Map<String, MesWorkCenterNotReachReason> workCenterNotReachReasonMapByWorkCenterCode = getMesWorkCenterNotReachReasonMapByWorkCenterCode("", workTime, organizeCode);

        Map<String, List<MesWorkOrder>> workOrderMapByWorkCenterCode = getMesWorkOrderMapByWorkCenterCode("", workTime, organizeCode);

        Map<String, List<MesOutPutStatistics>> outPutStatisticsMapByWorkCenterCode = getMesOutPutStatisticsMapByWorkCenterCode("", workTime, organizeCode);

        List<String> workCenterCodeList = getWorkCenterCodeList(workOrderMapByWorkCenterCode, outPutStatisticsMapByWorkCenterCode);

        for (String workCenterCode : workCenterCodeList) {
            addWorkCenterNotReachReasonList(workTime, workCenterNotReachReasonList, workCenterNotReachReasonMapByWorkCenterCode, workOrderMapByWorkCenterCode, outPutStatisticsMapByWorkCenterCode, workCenterCode);
        }

        if (!CollectionUtils.isEmpty(workCenterNotReachReasonList)) {
            workCenterNotReachReasonList.forEach(o -> {
                ConvertBean.serviceModelInitialize(o, "JOB");
                o.setSystemSyncStatus(MesExtEnumUtil.IF_SYNC_STATUS.NO_SYNC.getValue());
                o.setOrganizeCode(organizeCode);
            });
            workCenterNotReachReasonRepository.saveAll(workCenterNotReachReasonList);
        }
    }

    public Map<String, MesWorkCenterNotReachReason> getMesWorkCenterNotReachReasonMapByWorkCenterCode(String workCenterCode, String workTime, String organizeCode) {
        List<MesWorkCenterNotReachReason> mesWorkCenterNotReachReasonDbList = getMesWorkCenterNotReachReasonList(workCenterCode, workTime, organizeCode);
        return mesWorkCenterNotReachReasonDbList.stream().collect(Collectors.toMap(MesWorkCenterNotReachReason::getWorkCenterCode, Function.identity(), (x, y) -> y));
    }

    public Map<String, List<MesWorkOrder>> getMesWorkOrderMapByWorkCenterCode(String workCenterCode, String workTime, String organizeCode) {
        List<MesWorkOrder> workOrderDbList = getMesWorkOrderList(organizeCode, workTime, workCenterCode);
        if (!CollectionUtils.isEmpty(workOrderDbList)) workOrderDbList = workOrderDbList.stream().filter(o -> o.getWorkOrderStatus() != MesExtEnumUtil.WORK_ORDER_STATUS.REMOVE.getValue() || o.getWorkOrderStatus() != MesExtEnumUtil.WORK_ORDER_STATUS.CREATE.getValue() || o.getWorkOrderType() != MesExtEnumUtil.WORK_ORDER_TYPE.REWORK_ORDER.getValue() ).collect(Collectors.toList());
        return CollectionUtils.isEmpty(workOrderDbList) ? null : workOrderDbList.stream().collect(Collectors.groupingBy(MesWorkOrder::getWorkCenterCode));
    }

    public Map<String, List<MesOutPutStatistics>> getMesOutPutStatisticsMapByWorkCenterCode(String workCenterCode, String workTime, String organizeCode) {
        List<MesOutPutStatistics> outPutStatisticsList = getOutPutStatisticsList(workCenterCode, workTime, organizeCode);
        return outPutStatisticsList.stream().collect(Collectors.groupingBy(MesOutPutStatistics::getWorkCenterCode));
    }

    public void addWorkCenterNotReachReasonList(String workTime, List<MesWorkCenterNotReachReason> workCenterNotReachReasonList,
                                                 Map<String, MesWorkCenterNotReachReason> workCenterNotReachReasonMapByWo,
                                                 Map<String, List<MesWorkOrder>> workOrderMapByWorkCenterCode,
                                                 Map<String, List<MesOutPutStatistics>> outPutStatisticsByWorkCenterCodeMap,
                                                 String workCenterCode) {

        List<MesWorkOrder> workOrderList = CollectionUtils.isEmpty(workOrderMapByWorkCenterCode) ? null : workOrderMapByWorkCenterCode.get(workCenterCode);

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

        List<MesOutPutStatistics> outPutStatisticsByWorkCenterCode = CollectionUtils.isEmpty(outPutStatisticsByWorkCenterCodeMap) ? null : outPutStatisticsByWorkCenterCodeMap.get(workCenterCode);

        Map<String, MesWorkOrder> workOrderMapByOrderNo = CollectionUtils.isEmpty(workOrderList) ? new HashMap<>(0) : workOrderList.stream().collect(Collectors.toMap(MesWorkOrder::getOrderNo, Function.identity(), (x, y) -> y));

        Map<String, List<MesOutPutStatistics>> outPutStatisticsMapByOrderNo = CollectionUtils.isEmpty(outPutStatisticsByWorkCenterCode) ? new HashMap<>(0) : outPutStatisticsByWorkCenterCode.stream().collect(Collectors.groupingBy(MesOutPutStatistics::getWorkOrderNo));

        if (!CollectionUtils.isEmpty(workOrderMapByOrderNo)) workOrderNoList.addAll(workOrderMapByOrderNo.keySet());

        if (!CollectionUtils.isEmpty(outPutStatisticsMapByOrderNo)) workOrderNoList.addAll(outPutStatisticsMapByOrderNo.keySet());

        workOrderNoList = workOrderNoList.stream().distinct().collect(Collectors.toList());

        Map<String, Double> planQtyMapByPartNo = new HashMap<>();
        Map<String, Double> completeQtyMapByPartNo = new HashMap<>();

        for (String workOrderNo : workOrderNoList) {

            if (outPutStatisticsMapByOrderNo.containsKey(workOrderNo)) {

                List<MesOutPutStatistics> outPutStatisticsByWorkOrderNo = outPutStatisticsMapByOrderNo.get(workOrderNo);

                List<MesOutPutStatistics> useOutPutStatisticsQtyList = CollectionUtils.isEmpty(outPutStatisticsByWorkOrderNo) ? new ArrayList<>(0) : outPutStatisticsByWorkOrderNo.stream().filter(o -> CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValueStr().equals(o.getOutPutFlag())).collect(Collectors.toList());

                String partNo;
                if (CollectionUtils.isEmpty(useOutPutStatisticsQtyList) && workOrderMapByOrderNo.containsKey(workOrderNo)) {

                    MesWorkOrder workOrder = workOrderMapByOrderNo.get(workOrderNo);
                    partNo = workOrder.getPartNo();

                    planQtyMapByPartNo.put(workOrder.getPartNo(), MathOperation.add(planQtyMapByPartNo.getOrDefault(workOrder.getPartNo(), 0.0), workOrder.getQty()));

                } else if(CollectionUtils.isEmpty(useOutPutStatisticsQtyList) && !workOrderMapByOrderNo.containsKey(workOrderNo)) {

                    partNo = outPutStatisticsByWorkOrderNo.get(0).getPartNo();

                    planQtyMapByPartNo.put(partNo, MathOperation.add(planQtyMapByPartNo.getOrDefault(partNo, 0.0), 0));

                } else {

                    partNo = useOutPutStatisticsQtyList.get(0).getPartNo();

                    planQtyMapByPartNo.put(partNo, MathOperation.add(planQtyMapByPartNo.getOrDefault(partNo, 0.0), useOutPutStatisticsQtyList.stream().mapToDouble(MesOutPutStatistics::getQty).sum()));
                }

                completeQtyMapByPartNo.put(partNo, MathOperation.add(completeQtyMapByPartNo.getOrDefault(partNo, 0.0),
                        MathOperation.sub(outPutStatisticsByWorkOrderNo.stream().mapToDouble(MesOutPutStatistics::getQty).sum(),
                                outPutStatisticsByWorkOrderNo.stream().mapToDouble(MesOutPutStatistics::getRemakeQty).sum())));

            } else {
                MesWorkOrder workOrder = workOrderMapByOrderNo.get(workOrderNo);
                planQtyMapByPartNo.put(workOrder.getPartNo(), MathOperation.add(planQtyMapByPartNo.getOrDefault(workOrder.getPartNo(), 0.0), workOrder.getQty()));
                if (!completeQtyMapByPartNo.containsKey(workOrder.getPartNo())) completeQtyMapByPartNo.put(workOrder.getPartNo(), 0.0);
            }
        }

                    /* 完结率=完成个数➗总个数
                       产量➗计划数>=95% 算完成，按物料编码分组。*/
        int completeCount = 0;
        for (Map.Entry<String, Double> completeQtyEntry : completeQtyMapByPartNo.entrySet()) {
            if (Double.compare(MathOperation.div(completeQtyEntry.getValue(), planQtyMapByPartNo.get(completeQtyEntry.getKey())), 0.95) >= 0) completeCount++;
        }

        // 完结率
        BigDecimal completeRate = divThenMul100Format2RoundHalfUp((double)completeCount, (double)completeQtyMapByPartNo.keySet().size());

        // 总计划数量
        double totalPlanQty = planQtyMapByPartNo.values().stream().mapToDouble(Double::doubleValue).sum();
        // 总完成数量
        double totalCompleteQty = completeQtyMapByPartNo.values().stream().mapToDouble(Double::doubleValue).sum();

        // 达成率
        BigDecimal yieldRate = divThenMul100Format2RoundHalfUp(totalCompleteQty, totalPlanQty);

        MesWorkCenterNotReachReason workCenterNotReachReasonDb = workCenterNotReachReasonMapByWo.get(workCenterCode);

        MesWorkCenterNotReachReason workCenterNotReachReason = new MesWorkCenterNotReachReason();
        workCenterNotReachReason.setWorkTime(workTime);
        workCenterNotReachReason.setWorkCenterCode(workCenterCode);
        workCenterNotReachReason.setPlanQty(totalPlanQty);
        workCenterNotReachReason.setCompleteQty(totalCompleteQty);
        workCenterNotReachReason.setPartNo(workCenterNotReachReasonDb != null && !StringUtils.isEmpty(workCenterNotReachReasonDb.getPartNo())? workCenterNotReachReasonDb.getPartNo() : "");
        workCenterNotReachReason.setNotReachReason(workCenterNotReachReasonDb != null && !StringUtils.isEmpty(workCenterNotReachReasonDb.getNotReachReason())? workCenterNotReachReasonDb.getNotReachReason() : "");
        workCenterNotReachReason.setCompleteRate(completeRate.doubleValue());
        workCenterNotReachReason.setYieldRate(yieldRate.doubleValue());
        workCenterNotReachReason.setCompleteQtyMapByPartNo(completeQtyMapByPartNo);
        workCenterNotReachReason.setPlanQtyMapByPartNo(planQtyMapByPartNo);
        workCenterNotReachReasonList.add(workCenterNotReachReason);
    }

    @Override
    public void doSaveSxWorkCenterNotReachReason(MesWorkCenterNotReachReason notReachReason) {
        MesWorkCenterNotReachReason workCenterNotReachReasonDb = workCenterNotReachReasonRepository.getByProperty(
                new String[]{MesExtConstWords.WORK_TIME, MesExtConstWords.WORK_CENTER_CODE, MesExtConstWords.ORGANIZE_CODE, MesExtConstWords.IS_VALID, MesExtConstWords.IS_DELETED},
                new Object[]{notReachReason.getWorkTime(), notReachReason.getWorkCenterCode(), notReachReason.getOrganizeCode(),
                        CommonEnumUtil.IS_VAILD.VAILD.getValue(),
                        CommonEnumUtil.IS_DEAL.NO.getValue()});

        if (workCenterNotReachReasonDb == null) {
            workCenterNotReachReasonDb = new MesWorkCenterNotReachReason();
            BeanUtils.copyProperties(notReachReason, workCenterNotReachReasonDb);
            ConvertBean.serviceModelInitialize(workCenterNotReachReasonDb, notReachReason.getModifyUser());
        } else {
            BeanUtils.copyProperties(notReachReason, workCenterNotReachReasonDb, MesExtConstWords.ID, MesExtConstWords.CREATE_USER, MesExtConstWords.CREATE_DATE_TIME, MesExtConstWords.MODIFY_USER, MesExtConstWords.MODIFY_DATE_TIME);
            ConvertBean.serviceModelUpdate(workCenterNotReachReasonDb, notReachReason.getModifyUser());
        }
        workCenterNotReachReasonDb.setSystemSyncStatus(MesExtEnumUtil.IF_SYNC_STATUS.NO_SYNC.getValue());

        workCenterNotReachReasonRepository.save(workCenterNotReachReasonDb);

    }

    private String getRate(Double value1, Double value2){
        return getBigDecimalPercent(format2RounHalfUp(MathOperation.mul(MathOperation.div(value1, value2), 100)));
    }

    public BigDecimal divThenMul100Format2RoundHalfUp(Double d1, Double d2) {
        return format2RounHalfUp(MathOperation.mul(MathOperation.div(d1, d2), 100));
    }

    private BigDecimal format2RounHalfUp(Double d) {
        if (StringUtils.isEmpty(d)) d = new Double(0);
        BigDecimal bd = new BigDecimal(d);
        bd = bd.setScale(2, BigDecimal.ROUND_HALF_UP);
        return bd;
    }

    private String getBigDecimalPercent(BigDecimal b) {
        return b + MesExtConstWords.PERCENT;
    }

    private Map<String, MesWorkCenterExt> getWorkCenterMap(String workCenterCode) {
        DdlPackBean packBean = DdlPackBean.getDdlPackBean();
        DdlPreparedPack.getStringEqualPack(workCenterCode, MesExtConstWords.WORK_CENTER_CODE, packBean);
        List<MesWorkCenterExt> workCenterExtList = workCenterExtRepository.findByHqlWhere(packBean);
        return workCenterExtList.stream().collect(Collectors.toMap(MesWorkCenterExt::getWorkCenterCode, Function.identity(), (x, y) -> y));
    }

    public List<String> getWorkCenterCodeList(Map<String, List<MesWorkOrder>> workOrderMapByWorkCenterCode, Map<String, List<MesOutPutStatistics>> outPutStatisticsByWorkCenterCodeMap) {

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

        if (!CollectionUtils.isEmpty(workOrderMapByWorkCenterCode)) workCenterCodeList.addAll(workOrderMapByWorkCenterCode.keySet());

        if (!CollectionUtils.isEmpty(outPutStatisticsByWorkCenterCodeMap)) workCenterCodeList.addAll(outPutStatisticsByWorkCenterCodeMap.keySet());

        workCenterCodeList = workCenterCodeList.stream().distinct().collect(Collectors.toList());

        workCenterCodeList.sort(Comparator.comparing(String::toString));

        return workCenterCodeList;
    }

    private List<MesOutPutStatistics> getOutPutStatisticsList(String workCenterCode, String workTime, String organizeCode) {
        DdlPackBean packBean = DdlPackBean.getDdlPackBean(organizeCode);
        DdlPreparedPack.getStringEqualPack(workCenterCode, MesExtConstWords.WORK_CENTER_CODE, packBean);
        DdlPreparedPack.getStringEqualPack(workTime, MesExtConstWords.WORK_TIME, packBean);
        return outPutStatisticsRepository.findByHqlWhere(packBean);
    }

    private List<MesWorkOrder> getMesWorkOrderList(String organizeCode, String workTime, String workCenterCode) {
        DdlPackBean packBean = DdlPackBean.getDdlPackBean(organizeCode);
        DdlPreparedPack.timeBuilder(workTime + MesExtConstWords.APPEND_ZERO_HMS, workTime + MesExtConstWords.APPEND_24_HMS, MesExtConstWords.START_TIME, packBean, true);
        DdlPreparedPack.getNumEqualPack(MesExtEnumUtil.WORK_ORDER_TYPE.STANDARD_ORDER.getValue(), MesExtConstWords.WORK_ORDER_TYPE, packBean);
        DdlPreparedPack.getStringEqualPack(workCenterCode, MesExtConstWords.WORK_CENTER_CODE, packBean);
        return workOrderRepository.findByHqlWhere(packBean);
    }

    private List<MesWorkCenterNotReachReason> getMesWorkCenterNotReachReasonList(String workCenterCode, String workTime, String organizeCode) {
        DdlPackBean packBean = DdlPackBean.getDdlPackBean(organizeCode);
        DdlPreparedPack.getStringEqualPack(workTime, MesExtConstWords.WORK_TIME, packBean);
        DdlPreparedPack.getStringEqualPack(workCenterCode, MesExtConstWords.WORK_CENTER_CODE, packBean);
        return workCenterNotReachReasonRepository.findByHqlWhere(packBean);
    }
}
