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

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.hz.pm.api.common.enumeration.ProjectProcessStageEnum;
import com.hz.pm.api.common.model.entity.DataDTO;
import com.hz.pm.api.dashboard.model.vo.EarlyWarningStatisticsVO;
import com.hz.pm.api.projectlib.model.enumeration.ProjectStatusEnum;
import com.hz.pm.api.projectlib.model.enumeration.WarningFlowTypeEnum;
import com.hz.pm.api.projectlib.model.enumeration.WarningOperationTypeEnum;
import com.hz.pm.api.projectlib.model.entity.Project;
import com.hz.pm.api.projectlib.model.entity.ProjectInst;
import com.hz.pm.api.projectlib.service.IProjectInstService;
import com.hz.pm.api.projectlib.service.IProjectService;
import com.hz.pm.api.sys.model.entity.WflowEarlyWarningRecords;
import com.hz.pm.api.sys.service.IEarlyWarningRecordsService;
import com.hz.pm.api.sys.utils.EarlyWarningUtil;
import com.hz.pm.api.user.security.model.UserInfoDetails;
import com.hz.pm.api.user.util.LoginUserUtil;
import com.wflow.enums.WarningNoticeTypeEnum;
import com.wflow.enums.WarningRuleTypeEnum;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.engine.HistoryService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @Classname EarlyWarningStatisticsManage
 * @Description
 * @Date 2023/12/05 17:44
 * @Author PoffyZhang
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class EarlyWarningStatisticsManage {

    private final IEarlyWarningRecordsService earlyWarningRecordsService;

    private final HistoryService historyService;

    private final IProjectInstService projectInstService;

    private final IProjectService projectService;

    private final TaskService taskService;

    private static final List<Integer> threeYears = Lists.newArrayList(LocalDateTime.now().getYear() - 2,
            LocalDateTime.now().getYear() - 1,LocalDateTime.now().getYear());

    public EarlyWarningStatisticsVO statistics(Integer year) {
        UserInfoDetails user = LoginUserUtil.loginUserDetail();

        //项目
        List<Project> projects = projectService.list(Wrappers.lambdaQuery(Project.class)
                        .eq(Project::getNewest, Boolean.TRUE));

        EarlyWarningStatisticsVO res = new EarlyWarningStatisticsVO();
        LambdaQueryWrapper<WflowEarlyWarningRecords> wrapper = Wrappers.lambdaQuery(WflowEarlyWarningRecords.class);
        EarlyWarningUtil.buildPermissionWrapper(wrapper,user);
        List<WflowEarlyWarningRecords> records = earlyWarningRecordsService.list(wrapper);
        //累积总告警数
        res.setAccumulatedAlarmsTotal(records.size());
        res.setAccumulatedAlarmsFlow(records.stream().filter(a -> {
            if (Objects.nonNull(a.getRuleType()) &&
                    a.getRuleType().equals(WarningRuleTypeEnum.PROCESS_WARNING.getCode())) {
                return Boolean.TRUE;
            }
            return Boolean.FALSE;
        }).collect(Collectors.toList()).size());
        res.setAccumulatedAlarmsFilling(records.stream().filter(a -> {
            if (Objects.nonNull(a.getRuleType()) &&
                    a.getRuleType().equals(WarningRuleTypeEnum.DECLARED_WARNING.getCode())) {
                return Boolean.TRUE;
            }
            return Boolean.FALSE;
        }).collect(Collectors.toList()).size());

        //近三年
        List<DataDTO> threeYearsAlarms = Lists.newArrayList();
        for(Integer thisYear : threeYears){
            DataDTO yearData = new DataDTO();
            yearData.setName(thisYear.toString());
            yearData.setNum(records.stream().filter(r -> {
                if(Objects.nonNull(r.getWarningTime()) &&
                        thisYear.equals(r.getWarningTime().getYear())){
                    return Boolean.TRUE;
                }
                return Boolean.FALSE;
            }).collect(Collectors.toList()).size());
            threeYearsAlarms.add(yearData);
        }
        res.setThreeYearsAlarms(threeYearsAlarms);

        //当前各流程报警数
        List<DataDTO> alarmsFlowsNow = Lists.newArrayList();
        for(ProjectProcessStageEnum stageEnum : ProjectProcessStageEnum.values()){
            String processName = stageEnum.getDesc();
            Integer processType = stageEnum.getCode();
            alarmsFlowsNow.add(DataDTO.of(processName,computeFlowWarningNow(processType,records)));
        }
        res.setAlarmsFlowsNow(alarmsFlowsNow);

        //当前各填报报警数
        List<DataDTO> alarmsFillingNow = Lists.newArrayList();
        for(WarningFlowTypeEnum fillingEnum : WarningFlowTypeEnum.values()){
            String processName = fillingEnum.getDesc();
            Integer projectStutas = fillingEnum.getProjectStutas();
            alarmsFillingNow.add(DataDTO.of(processName,computeFillingNow(projectStutas,records,projects)));
        }
        res.setAlarmsFillingNow(alarmsFillingNow);

        //实施预警
        List<DataDTO> alarmsConstruction = Lists.newArrayList();
        for(WarningNoticeTypeEnum noticeTypeEnum : WarningNoticeTypeEnum.values()){
            String name = noticeTypeEnum.getDesc();
            Integer noticeType = noticeTypeEnum.getCode();
            alarmsConstruction.add(DataDTO.of(name,computeConstruction(noticeType,records,projects)));
        }
        res.setConstructionAlarms(alarmsConstruction);

        //安全监测
        List<DataDTO> safetyMonitoring = Lists.newArrayList();
        //--1 安全设计
        safetyMonitoring.add(DataDTO.of("安全设计",0));
        //--2 安全投入低于5%
        safetyMonitoring.add(DataDTO.of("安全投入低于5%",computeSafeLessFive(projects)));
        //--3 非信创
        safetyMonitoring.add(DataDTO.of("非信创",computeNotXinChuang(projects)));
        //--4 等保未做
        safetyMonitoring.add(DataDTO.of("等保未做",0));
        //--5 密评未做
        safetyMonitoring.add(DataDTO.of("密评未做",0));
        res.setSafetyMonitoring(safetyMonitoring);
        return res;
    }

    //计算非信创
    private Integer computeNotXinChuang(List<Project> projects) {
        return projects.stream().filter(p -> StringUtils.isNotBlank(p.getCloudType()) &&
                        !p.getCloudType().contains("2"))
                .collect(Collectors.toList()).size();
    }

    //计算安全投入低于5%
    private Integer computeSafeLessFive(List<Project> projects) {
        return projects.stream().filter(p -> {
            //计算出 安全投入
            BigDecimal rate = new BigDecimal(DashboardProjectManage.convertSafetyInputRate(p));
            if (rate.compareTo(new BigDecimal(5)) < 0) {
                return Boolean.TRUE;
            }
            return Boolean.FALSE;
        }).collect(Collectors.toList()).size();
    }

    private Integer computeConstruction(Integer noticeType, List<WflowEarlyWarningRecords> records,
                                        List<Project> projects) {
        //终验告警
        List<WflowEarlyWarningRecords> constructionRecords = records.stream().filter(r -> {
            if (Objects.nonNull(r.getRuleType()) &&
                    WarningRuleTypeEnum.OPERATION_WARNING.getCode().equals(r.getRuleType())) {
                return Boolean.TRUE;
            }
            return Boolean.FALSE;
        }).filter(r -> Objects.nonNull(r.getNoticeType()) && r.getNoticeType().equals(noticeType) &&
                        WarningOperationTypeEnum.ZHONGYAN.getCode().equals(r.getBiz()))
                .collect(Collectors.toList());
        if(CollUtil.isEmpty(constructionRecords)){
            return 0;
        }
        Map<String, List<WflowEarlyWarningRecords>> recordsMap = constructionRecords.stream().collect(Collectors.groupingBy(
                WflowEarlyWarningRecords::getProjectCode));

        //待终验的项目
        List<Project> tobeFinalProjects = projects.stream().filter(p -> {
                    if(Objects.nonNull(p.getStatus()) &&
                            p.getStatus().equals(ProjectStatusEnum.TO_BE_FINALLY_INSPECTED.getCode())){
                        return Boolean.TRUE;
                    }
                    return Boolean.FALSE;
                }).collect(Collectors.toList());

        return tobeFinalProjects.stream().filter(p -> {
            if(recordsMap.containsKey(p.getProjectCode())){
                List<WflowEarlyWarningRecords> warningRecords = recordsMap.get(p.getProjectCode());
                //如果是临期的 不能包含有超期的
                if(noticeType.equals(WarningNoticeTypeEnum.ADVENT.getCode())){
                    for(WflowEarlyWarningRecords warningRecord : warningRecords){
                        if(Objects.isNull(warningRecord.getNoticeType()) || warningRecord.getNoticeType()
                                .equals(WarningNoticeTypeEnum.OVER.getCode())){
                            return Boolean.FALSE;
                        }
                    }
                }
                //这个状态有告警 并且 这个项目也是这个类型
                return Boolean.TRUE;
            }
            return Boolean.FALSE;
        }).collect(Collectors.toList()).size();
    }

    private Integer computeFillingNow(Integer projectStutas, List<WflowEarlyWarningRecords> records,
                                      List<Project> projects) {
        List<WflowEarlyWarningRecords> fillingRecords = records.stream().filter(r -> {
            if (Objects.nonNull(r.getRuleType()) &&
                    WarningRuleTypeEnum.DECLARED_WARNING.getCode().equals(r.getRuleType())) {
                return Boolean.TRUE;
            }
            return Boolean.FALSE;
        }).filter(r -> Objects.nonNull(r.getNodeId())).collect(Collectors.toList());
        if(CollUtil.isEmpty(fillingRecords)){
            return 0;
        }
        Map<String, List<WflowEarlyWarningRecords>> warningMap = fillingRecords.stream()
                .collect(Collectors.groupingBy(WflowEarlyWarningRecords::getProjectCode));

        List<Project> thisStatusProjects = projects.stream().filter(p -> {
            if(Objects.nonNull(p.getStatus()) && p.getStatus().equals(projectStutas)){
                return Boolean.TRUE;
            }
            return Boolean.FALSE;
        }).collect(Collectors.toList());

        return thisStatusProjects.stream().filter(p -> {
            if(warningMap.containsKey(p.getProjectCode())){
                //这个状态有告警 并且 这个项目也是这个类型
                return Boolean.TRUE;
            }
            return Boolean.FALSE;
        }).collect(Collectors.toList()).size();
    }

    //计算某个流程当前正在告警的数量
    private Integer computeFlowWarningNow(Integer processType,List<WflowEarlyWarningRecords> records) {
        List<WflowEarlyWarningRecords> processRecords = records.stream().filter(r -> {
            if (Objects.nonNull(r.getRuleType()) &&
                    WarningRuleTypeEnum.PROCESS_WARNING.getCode().equals(r.getRuleType())) {
                return Boolean.TRUE;
            }
            return Boolean.FALSE;
        }).filter(r -> Objects.nonNull(r.getNodeId())).collect(Collectors.toList());
        if(CollUtil.isEmpty(processRecords)){
            return 0;
        }
        Map<String, List<WflowEarlyWarningRecords>> warningMap = processRecords.stream()
                .collect(Collectors.groupingBy(WflowEarlyWarningRecords::getNodeId));

        //1.先查出 所育 这个类型的 流程
        List<ProjectInst> proInsts = projectInstService.list(Wrappers.lambdaQuery(ProjectInst.class)
                .eq(ProjectInst::getInstType, processType));
        if(CollUtil.isEmpty(proInsts)){
            return 0;
        }
        //2.再筛选一下 选出 正在审核 未完成的
        Set<String> instCodes = proInsts.stream().map(ProjectInst::getInstCode)
                .collect(Collectors.toSet());
        List<HistoricProcessInstance> processes = historyService.createHistoricProcessInstanceQuery()
                .processInstanceIds(instCodes)
                .unfinished()
                .orderByProcessInstanceStartTime()
                .asc()
                .list();
        if(CollUtil.isEmpty(processes)){
            return 0;
        }
        //查出所有正在执行的工作流任务
        List<Task> tasks = taskService.createTaskQuery()
                .list();
        if(CollUtil.isEmpty(tasks)){
            return 0;
        }
        Map<String, List<Task>> taskMap = tasks.stream()
                .collect(Collectors.groupingBy(Task::getProcessInstanceId));
        return processes.stream().filter(p -> {
            if(taskMap.containsKey(p.getId())){
                List<Task> tasksThis = taskMap.get(p.getId());
                for(Task taskThis : tasksThis){
                    //说明 当前有告警
                    if(warningMap.containsKey(taskThis.getTaskDefinitionKey())){
                        return Boolean.TRUE;
                    }
                }
            }
            return Boolean.FALSE;
        }).collect(Collectors.toList()).size();
    }
}
