package com.ximai.mes.kanban.service;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ximai.common.utils.data.StringUtils;
import com.ximai.mes.cal.domain.CalPlanWorkunit;
import com.ximai.mes.cal.mapper.CalPlanWorkunitMapper;
import com.ximai.mes.constant.QcAbnormalTypeEnum;
import com.ximai.mes.kanban.dto.equipment.EquipmentQuery;
import com.ximai.mes.kanban.dto.equipment.EquipmentStatDto;
import com.ximai.mes.md.domain.MdWorkunit;
import com.ximai.mes.md.service.IMdWorkunitService;
import com.ximai.mes.pro.domain.ProFeedback;
import com.ximai.mes.pro.domain.vo.ProFeedbackVo;
import com.ximai.mes.pro.dto.task.WorkunitProcessingDto;
import com.ximai.mes.pro.mapper.task.ProTaskWorkunitMapper;
import com.ximai.mes.pro.service.IProFeedbackService;
import com.ximai.mes.pro.service.task.IProTaskWorkunitService;
import com.ximai.mes.qc.domain.QcAbnormalReport;
import com.ximai.mes.qc.dto.QcAbnormalReportDto;
import com.ximai.mes.qc.dto.QcAbnormalReportStatusEnum;
import com.ximai.mes.qc.mapper.QcAbnormalReportMapper;
import com.ximai.mes.qc.service.IQcAbnormalReportService;
import com.ximai.system.service.ISysConfigService;
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.Duration;
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 KanbanEquipmentService {

    @Autowired
    private IProFeedbackService feedbackService;
    @Autowired
    private ProTaskWorkunitMapper taskWorkunitMapper;
    @Autowired
    private CalPlanWorkunitMapper calPlanWorkunitMapper;
    @Autowired
    private IMdWorkunitService workunitService;
    @Autowired
    IQcAbnormalReportService abnormalReportService;
    @Autowired
    QcAbnormalReportMapper abnormalReportMapper;
    @Autowired
    ISysConfigService configService;
    //利用率增长
    private String OT_RATIO_APPEND_KEY= "kanban.equ.output.ratio.append";
    //最低利用率
    private String OT_RATIO_LOWER_KEY= "kanban.equ.output.ratio.lower";

    @ApiOperation("设备统计")
    @PostMapping("/currentMonthStat")
    public EquipmentStatDto currentMonthStat(EquipmentQuery equipmentQuery) {
        EquipmentStatDto rst = new EquipmentStatDto();
        LocalDateTime currDate = LocalDateTime.now().with(LocalTime.MIN);
        //查询所有设备
        List<MdWorkunit> list = workunitService.selectListByQw(new QueryWrapper<MdWorkunit>().eq("t1.enable_flag","Y")
                .orderByAsc("t1.serial","t1.create_time"));
        Map<String, List<MdWorkunit>> equipMap = list.stream().map(s->{
            s.setLineName(org.apache.commons.lang.StringUtils.defaultString(s.getLineName(),"未指定"));
            return s;
        }).collect(Collectors.groupingBy(s->s.getLineName()));
        List<MdWorkunit> tempList = new ArrayList<>();
        if(StringUtils.isNotEmpty(equipmentQuery.getLineName())){
            tempList.addAll(equipMap.get(equipmentQuery.getLineName()));
        }else{
            tempList.addAll(list);
        }
        //查询当日报工
        List<ProFeedbackVo> feedbackList = feedbackService.queryProFeedbackListJoinTaskWorkUnit(new QueryWrapper<ProFeedback>().gt("f.feedback_time", currDate));
        //查询当日排班日历
        List<CalPlanWorkunit> calPlanWorkunitList = calPlanWorkunitMapper.selectListByDate(currDate);
        //派工任务
        //加工中设备
        List<WorkunitProcessingDto> workunitProcessingList = taskWorkunitMapper.selectWorkunitProcessing();

        Map<String,List<EquipmentStatDto.EquipmentInfo>> equipInfoMap = this.getEquipmentInfo(tempList, calPlanWorkunitList,
                feedbackList, workunitProcessingList);
        rst.setEquipMap(MapUtil.sort(equipInfoMap));
        //查询当日设备报异常
        QueryWrapper<QcAbnormalReport> query = new QueryWrapper<>();
        query.ge("t1.create_time", currDate);
        query.eq("t1.abnormal_type", QcAbnormalTypeEnum.DEVI.getType());
        List<QcAbnormalReportDto> abnormalList = abnormalReportService.selectQcAbnormalReportDtoList(query);
        List<EquipmentStatDto.AbnormalInfo> abnormalInfoList = this.getAbnormalInfo(tempList, abnormalList);
        rst.setAbnormalInfoList(abnormalInfoList);
        rst.setEquipNum(tempList.size());//总设备
        long ct = abnormalList.stream().filter(s->StringUtils.isNotEmpty(s.getWorkunitId())&&tempList.stream().anyMatch(s2->s.getWorkunitId().equals(s2.getWorkunitId()))).count();
        rst.setAbnormalNum((int)ct);//异常数
        QueryWrapper<QcAbnormalReport> abnormalQuery = new QueryWrapper<QcAbnormalReport>()
                .in("abnormal_status", new Object[]{QcAbnormalReportStatusEnum.SUBMIT.getStatus(),QcAbnormalReportStatusEnum.NOT.getStatus()})
                .eq("abnormal_type", com.ximai.mes.qc.dto.QcAbnormalTypeEnum.DEVI.getType());
        if(StringUtils.isNotEmpty(equipmentQuery.getLineName())){
            abnormalQuery.in("workunit_id", tempList.stream().map(s->s.getWorkunitId()).toArray());
        }
        Integer unprocessNum = abnormalReportMapper.selectCount(abnormalQuery);
        rst.setUnProcessNum(unprocessNum);//历史待处理数
        this.statIndexes(rst, tempList, feedbackList, calPlanWorkunitList);
        return rst;
    }

    private Map<String,List<EquipmentStatDto.EquipmentInfo>> getEquipmentInfo(List<MdWorkunit> list, List<CalPlanWorkunit> calPlanWorkunitList,
                                                                              List<ProFeedbackVo> feedbackList, List<WorkunitProcessingDto> workunitProcessingList){
        Map<Long,WorkunitProcessingDto> workunitProcessingMap = workunitProcessingList.stream().collect(Collectors.toMap(s->s.getWorkunitId(),s->s));
        List<EquipmentStatDto.EquipmentInfo> rst = new ArrayList<>();
        list.forEach(s->{
            EquipmentStatDto.EquipmentInfo info = new EquipmentStatDto.EquipmentInfo();
            info.setEquipmentNo(s.getWorkunitCode());
            info.setEquipmentName(s.getWorkunitName());
            BigDecimal calTime = this.getCalTime(calPlanWorkunitList, s);
            AtomicReference<BigDecimal> machineTime = new AtomicReference<BigDecimal>(BigDecimal.ZERO);
            AtomicReference<BigDecimal> output = new AtomicReference<BigDecimal>(BigDecimal.ZERO);
            feedbackList.stream().filter(s2->s2.getWorkunitId().equals(s.getWorkunitId())).forEach(s2->{
                if(StringUtils.isNotEmpty(s2.getMachineTime())){
                    machineTime.set(machineTime.get().add(new BigDecimal(s2.getMachineTime())));
                }
                output.set(output.get().add(s2.getQuantityQualify().add(s2.getQuantityUnqualify())));
            });
            if(calTime.compareTo(BigDecimal.ZERO)>0){
                info.setOutputRatio(machineTime.get().divide(calTime, 3, BigDecimal.ROUND_DOWN).multiply(new BigDecimal("100")));//利用率
            }
            info.setOutput(output.get());//日产出
            if("Y".equals(s.getFaultFlag())){
                info.setState(2);//状态
            }else if(workunitProcessingMap.containsKey(s.getWorkunitId())){
                info.setState(0);
            }else{
                info.setState(1);
            }
            info.setLineName(s.getLineName());
            rst.add(info);
        });
        return rst.stream().collect(Collectors.groupingBy(s->s.getLineName()));
    }

    private List<EquipmentStatDto.AbnormalInfo> getAbnormalInfo(List<MdWorkunit> list, List<QcAbnormalReportDto> abnormalList){
        List<EquipmentStatDto.AbnormalInfo> rst = new ArrayList<>();
        abnormalList.forEach(s->{
            //未关闭
            if(QcAbnormalReportStatusEnum.NOT.getStatus().equals( s.getAbnormalStatus())||
                    QcAbnormalReportStatusEnum.SUBMIT.getStatus().equals( s.getAbnormalStatus())){
                //在查询设备范围内
                list.stream().filter(e->e.getWorkunitId().equals(s.getWorkunitId())).findFirst().ifPresent(e->{
                    EquipmentStatDto.AbnormalInfo temp = new EquipmentStatDto.AbnormalInfo();
                    temp.setEquipmentNo(e.getWorkunitCode());
                    temp.setEquipmentName(e.getWorkunitName());
                    temp.setState(s.getAbnormalStatus());
                    temp.setCause(s.getAbnormalReason());
                    rst.add(temp);
                });
            }
        });
        return rst;
    }

    private void statIndexes(EquipmentStatDto rst, List<MdWorkunit> equipList,
                             List<ProFeedbackVo> feedbackList, List<CalPlanWorkunit> calPlanWorkunitList){
        AtomicReference<BigDecimal> qualifiedRatioNumerator = new AtomicReference<>(BigDecimal.ZERO);//合格数
        AtomicReference<BigDecimal> machineTime = new AtomicReference<>(BigDecimal.ZERO);//报工工时
        for (MdWorkunit mdWorkunit : equipList) {
            feedbackList.stream().filter(s->s.getWorkunitId().equals(mdWorkunit.getWorkunitId())).forEach(s->{
                rst.setTotalOutput(rst.getTotalOutput().add(s.getQuantityQualify().add(s.getQuantityUnqualify())));
                qualifiedRatioNumerator.set(qualifiedRatioNumerator.get().add(s.getQuantityQualify()));
                if(StringUtils.isNotEmpty(s.getMachineTime())){
                    machineTime.set(machineTime.get().add(new BigDecimal(s.getMachineTime())));
                }
            });
        }
        AtomicReference<BigDecimal> calTime = new AtomicReference<>(BigDecimal.ZERO);//排班工时
        for (MdWorkunit mdWorkunit : equipList) {
            calTime.set(calTime.get().add(this.getCalTime(calPlanWorkunitList, mdWorkunit)));
        }
        if(rst.getTotalOutput().compareTo(BigDecimal.ZERO)>0){
            rst.setQualifiedRatio(qualifiedRatioNumerator.get().divide(rst.getTotalOutput(), 3, BigDecimal.ROUND_DOWN).multiply(new BigDecimal("100")));//合格率
        }
        if(calTime.get().compareTo(BigDecimal.ZERO)>0){
            rst.setEquipAvailability(machineTime.get().divide(calTime.get(), 3, BigDecimal.ROUND_DOWN).multiply(new BigDecimal("100")));//利用率
            rst.setEquipAvailability(this.resetAvailability(rst.getEquipAvailability()));
        }
    }

    private BigDecimal getCalTime(List<CalPlanWorkunit> calPlanWorkunitList, MdWorkunit mdWorkunit){
        AtomicReference<BigDecimal> calTime = new AtomicReference<>(BigDecimal.ZERO);//排班工时
        LocalDateTime currDate = LocalDateTime.now().with(LocalTime.MIN);
        LocalDateTime morrowDate = LocalDateTime.now().plusDays(1).with(LocalTime.MIN);
        calPlanWorkunitList.stream().filter(s->s.getWorkunitId().equals(mdWorkunit.getWorkunitId())).forEach(s->{
            LocalDateTime tempStart = DateUtil.toLocalDateTime(s.getStartDate());
            LocalDateTime tempEnd = DateUtil.toLocalDateTime(s.getEndDate());
            //获得排班结束与开始间隔
            //如果开始时间大于当天，使用当天
            if(tempStart.compareTo(currDate)<0){
                tempStart = currDate;
            }
            //如果结束时间大于当天+1，使用当天+1
            if(tempEnd.compareTo(morrowDate)>0){
                tempEnd = morrowDate;
            }
            long minutes=Duration.between(tempStart, tempEnd).toMinutes();
            calTime.set(calTime.get().add(new BigDecimal(minutes)));
        });
        return calTime.get();
    }

    private BigDecimal resetAvailability(BigDecimal source){
        String otRatioAppend = configService.selectConfigByKey(OT_RATIO_APPEND_KEY);
        String otRatioLower = configService.selectConfigByKey(OT_RATIO_LOWER_KEY);
        if(StrUtil.isNumeric(otRatioAppend) && StrUtil.isNumeric(otRatioLower)){
            BigDecimal otRatioAppend1 = new BigDecimal(otRatioAppend);
            BigDecimal otRatioLower1 = new BigDecimal(otRatioLower);
            if(source.compareTo(otRatioLower1)<0){
                source = source.add(otRatioAppend1);
            }
        }
        return source;
    }

}
