package cn.fateverse.workflow.process.listener;

import cn.fateverse.admin.dubbo.DubboDeptService;
import cn.fateverse.admin.dubbo.DubboUserService;
import cn.fateverse.admin.entity.Dept;
import cn.fateverse.admin.entity.User;
import cn.fateverse.admin.vo.DeptVo;
import cn.fateverse.admin.vo.UserVo;
import cn.fateverse.common.core.exception.CustomException;
import cn.fateverse.workflow.constant.ProcessConstant;
import cn.fateverse.workflow.entity.HistoricalOperation;
import cn.fateverse.workflow.entity.bpmn.ProcessNode;
import cn.fateverse.workflow.entity.bpmn.Properties;
import cn.fateverse.workflow.entity.bpmn.RoleInfo;
import cn.fateverse.workflow.entity.bpmn.UserInfo;
import cn.fateverse.workflow.enums.AssigneeTypeEnums;
import cn.fateverse.workflow.enums.ModeEnums;
import cn.fateverse.workflow.enums.OperationStateEnums;
import cn.fateverse.workflow.mapper.HistoricalOperationMapper;
import cn.fateverse.workflow.process.ProcessServiceUtils;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import org.apache.dubbo.config.annotation.DubboReference;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener;
import org.springframework.stereotype.Component;

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

/**
 * 会签监听器
 *
 * @author Clay
 * @date 2022/12/29
 */
@Component
public class CounterSignListener implements ExecutionListener {

    private final RepositoryService repositoryService;

    private final HistoricalOperationMapper operationMapper;

    @DubboReference
    private DubboUserService userService;

    @DubboReference
    private DubboDeptService deptService;

    public CounterSignListener(RepositoryService repositoryService,
                               HistoricalOperationMapper operationMapper) {
        this.repositoryService = repositoryService;
        this.operationMapper = operationMapper;
    }

    @Override
    public void notify(DelegateExecution execution) {
        String value = ProcessServiceUtils.getBpmnValue(repositoryService, execution.getProcessDefinitionId(), ProcessConstant.BPMN_FORM_PROCESS);
        //获取到节点的数据信息
        String currentActivityId = execution.getCurrentActivityId();
        //获取到流程节点的数据信息
        Optional<ProcessNode> nodeOptional = JSON.parseArray(value, ProcessNode.class).stream().filter(node -> currentActivityId.equals(node.getId())).findAny();
        if (nodeOptional.isEmpty()) {
            throw new CustomException("数据异常!");
        }
        ProcessNode processNode = nodeOptional.get();
        //获取到流程参数数据
        Map<String, Object> variables = execution.getVariables();
        //创建审批人用户列表
        List<String> assignedUserList = new ArrayList<>();
        //创建当前需要使用审批人列表的assigneeList,配合MultiInstanceLoopCharacteristics实现会签功能
        String variableKey = currentActivityId + ProcessConstant.ASSIGNEE_LIST;
        //获取到当前节点的配置信息
        Properties props = processNode.getProps();
        //获取到当前节点的审批类型枚举
        AssigneeTypeEnums assignedType = props.getAssignedType();
        switch (assignedType) {
            case ASSIGN_USER:
                //用户自己选择审批人
                List<UserInfo> assignedUser = props.getAssignedUser();
                for (UserInfo userInfo : assignedUser) {
                    assignedUserList.add(userInfo.getId());
                }
                break;
            //用户自己审批
            case SELF:
                String userId = variables.get(ProcessConstant.START_USER_ID).toString();
                assignedUserList.add(userId);
                break;
            //用户自选
            case SELF_SELECT:
                setSelfSelect(processNode.getId(), assignedUserList, variables);
                break;
                //角色
            case ROLE:
                setRoleUser(assignedUserList, props);
                break;
            //连续多级主管
            case LEADER_TOP:
                setLeaderUserTop(assignedUserList, props, variables);
                break;
            //主管
            case LEADER:
                setLeaderUser(assignedUserList, props, variables);
                break;
            //表单内联系人
            case FORM_USER:
                break;
            default:
                throw new CustomException("数据异常!");
        }
        //当没有查询到审批人的时候,进行的业务处理
        Map<String, Object> nobody = props.getNobody();
        boolean hasAssignedUser = true;
        OperationStateEnums assigneeEnums = null;
        if (assignedUserList.isEmpty()) {
            switch (nobody.get("handler").toString()) {
                case "TO_PASS":
                    //todo 直接通过
                    assigneeEnums = OperationStateEnums.AUTO_PASS;
                    assignedUserList.add(OperationStateEnums.AUTO_PASS.getState());
                    execution.setVariable(OperationStateEnums.AUTO_PASS.getState(), Boolean.TRUE);
                    hasAssignedUser = false;
                    break;
                case "TO_REFUSE":
                    //todo 直接拒绝
                    assigneeEnums = OperationStateEnums.AUTO_REFUSE;
                    assignedUserList.add(OperationStateEnums.AUTO_REFUSE.getState());
                    execution.setVariable(OperationStateEnums.AUTO_REFUSE.getState(), Boolean.TRUE);
                    hasAssignedUser = false;
                    break;
                case "TO_ADMIN":
                    //交给admin审批
                    //todo 需要去查询所有为admin角色的用户
                    List<UserVo> userList = userService.searchUserListByRoleIds(Collections.singletonList(1L));
                    userList = checkResult(userList, "获取到用户数据为空!");
                    userList.forEach(user -> assignedUserList.add(user.getUserId().toString()));
                    break;
                case "TO_USER":
                    //指定到用户
                    Object userObj = nobody.get("assignedUser");
                    if (!Objects.isNull(userObj)) {
                        List<UserInfo> userInfoList = JSON.parseArray(JSON.toJSONString(userObj), UserInfo.class);
                        if (userInfoList.size() > 0) {
                            for (UserInfo userInfo : userInfoList) {
                                assignedUserList.add(userInfo.getId());
                            }
                        } else {
                            execution.setVariable(OperationStateEnums.AUTO_PASS.getState(), Boolean.TRUE);
                        }
                    }
                    break;
                default:
                    throw new CustomException("数据异常!");
            }
        }
        List<Long> userIds = assignedUserList.stream().map(Long::valueOf).collect(Collectors.toList());
        List<UserVo> userList = userService.searchUserListByUserIds(userIds);
        List<UserInfo> userInfoList = userList.stream().map(UserInfo::toUserInfo).collect(Collectors.toList());
        String approvalMode = ModeEnums.NORMAL.getTypeName();
        if (userInfoList.size() > 1) {
            approvalMode = props.getMode();
        }
        HistoricalOperation operation = HistoricalOperation.toHistoricalOperation(execution, userInfoList, processNode.getName(), approvalMode);
        if (!hasAssignedUser) {
            operation.setUserInfo(null);
            operation.setState(assigneeEnums);
        }
        int count = operationMapper.selectCountByProcessInstanceIdAndNodeId(execution.getProcessInstanceId(), processNode.getId());
        if (count <= 0) {
            operationMapper.insert(operation);
        }
        //将参数设置回流程数据中
        execution.setVariable(variableKey, assignedUserList, false);
    }

    /**
     * 设置用户自选模式
     *
     * @param assignedUserList 审批人
     * @param nodeId           节点id
     * @param variables        节点数据
     */
    private void setSelfSelect(String nodeId, List<String> assignedUserList, Map<String, Object> variables) {
        JSONObject optionalUser = (JSONObject) variables.get(ProcessConstant.OPTIONAL_USER);
        List<UserInfo> userInfos = optionalUser.getList(nodeId,UserInfo.class);
        userInfos.forEach(user -> assignedUserList.add(user.getId()));
    }

    /**
     * 连续多级主管
     *
     * @param assignedUserList 审批人
     * @param props            配置数据
     * @param variables        节点数据
     */
    private void setLeaderUserTop(List<String> assignedUserList, Properties props, Map<String, Object> variables) {
        Map<String, Object> leaderTop = props.getLeaderTop();
        String endCondition = leaderTop.get("endCondition").toString();
        Dept dept = getDept(variables);
        List<Long> parentIds = Arrays.stream(dept.getAncestors().split(",")).map(Long::valueOf).collect(Collectors.toList());
        List<DeptVo> deptVoList;
        switch (endCondition) {
            case "TOP"://直接到最上层
                deptVoList = deptService.searchDeptByDeptId(parentIds.subList(1, parentIds.size()));
                break;
            case "LEAVE"://到指定级别
                int level = Integer.parseInt(leaderTop.get("level").toString());
                List<Long> leaderDeptIds;
                int length = parentIds.size() - 1;
                if (level > length) {
                    leaderDeptIds = parentIds.subList(1, parentIds.size());
                } else {
                    leaderDeptIds = parentIds.subList(parentIds.size() - level, parentIds.size());
                }
                deptVoList = deptService.searchDeptByDeptId(leaderDeptIds);
                break;
            default:
                throw new CustomException("当前选项还未实现!");
        }
        deptVoList = checkResult(deptVoList, "获取到部门数据为空!");
        deptVoList.forEach(deptVo -> assignedUserList.add(deptVo.getLeaderId().toString()));
    }

    /**
     * 根据领导选择用户
     *
     * @param assignedUserList 审批人
     * @param props            配置数据
     * @param variables        节点数据
     */
    private void setLeaderUser(List<String> assignedUserList, Properties props, Map<String, Object> variables) {
        int level = Integer.parseInt(props.getLeader().get("level").toString());
        Dept dept = getDept(variables);
        if (1 == level) {
            String leaderId = dept.getLeaderId().toString();
            assignedUserList.add(leaderId);
        } else {
            List<Long> parentIds = Arrays.stream(dept.getAncestors().split(",")).map(Long::valueOf).collect(Collectors.toList());
            Long leaderDeptId;
            int length = parentIds.size() - 1;
            if (level > length) {
                leaderDeptId = parentIds.get(1);
            } else {
                leaderDeptId = parentIds.get(parentIds.size() - level);
            }
            List<DeptVo> deptVoList = deptService.searchDeptByDeptId(Collections.singletonList(leaderDeptId));
            deptVoList = checkResult(deptVoList, "获取到部门数据为空!");
            DeptVo deptVo = deptVoList.get(0);
            assignedUserList.add(deptVo.getLeaderId().toString());
        }
    }

    private <T> List<T> checkResult(List<T> result, String msg) {
        if (null == result || result.isEmpty()) {
            throw new CustomException(msg);
        }
        return result;
    }

    /**
     * 根据角色选择审批人
     *
     * @param assignedUserList 审批人
     * @param props            配置数据
     */
    private void setRoleUser(List<String> assignedUserList, Properties props) {
        List<Long> roleIds = props.getRoleList().stream().map(RoleInfo::getRoleId).collect(Collectors.toList());
        //rpc远程通过角色id获取到当前角色下的所有用户数据
        if (roleIds.isEmpty()){
            throw new CustomException("系统错误,请稍后再试");
        }
        List<UserVo> userList = userService.searchUserListByRoleIds(roleIds);
        if (null == userList || userList.isEmpty()) {
            throw new CustomException("系统错误,请稍后再试");
        }
        //todo 将userInfo加入到流程数据中
        for (UserVo user : userList) {
            assignedUserList.add(user.getUserId().toString());
        }
    }

    /**
     * @param variables 节点数据
     * @return 返回部门
     */
    public Dept getDept(Map<String, Object> variables) {
        User user = (User) variables.get(ProcessConstant.START_USER_INFO);
        return user.getDept();
    }
}
