package com.ximai.mes.kanban.service;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ximai.common.utils.data.StringUtils;
import com.ximai.mes.constant.TaskWorkunitStatusEnum;
import com.ximai.mes.kanban.dto.task.TaskPlanQuery;
import com.ximai.mes.kanban.dto.task.TaskPlanStatDto;
import com.ximai.mes.kanban.dto.task.TaskStatDto;
import com.ximai.mes.pro.domain.ProFeedback;
import com.ximai.mes.pro.domain.proWorkOrder.ProWorkorder;
import com.ximai.mes.pro.domain.task.ProTaskWorkunit;
import com.ximai.mes.pro.dto.ProFeedbackDto;
import com.ximai.mes.pro.dto.task.ProTaskWorkunitDto;
import com.ximai.mes.pro.mapper.task.ProTaskWorkunitMapper;
import com.ximai.mes.pro.service.IProFeedbackService;
import com.ximai.mes.pro.service.task.IProTaskWorkunitService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PostMapping;

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

@Service
public class KanbanTaskService {

    @Autowired
    private IProTaskWorkunitService taskWorkunitService;
    @Autowired
    private IProFeedbackService feedbackService;
    @Autowired
    private ProTaskWorkunitMapper taskWorkunitMapper;

    @ApiOperation("工序当月统计")
    public TaskStatDto currentMonthStat() {
        LocalDateTime monthFirstDay = LocalDateTime.now().withDayOfMonth(1).with(LocalTime.MIN);
        LocalDateTime nextMonth = monthFirstDay.plusMonths(1);
        //当前月派工任务（计划完成时间在当月）-按工序名分组
        QueryWrapper<ProTaskWorkunit> taskWorkunitQuery = new QueryWrapper<>();
        taskWorkunitQuery.ge("ptw.schedule_end_date", monthFirstDay);
        taskWorkunitQuery.le("ptw.schedule_end_date", nextMonth);
        taskWorkunitQuery.gt("ptw.quantity", 0);
        Map<String,List<ProTaskWorkunitDto>> taskWorkunitDtoMap1 = taskWorkunitService.selectTaskWorkUnitDtoJoinTask(taskWorkunitQuery)
                .stream().collect(Collectors.groupingBy(s->s.getProcessName()));
        taskWorkunitQuery.clear();
        //当月派工任务（计划开始时间在当月）-按工序名分组
        taskWorkunitQuery.ge("ptw.schedule_start_date", monthFirstDay);
        taskWorkunitQuery.le("ptw.schedule_start_date", nextMonth);
        taskWorkunitQuery.gt("ptw.quantity", 0);
        Map<String,List<ProTaskWorkunitDto>> taskWorkunitDtoMap2 = taskWorkunitService.selectTaskWorkUnitDtoJoinTask(taskWorkunitQuery)
                .stream().collect(Collectors.groupingBy(s->s.getProcessName()));
        //当月所有报工
        QueryWrapper<ProFeedback> feedbackQuery = new QueryWrapper<>();
        feedbackQuery.ge("f.feedback_time", monthFirstDay);
        List<ProFeedback> feedbackList = feedbackService.selectProFeedbackList(feedbackQuery);
        //当月所有报工数据-时间倒序、并按派工任务ID分组
        Map<Long, List<ProFeedback>> feedbackDtoMap1 = feedbackList
                .stream().sorted(Comparator.comparing(ProFeedback::getFeedbackTime))
                .collect(Collectors.groupingBy(s->s.getTaskWorkunitId()));
        //当前月派工任务（计划完成时间在当月）关联报工数据-时间倒序、并按派工任务ID分组
        feedbackQuery.clear();
        feedbackQuery.ge("tw.schedule_end_date", monthFirstDay);
        Map<Long, List<ProFeedbackDto>> feedbackDtoMap2 = feedbackService.queryProFeedbackDtoJoinTaskWorkUnit(feedbackQuery)
                .stream().sorted(Comparator.comparing(ProFeedbackDto::getFeedbackTime))
                .collect(Collectors.groupingBy(s->s.getTaskWorkunitId()));

        TaskStatDto rst = new TaskStatDto();
        TaskStatDto.ProcessMonthlyIndexDto processMonthlyIndexDto=this.stat(taskWorkunitDtoMap1, taskWorkunitDtoMap2, feedbackDtoMap1, feedbackDtoMap2);
        //历史月未完工任务（当月之前月份，工序任务状态不是已完工的工序任务单数量之和）
        taskWorkunitQuery.clear();
        taskWorkunitQuery.lt("schedule_end_date", monthFirstDay);
        taskWorkunitQuery.gt("quantity", 0);
        taskWorkunitQuery.in("status", TaskWorkunitStatusEnum.BEGINNING.getStatus(),
                TaskWorkunitStatusEnum.PAUSE.getStatus(), TaskWorkunitStatusEnum.PREPARE.getStatus(),
                TaskWorkunitStatusEnum.ERROR_STOP.getStatus());
        Integer ct = taskWorkunitMapper.selectCount(taskWorkunitQuery);
        processMonthlyIndexDto.setHisPendingQuantity(new BigDecimal(ct));
        rst.setProcessMonthlyIndexDto(processMonthlyIndexDto);
        List<TaskStatDto.ProcessOutput> outputList=this.statOutput(taskWorkunitDtoMap1);
        List<TaskStatDto.ProcessQualifiedRate> qualifiedRateList=this.processQualifiedRates(feedbackList);
        rst.setOutputList(outputList);
        rst.setQualifiedRateList(qualifiedRateList);
        List<TaskStatDto.AbnormalCauseStat> abnormalCauseData = this.unQualifiedDist(feedbackList);
        rst.setAbnormalCauseData(abnormalCauseData);
        return rst;
    }


    private TaskStatDto.ProcessMonthlyIndexDto stat(Map<String,List<ProTaskWorkunitDto>> taskWorkunitDtoMap,
                                                    Map<String,List<ProTaskWorkunitDto>> taskWorkunitDtoMap2,
                                                    Map<Long, List<ProFeedback>> feedbackDtoMap1,
                                                    Map<Long, List<ProFeedbackDto>> feedbackDtoMap2){
        Calendar curr = Calendar.getInstance();
        TaskStatDto.ProcessMonthlyIndexDto processMonthlyIndexDto = new TaskStatDto.ProcessMonthlyIndexDto();
        //当月工序任务已完工，最后一次报工时间晚于计划结束时间的工序任务单数量之和
        AtomicReference<BigDecimal> endOnTimeRatioNumerator = new AtomicReference<>(BigDecimal.ZERO);
        //工序任务计划结束时间是当月1号至当天的工序任务单数量之和
        AtomicReference<BigDecimal> endOnTimeRatioDenominator = new AtomicReference<>(BigDecimal.ZERO);
        taskWorkunitDtoMap.forEach((k,v)->{
            v.forEach(s->{
                //工序任务计划结束时间为当月的工序任务单总数
                processMonthlyIndexDto.setMonthPlanQuantity(processMonthlyIndexDto.getMonthPlanQuantity().add(s.getQuantity()));
                //获取工序任务计划结束时间为当月 任务状态为完工的工序任务单数量
                processMonthlyIndexDto.setActualFinishQuantity(processMonthlyIndexDto.getActualFinishQuantity().add(s.getQuantityProduced()));

                //是否存在超期报工
                List<ProFeedbackDto> feedbackList = feedbackDtoMap2.getOrDefault(s.getTaskWorkunitId(), new ArrayList<>());
                for(ProFeedbackDto feedbackDto:feedbackList){
                    if(feedbackDto.getFeedbackTime().compareTo(s.getScheduleEndDate())>0){
                        //报工时间大于计划结束时间工序任务单数量
                        processMonthlyIndexDto.setExpireFinishQuantity(processMonthlyIndexDto.getExpireFinishQuantity()
                                .add(feedbackDto.getQuantityQualify().add(feedbackDto.getQuantityUnqualify())));
                    }
                }
                if(s.getActualEndDate()!=null&&s.getActualEndDate().compareTo(s.getScheduleEndDate())<0){
                    endOnTimeRatioNumerator.set(endOnTimeRatioNumerator.get().add(BigDecimal.ONE));
                }
                if(s.getScheduleEndDate().compareTo(curr.getTime())<0){
                    endOnTimeRatioDenominator.set(endOnTimeRatioDenominator.get().add(BigDecimal.ONE));
                }
            });
        });
        if(endOnTimeRatioNumerator.get().compareTo(BigDecimal.ZERO)!=0){
            processMonthlyIndexDto.setEndOnTimeRatio(endOnTimeRatioNumerator.get()
                    .divide(endOnTimeRatioDenominator.get(), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal("100")));
        }
        if(processMonthlyIndexDto.getActualFinishQuantity().compareTo(BigDecimal.ZERO)>0){
            processMonthlyIndexDto.setMonthPlanConcludeRatio(processMonthlyIndexDto.getActualFinishQuantity()
                    .divide(processMonthlyIndexDto.getMonthPlanQuantity(), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal("100")));
        }
        //当月工序任务，开工时间早于计划时间的工序任务单数量之和
        AtomicReference<BigDecimal> startOnTimeRatioNumerator = new AtomicReference<>(BigDecimal.ZERO);
        //工序任务计划开工时间是当月1号至当天的工序任务单数量之和
        AtomicReference<BigDecimal> startOnTimeRatioDenominator = new AtomicReference<>(BigDecimal.ZERO);
        taskWorkunitDtoMap2.forEach((k,v)->{
            v.forEach(s->{
                if(s.getActualStartDate()!=null&&s.getActualStartDate().compareTo(s.getScheduleStartDate())<0){
                    startOnTimeRatioNumerator.set(startOnTimeRatioNumerator.get().add(BigDecimal.ONE));
                }
                if(s.getScheduleStartDate().compareTo(curr.getTime())<0){
                    startOnTimeRatioDenominator.set(startOnTimeRatioDenominator.get().add(BigDecimal.ONE));
                }
            });
        });
        if(startOnTimeRatioNumerator.get().compareTo(BigDecimal.ZERO)!=0){
            processMonthlyIndexDto.setStartOnTimeRatio(startOnTimeRatioNumerator.get()
                    .divide(startOnTimeRatioDenominator.get(), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal("100")));
        }
        AtomicReference<BigDecimal> qualifiedRatioNumerator = new AtomicReference<>(BigDecimal.ZERO);//当月报工合格量
        AtomicReference<BigDecimal> qualifiedRatioDenominator = new AtomicReference<>(BigDecimal.ZERO);//当月报工数量
        feedbackDtoMap1.forEach((k,v)->{
            v.forEach(s->{
                qualifiedRatioNumerator.set(qualifiedRatioNumerator.get().add(s.getQuantityQualify()));
                qualifiedRatioDenominator.set(qualifiedRatioDenominator.get().add(s.getQuantityQualify().add(s.getQuantityUnqualify())));
            });
        });
        if(qualifiedRatioNumerator.get().compareTo(BigDecimal.ZERO)!=0) {
            processMonthlyIndexDto.setQualifiedRatio(qualifiedRatioNumerator.get()
                    .divide(qualifiedRatioDenominator.get(), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal("100")));
        }
        return processMonthlyIndexDto;
    }

    //统计工序产量
    private List<TaskStatDto.ProcessOutput> statOutput(Map<String,List<ProTaskWorkunitDto>> taskWorkunitDtoMap){
        List<TaskStatDto.ProcessOutput> rst = new ArrayList<>();
        taskWorkunitDtoMap.forEach((k,v)->{
            TaskStatDto.ProcessOutput tempLine = new TaskStatDto.ProcessOutput();
            tempLine.setProcessName(k);
            v.forEach(s->{
                //获取工序对应工序任务结束时间在本月的任务生产数量之和
                tempLine.setPlanQuantity(tempLine.getPlanQuantity().add(s.getQuantity()));
                //获取工序对应工序任务结束时间在本月的报工数量（合格量+不合格量）
                tempLine.setFinishQuantity(tempLine.getFinishQuantity().add(s.getQuantityProduced()));
                //获取工序对应工序任务结束时间在本月的报工合格数量
                tempLine.setQualifiedQuantity(tempLine.getQualifiedQuantity().add(s.getQuantityQualify()));
            });
            rst.add(tempLine);
        });
        return rst;
    }

    //工序合格率
    private List<TaskStatDto.ProcessQualifiedRate> processQualifiedRates(List<ProFeedback> feedbackList){
        List<TaskStatDto.ProcessQualifiedRate> rst = new ArrayList<>();
        //按工序名分组
        Map<String, List<ProFeedback>> feedbackMap = feedbackList.stream().collect(Collectors.groupingBy(s->s.getProcessName()));
        feedbackMap.forEach((k,v)->{
            TaskStatDto.ProcessQualifiedRate tempLine = new TaskStatDto.ProcessQualifiedRate();
            tempLine.setProcessName(k);
            AtomicReference<BigDecimal> qualifiedRateNumerator = new AtomicReference<>(BigDecimal.ZERO);//当月报工合格量
            AtomicReference<BigDecimal> qualifiedRateDenominator = new AtomicReference<>(BigDecimal.ZERO);//当月报工数量
            v.forEach(s->{
                qualifiedRateNumerator.set(qualifiedRateNumerator.get().add(s.getQuantityQualify()));
                qualifiedRateDenominator.set(qualifiedRateDenominator.get().add(s.getQuantityQualify().add(s.getQuantityUnqualify())));
            });
            if(qualifiedRateNumerator.get().compareTo(BigDecimal.ZERO)!=0) {
                tempLine.setQualifiedRate(qualifiedRateNumerator.get()
                        .divide(qualifiedRateDenominator.get(), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal("100")));
            }
            rst.add(tempLine);
        });
        return rst;
    }

    //不合格原因分布
    private List<TaskStatDto.AbnormalCauseStat> unQualifiedDist(List<ProFeedback> feedbackList){
        List<TaskStatDto.AbnormalCauseStat> rst = new ArrayList<>();
        //按不合格原因分组
        Map<String, List<ProFeedback>> feedbackMap = feedbackList.stream().collect(Collectors.groupingBy(s->s.getAbnormalReason()));
        feedbackMap.forEach((k,v)->{
            if(StringUtils.isBlank(k)){
                return;
            }
            TaskStatDto.AbnormalCauseStat tempLine = new TaskStatDto.AbnormalCauseStat();
            tempLine.setCause(k);
            v.forEach(s->{
                tempLine.setCt(tempLine.getCt()+s.getQuantityUnqualify().intValue());
            });
            rst.add(tempLine);
        });
        return rst;
    }

    @ApiOperation("工序计划统计")
    public TaskPlanStatDto taskPlanStat(TaskPlanQuery taskPlanQuery) {
        TaskPlanStatDto rst = new TaskPlanStatDto();
        List<TaskPlanStatDto.TaskPlanDay> taskPlanDayList = new ArrayList<>();
        QueryWrapper<ProTaskWorkunit> query = new QueryWrapper<>();
        LocalDateTime curr = LocalDateTime.now().with(LocalTime.MIN);
        query.le("ptw.schedule_start_date", curr.plusDays(3));
        query.gt("ptw.schedule_start_date", curr.plusDays(-3));
        query.eq(StringUtils.isNotEmpty(taskPlanQuery.getLineName()), "wu.line_name", taskPlanQuery.getLineName());
        query.orderByDesc("ordinal","schedule_start_date");
        List<ProTaskWorkunitDto> dataList = taskWorkunitMapper.selectTaskWorkUnitWithKanban(query);
        //合并相同批号任务
        Map<String,List<ProTaskWorkunitDto>> dataMap = dataList.stream().collect(Collectors.groupingBy(s->s.getTaskBatch()));
        //转换数据-》获取首工序最早开始时间
        List<TaskPlanStatDto.TaskWorkunitGroup> taskWorkunitGroupList = new ArrayList<>();
        dataMap.forEach((k,v)->{
            TaskPlanStatDto.TaskWorkunitGroup temp = new TaskPlanStatDto.TaskWorkunitGroup();
            temp.setTaskWorkunitList(v);
            temp.setItemCode(v.get(0).getItemCode());
            temp.setStartDate(DateUtil.format(v.get(0).getScheduleStartDate(), "yyyy-MM-dd"));
            taskWorkunitGroupList.add(temp);
        });
        //按时间分组数据
        Map<String, List<TaskPlanStatDto.TaskWorkunitGroup>> taskWorkunitGroupMap = taskWorkunitGroupList.stream().collect(Collectors.groupingBy(s->s.getStartDate()));
        taskWorkunitGroupMap = MapUtil.sort(taskWorkunitGroupMap);
        taskWorkunitGroupMap.forEach((k,v)->{
            TaskPlanStatDto.TaskPlanDay taskPlanDay = new TaskPlanStatDto.TaskPlanDay();
            taskPlanDay.setStartDate(k);
            List<TaskPlanStatDto.ProductTaskInfo> productList = new ArrayList<>();
            //按产品分组
            Map<String, List<TaskPlanStatDto.TaskWorkunitGroup>> taskWorkunitGroupMap2 = v.stream().collect(Collectors.groupingBy(s->s.getItemCode()));
            taskWorkunitGroupMap2.forEach((k2,v2)->{
                TaskPlanStatDto.ProductTaskInfo productTaskInfo = new TaskPlanStatDto.ProductTaskInfo();
                productTaskInfo.setItemCode(k2);
                List<ProTaskWorkunitDto> tempTaskWorkunitList = new ArrayList<>();
                for (TaskPlanStatDto.TaskWorkunitGroup taskWorkunitGroup : v2) {
                    tempTaskWorkunitList.addAll(taskWorkunitGroup.getTaskWorkunitList());
                }
                //按工序分组任务
                Map<TaskPlanStatDto.TaskWorkunitProcessKey, List<ProTaskWorkunitDto>> taskWorkunitMap = tempTaskWorkunitList.stream().collect(Collectors.groupingBy(s->{
                    return new TaskPlanStatDto.TaskWorkunitProcessKey(s.getProcessName(), s.getIdx());
                }));
                taskWorkunitMap = MapUtil.sort(taskWorkunitMap, Comparator.comparing(TaskPlanStatDto.TaskWorkunitProcessKey::getIdx));
                List<TaskPlanStatDto.ProcessTaskInfo> processList = new ArrayList<>();
                taskWorkunitMap.forEach((k3,v3)->{
                    BigDecimal reportQuantity = BigDecimal.ZERO;
                    BigDecimal qualifyQuantity = BigDecimal.ZERO;
                    BigDecimal planQuantity = BigDecimal.ZERO;
                    int state = 0;
                    for(ProTaskWorkunitDto workunit:v3){
                        reportQuantity = reportQuantity.add(workunit.getQuantityProduced());
                        qualifyQuantity = qualifyQuantity.add(workunit.getQuantityQualify());
                        planQuantity = planQuantity.add(workunit.getQuantity());
                    }
                    TaskPlanStatDto.ProcessTaskInfo processTaskInfo = new TaskPlanStatDto.ProcessTaskInfo();
                    processTaskInfo.setState(this.getWorkunitState(v3));
                    processTaskInfo.setProcessName(k3.getProcessName());
                    processTaskInfo.setQualifyQuantity(qualifyQuantity);
                    processTaskInfo.setReportQuantity(reportQuantity);
                    processTaskInfo.setPlanQuantity(planQuantity);
                    if(reportQuantity.compareTo(BigDecimal.ZERO)>0){
                        processTaskInfo.setQualifiedRate(qualifyQuantity.divide(reportQuantity, 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal("100")));
                    }
                    processList.add(processTaskInfo);
                });
                productTaskInfo.setPlanQuantity(processList.get(0).getPlanQuantity());
                productTaskInfo.setProcessList(processList);
                productList.add(productTaskInfo);
            });
            taskPlanDay.setProductList(productList);
            taskPlanDayList.add(taskPlanDay);
        });
        rst.setTaskPlanDayList(taskPlanDayList);
        return rst;
    }

    private int getWorkunitState(List<ProTaskWorkunitDto> list){
        Set<String> temp = new HashSet<>();
        list.forEach(s->{
            if(s.getStatus().equals(TaskWorkunitStatusEnum.FINISHED.getStatus())
            || s.getQuantity().compareTo(s.getQuantityProduced())<=0){
                if(s.getActualEndDate()==null){
                    temp.add("finish");
                }else if(s.getActualEndDate().compareTo(s.getScheduleEndDate())<=0){
                    temp.add("finish");
                }else{
                    temp.add("expireFinish");
                }
            }else{
                temp.add("other");
            }
        });
        if(temp.contains("other")){
            return 0;
        }else if(temp.contains("expireFinish")){
            return 2;
        }
        return 1;
    }



}
