package cn.axzo.workflow.core.converter.json;

import cn.axzo.workflow.common.enums.ApprovalMethodEnum;
import cn.axzo.workflow.common.enums.BpmnFlowNodeMode;
import cn.axzo.workflow.common.enums.BpmnFlowNodeType;
import cn.axzo.workflow.common.model.request.bpmn.BpmnButtonConf;
import cn.axzo.workflow.common.model.request.bpmn.BpmnJsonNode;
import cn.axzo.workflow.common.model.request.bpmn.BpmnJsonNodeProperty;
import org.flowable.bpmn.model.ExtensionAttribute;
import org.flowable.bpmn.model.ExtensionElement;
import org.flowable.bpmn.model.FlowableListener;
import org.flowable.bpmn.model.MultiInstanceLoopCharacteristics;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.delegate.BaseExecutionListener;
import org.flowable.engine.delegate.TaskListener;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import static cn.axzo.workflow.common.constant.BpmnConstants.AND_SIGN_EXPRESSION;
import static cn.axzo.workflow.common.constant.BpmnConstants.BPM_ALLOW_SKIP_USER_TASK;
import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_APPROVAL_METHOD;
import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_APPROVER_EMPTY_HANDLE_TYPE;
import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_APPROVER_MODE_TYPE;
import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_APPROVER_SCOPE;
import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_APPROVER_SPECIFY;
import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_BUTTON;
import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_BUTTON_TYPE_CARBON_COPY;
import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_BUTTON_TYPE_CURRENT;
import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_BUTTON_TYPE_HISTORY;
import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_BUTTON_TYPE_INITIATOR;
import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_FIELD_PERMISSION;
import static cn.axzo.workflow.common.constant.BpmnConstants.ELEMENT_ATTRIBUTE_DESC;
import static cn.axzo.workflow.common.constant.BpmnConstants.ELEMENT_ATTRIBUTE_VALUE;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO;
import static cn.axzo.workflow.common.constant.BpmnConstants.OR_SIGN_EXPRESSION_ONLY_ONE;
import static cn.axzo.workflow.common.constant.BpmnConstants.TEMPLATE_PENDING_MESSAGE_ID;
import static cn.axzo.workflow.common.enums.ApproverEmptyHandleTypeEnum.autoPassed;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_STARTER;
import static cn.axzo.workflow.core.common.utils.BpmnJsonConverterUtil.buildMetaButton;
import static org.flowable.bpmn.model.ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION;

/**
 * 用户任务节点
 *
 * @author wangli
 * @since 2023/10/13 17:01
 */
public class UserTaskJsonConverter extends AbstractBpmnJsonConverter<UserTask> {

    @Override
    public UserTask convertJsonToElement(BpmnJsonNode node, Process process) {
        UserTask userTask = new UserTask();

        userTask.setId(node.getId());
        userTask.setName(node.getName());

        // 设置会签或签
        setMultiInstance(node, userTask);
        // 设置全局任务监听器
        setTaskListeners(userTask);
        // 设置全局执行监听器

        setExecutionListeners(node, userTask);

        //以下是保存配置信息, 引擎完全不会 care 这里的信息
        // "设置审批人"
        setApprovalExtensionElement(node, userTask);
        // "权限设置"
        setFieldExtensionElement(node, userTask);
        // "高级设置"
        setButtonExtensionElement(node, userTask);
        // "待办消息模板配置"
        setPendingMessageExtensionElement(node, userTask);

        // 如果审批人为空时,同时选择了自动通过, 则设置跳过表达式, 但由于使用 Flowable 框架变量操作不当会引起后续节点异常,
        // 所以统一通过 AutoOperatorEventListener 类解决自动操作
        // setEmptyAssigneeAutoPassed(node, userTask);

        return userTask;
    }

    private static void setPendingMessageExtensionElement(BpmnJsonNode node, UserTask userTask) {
        BpmnJsonNodeProperty property = node.getProperty();
        if (Objects.isNull(property) || Objects.isNull(property.getPending())) {
            return;
        }
        ExtensionElement pendingMessageElement = new ExtensionElement();
        pendingMessageElement.setName(TEMPLATE_PENDING_MESSAGE_ID);
        ExtensionAttribute pendingMessageAttribute = new ExtensionAttribute();
        pendingMessageAttribute.setName(ELEMENT_ATTRIBUTE_VALUE);
        pendingMessageAttribute.setValue(property.getPending().getPendingMessageId());
        pendingMessageElement.addAttribute(pendingMessageAttribute);
        pendingMessageElement.setElementText(StringUtils.hasText(property.getPending().getViewJson()) ?
                property.getPending().getViewJson() : "");
        userTask.addExtensionElement(pendingMessageElement);
    }

    private static void setEmptyAssigneeAutoPassed(BpmnJsonNode node, UserTask userTask) {
        if (Objects.nonNull(node.getProperty()) &&
                Objects.equals(node.getProperty().getApproverEmptyHandleType(), autoPassed)) {
            userTask.setSkipExpression("${" + BPM_ALLOW_SKIP_USER_TASK + userTask.getId() + "}");
        }
    }

    private static void setMultiInstance(BpmnJsonNode node, UserTask userTask) {
        if (Objects.isNull(node.getProperty()) || Objects.equals(Boolean.FALSE, node.getProperty().getIsMultiTask())) {
            return;
        }
        MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics =
                new MultiInstanceLoopCharacteristics();
        // 审批人集合参数
        multiInstanceLoopCharacteristics.setInputDataItem(INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO + userTask.getId());
        // 迭代集合
        multiInstanceLoopCharacteristics.setElementVariable("assigneeName");
        // 并行
        multiInstanceLoopCharacteristics.setSequential(node.getProperty().getIsSequential());
        userTask.setAssignee("${assigneeName}");
        // 设置多实例属性
        userTask.setLoopCharacteristics(multiInstanceLoopCharacteristics);

        // 设置会签或签
        if (BpmnFlowNodeMode.OR.equals(node.getProperty().getMultiMode())) {
            multiInstanceLoopCharacteristics.setCompletionCondition(OR_SIGN_EXPRESSION_ONLY_ONE);
        } else if (BpmnFlowNodeMode.AND.equals(node.getProperty().getMultiMode())) {
            multiInstanceLoopCharacteristics.setCompletionCondition(AND_SIGN_EXPRESSION);
        }
    }

    private static void setButtonExtensionElement(BpmnJsonNode node, UserTask userTask) {
        if (Objects.isNull(node.getProperty())) {
            return;
        }
        BpmnButtonConf buttonConf = node.getProperty().getButtonPermission();
        ExtensionElement buttonConfigElement = new ExtensionElement();
        buttonConfigElement.setName(CONFIG_BUTTON);

        if (Objects.nonNull(buttonConf)) {
            buildMetaButton(buttonConf.getInitiator(), CONFIG_BUTTON_TYPE_INITIATOR, buttonConfigElement);
            buildMetaButton(buttonConf.getCurrent(), CONFIG_BUTTON_TYPE_CURRENT, buttonConfigElement);
            buildMetaButton(buttonConf.getHistory(), CONFIG_BUTTON_TYPE_HISTORY, buttonConfigElement);
            buildMetaButton(buttonConf.getCarbonCopy(), CONFIG_BUTTON_TYPE_CARBON_COPY, buttonConfigElement);
        }

        userTask.addExtensionElement(buttonConfigElement);
    }

    private static void setFieldExtensionElement(BpmnJsonNode node, UserTask userTask) {
        if (Objects.isNull(node.getProperty())) {
            return;
        }
        ExtensionElement fieldElement = new ExtensionElement();
        fieldElement.setName(CONFIG_FIELD_PERMISSION);
        fieldElement.setElementText(node.getProperty().getFieldPermission());
        userTask.addExtensionElement(fieldElement);
    }

    private static void setApprovalExtensionElement(BpmnJsonNode node, UserTask userTask) {
        BpmnJsonNodeProperty property = node.getProperty();
        if (Objects.isNull(property)) {
            return;
        }

        // 审批方式
        ExtensionElement approvalMethodElement = new ExtensionElement();
        approvalMethodElement.setName(CONFIG_APPROVAL_METHOD);

        ExtensionAttribute approvalMethodValueAttribute = new ExtensionAttribute();
        approvalMethodValueAttribute.setName(ELEMENT_ATTRIBUTE_VALUE);
        approvalMethodValueAttribute.setValue(property.getApprovalMethod().getType());
        approvalMethodElement.addAttribute(approvalMethodValueAttribute);

        ExtensionAttribute approvalMethodDescAttribute = new ExtensionAttribute();
        approvalMethodDescAttribute.setName(ELEMENT_ATTRIBUTE_DESC);
        approvalMethodDescAttribute.setValue("审批方式");
        approvalMethodElement.addAttribute(approvalMethodDescAttribute);
        userTask.addExtensionElement(approvalMethodElement);

        if (BpmnFlowNodeType.NODE_BUSINESS.equals(node.getType()) && ApprovalMethodEnum.nobody.equals(property.getApprovalMethod())) {
            // 业务节点有一种不设置审批人的选项, 除"审批方式"外都是空配置, 所以在此直接中断后续的扩展属性配置
            return;
        }
        // 审批人所在范围
        ExtensionElement approverScopeElement = new ExtensionElement();
        approverScopeElement.setName(CONFIG_APPROVER_SCOPE);

        ExtensionAttribute approverScopeValueAttribute = new ExtensionAttribute();
        approverScopeValueAttribute.setName(ELEMENT_ATTRIBUTE_VALUE);
        approverScopeValueAttribute.setValue(property.getApproverScope().getType());
        approverScopeElement.addAttribute(approverScopeValueAttribute);

        ExtensionAttribute approverScopeDescAttribute = new ExtensionAttribute();
        approverScopeDescAttribute.setName(ELEMENT_ATTRIBUTE_DESC);
        approverScopeDescAttribute.setValue("审批人所在范围");
        approverScopeElement.addAttribute(approverScopeDescAttribute);
        userTask.addExtensionElement(approverScopeElement);

        // 审批人指定
        ExtensionElement approverSpecifyElement = new ExtensionElement();
        approverSpecifyElement.setName(CONFIG_APPROVER_SPECIFY);

        ExtensionAttribute approverSpecifyValueAttribute = new ExtensionAttribute();
        approverSpecifyValueAttribute.setName(ELEMENT_ATTRIBUTE_VALUE);
        approverSpecifyValueAttribute.setValue(property.getApproverSpecify().getType());
        approverSpecifyElement.addAttribute(approverSpecifyValueAttribute);

        ExtensionAttribute approverSpecifyDescAttribute = new ExtensionAttribute();
        approverSpecifyDescAttribute.setName(ELEMENT_ATTRIBUTE_DESC);
        approverSpecifyDescAttribute.setValue("审批人指定");
        approverSpecifyElement.addAttribute(approverSpecifyDescAttribute);
        // 审批人指定的具体值
        approverSpecifyElement.setElementText(property.getSpecifyValue());
        userTask.addExtensionElement(approverSpecifyElement);

        // 多人审批时审批模式
        ExtensionElement approverModeTypeElement = new ExtensionElement();
        approverModeTypeElement.setName(CONFIG_APPROVER_MODE_TYPE);

        ExtensionAttribute approverModeTypeValueAttribute = new ExtensionAttribute();
        approverModeTypeValueAttribute.setName(ELEMENT_ATTRIBUTE_VALUE);
        approverModeTypeValueAttribute.setValue(property.getMultiMode().getType());
        approverModeTypeElement.addAttribute(approverModeTypeValueAttribute);

        ExtensionAttribute approverModeTypeDescAttribute = new ExtensionAttribute();
        approverModeTypeDescAttribute.setName(ELEMENT_ATTRIBUTE_DESC);
        approverModeTypeDescAttribute.setValue("多人审批时审批模式");
        approverModeTypeElement.addAttribute(approverModeTypeDescAttribute);
        userTask.addExtensionElement(approverModeTypeElement);

        // 审批人为空时
        ExtensionElement approverEmptyHandleTypeElement = new ExtensionElement();
        approverEmptyHandleTypeElement.setName(CONFIG_APPROVER_EMPTY_HANDLE_TYPE);
        ExtensionAttribute approverEmptyHandleTypeValueAttribute = new ExtensionAttribute();
        approverEmptyHandleTypeValueAttribute.setName(ELEMENT_ATTRIBUTE_VALUE);
        approverEmptyHandleTypeValueAttribute.setValue(property.getApproverEmptyHandleType().getType());
        approverEmptyHandleTypeElement.setElementText(StringUtils.hasLength(property.getEmptyApproverSpecify()) ?
                property.getEmptyApproverSpecify() : "[]");
        approverEmptyHandleTypeElement.addAttribute(approverEmptyHandleTypeValueAttribute);

        ExtensionAttribute approverEmptyHandleTypeDescAttribute = new ExtensionAttribute();
        approverEmptyHandleTypeDescAttribute.setName(ELEMENT_ATTRIBUTE_DESC);
        approverEmptyHandleTypeDescAttribute.setValue("审批人为空时");
        approverEmptyHandleTypeElement.addAttribute(approverEmptyHandleTypeDescAttribute);
        userTask.addExtensionElement(approverEmptyHandleTypeElement);
    }

    /**
     * 设置执行监听
     *
     * @param node
     * @param userTask
     */
    private static void setExecutionListeners(BpmnJsonNode node, UserTask userTask) {

        List<FlowableListener> executionListeners = new ArrayList<>();
        // 设置执行监听
        FlowableListener executionListener = new FlowableListener();
        executionListener.setEvent(BaseExecutionListener.EVENTNAME_START);
        executionListener.setImplementationType(IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
        executionListener.setImplementation("${engineExecutionStartListener}");
        executionListeners.add(executionListener);

        if (!Objects.equals(NODE_STARTER, node.getType())) {
            FlowableListener activityStartListener = new FlowableListener();
            activityStartListener.setEvent(BaseExecutionListener.EVENTNAME_START);
            activityStartListener.setImplementationType(IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
            activityStartListener.setImplementation("${engineActivityStartEventListener}");
            executionListeners.add(activityStartListener);

            FlowableListener activityTakeListener = new FlowableListener();
            activityTakeListener.setEvent(BaseExecutionListener.EVENTNAME_TAKE);
            activityTakeListener.setImplementationType(IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
            activityTakeListener.setImplementation("${engineActivityTakeEventListener}");
            executionListeners.add(activityTakeListener);

            FlowableListener activityEndListener = new FlowableListener();
            activityEndListener.setEvent(BaseExecutionListener.EVENTNAME_END);
            activityEndListener.setImplementationType(IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
            activityEndListener.setImplementation("${engineActivityEndEventListener}");
            executionListeners.add(activityEndListener);
        }

        userTask.setExecutionListeners(executionListeners);
    }

    /**
     * 设置任务监听
     *
     * @param userTask
     */
    private static void setTaskListeners(UserTask userTask) {
        List<FlowableListener> taskListeners = new ArrayList<>();
        // 设置任务监听
        FlowableListener taskListener = new FlowableListener();
        taskListener.setEvent(TaskListener.EVENTNAME_ALL_EVENTS);
        taskListener.setImplementationType(IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);

        taskListener.setImplementation("${engineTaskEventListener}");
        taskListeners.add(taskListener);

        userTask.setTaskListeners(taskListeners);
    }

}
