package com.hz.pm.api.sys.manage;

import cn.hutool.core.util.NumberUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ningdatech.basic.exception.BizException;
import com.ningdatech.basic.util.CollUtils;
import com.hz.pm.api.projectlib.model.enumeration.InstTypeEnum;
import com.hz.pm.api.projectlib.model.entity.ProjectInst;
import com.hz.pm.api.projectlib.service.IProjectInstService;
import com.hz.pm.api.sys.model.vo.ProcessDetailStatVO;
import lombok.AllArgsConstructor;
import org.flowable.engine.HistoryService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * ProcessStatisticsManage
 * </p>
 *
 * @author WendyYang
 * @since 2023/7/31
 **/
@Component
@AllArgsConstructor
public class ProcessStatisticsManage {

    private final HistoryService historyService;
    private final IProjectInstService projectInstService;

    public ProcessDetailStatVO processStatDetail(TimeUnit unit, Integer instType) {
        unitCheck(unit);
        ProcessDetailStatVO detailStat = ProcessDetailStatVO.init();
        LambdaQueryWrapper<ProjectInst> modelQuery = Wrappers.lambdaQuery(ProjectInst.class)
                .select(ProjectInst::getInstCode)
                .eq(ProjectInst::getInstType, instType);
        List<ProjectInst> projectInsts = projectInstService.list(modelQuery);
        if (projectInsts.isEmpty()) {
            return detailStat;
        }
        List<String> instCodeList = CollUtils.fieldList(projectInsts, ProjectInst::getInstCode);
        List<HistoricProcessInstance> instances = historyService.createHistoricProcessInstanceQuery()
                .processInstanceIds(new HashSet<>(instCodeList))
                .list();
        buildProcessDetailStat(unit, detailStat, instances);
        return detailStat;
    }

    private static void unitCheck(TimeUnit unit) {
        if (!TimeUnit.DAYS.equals(unit) && !TimeUnit.HOURS.equals(unit)) {
            throw BizException.wrap("仅支持以天和小时为单位的统计");
        }
    }

    private static void buildProcessDetailStat(TimeUnit unit, ProcessDetailStatVO stat,
                                               List<HistoricProcessInstance> instances) {
        stat.setTotalInst(instances.size());
        LongSummaryStatistics statistics = instances.stream().filter(w -> {
                    boolean finished = w.getEndTime() != null;
                    if (finished) {
                        stat.incrFinished();
                    } else {
                        stat.incrPending();
                    }
                    return finished;
                }).map(HistoricProcessInstance::getDurationInMillis)
                .collect(Collectors.summarizingLong(Long::longValue));
        if (stat.getFinishedInst() != 0) {
            long unitMillis = unit.toMillis(1);
            stat.setAvgTime(NumberUtil.div(statistics.getAverage(), unitMillis, 1));
            stat.setMaxTime(NumberUtil.div(statistics.getMax(), unitMillis, 1));
            stat.setMinTime(NumberUtil.div(statistics.getMin(), unitMillis, 1));
        }
    }

    public List<ProcessDetailStatVO> processStatDetailList(TimeUnit unit) {
        unitCheck(unit);
        List<ProjectInst> projectInsts = projectInstService.list();
        if (projectInsts.isEmpty()) {
            return Collections.emptyList();
        }
        HashSet<String> instCodes = new HashSet<>();
        Map<InstTypeEnum, List<String>> mapByInstType = projectInsts.stream()
                .filter(w -> InstTypeEnum.getByCode(w.getInstType()) != null)
                .collect(Collectors.groupingBy(w -> InstTypeEnum.getByCode(w.getInstType()),
                        Collectors.mapping(w -> {
                            instCodes.add(w.getInstCode());
                            return w.getInstCode();
                        }, Collectors.toList())));
        List<HistoricProcessInstance> instances = historyService.createHistoricProcessInstanceQuery()
                .processInstanceIds(new HashSet<>(instCodes))
                .list();
        List<ProcessDetailStatVO> res = new ArrayList<>();
        for (Map.Entry<InstTypeEnum, List<String>> entry : mapByInstType.entrySet()) {
            List<String> currInstCodes = entry.getValue();
            List<HistoricProcessInstance> currInsts = new ArrayList<>();
            instances.removeIf(w -> {
                boolean contains = currInstCodes.contains(w.getId());
                if (contains) {
                    currInsts.add(w);
                }
                return contains;
            });
            ProcessDetailStatVO item = ProcessDetailStatVO.init();
            InstTypeEnum instType = entry.getKey();
            item.setInstType(instType.getCode());
            item.setInstTypeName(instType.getDesc());
            buildProcessDetailStat(unit, item, currInsts);
            res.add(item);
        }
        res.sort(Comparator.comparing(ProcessDetailStatVO::getInstType));
        return res;
    }

}
