package ink.wgink.module.activiti.service.oa.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import ink.wgink.common.base.DefaultBaseService;
import ink.wgink.exceptions.SearchException;
import ink.wgink.interfaces.user.IUserBaseService;
import ink.wgink.module.activiti.enums.oa.TaskCommentTypeEnum;
import ink.wgink.module.activiti.enums.oa.assignee.AssigneeTypeEnum;
import ink.wgink.module.activiti.pojo.bos.oa.TaskCommentBO;
import ink.wgink.module.activiti.pojo.dtos.oa.NodeButtonDTO;
import ink.wgink.module.activiti.pojo.dtos.oa.NodeFieldDTO;
import ink.wgink.module.activiti.pojo.pos.oa.OaNodeAssigneePO;
import ink.wgink.module.activiti.pojo.vos.oa.page.*;
import ink.wgink.module.activiti.service.activiti.IActivitiModelService;
import ink.wgink.module.activiti.service.oa.*;
import ink.wgink.module.form.enums.design.FormTypeEnum;
import ink.wgink.module.form.pojo.pos.design.FormPO;
import ink.wgink.module.form.service.design.IFormDesignService;
import ink.wgink.module.form.service.design.IFormFieldService;
import ink.wgink.module.form.service.design.IFormService;
import ink.wgink.module.form.service.report.IFormReportRouteService;
import ink.wgink.module.form.service.report.IFormReportService;
import ink.wgink.pojo.app.AppTokenUser;
import ink.wgink.pojo.app.AppTokenUserDepartment;
import ink.wgink.pojo.bos.UserInfoBO;
import ink.wgink.pojo.dtos.department.DepartmentSimpleDTO;
import ink.wgink.pojo.dtos.user.UserDTO;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.bpmn.model.UserTask;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @ClassName: OaFormReportRouteServiceImpl
 * @Description: oa表单提交
 * @Author: wanggeng
 * @Date: 2022/3/24 09:31
 * @Version: 1.0
 */
@Service
public class OaFormReportRouteServiceImpl extends DefaultBaseService implements IOaFormReportRouteService {

    @Autowired
    private TaskService taskService;
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private HistoryService historyService;
    @Autowired
    private IFormReportRouteService formReportRouteService;
    @Autowired
    private IUserBaseService userBaseService;
    @Autowired
    private IActivitiModelService activitiModelService;
    @Autowired
    private INodeAssigneeService nodeAssigneeService;
    @Autowired
    private IFormFieldService formFieldService;
    @Autowired
    private INodeFieldService nodeFieldService;
    @Autowired
    private INodeButtonService nodeButtonService;
    @Autowired
    private IOaCcService oaCcService;
    @Autowired
    private IFormService formService;
    @Autowired
    private IFormReportService formReportService;

    @Override
    public void save(String processDefinitionId, String formCode, Integer formVersion, HttpSession httpSession, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
        UserInfoBO currentUser = securityComponent.getCurrentUser();
        Map<String, Object> model = getSaveModel(currentUser.getUserId(), processDefinitionId);
        setPageCurrentUser(currentUser.getUserId(), currentUser.getUserName(), currentUser.getDepartments(), model);
        formReportRouteService.save(formCode, formVersion, httpSession, httpServletRequest, httpServletResponse, model);
    }

    @Override
    public void update(String taskId, String formCode, Integer formVersion, Integer isNeedClaim, HttpSession httpSession, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
        UserInfoBO currentUser = securityComponent.getCurrentUser();
        Map<String, Object> model = getUpdateModel(currentUser.getUserId(), taskId, isNeedClaim);
        setPageCurrentUser(currentUser.getUserId(), currentUser.getUserName(), currentUser.getDepartments(), model);
        // 设置代理人
        formReportRouteService.update(formCode, formVersion, httpSession, httpServletRequest, httpServletResponse, model);
    }

    @Override
    public void show(String formCode, Integer formVersion, HttpSession httpSession, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
        Map<String, Object> model = getHashMap(10);
        formReportRouteService.show(formCode, formVersion, httpSession, httpServletRequest, httpServletResponse, model);
    }

    @Override
    public void print(String formCode, Integer formVersion, String uid, HttpSession httpSession, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
        FormPO formPO = formService.getPOByCodeAndVersion(formCode, formVersion);
        if (formPO == null) {
            return;
        }
        // 处理附件、文件、视频、音频、签批等功能
        Map<String, Object> reportForm = formReportService.get(formCode, formVersion, uid);
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().includeProcessVariables().processInstanceId(reportForm.get(IFormDesignService.PROCESS_INSTANCE_ID).toString()).singleResult();
        if (historicProcessInstance == null) {
            throw new SearchException("流程实例不存在");
        }
        // 设置签字
        setSign(reportForm, historicProcessInstance);
        // 设置会签
        setJointlySign(reportForm);
        Map<String, Object> modal = getHashMap(10);
        modal.put("formData", reportForm);
        formReportRouteService.setModal(formPO, modal);
        formReportRouteService.showPage(modal, formCode, formPO.getPrintPageCode(), httpSession, httpServletRequest, httpServletResponse);
    }

    @Override
    public void updateRead(String formCode, Integer formVersion, String ccId, HttpSession httpSession, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
        oaCcService.updateRead(ccId, 1);
        show(formCode, formVersion, httpSession, httpServletRequest, httpServletResponse);
    }

    @Override
    public void updateRead(String token, String formCode, Integer formVersion, String ccId, HttpSession httpSession, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
        oaCcService.updateRead(token, ccId, 1);
        appShow(token, formCode, formVersion, httpSession, httpServletRequest, httpServletResponse);
    }

    @Override
    public void appSave(String token, String processDefinitionId, String formCode, Integer formVersion, HttpSession httpSession, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
        AppTokenUser appTokenUser = getAppTokenUser(token);
        Map<String, Object> model = getSaveModel(appTokenUser.getId(), processDefinitionId);
        setAppPageCurrentUser(appTokenUser.getId(), appTokenUser.getName(), appTokenUser.getDepartments(), model);
        formReportRouteService.appSave(formCode, formVersion, httpSession, httpServletRequest, httpServletResponse, model);
    }

    @Override
    public void appUpdate(String token, String taskId, String formCode, Integer formVersion, Integer isNeedClaim, HttpSession httpSession, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
        AppTokenUser appTokenUser = getAppTokenUser(token);
        Map<String, Object> model = getUpdateModel(appTokenUser.getId(), taskId, isNeedClaim);
        setAppPageCurrentUser(appTokenUser.getId(), appTokenUser.getName(), appTokenUser.getDepartments(), model);
        formReportRouteService.appUpdate(formCode, formVersion, httpSession, httpServletRequest, httpServletResponse, model);
    }

    @Override
    public void appShow(String token, String formCode, Integer formVersion, HttpSession httpSession, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
        Map<String, Object> model = getHashMap(10);
        formReportRouteService.appShow(formCode, formVersion, httpSession, httpServletRequest, httpServletResponse, model);
    }

    /**
     * 设置签批
     *
     * @param reportForm
     * @param historicProcessInstance
     */
    private void setSign(Map<String, Object> reportForm, HistoricProcessInstance historicProcessInstance) {
        Map<String, Object> processVariables = historicProcessInstance.getProcessVariables();
        for (Map.Entry<String, Object> reportFormEntry : reportForm.entrySet()) {
            Object signObject = processVariables.get(reportFormEntry.getKey() + IOaFormReportService.EDIT_SIGN_KEY_SUFFIX);
            if (signObject != null) {
                reportFormEntry.setValue(signObject);
            }
        }
    }


    /**
     * 设置会签
     *
     * @param reportForm
     */
    private void setJointlySign(Map<String, Object> reportForm) {
        List<Comment> processInstanceComments = taskService.getProcessInstanceComments(reportForm.get(IFormDesignService.PROCESS_INSTANCE_ID).toString());
        Map<String, String> jointlySignMap = new HashMap<>(16);
        // 先处理会签字段
        for (Comment comment : processInstanceComments) {
            // 会签字段
            if (!StringUtils.equals(TaskCommentTypeEnum.JOINTLY_SIGN.getValue(), comment.getType())) {
                continue;
            }
            TaskCommentBO taskCommentBO = JSONObject.parseObject(comment.getFullMessage(), TaskCommentBO.class);
            String jointlySignedValue = jointlySignMap.get(taskCommentBO.getFieldName());
            if (jointlySignedValue == null) {
                jointlySignedValue = "";
            }
            if (!jointlySignedValue.isEmpty()) {
                jointlySignedValue += "<br/>";
            }
            jointlySignedValue += taskCommentBO.getContent() + "　" + taskCommentBO.getUserName() + "→" + taskCommentBO.getTime();
            jointlySignMap.put(taskCommentBO.getFieldName(), jointlySignedValue);
        }
        // 替换会签的内容
        for (Map.Entry<String, Object> reportFormEntry : reportForm.entrySet()) {
            String jointlySignValue = jointlySignMap.get(reportFormEntry.getKey());
            if (StringUtils.isBlank(jointlySignValue)) {
                continue;
            }
            reportFormEntry.setValue(jointlySignValue);
        }
    }

    /**
     * 设置页面当前用户
     *
     * @param model
     */
    private void setPageCurrentUser(String userId, String userName, List<DepartmentSimpleDTO> departmentSimpleDTOS, Map<String, Object> model) {
        CurrentUserVO currentUserVO = new CurrentUserVO();
        currentUserVO.setUserId(userId);
        currentUserVO.setUserName(userName);
        currentUserVO.setDepartments(departmentSimpleDTOS);
        model.put("currentUser", JSONObject.toJSONString(currentUserVO));
    }

    /**
     * 设置APP页面当前用户
     *
     * @param model
     */
    private void setAppPageCurrentUser(String userId, String userName, List<AppTokenUserDepartment> appTokenUserDepartments, Map<String, Object> model) {
        CurrentUserVO currentUserVO = new CurrentUserVO();
        currentUserVO.setUserId(userId);
        currentUserVO.setUserName(userName);
        currentUserVO.setDepartments(appTokenUserDepartments.stream().map(appTokenUserDepartment -> {
            DepartmentSimpleDTO departmentSimpleDTO = new DepartmentSimpleDTO();
            departmentSimpleDTO.setDepartmentId(appTokenUserDepartment.getDepartmentId());
            departmentSimpleDTO.setDepartmentName(appTokenUserDepartment.getDepartmentName());
            return departmentSimpleDTO;
        }).collect(Collectors.toList()));
        model.put("currentUser", JSONObject.toJSONString(currentUserVO));
    }

    /**
     * 新增页面Model
     *
     * @param processDefinitionId
     * @return
     */
    private Map<String, Object> getSaveModel(String currentUserId, String processDefinitionId) {
        Map<String, Object> model = getHashMap(10);
        model.put("processDefinitionId", processDefinitionId);
        model.put("formType", FormTypeEnum.OA.getValue());

        LOG.debug("查询流程部署ID");
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
        String deploymentId = processDefinition.getDeploymentId();

        LOG.debug("发起流程，查询第一个用户任务");
        UserTask firstUserTask = activitiModelService.getFirstUserTaskByProcessDefinitionId(processDefinitionId);
        LOG.debug("发起流程，查询第一个用户任务后直连用户任务列表");
        List<ConfirmAssigneeVO> confirmAssigneeVOs = listConfirmAssignee(null, currentUserId, deploymentId, firstUserTask);
        model.put("confirmAssignees", JSON.toJSONString(confirmAssigneeVOs));

        setPageFields(deploymentId, firstUserTask, model);
        setPageFormButtonsAndHistoryUserTasks(deploymentId, null, null, firstUserTask, model);
        return model;
    }

    /**
     * 编辑页面Model
     *
     * @param currentUserId
     * @param taskId
     * @param isNeedClaim
     * @return
     */
    private Map<String, Object> getUpdateModel(String currentUserId, String taskId, Integer isNeedClaim) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task == null) {
            throw new SearchException("任务不存在");
        }

        LOG.debug("查询流程部署ID");
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
        String deploymentId = processDefinition.getDeploymentId();

        LOG.debug("获取下一个用户任务");
        FlowNode flowNode = activitiModelService.getFlowNodeByNodeIdAndProcessDefinitionId(task.getTaskDefinitionKey(), task.getProcessDefinitionId());
        UserTask currentUserTask = (UserTask) flowNode;

        Map<String, Object> model = getHashMap(10);
        model.put("formType", FormTypeEnum.OA.getValue());
        model.put("taskId", taskId);
        model.put("processInstanceId", task.getProcessInstanceId());
        model.put("isNeedClaim", isNeedClaim);
        LOG.debug("查询下一个用户任务后直连用户任务列表");
        List<ConfirmAssigneeVO> confirmAssigneeVOs = listConfirmAssignee(task.getProcessInstanceId(), currentUserId, deploymentId, currentUserTask);
        model.put("confirmAssignees", JSON.toJSONString(confirmAssigneeVOs));

        setPageFields(deploymentId, currentUserTask, model);
        setPageFormButtonsAndHistoryUserTasks(deploymentId, task.getId(), task.getProcessInstanceId(), currentUserTask, model);
        return model;
    }

    /**
     * 设置页面字段
     *
     * @param deploymentId
     * @param userTask
     * @param model
     */
    private void setPageFields(String deploymentId, UserTask userTask, Map<String, Object> model) {
        LOG.debug("查询节点字段信息");
        List<NodeFieldDTO> nodeFieldDTOs = nodeFieldService.listByDeploymentIdAndNodeId(deploymentId, userTask.getId());
        List<FieldVO> fieldVOs = nodeFieldDTOs.stream().map(nodeFieldDTO -> {
            FieldVO fieldVO = new FieldVO();
            fieldVO.setFieldName(nodeFieldDTO.getFieldName());
            fieldVO.setIsEditable(nodeFieldDTO.getIsEditable());
            fieldVO.setIsVisible(nodeFieldDTO.getIsVisible());
            fieldVO.setEditHistory(nodeFieldDTO.getEditHistory());
            fieldVO.setAutoBackFill(nodeFieldDTO.getAutoBackFill());
            return fieldVO;
        }).collect(Collectors.toList());
        model.put("fields", JSONObject.toJSONString(fieldVOs));
    }

    /**
     * 设置页面按钮与历史任务，如果没有回退按钮，不设置回退
     *
     * @param deploymentId
     * @param currentTaskId
     * @param processInstanceId
     * @param userTask
     * @param model
     */
    private void setPageFormButtonsAndHistoryUserTasks(String deploymentId, String currentTaskId, String processInstanceId, UserTask userTask, Map<String, Object> model) {
        NodeButtonDTO nodeButtonDTO = nodeButtonService.getByDeploymentIdAndNodeId(deploymentId, userTask.getId());
        FormButtonVO formButtonVO = new FormButtonVO();
        if (nodeButtonDTO == null) {
            model.put("formButton", JSONObject.toJSONString(formButtonVO));
            return;
        }
        formButtonVO.setBtnAttachment(nodeButtonDTO.getBtnAttachment());
        formButtonVO.setBtnCc(nodeButtonDTO.getBtnCc());
        formButtonVO.setBtnPrint(nodeButtonDTO.getBtnPrint());
        formButtonVO.setBtnTransfer(nodeButtonDTO.getBtnTransfer());
        if (StringUtils.isBlank(currentTaskId)) {
            formButtonVO.setBtnGoBack(0);
            formButtonVO.setBtnForcedEnd(0);
            formButtonVO.setBtnPrint(0);
        } else {
            formButtonVO.setBtnGoBack(nodeButtonDTO.getBtnGoBack());
            formButtonVO.setBtnForcedEnd(nodeButtonDTO.getBtnForcedEnd());
            formButtonVO.setBtnPrint(nodeButtonDTO.getBtnPrint());
        }
        if (formButtonVO.getBtnGoBack() == 1) {
            LOG.debug("存在回退按钮，查询历史节点");
            List<HistoricTaskInstance> historicTaskInstances = historyService.createHistoricTaskInstanceQuery()
                    .includeTaskLocalVariables()
                    .processInstanceId(processInstanceId)
                    .finished()
                    .orderByHistoricTaskInstanceEndTime()
                    .asc()
                    .list();
            List<UserTask> beforeUserTasks = activitiModelService.listBeforeUserTaskByUserTask(userTask);
            // 去除历史流程中没有走的节点任务
            List<GoBackUserTaskVO> goBackUserTaskVOs = new ArrayList<>();
            for (int i = 0; i < beforeUserTasks.size(); i++) {
                UserTask beforeUserTask = beforeUserTasks.get(i);
                boolean isExist = false;
                for (HistoricTaskInstance historicTaskInstance : historicTaskInstances) {
                    if (StringUtils.equals(beforeUserTask.getId(), historicTaskInstance.getTaskDefinitionKey())) {
                        isExist = true;
                        break;
                    }
                }
                if (!isExist) {
                    beforeUserTasks.remove(i);
                    i--;
                    continue;
                }
                GoBackUserTaskVO goBackUserTaskVO = new GoBackUserTaskVO();
                goBackUserTaskVO.setNodeId(beforeUserTask.getId());
                goBackUserTaskVO.setTaskName(beforeUserTask.getName());
                goBackUserTaskVOs.add(goBackUserTaskVO);
            }
            formButtonVO.setGoBackUserTasks(goBackUserTaskVOs);
        }
        model.put("formButton", JSONObject.toJSONString(formButtonVO));
    }

    /**
     * 确认代理人列表
     *
     * @param deploymentId
     * @param currentUserTask
     * @return
     */
    private List<ConfirmAssigneeVO> listConfirmAssignee(String processInstanceId, String currentUserId, String deploymentId, UserTask currentUserTask) {
        LOG.debug("1.获取后续任务节点");
        List<UserTask> nextUserTasks = activitiModelService.listNextUserTasksByUserTask(currentUserTask);
        clearUserTask(nextUserTasks);
        LOG.debug("2.发起流程，查询第一个用户任务后直连用户任务列表的流入序列流");
        // 序列流的数量决定了提交按钮的数量，名字决定了提交按钮的名称，没有名称默认
        List<SequenceFlow> sequenceFlows = activitiModelService.listOutgoingSequenceFlowToNotGateway(currentUserTask);
        LOG.debug("3.遍历后续的用户列表");
        // 按照流入序列流进行区分，页面上体现出为按钮中对应的不同的代理人列表
        List<ConfirmAssigneeVO> confirmAssigneeVOs = new ArrayList<>();
        for (int i = 0; i < sequenceFlows.size(); i++) {
            SequenceFlow sequenceFlow = sequenceFlows.get(i);
            String name = sequenceFlow.getName();
            ConfirmAssigneeVO confirmAssigneeVO = new ConfirmAssigneeVO();
            // 当有输出流
            confirmAssigneeVO.setNextEndEvent(nextUserTasks.isEmpty() ? true : false);
            confirmAssigneeVO.setBtnExc(getSequenceFlowExcCondition(sequenceFlow.getConditionExpression()));
            if (StringUtils.isBlank(name)) {
                confirmAssigneeVO.setBtnText("提交");
            } else {
                confirmAssigneeVO.setBtnText(name);
            }
            for (UserTask userTask : nextUserTasks) {
                if (!StringUtils.equals(sequenceFlow.getTargetFlowElement().getId(), userTask.getId())) {
                    continue;
                }
                OaNodeAssigneePO oaNodeAssigneePO = nodeAssigneeService.getPOByDeploymentIdAndNodeId(deploymentId, userTask.getId());
                confirmAssigneeVO.setAssigneeType(oaNodeAssigneePO.getAssigneeType());
                // 候选人来源于表单，设置表单候选人字段，表单候选人的字段值为 assignee 的值
                if (StringUtils.equals(AssigneeTypeEnum.FORM.getValue(), oaNodeAssigneePO.getAssigneeType())) {
                    confirmAssigneeVO.setFormAssigneeField(oaNodeAssigneePO.getAssignee());
                }
                // 设置节点类型
                confirmAssigneeVO.setNodeType(oaNodeAssigneePO.getNodeType());
                // 设置节点代理人列表
                List<String> assigneeList;
                if (StringUtils.isBlank(processInstanceId)) {
                    assigneeList = nodeAssigneeService.listAssigneeUserId(currentUserId, oaNodeAssigneePO);
                } else {
                    assigneeList = nodeAssigneeService.listAssigneeUserId(processInstanceId, currentUserId, oaNodeAssigneePO);
                }
                if(!assigneeList.isEmpty()) {
                    List<UserDTO> userDTOs = userBaseService.listByUserIds(assigneeList);
                    List<ConfirmAssigneeVO.AssigneeVO> assigneeVOs = userDTOs.stream().map(userDTO -> {
                        ConfirmAssigneeVO.AssigneeVO assigneeVO = new ConfirmAssigneeVO.AssigneeVO();
                        BeanUtils.copyProperties(userDTO, assigneeVO);
                        return assigneeVO;
                    }).collect(Collectors.toList());
                    confirmAssigneeVO.setAssignees(assigneeVOs);
                }
            }
            confirmAssigneeVOs.add(confirmAssigneeVO);
        }
        return confirmAssigneeVOs;
    }

    /**
     * 获取序列流上的exc条件
     *
     * @param conditionExpression
     * @return
     */
    private String getSequenceFlowExcCondition(String conditionExpression) {
        if (StringUtils.isBlank(conditionExpression)) {
            return null;
        }
        String[] expressionArray = conditionExpression.split("[{}$&|]");
        String excCondition = null;
        for (String expression : expressionArray) {
            if (!expression.contains("==")) {
                continue;
            }
            if (!expression.contains("exc")) {
                continue;
            }
            String[] conditionArray = expression.split("==");
            String condition = conditionArray[1];
            condition = condition.replaceAll("'", "");
            condition = condition.replaceAll("\"", "");
            excCondition = condition;
        }
        return excCondition;
    }

    /**
     * 清除相同的序列流（目标相同）
     *
     * @param userTasks
     */
    private void clearUserTask(List<UserTask> userTasks) {
        for (int i = 0; i < userTasks.size(); i++) {
            UserTask userTask = userTasks.get(i);
            boolean isExist = false;
            for (int j = i + 1; j < userTasks.size(); j++) {
                if (StringUtils.equals(userTask.getId(), userTasks.get(j).getId())) {
                    isExist = true;
                    userTasks.remove(i);
                    break;
                }
            }
            if (isExist) {
                i--;
            }
        }
    }

}
