package com.jeelowcode.service.bpm.config.framework.flowable.core.listener;

import cn.hutool.core.date.DateUtil;
import com.google.common.collect.ImmutableSet;
import com.jeelowcode.framework.utils.tool.spring.SpringUtils;
import com.jeelowcode.service.bpm.config.framework.portal.core.PortalRequest;
import com.jeelowcode.service.bpm.config.framework.portal.core.dto.DeleteRequestInfoDTO;
import com.jeelowcode.service.bpm.config.framework.portal.core.dto.DeleteUserRequestInfoDTO;
import com.jeelowcode.service.bpm.config.framework.portal.core.dto.PortalTodoResponseDTO;
import com.jeelowcode.service.bpm.config.framework.portal.core.dto.ReceiveRequestInfoDTO;
import com.jeelowcode.service.bpm.controller.vo.instance.BpmProcessInstanceRespVO;
import com.jeelowcode.service.bpm.controller.vo.task.BpmTaskRespVO;
import com.jeelowcode.service.bpm.entity.BpmProcessDefinitionExtDO;
import com.jeelowcode.service.bpm.service.IBpmProcessDefinitionService;
import com.jeelowcode.service.bpm.service.IBpmProcessInstanceService;
import com.jeelowcode.service.bpm.service.IBpmTaskService;
import com.jeelowcode.service.system.api.IApiAdminUserApi;
import com.jeelowcode.service.system.dto.AdminUserRespDTO;
import com.jeelowcode.tool.framework.common.util.date.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener;
import org.flowable.engine.delegate.event.FlowableCancelledEvent;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import static cn.hutool.core.collection.CollUtil.getLast;
import static com.jeelowcode.tool.framework.common.util.json.JsonUtils.toJsonString;

/**
 * 监听一些事件推送到门户待办模块
 *
 * @author shelly
 */
@Slf4j
@Component
public class BpmPortalTodoEventListener extends AbstractFlowableEngineEventListener {

    @Lazy
    @Resource
    private IBpmTaskService bpmTaskService;
    @Lazy
    @Resource
    private IBpmProcessInstanceService bpmProcessInstanceService;
    @Lazy
    @Resource
    private IBpmProcessDefinitionService bpmProcessDefinitionService;
    @Lazy
    @Resource
    private IApiAdminUserApi apiAdminUserApi;

    /**
     * 需要本监听器监听的事件
     */
    public static final Set<FlowableEngineEventType> TASK_EVENTS = ImmutableSet.<FlowableEngineEventType>builder()
            .add(FlowableEngineEventType.PROCESS_CREATED)
            .add(FlowableEngineEventType.PROCESS_CANCELLED)
            .add(FlowableEngineEventType.PROCESS_COMPLETED)
            .add(FlowableEngineEventType.TASK_CREATED)
            .add(FlowableEngineEventType.TASK_ASSIGNED)
            .add(FlowableEngineEventType.TASK_COMPLETED)
            .add(FlowableEngineEventType.ACTIVITY_CANCELLED)
            .build();

    /**
     * 构造函数，把需要监听的事件传给父构造器
     */
    public BpmPortalTodoEventListener() {
        super(TASK_EVENTS);
    }

    @Override
    protected void processCreated(FlowableEngineEntityEvent event) {

    }

    @Override
    protected void processCancelled(FlowableCancelledEvent event) {
        // 流程取消事件，这里对应调用流程删除接口
        PortalRequest portalRequest = SpringUtils.getBean(PortalRequest.class);
        if (!Objects.requireNonNull(portalRequest).available()) {
            // 如果没有配置待办平台，则结束这个监听任务的执行
            return;
        }

        try {
            ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(event.getProcessInstanceId());
            DeleteRequestInfoDTO requestDTO = new DeleteRequestInfoDTO()
                    .setFlowId(event.getProcessInstanceId());
            PortalTodoResponseDTO response = portalRequest.deleteRequestInfo(requestDTO);
            log.info("[processCancelled][删除待办信息成功，参数：{}，结果：{}]", toJsonString(requestDTO), toJsonString(response));
        } catch (Exception e) {
            log.error("[processCancelled][删除待办信息失败，参数]", e);
        }

    }

    @Override
    protected void processCompleted(FlowableEngineEntityEvent event) {
        // 流程完成事件，对应流程办结
        PortalRequest portalRequest = SpringUtils.getBean(PortalRequest.class);
        if (!Objects.requireNonNull(portalRequest).available()) {
            // 如果没有配置待办平台，则结束这个监听任务的执行
            return;
        }

        ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(event.getProcessInstanceId());
        BpmProcessDefinitionExtDO processDefinition = bpmProcessDefinitionService.getProcessDefinitionExt(event.getProcessDefinitionId());
        BpmTaskRespVO task = getLast(bpmTaskService.getTaskListByProcessInstanceId(event.getProcessInstanceId()));
        BpmProcessInstanceRespVO processInstanceVO = bpmProcessInstanceService.getProcessInstanceVO(processInstance.getId());

        String processInstanceId = event.getProcessInstanceId();
        String processName = processInstance.getName();
        String processDefinitionName = processInstance.getProcessDefinitionName();
        String activityName = task.getName();
        String pcPath = "/process-instance/detail?id=" + processInstanceId;
        String processStatus = "4";
        String viewStatus = "1";
        String startUserId = processInstance.getStartUserId();
        AdminUserRespDTO startUser = apiAdminUserApi.getUser(Long.valueOf(startUserId));
        Date startTime = processInstance.getStartTime();
        String assignee = String.valueOf(task.getAssigneeUser().getId());
        AdminUserRespDTO assignUser = apiAdminUserApi.getUser(Long.valueOf(assignee));
        LocalDateTime assignTime = task.getCreateTime();
        // 当前时间戳，毫秒级时间戳，13位
        String timestamp = String.valueOf(System.currentTimeMillis());

        String title = processInstance.getName() + "-" + startUser.getNickname() + "-" + DateUtil.formatDateTime(processInstance.getStartTime());

        // 接收请求参数
        ReceiveRequestInfoDTO requestDTO = new ReceiveRequestInfoDTO()
                .setFlowId(processInstanceId)
                .setRequestName(title)
                .setWorkflowName(processDefinitionName)
                .setNodeName(activityName)
                .setPcUrl(pcPath)
                .setIsRemark(processStatus)
                .setViewType(viewStatus)
                .setCreator(startUser.getUsername())
                .setCreateDateTime(startTime)
                .setReceiver(assignUser.getUsername())
                .setReceiveDateTime(DateUtils.of(assignTime))
                .setReceiveTs(timestamp);

        // 调用待办平台
        try {
            PortalTodoResponseDTO response = portalRequest.receiveRequestInfo(requestDTO);
            log.info("[processCompleted][接收待办信息成功，参数({})]", toJsonString(response));
        } catch (Exception e) {
            log.error("接收异构系统流程失败", e);
        }

    }

    @Override
    protected void taskCreated(FlowableEngineEntityEvent event) {

    }

    @Override
    protected void taskAssigned(FlowableEngineEntityEvent event) {
        // 任务分配事件,对应待办
        PortalRequest portalRequest = SpringUtils.getBean(PortalRequest.class);
        if (!Objects.requireNonNull(portalRequest).available()) {
            // 如果没有配置待办平台，则结束这个监听任务的执行
            return;
        }

        Task task = (Task) event.getEntity();
        ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(event.getProcessInstanceId());
        BpmProcessDefinitionExtDO processDefinition = bpmProcessDefinitionService.getProcessDefinitionExt(event.getProcessDefinitionId());
        BpmProcessInstanceRespVO processInstanceVO = bpmProcessInstanceService.getProcessInstanceVO(event.getProcessInstanceId());

        // 获取流程实例id
        String processInstanceId = event.getProcessInstanceId();
        // 流程类型名称
        String processDefinitionName = processInstance.getProcessDefinitionName();
        // 步骤名称
        String activityName = task.getName();
        // pc url 地址
        String pcPath = "/process-instance/detail?id=" + processInstanceId;
        // 流程处理状态: 待办
        String processStatus = "0";
        // 流程查看状态: 未读
        String viewStatus = "1";
        // 创建人
        String startUserId = processInstance.getStartUserId();
        AdminUserRespDTO startUser = apiAdminUserApi.getUser(Long.valueOf(startUserId));

        // 创建时间
        Date startTime = processInstance.getStartTime();
        // 接收人
        String assignee = task.getAssignee();
        AdminUserRespDTO assigneeUser = apiAdminUserApi.getUser(Long.valueOf(assignee));

        // 接收时间
        Date assigneeTime = task.getCreateTime();
        // 当前时间戳，毫秒级时间戳，13位
        String timestamp = String.valueOf(System.currentTimeMillis());
        // 待办标题
        String title = processInstance.getName() + "-" + startUser.getNickname() + "-" + DateUtil.formatDateTime(processInstance.getStartTime());

        // 接收请求参数
        ReceiveRequestInfoDTO requestDTO = new ReceiveRequestInfoDTO()
                .setFlowId(processInstanceId)
                .setRequestName(title)
                .setWorkflowName(processDefinitionName)
                .setNodeName(activityName)
                .setPcUrl(pcPath)
                .setIsRemark(processStatus)
                .setViewType(viewStatus)
                .setCreator(startUser.getUsername())
                .setCreateDateTime(startTime)
                .setReceiver(assigneeUser.getUsername())
                .setReceiveDateTime(assigneeTime)
                .setReceiveTs(timestamp);

        // 调用待办平台
        try {
            log.error("接收异构系统流程参数({})", toJsonString(requestDTO));
            PortalTodoResponseDTO response = portalRequest.receiveRequestInfo(requestDTO);
            log.error("接收异构系统流程成功({})", toJsonString(response));
        } catch (Exception e) {
            log.error("接收异构系统流程失败", e);
        }

    }

    @Override
    protected void taskCompleted(FlowableEngineEntityEvent event) {
        // 任务完成事件，这里处理任务已办
        PortalRequest portalRequest = SpringUtils.getBean(PortalRequest.class);
        if (!Objects.requireNonNull(portalRequest).available()) {
            // 如果没有配置待办平台，则结束这个监听任务的执行
            return;
        }

        Task task = (Task) event.getEntity();
        ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(event.getProcessInstanceId());
        BpmProcessDefinitionExtDO processDefinition = bpmProcessDefinitionService.getProcessDefinitionExt(event.getProcessDefinitionId());
        BpmProcessInstanceRespVO processInstanceVO = bpmProcessInstanceService.getProcessInstanceVO(event.getProcessInstanceId());

        // 获取流程实例id
        String processInstanceId = event.getProcessInstanceId();
        // 流程类型名称
        String processDefinitionName = processInstance.getProcessDefinitionName();
        // 步骤名称
        String activityName = task.getName();
        // pc url 地址
        String pcPath = "/process-instance/detail?id=" + processInstanceId;
        // 流程处理状态: 已办
        String processStatus = "2";
        // 流程查看状态: 未读
        String viewStatus = "1";
        // 创建人
        String startUserId = processInstance.getStartUserId();
        AdminUserRespDTO startUser = apiAdminUserApi.getUser(Long.valueOf(startUserId));

        // 创建时间
        Date startTime = processInstance.getStartTime();
        // 接收人
        String assignee = task.getAssignee();
        AdminUserRespDTO assigneeUser = apiAdminUserApi.getUser(Long.valueOf(assignee));

        // 接收时间
        Date assigneeTime = task.getCreateTime();
        // 当前时间戳，毫秒级时间戳，13位
        String timestamp = String.valueOf(System.currentTimeMillis());
        // 待办标题
        String title = processInstance.getName() + "-" + startUser.getNickname() + "-" + DateUtil.formatDateTime(processInstance.getStartTime());

        // 接收请求参数
        ReceiveRequestInfoDTO requestDTO = new ReceiveRequestInfoDTO()
                .setFlowId(processInstanceId)
                .setRequestName(title)
                .setWorkflowName(processDefinitionName)
                .setNodeName(activityName)
                .setPcUrl(pcPath)
                .setIsRemark(processStatus)
                .setViewType(viewStatus)
                .setCreator(startUser.getUsername())
                .setCreateDateTime(startTime)
                .setReceiver(assigneeUser.getUsername())
                .setReceiveDateTime(assigneeTime)
                .setReceiveTs(timestamp);

        // 调用待办平台
        try {
            PortalTodoResponseDTO response = portalRequest.receiveRequestInfo(requestDTO);
            log.info("[taskCompleted][请求待办平台成功 response({})]", toJsonString(response));
        } catch (Exception e) {
            log.error("接收异构系统流程失败", e);
        }

        // 判断是否为多实例任务(会签任务)
        if (isMultiInstanceTask(task)) {
            // 检查会签任务是否全部完成
            if (isMultiInstanceCompleted(task)) {
                log.info("[会签任务完成] 流程实例ID: {}, 任务定义Key: {}",
                        event.getProcessInstanceId(), task.getTaskDefinitionKey());

                // 在这里添加会签完成后的业务逻辑
                handleMultiInstanceCompletion(event.getProcessInstanceId(), task, portalRequest);
            } else {
                log.info("[会签任务进行中] 流程实例ID: {}, 任务定义Key: {}",
                        event.getProcessInstanceId(), task.getTaskDefinitionKey());
            }
        }

    }

    /**
     * 判断是否为多实例任务
     *
     * @param task 任务实例
     * @return 是否为多实例任务
     */
    private boolean isMultiInstanceTask(Task task) {
        // 通过任务变量判断是否为多实例任务
        return task.getProcessVariables().containsKey("nrOfInstances") &&
                task.getProcessVariables().containsKey("nrOfCompletedInstances");
    }

    /**
     * 判断多实例任务是否已完成
     *
     * @param task 任务实例
     * @return 是否已完成
     */
    private boolean isMultiInstanceCompleted(Task task) {
        // 获取多实例相关变量
        Integer nrOfInstances = (Integer) task.getProcessVariables().get("nrOfInstances");
        Integer nrOfCompletedInstances = (Integer) task.getProcessVariables().get("nrOfCompletedInstances");

        // 如果实例总数等于已完成实例数，则表示会签完成
        return nrOfInstances != null && nrOfCompletedInstances != null &&
                nrOfInstances.compareTo(nrOfCompletedInstances) == 0;
    }

    /**
     * 处理会签完成事件
     *
     * @param processInstanceId 流程实例ID
     * @param task              任务实例
     */
    private void handleMultiInstanceCompletion(String processInstanceId, Task task, PortalRequest portalRequest) {
        // 这里可以添加会签完成后的业务逻辑
        log.info("会签任务完成处理: 流程实例ID={}, 任务名称={}, 任务Key={}",
                processInstanceId, task.getName(), task.getTaskDefinitionKey());

        // 查询目前谁还没有完成会签任务，将没有完成的人的任务发送到待办平台取消掉
        List<String> candidateUsers = bpmTaskService.getIdentityLinksForTask(task.getId());
        List<String> completedUsers = bpmTaskService.getCompletedUsers(processInstanceId, task.getTaskDefinitionKey());
        List<String> pendingUsers = candidateUsers.stream()
                .filter(userId -> !completedUsers.contains(userId))
                .collect(Collectors.toList());

        // 计算出了哪些用户没有完成会签任务，此时需要将这些用户的任务取消掉
        if (pendingUsers != null && !pendingUsers.isEmpty()) {
            try {
                List<DeleteUserRequestInfoDTO> requestInfoDTOList = pendingUsers.stream()
                        .map(userId -> {
                            AdminUserRespDTO pendingUser = apiAdminUserApi.getUser(Long.valueOf(userId));
                            return new DeleteUserRequestInfoDTO()
                                    .setFlowId(processInstanceId).setUserId(pendingUser.getUsername());
                        })
                        .collect(Collectors.toList());
                PortalTodoResponseDTO response = portalRequest.batchDeleteUserRequestInfo(requestInfoDTOList);
                log.info("[handleMultiInstanceCompletion][取消待办成功 response({})]", toJsonString(response));
            } catch (Exception e) {
                log.error("取消待办失败", e);
            }
        }

    }

}
