package com.ximai.mes.kanban.service;

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.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("工序当月统计")
    @PostMapping("/currentMonthStat")
    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;
    }


}
