package com.ximai.mes.pro.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ximai.common.utils.MessageUtils;
import com.ximai.common.utils.data.DataUtil;
import com.ximai.common.utils.data.ExceptionUtil;
import com.ximai.common.utils.data.StringUtils;
import com.ximai.mes.cal.domain.CalPlanWorkunit;
import com.ximai.mes.cal.service.ICalPlanWorkunitService;
import com.ximai.mes.constant.ProStartWorkEnum;
import com.ximai.mes.constant.ProStartWorkStartTypeEnum;
import com.ximai.mes.pro.domain.ProFeedback;
import com.ximai.mes.pro.domain.ProStartWork;
import com.ximai.mes.pro.domain.task.ProTask;
import com.ximai.mes.pro.domain.task.ProTaskWorkunit;
import com.ximai.mes.pro.mapper.ProStartWorkMapper;
import com.ximai.mes.pro.mapper.task.ProTaskMapper;
import com.ximai.mes.pro.mapper.task.ProTaskWorkunitMapper;
import com.ximai.mes.pro.service.IProStartWorkService;
import com.ximai.mes.pro.service.task.IProTaskWorkunitService;
import com.ximai.mes.qc.domain.QcAbnormalReport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static com.ximai.mes.constant.ProStartWorkEnum.FEEDBACK;

/**
 * 开始作业记录表Service业务层处理
 *
 * @author generator
 * @date 2024-03-11
 */
@Service
public class ProStartWorkServiceImpl implements IProStartWorkService {
    @Autowired
    private ProStartWorkMapper proStartWorkMapper;
    @Autowired
    private ICalPlanWorkunitService calPlanWorkunitService;

    @Autowired
    private ProTaskMapper proTaskMapper;
    @Resource
    private ProTaskWorkunitMapper proTaskWorkunitMapper;

    @Autowired
    private IProTaskWorkunitService proTaskWorkunitService;

    /**
     * 查询开始作业记录表
     *
     * @param startWorkId 开始作业记录表主键
     * @return 开始作业记录表
     */
    @Override
    public ProStartWork selectProStartWorkByStartWorkId(Long startWorkId) {
        return proStartWorkMapper.selectProStartWorkByStartWorkId(startWorkId);
    }

    /**
     * 查询开始作业记录表列表
     *
     * @param proStartWork 开始作业记录表
     * @return 开始作业记录表
     */
    @Override
    public List<ProStartWork> selectProStartWorkList(ProStartWork proStartWork) {
        return proStartWorkMapper.selectProStartWorkList(proStartWork);
    }

    /**
     * 新增开始作业记录表
     *
     * @param proStartWork 开始作业记录表
     * @return 结果
     */
    @Override
    public int insertProStartWork(ProStartWork proStartWork) {
        proStartWork.createAction();
        if (!Objects.equals(proStartWork.getRecordType(), ProStartWorkEnum.START.getType())) {
            proStartWork.setRecordUuid(DataUtil.getUuid());
        } else {
            proStartWork.setRecordUuid("unique");
        }
        return proStartWorkMapper.insertProStartWork(proStartWork);
    }

    /**
     * 修改开始作业记录表
     *
     * @param proStartWork 开始作业记录表
     * @return 结果
     */
    @Override
    public int updateProStartWork(ProStartWork proStartWork) {
        proStartWork.updateAction();
        proStartWork.setRecordUuid(DataUtil.getUuid());
        return proStartWorkMapper.updateProStartWork(proStartWork);
    }

    /**
     * 批量删除开始作业记录表
     *
     * @param startWorkIds 需要删除的开始作业记录表主键
     * @return 结果
     */
    @Override
    public int deleteProStartWorkByStartWorkIds(Long[] startWorkIds) {
        return proStartWorkMapper.deleteProStartWorkByStartWorkIds(startWorkIds);
    }

    /**
     * 删除开始作业记录表信息
     *
     * @param startWorkId 开始作业记录表主键
     * @return 结果
     */
    @Override
    public int deleteProStartWorkByStartWorkId(Long startWorkId) {
        return proStartWorkMapper.deleteProStartWorkByStartWorkId(startWorkId);
    }

    @Override
    public List<ProStartWork> selectListByTwId(Long taskWorkunitId) {
        return proStartWorkMapper.selectListByQw(new QueryWrapper<ProStartWork>().eq("task_workunit_Id", taskWorkunitId));
    }

    @Override
    public void updateObj(QcAbnormalReport qcAbnormalReport) {
        ProTask pro = new ProTask();
        pro.setTaskId(qcAbnormalReport.getTaskId());
        pro.setTaskWorkunitId(qcAbnormalReport.getTaskWorkunitId());
        pro.setWorkunitId(qcAbnormalReport.getWorkstationId());
        pro.setAttr1(qcAbnormalReport.getAbnormalReason());
        pro.setAttr2(qcAbnormalReport.getAbnormalReason());
        pro.setPurseType(ProStartWorkEnum.EXCEPTION.getType());
        this.updateObj(pro);
    }

    @Override
    public void insertObj(ProTask proTask) {
        QueryWrapper<ProStartWork> proStartWorkQueryWrapper = new QueryWrapper<>();
        proStartWorkQueryWrapper.eq(StringUtils.isNotEmpty(proTask.getTaskId()), "task_id", proTask.getTaskId());
        proStartWorkQueryWrapper.eq(StringUtils.isNotEmpty(proTask.getTaskWorkunitId()), "task_workunit_id", proTask.getTaskWorkunitId());
        proStartWorkQueryWrapper.eq(StringUtils.isNotEmpty(proTask.getArrangeCode()), "arrange_code", proTask.getArrangeCode());
        proStartWorkQueryWrapper.eq(StringUtils.isNotEmpty(proTask.getWorkunitId()), "workunit_id", proTask.getWorkunitId());
        List<ProStartWork> proStartWorks = proStartWorkMapper.selectListByQw(proStartWorkQueryWrapper);
        List<ProStartWork> startRecords = proStartWorks.stream().filter(x -> Objects.equals(x.getRecordType(), ProStartWorkEnum.START.getType())).collect(Collectors.toList());
        ExceptionUtil.checkTrueThrowException(CollectionUtil.isNotEmpty(startRecords), MessageUtils.message("pro.feedback.error.error3"));

        ProStartWork proStartWork = new ProStartWork();
        proStartWork.setArrangeCode(proTaskMapper.selectListByQw(new QueryWrapper<ProTask>().eq("task_id", proTask.getTaskId())).get(0).getArrangeCode());
        proStartWork.setTaskWorkunitId(proTask.getTaskWorkunitId());
        proStartWork.setTaskId(proTask.getTaskId());
        proStartWork.setWorkunitId(proTask.getWorkunitId());

        int idx = 1;
        if (CollectionUtil.isNotEmpty(proStartWorks)) {
            idx = proStartWorks.size() + idx;
        }

        proStartWork.setIdx(idx);
        proStartWork.setStartDate(new Date());
        proStartWork.setRecordType(ProStartWorkEnum.START.getType());
        this.insertProStartWork(proStartWork);
    }


    @Override
    public void updateObj(ProFeedback proFeedback) {
        Long taskWorkunitId = proFeedback.getTaskWorkunitId();
        ProTaskWorkunit proTaskWorkunit = proTaskWorkunitService.selectProTaskWorkunitByTaskWorkunitId(taskWorkunitId);

        ProTask pro = new ProTask();
        pro.setTaskId(proFeedback.getTaskId());
        pro.setTaskWorkunitId(proFeedback.getTaskWorkunitId());
        pro.setWorkunitId(proTaskWorkunit.getWorkunitId());
        pro.setPurseType(FEEDBACK.getType());
        this.updateObj(pro);
    }

    @Override
    public void closeObj(ProFeedback proFeedback) {
        Long taskWorkunitId = proFeedback.getTaskWorkunitId();
        ProTaskWorkunit proTaskWorkunit = proTaskWorkunitService.selectProTaskWorkunitByTaskWorkunitId(taskWorkunitId);

        ProTask pro = new ProTask();
        pro.setTaskId(proFeedback.getTaskId());
        pro.setTaskWorkunitId(taskWorkunitId);
        pro.setWorkunitId(proTaskWorkunit.getWorkunitId());
        pro.setPurseType(ProStartWorkEnum.CLOSE.getType());
        this.updateObj(pro);
    }


    @Override
    public void updateObj(ProTask proTask) {
        QueryWrapper<ProStartWork> qw = new QueryWrapper<>();
        qw.eq(StringUtils.isNotEmpty(proTask.getTaskId()), "task_id", proTask.getTaskId());
        qw.eq(StringUtils.isNotEmpty(proTask.getTaskWorkunitId()), "task_workunit_id", proTask.getTaskWorkunitId());
        qw.eq(StringUtils.isNotEmpty(proTask.getArrangeCode()), "arrange_code", proTask.getArrangeCode());
        qw.eq(StringUtils.isNotEmpty(proTask.getWorkunitId()), "workunit_id", proTask.getWorkunitId());
        List<ProStartWork> startRecords = proStartWorkMapper.selectListByQw(qw).stream().filter(x -> Objects.equals(x.getRecordType(), ProStartWorkEnum.START.getType())).collect(Collectors.toList());
        ExceptionUtil.checkTrueThrowException(CollectionUtil.isEmpty(startRecords), MessageUtils.message("pro.feedback.error.not.find.start"));

        ProStartWork startWork = startRecords.get(0);
        Integer purseType = proTask.getPurseType();
        ProStartWorkEnum proStartWorkEnum = ProStartWorkEnum.get(purseType);
        switch (proStartWorkEnum) {
            case SUSPENDED:
                startWork.setAbnormalReason(proTask.getAttr1());
                startWork.setAbnormalDescribe(proTask.getAttr2());
                startWork.setPauseType(ProStartWorkStartTypeEnum.NORMAL_STOP.getType());
                startWork.setRecordType(ProStartWorkEnum.SUSPENDED.getType());
                break;
            case EXCEPTION:
                startWork.setAbnormalReason(proTask.getAttr1());
                startWork.setAbnormalDescribe(proTask.getAttr2());
                startWork.setPauseType(ProStartWorkStartTypeEnum.EXCEPTION_STOP.getType());
                startWork.setRecordType(ProStartWorkEnum.EXCEPTION.getType());
                break;
            case CLOSE:
                startWork.setPauseType(ProStartWorkStartTypeEnum.NORMAL_STOP.getType());
                startWork.setRecordType(ProStartWorkEnum.CLOSE.getType());
                break;
            case FEEDBACK:
                startWork.setPauseType(ProStartWorkStartTypeEnum.NORMAL_STOP.getType());
                startWork.setRecordType(FEEDBACK.getType());
                proTask.setPurseType(FEEDBACK.getType());
                break;
            default:
                break;
        }

        startWork.setEndDate(new Date());
        Map<String, Long> map = computeWorkTime(startWork, proTaskWorkunitMapper.selectListByQw(new QueryWrapper<ProTaskWorkunit>().eq("task_id", proTask.getTaskId())).get(0).getWorkunitId());
        startWork.setWorkTimeSec(DataUtil.getNormalData(map.get("workTimeSec"), Long.class));
        startWork.setBreakTimeSec(DataUtil.getNormalData(map.get("breakTimeSec"), Long.class));

        // 计算工时
        this.updateProStartWork(startWork);
        if (Objects.equals(proStartWorkEnum.getType(), FEEDBACK.getType())) {
            this.insertObj(proTask);
        }
    }

    private Map<String, Long> computeWorkTime(ProStartWork startWork, Long workunitId) {
        QueryWrapper<CalPlanWorkunit> calPlanWorkunitQueryWrapper = new QueryWrapper<>();
        Date startDate = startWork.getStartDate();
        Date endDate = startWork.getEndDate();
        calPlanWorkunitQueryWrapper.between("start_date", startDate, endDate).eq("rest_flag", 1).eq("workunit_id", workunitId)
                .or()
                .between("end_date", startDate, endDate).eq("rest_flag", 1).eq("workunit_id", workunitId);
        List<CalPlanWorkunit> calPlanWorkunits = calPlanWorkunitService.selectListByQw(calPlanWorkunitQueryWrapper);
        long startWorkDateTimeStamp = startDate.getTime();
        long startWorkendDateTimeStamp = endDate.getTime();
        List<CalPlanWorkunit> planWorkunitList = calPlanWorkunits.stream().filter(x -> x.getStartDate().getTime() < x.getEndDate().getTime()).collect(Collectors.toList());
        LongSummaryStatistics breakTimeSum = planWorkunitList.stream().map(x -> {
            long cPwStartTimeStamp = x.getStartDate().getTime();
            long endTimeStamp = x.getEndDate().getTime();
            if (cPwStartTimeStamp < startWorkDateTimeStamp) {
                cPwStartTimeStamp = startWorkDateTimeStamp;
            }

            if (endTimeStamp > startWorkendDateTimeStamp) {
                endTimeStamp = startWorkendDateTimeStamp;
            }
            return endTimeStamp > cPwStartTimeStamp ? (endTimeStamp - cPwStartTimeStamp) : 0;
        }).collect(Collectors.summarizingLong(x -> x));

        long breakTimeSec = breakTimeSum.getSum();
        long workTimeSec = startWorkendDateTimeStamp - startWorkDateTimeStamp - breakTimeSec;
        workTimeSec = workTimeSec < 0 ? 0L : workTimeSec;
        Map<String, Long> map = new HashMap<>();
        map.put("breakTimeSec", breakTimeSec / 1000);
        map.put("workTimeSec", workTimeSec / 1000);
        return map;
    }


}
