package com.ningdatech.pmapi.scheduler.listener;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.ningdatech.basic.exception.BizException;
import com.ningdatech.file.service.FileService;
import com.ningdatech.pmapi.common.constant.BizConst;
import com.ningdatech.pmapi.common.constant.ProjectDeclareConst;
import com.ningdatech.pmapi.common.statemachine.util.StateMachineUtils;
import com.ningdatech.pmapi.projectdeclared.converter.ApplicationConverter;
import com.ningdatech.pmapi.projectlib.enumeration.InstTypeEnum;
import com.ningdatech.pmapi.projectlib.enumeration.ProjectStatusEnum;
import com.ningdatech.pmapi.projectlib.model.entity.Project;
import com.ningdatech.pmapi.projectlib.model.entity.ProjectApplication;
import com.ningdatech.pmapi.projectlib.model.entity.ProjectInst;
import com.ningdatech.pmapi.projectlib.service.IProjectApplicationService;
import com.ningdatech.pmapi.projectlib.service.IProjectInstService;
import com.ningdatech.pmapi.projectlib.service.IProjectService;
import com.ningdatech.pmapi.provincial.service.IJoinReviewProvincialBureauService;
import com.ningdatech.pmapi.staging.enums.MsgTypeEnum;
import com.ningdatech.pmapi.staging.service.IProjectStagingService;
import com.ningdatech.pmapi.sys.manage.NoticeManage;
import com.ningdatech.pmapi.todocenter.manage.HandlerManage;
import com.wflow.contants.HisProInsEndActId;
import com.wflow.workflow.bean.process.ProgressNode;
import com.wflow.workflow.bean.process.enums.NodeTypeEnum;
import com.wflow.workflow.bean.vo.ProcessProgressVo;
import com.wflow.workflow.notify.event.ProcessEndEvent;
import com.wflow.workflow.service.ProcessInstanceService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.HistoryService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.ningdatech.pmapi.todocenter.constant.WorkNoticeConstant.PASS_MSG_TEMPLATE;
import static com.ningdatech.pmapi.todocenter.constant.WorkNoticeConstant.PASS_MSG_TEMPLATE2;

/**
 * 工作流开始 结束监听 自动审批的话 会漏掉 调用状态机
 *
 * @author ZPF
 * @return
 * @since 2023/04/14 14:19
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class ProcessEndListener {
    private final TaskService taskService;
    private final HistoryService historyService;
    private final IProjectInstService projectInstService;

    private final IProjectService projectService;

    private final ProcessInstanceService processInstanceService;

    private final HandlerManage handlerManage;

    private final IProjectStagingService projectStagingService;

    private final NoticeManage noticeManage;

    private final IJoinReviewProvincialBureauService joinReviewProvincialBureauService;

    private final IProjectApplicationService projectApplicationService;

    private final FileService fileService;

    @Value("${spring.profiles.active}")
    private String active;

    @Async
    @EventListener
    public void onApplicationEvent(ProcessEndEvent event) {
        log.info("进入工作流结束的 事件监听!{}", event.getInstCode());
        String instCode = event.getInstCode();

        log.info("instCode:{}", instCode);

        //休息3秒
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        //1.去查找 项目实例关系表
        ProjectInst projectInst = projectInstService.getOne(Wrappers.lambdaQuery(ProjectInst.class)
                .eq(ProjectInst::getInstCode, instCode)
                .last(BizConst.LIMIT_1));

        if (Objects.isNull(projectInst)) {
            return;
        }

        List<Task> tasks = taskService.createTaskQuery()
                .processInstanceId(instCode)
                .active()
                .list();

        List<HistoricTaskInstance> historyTasks = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(instCode)
                .unfinished()
                .list();

        //已经没有任务了
        if (CollUtil.isEmpty(tasks) && CollUtil.isEmpty(historyTasks)) {
            List<HistoricTaskInstance> historyTasksFinished = historyService.createHistoricTaskInstanceQuery()
                    .processInstanceId(instCode)
                    .finished()
                    .list();

            Long projectId = projectInst.getProjectId();
            Project project = projectService.getNewProject(projectId);
            HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(instCode).singleResult();
            //通过审核后 所处理的逻辑
            afterPassTodo(project,instance);
        }
    }

    /**
     * 审核通过后 所处理的逻辑
     * @param declaredProject
     * @param instance
     */
    public void afterPassTodo(Project declaredProject, HistoricProcessInstance instance){
        Long userId = 110L;
        // 获取流程通过后的流程实例
        HistoricProcessInstance newInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(instance.getId())
                .singleResult();
        // 获取当前流程实例类型
        String instanceId = newInstance.getId();
        ProjectInst projectInst = projectInstService.getOne(Wrappers.lambdaQuery(ProjectInst.class)
                .eq(ProjectInst::getInstCode, instanceId)
                .last(BizConst.LIMIT_1));
        Integer instType = projectInst.getInstType();
        InstTypeEnum instTypeEnum = InstTypeEnum.getByCode(instType);
        if (Objects.isNull(instTypeEnum)){
            throw new BizException("当前审批流类型不存在,流程类型code:" + instType);
        }

        // 获取流程通过后当前流程详情
        ProcessProgressVo newInstanceDetail = processInstanceService.getProgressInstanceDetail(null, instance.getId());
        // 获取流程通过后当前审核人信息，向其发送工作通知
        // 会签/或签会有多个审核人
        List<String> currentEmployeeCodeList = Lists.newArrayList();
        List<ProgressNode> newProgressInfo = newInstanceDetail.getProgressInfo();
        ProgressNode currentNode = newProgressInfo.get(newProgressInfo.size() - 1);

        // 说明当前节点是子流程节点
        if (currentNode.getNodeType().name().equals(NodeTypeEnum.SUB.name())) {
            List<ProgressNode> children = currentNode.getChildren();
            // 获取子流程当前审核人节点
            ProgressNode subCurrentNode = CollUtil.isEmpty(children) ? new ProgressNode() : children.get(children.size() - 1);
            // 获取节点ID相同地审核节点
            List<ProgressNode> nodeList = children.stream()
                    .filter(c -> subCurrentNode.getNodeId().equals(c.getNodeId()))
                    .collect(Collectors.toList());
            List<String> userIdList = nodeList.stream().map(ProgressNode::getUserId).collect(Collectors.toList());
            currentEmployeeCodeList.addAll(userIdList);
        } else {
            List<ProgressNode> nodeList = newProgressInfo.stream()
                    .filter(c -> currentNode.getNodeId().equals(c.getNodeId()))
                    .collect(Collectors.toList());
            List<String> userIdList = nodeList.stream().map(ProgressNode::getUserId).collect(Collectors.toList());
            currentEmployeeCodeList.addAll(userIdList);
        }

        // 流程通过后，判断当前登录用户是不是最后一个审核人
        // 若当前登录用户是最后一个审批人，需更新流程状态为审核完成，项目状态到下个状态
        // 并向流程发起人发送浙政钉工作通知：【项目名称】已通过【流程名称】，请及时开始下一步操作。
        if (HisProInsEndActId.END.equals(newInstance.getEndActivityId())) {
            // 如果是申请延期和申请借阅审批流程，不走状态机
            if (InstTypeEnum.APPLY_DELAY.getCode().equals(instType) || InstTypeEnum.APPLY_BORROW.getCode().equals(instType)){
                switch (instTypeEnum) {
                    case APPLY_DELAY:
                        // 保存延期申请记录,更新项目建设周期和计划验收时间
                        handlerManage.updateProjectDelayApplyInfo(declaredProject,instanceId);
                        break;
                    case APPLY_BORROW:
                        // 更新申请借阅状态为成功
                        handlerManage.updateProjectApplyBorrowInfo(declaredProject,instanceId);
                        break;
                    default:
                        throw new BizException("传入实例类型错误: " + instTypeEnum);
                }
            }else {
                switch (Objects.requireNonNull(ProjectStatusEnum.getValue(declaredProject.getStatus()))) {
                    // 当前项目状态是预审中
                    case PRE_APPLYING:
                        //先修改项目状态
                        handlerManage.updatePassProjectStatus(userId, declaredProject);
                        //然后入库暂存库
                        projectStagingService.addByProject(declaredProject, "暂存入库 待提交部门联审");
                        break;
                    // 当前项目状态是单位内部审核中
                    case UNDER_INTERNAL_AUDIT:
                        // 当前项目状态是部门联审中
                    case DEPARTMENT_JOINT_REVIEW:
                        //如果是 区县 并且 500万及以上要推送省局重大项目
                        if (!StateMachineUtils.isCityProject(declaredProject) &&
                                StateMachineUtils.judgeDeclareAmount(declaredProject,
                                        ProjectDeclareConst.Number.DECLARE_COUNTY_AMOUNT_JUDGEMENT)) {
                            try {
                                List<ProjectApplication> applications = projectApplicationService
                                        .list(Wrappers.lambdaQuery(ProjectApplication.class)
                                                .eq(ProjectApplication::getProjectId, declaredProject.getId()));
                                joinReviewProvincialBureauService.pushImportProject(
                                        ApplicationConverter.convertProject(declaredProject, applications, fileService,active));
                                handlerManage.updatePassProjectStatus(userId, declaredProject);
                                break;
                            } catch (Exception e) {
                                log.info("区县推送省级联审失败[{}],{}", declaredProject.getProjectName(), e.getMessage());
                                log.error("区县推送省级联审失败:" + e);
                            }
                        }
                        // 当前项目状态是方案评审中
                    case SCHEME_UNDER_REVIEW:
                        // 当前项目状态是终验审核中
                    case FINAL_ACCEPTANCE_IS_UNDER_REVIEW:
                        handlerManage.updatePassProjectStatus(userId, declaredProject);
                        break;
                    default:
                        throw new BizException("传入项目状态错误： " + declaredProject.getStatus());
                }
            }
            //发送消息
            noticeManage.sendNotice(instance.getStartUserId(),userId,declaredProject,instance.getProcessDefinitionName(),
                    PASS_MSG_TEMPLATE2, MsgTypeEnum.PROJECT_REVIEW_PASS);
        } else {
            // 若有下一个审核人(当前节点的用户)，会签/或签会有多个
            // 向其发送浙政钉工作通知：标题：审核任务 内容：【单位名称】的【项目名称】需要您审核。
            // 获取发送浙政钉工作通知必要信息
            for (String currentEmployeeCode : currentEmployeeCodeList) {
                if (Objects.isNull(currentEmployeeCode)) {
                    throw new BizException("审核人信息不存在!");
                }
                //发送消息
                noticeManage.sendNotice(currentEmployeeCode,userId,declaredProject,instance.getProcessDefinitionName(),
                        PASS_MSG_TEMPLATE,MsgTypeEnum.PROJECT_REVIEW);
            }
        }
    }
}