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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.rongwei.bscommon.sys.dao.ApsProcessOperationProcessEquDao;
import com.rongwei.bscommon.sys.service.ApsProcessOperationService;
import com.rongwei.bscommon.sys.service.GanttService;
import com.rongwei.bsentity.domain.ApsProcessOperationDo;
import com.rongwei.bsentity.vo.GanttVos;
import com.rongwei.bsentity.vo.ScheduleGanttVo;
import com.rongwei.rwcommon.base.R;
import com.rongwei.rwcommon.base.exception.CustomException;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

/**
 * GanttServiceImpl class
 *
 * @author XH
 * @date 2024/03/25
 */
@Service
public class GanttServiceImpl implements GanttService {
    public static final String GANTT_ITEM_NAME = "%s %d卷 %s %s %tF";
    public static final String GANTT_ITEM_NAME_PREFIX = "%s %s";
    public static final String GANTT_DESC_PREFIX = " 计划时间：%s-%s </br>" +
            "实际时间：%s-%s </br>" +
            "加工设备：%s/%s </br>";
    public static final String GANTT_DESC =
            "输入物料：%s %d卷</br>" +
                    "工序：%s </br>" +
                    "作业状态：%s </br>" +
                    "完成情况：已开工 %d/已报工 %d/已检验 %d/未完工 %d </br>" +
                    "客户订单号：%s </br>" +
                    "客户名称：%s </br>" +
                    "交货期：%tF </br>" +
                    "订单产品：%s </br>";
    public static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    public static final String DEFAULT_TYPE = "project";
    public static final String DEFAULT_RENDER = "split";
    public static final String COMBINED_PROCESSING = "合并加工";
    private final Logger log = LoggerFactory.getLogger(this.getClass().getName());
    @Autowired
    private ApsProcessOperationProcessEquDao apsProcessOperationProcessEquDao;
    @Autowired
    private ApsProcessOperationService apsProcessOperationService;

    /**
     * 根据条件获取gantt
     *
     * @param searchDate
     * @param factoryId
     * @param workShopIdList
     * @param orderNoList
     * @param productionLineId
     * @param equId
     * @return
     */
    @Override
    public R getListByCondition(Date startDate, Date endDate, String factoryId,
                                List<String> workShopIdList, List<String> orderNoList,
                                List<String> productionLineId, List<String> equId,
                                String productionLineName, String equName) {
        log.info("甘特图查询开始时间：" + DateUtil.now());
        if (endDate == null && startDate != null) {
            List<Date> searchDateList = getSearchDate(startDate);
            startDate = searchDateList.get(0);
            endDate = searchDateList.get(1);
        }
        List<GanttVos> apsProcessOperationProcessEquDos = apsProcessOperationProcessEquDao.getGanttDataList(
                startDate, endDate, factoryId, workShopIdList, orderNoList,
                null, productionLineId, equId, null, productionLineName, equName);
        List<ScheduleGanttVo> scheduleGanttVos = assembleGanttData(apsProcessOperationProcessEquDos);
        log.info("甘特图查询结束时间：" + DateUtil.now());
        return R.ok(scheduleGanttVos);
//        dataMap.forEach((k, v) -> {
//            ScheduleGanttVo vo = new ScheduleGanttVo();
//
//            List<ScheduleGanttProcessVo> processVos = new ArrayList<>();
//            // 对数据按照时间进行+设备进行分组
//            LinkedHashMap<String, List<GanttVos>> processMap = v.stream().collect(Collectors.groupingBy(info -> info.getProcessdeviceid(),
//                    LinkedHashMap::new, Collectors.toList()));
//            processMap.forEach((date, processs) -> {
//                ScheduleGanttProcessVo processVo = new ScheduleGanttProcessVo();
//                processVo.setProcessdevice(processs.get(0).getProcessdevice());
//                processVo.setProcessdeviceid(processs.get(0).getProcessdeviceid());
//                processVo.setPlanstartdate(processs.get(0).getPlanstartdate());
//                processVo.setPlanenddate(processs.get(0).getPlanenddate());
//                processVo.setProcessids(processs.stream().map(GanttVos::getProcessid).distinct().collect(Collectors.joining(",")));
//                processVo.setProcessdetailids(processs.stream().map(GanttVos::getId).distinct().collect(Collectors.joining(",")));
//
//                List<String> itemNames=new ArrayList<>();
//                // 按照工序作业ID分组
//                LinkedHashMap<String, List<GanttVos>> groupByProcessId = processs.stream().collect(Collectors.groupingBy(GanttVos::getProcessid, LinkedHashMap::new, Collectors.toList()));
//                groupByProcessId.forEach((processId,data2)->{
//                    // 按照工序作业明细ID 进行分组
//                    LinkedHashMap<String, List<GanttVos>> collect = data2.stream().collect(Collectors.groupingBy(GanttVos::getId, LinkedHashMap::new, Collectors.toList()));
//                    String itemName = collect.values().stream().map(info -> {
//                        String prefix = info.stream().map(data -> String.format(GANTT_ITEM_NAME_PREFIX, data.getCustomerabbreviation(), data.getProductname()))
//                                .collect(Collectors.joining(","));
//                        return String.format(GANTT_ITEM_NAME, prefix, info.get(0).getPlanprocessrall(), info.get(0).getDeliverydate());
//                    }).collect(Collectors.joining("</br>"));
//                    itemNames.add(itemName);
//                });
//                String itemPrefix=String.format(GANTT_DESC_PREFIX,
//                        dateFormat(processs.get(0).getPlanstartdate()),
//                        dateFormat(processs.get(0).getPlanenddate()),
//                        dateFormat(processs.get(0).getActualstartdate()),
//                        dateFormat(processs.get(0).getActualfinishdate()),
//                        processs.get(0).getProcessworkshop(),processs.get(0).getProcessdevice());
//                String itemDesc= processs.stream().map(info->{
//                    return String.format(GANTT_DESC, info.getProductname(),info.getDeliverydate(),
//                            info.getProcessname(),info.getPlanprocessrall(),info.getStartingroll(),info.getReportroll(),
//                            info.getCheckoutroll(),info.getUnfinishroll(),info.getWorkstatus() );
//                }).collect(Collectors.joining("</br>"));
//                processVo.setItemname(StringUtils.join(itemNames,"</br>"));
//                processVo.setItemdesc(itemPrefix+itemDesc);
//                processVos.add(processVo);
//            });
//            vo.setProcessList(processVos);
//            vo.setProcessworkshop(v.get(0).getProcessworkshop());
//            resultVo.add(vo);
//        });

    }

    @Override
    public List<ScheduleGanttVo> getListById(List<String> ids) {
        List<GanttVos> apsProcessOperationProcessEquDos = apsProcessOperationProcessEquDao.getGanttDataList(
                null, null, null, null, null,
                null, null, null, ids, null, null);
        return assembleGanttData(apsProcessOperationProcessEquDos);
    }

    public String dateFormat(Date date) {
        if (date == null) {
            return "";
        }
        return dateFormat.format(date);
    }

    /**
     * 根据传入日期获取 该日期的前6天和后6天
     *
     * @param date
     * @return
     */
    private List<Date> getSearchDate(Date date) {
        if (date == null) {
            return null;
        }
        LocalDateTime now = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
        // 获取前6天的0点0分01秒
        LocalDateTime sixDaysAgoStart = now.minusDays(6).truncatedTo(ChronoUnit.DAYS).plusSeconds(1);


        // 获取后6天的23点59分59秒
        LocalDateTime sixDaysLaterEnd = now.plusDays(6).truncatedTo(ChronoUnit.DAYS).plusHours(23).plusMinutes(59).plusSeconds(59);
        List<Date> returnDate = new ArrayList();
        returnDate.add(Date.from(sixDaysAgoStart.atZone(ZoneId.systemDefault()).toInstant()));
        returnDate.add(Date.from(sixDaysLaterEnd.atZone(ZoneId.systemDefault()).toInstant()));
        return returnDate;
    }

    /**
     * 查找上一个或者下一个工序节点
     *
     * @param ganttVosList
     * @param operatorType next：下一节点，pre：上一节点
     * @return
     */
    public List<GanttVos> getNextProcess(List<GanttVos> ganttVosList, String operatorType) {
        List<GanttVos> retGantvos = null;
        // 获取下一个或者上一个工序
        if (ganttVosList != null && ganttVosList.size() > 0) {
            List<String> processids = ganttVosList.stream().map(v -> v.getProcessid()).collect(Collectors.toList());
            // 获取上一节点或者下一节点
            List<ApsProcessOperationDo> processOperations = apsProcessOperationService.list(new LambdaQueryWrapper<ApsProcessOperationDo>()
                    .in(ApsProcessOperationDo::getId, processids));
            List<String> queryProcessids = new ArrayList<>();
            if (processOperations != null && processOperations.size() > 0) {
                for (ApsProcessOperationDo processOperation : processOperations) {
                    if ("next".equals(operatorType)) {
                        if (processOperation.getNextprocessid() != null) {
                            queryProcessids.addAll(Arrays.asList(processOperation.getNextprocessid().split(",")));
                        }
                    } else {
                        if (processOperation.getPreviousprocessid() != null) {
                            queryProcessids.addAll(Arrays.asList(processOperation.getPreviousprocessid().split(",")));
                        }
                    }
                }
                if (queryProcessids != null && queryProcessids.size() > 0) {
                    String joinids = "'" + CollUtil.join(queryProcessids, "','") + "'";
                    retGantvos = apsProcessOperationProcessEquDao.getGanttDataList(
                            null, null, null, null, null, joinids,
                            null, null, null, null, null);
                }
            }

        } else {
            if ("next".equals(operatorType)) {
                throw new CustomException("没有下一工序");
            } else {
                throw new CustomException("没有上一工序");
            }
        }
        return retGantvos;
    }

    /**
     * 工序名为 成退和中退 的清空下 加工时间相同的才可以合并
     *
     * @param apsProcessOperationProcessEquDos
     * @return
     */
    public List<ScheduleGanttVo> assembleGanttData(List<GanttVos> apsProcessOperationProcessEquDos) {
        // 需要返回给前端的数据
        List<ScheduleGanttVo> resultVo;
        if (apsProcessOperationProcessEquDos.isEmpty()) {
            return new ArrayList<>();
        }
        resultVo = new ArrayList<>();
        // 对数据按照加工车间分组
        LinkedHashMap<String, List<GanttVos>> dataMap = apsProcessOperationProcessEquDos.stream()
                .collect(Collectors.groupingBy(GanttVos::getProcessworkshopid,
                        LinkedHashMap::new, Collectors.toList()));
        /**
         * 工序的对象
         */
        ScheduleGanttVo workShopVo;
        // 设备的对象
        ScheduleGanttVo deviceVo;
        // 具体工序作业的对象
        ScheduleGanttVo dateVo;
        int workShopIndex = 1;
        for (Map.Entry<String, List<GanttVos>> workShopEntry : dataMap.entrySet()) {
            workShopVo = new ScheduleGanttVo();
            workShopVo.setId(workShopIndex++);
            workShopVo.setText(workShopEntry.getValue().get(0).getProcessworkshop());
            workShopVo.setOpen(true);
            workShopVo.setType(DEFAULT_TYPE);
            workShopVo.setStart_date(workShopEntry.getValue().get(0).getPlanstartdate());
            resultVo.add(workShopVo);
            // 对数据按照设备进行分组
            LinkedHashMap<String, List<GanttVos>> processMap = workShopEntry.getValue().stream().collect(Collectors.groupingBy(GanttVos::getProcessdeviceid,
                    LinkedHashMap::new, Collectors.toList()));
            // 对数据按照 设备进行分组
            for (Map.Entry<String, List<GanttVos>> deviceEntry : processMap.entrySet()) {
                deviceVo = new ScheduleGanttVo();
                deviceVo.setId(workShopIndex++);
                deviceVo.setText(deviceEntry.getValue().get(0).getProcessdevice());
                deviceVo.setOpen(true);
                deviceVo.setType(DEFAULT_TYPE);
                deviceVo.setParent(workShopVo.getId());
                deviceVo.setDevicetype(deviceEntry.getValue().get(0).getDevicetype());
                deviceVo.setRender(DEFAULT_RENDER);
                deviceVo.setStart_date(deviceEntry.getValue().get(0).getPlanstartdate());
                resultVo.add(deviceVo);

                // 对数据按照 计划开始时间+计划结束时间 分组
                LinkedHashMap<String, List<GanttVos>> planDataMap = deviceEntry.getValue().stream().collect(Collectors.groupingBy(info -> {
                            if (COMBINED_PROCESSING.equals(info.getProcessway())) {
                                return info.getPlanstartdate().toString() + info.getPlanenddate().toString();
                            }
                            return info.getUuid();
                        },
                        LinkedHashMap::new, Collectors.toList()));

                for (Map.Entry<String, List<GanttVos>> planDateEntry : planDataMap.entrySet()) {
                    List<GanttVos> ganttVos = planDateEntry.getValue();
                    List<String> itemNames = new ArrayList<>();
                    // 按照工序作业明细ID 进行分组 并且获取 图列的 名称和描述
                    LinkedHashMap<String, List<GanttVos>> collect = ganttVos.stream().collect(Collectors.groupingBy(GanttVos::getId, LinkedHashMap::new, Collectors.toList()));

                    String itemName = collect.values().stream().map(info -> {
//                        String prefix = info.stream().map(data -> String.format(GANTT_ITEM_NAME_PREFIX, data.getCustomerabbreviation(), data.getProductname()))
//                                .collect(Collectors.joining(","));
                        return String.format(GANTT_ITEM_NAME, info.get(0).getInmatercondition(), info.get(0).getPlanprocessrall(),
                                info.get(0).getCustomorderno(), info.get(0).getCustomerabbreviation(), info.get(0).getDeliverydate());
                    }).collect(Collectors.joining("</br>"));
                    itemNames.add(itemName);

                    String itemPrefix = String.format(GANTT_DESC_PREFIX,
                            dateFormat(ganttVos.get(0).getPlanstartdate()),
                            dateFormat(ganttVos.get(0).getPlanenddate()),
                            dateFormat(ganttVos.get(0).getActualstartdate()),
                            dateFormat(ganttVos.get(0).getActualfinishdate()),
                            ganttVos.get(0).getProcessworkshop(), ganttVos.get(0).getProcessdevice());
                    String itemDesc = ganttVos.stream().map(info -> {
                        return String.format(GANTT_DESC, info.getInmatercondition(), info.getPlanprocessrall(),
                                info.getProcessname(), info.getWorkstatus(),
                                info.getStartingroll(), info.getReportroll(),
                                info.getCheckoutroll(), info.getUnfinishroll(),
                                info.getCustomorderno(), info.getCustomerabbreviation(),
                                info.getDeliverydate(), info.getOutputorderproduct());
                    }).collect(Collectors.joining("</br>"));
                    dateVo = new ScheduleGanttVo();
                    dateVo.setId(workShopIndex++);
                    dateVo.setParent(deviceVo.getId());
                    dateVo.setText(StringUtils.join(itemNames, "</br>"));
                    dateVo.setTooltip(itemPrefix + itemDesc);
                    dateVo.setOpen(false);
                    dateVo.setStart_date(planDateEntry.getValue().get(0).getPlanstartdate());
                    dateVo.setEnd_date(planDateEntry.getValue().get(0).getPlanenddate());
                    dateVo.setName(StringUtils.join(itemNames, "</br>"));
                    dateVo.setDesc(itemPrefix + itemDesc);
                    dateVo.setProcessdetailids(ganttVos.stream().map(GanttVos::getId).distinct().collect(Collectors.joining(",")));
                    dateVo.setProcessids(ganttVos.stream().map(GanttVos::getProcessid).distinct().collect(Collectors.joining(",")));
                    dateVo.setGanttVos(ganttVos);
                    dateVo.setWorkstatus(ganttVos.stream().map(GanttVos::getWorkstatus).distinct().collect(Collectors.joining(",")));
                    dateVo.setLockmark(ganttVos.stream().map(GanttVos::getLockmark).distinct().collect(Collectors.joining(",")));
                    dateVo.setConflict(ganttVos.get(0).getConflict());
                    dateVo.setProductionorderid(ganttVos.stream().map(GanttVos::getProductionorderid).collect(Collectors.joining(",")));
                    // 冲突描述
                    List<GanttVos> conflictdes = ganttVos.stream().filter(v -> StringUtils.isNotBlank(v.getConflictdes())).collect(Collectors.toList());
                    if (conflictdes != null && conflictdes.size() > 0) {
                        String desc = conflictdes.stream().map(GanttVos::getConflictdes).collect(Collectors.joining(";"));
                        dateVo.setConflictdes(desc);
                        dateVo.setSoftconflictdes(conflictdes.stream().map(GanttVos::getSoftconflictdes).collect(Collectors.joining(";")));
                        // 有冲突描述时
                        if (StringUtils.isBlank(dateVo.getConflictdes()) && StringUtils.isBlank(dateVo.getSoftconflictdes())) {
                            dateVo.setConflict(true);
                        } else {
                            dateVo.setConflict(false);
                        }
                    }

                    resultVo.add(dateVo);
                }
            }

        }
        return resultVo;
    }

}
