package cn.axzo.workflow.core.engine.cmd;

import cn.axzo.workflow.common.exception.WorkflowEngineException;
import cn.axzo.workflow.common.model.request.bpmn.BpmnNoticeConf;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper;
import cn.axzo.workflow.core.conf.SupportRefreshProperties;
import cn.axzo.workflow.core.engine.event.MessagePushEventBuilder;
import cn.axzo.workflow.core.engine.event.MessagePushEventImpl;
import cn.axzo.workflow.core.engine.event.MessagePushEventType;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import org.apache.commons.collections.CollectionUtils;
import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_CANT_REMIND;
import static cn.axzo.workflow.common.code.BpmnTaskRespCode.REMIND_TASK_TOO_MANY;
import static cn.axzo.workflow.common.code.BpmnTaskRespCode.TASK_REMIND_ERROR_NOT_EXISTS;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_INFO;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getActivitySignature;

/**
 * 自定义(同步)催办任务的命令器实现
 *
 * @author wangli
 * @since 2025-06-13 13:39
 */
public class CustomRemindTaskCmd extends AbstractCommand<Void> implements Serializable {
    private static final Logger log = LoggerFactory.getLogger(CustomRemindTaskCmd.class);
    private final SupportRefreshProperties refreshProperties;
    /**
     * 操作催办时的终端类型，管理端cm、工人端cmp
     */
    private final String terminalType;
    private final String processInstanceId;
    private String taskDefinitionKey;
    /**
     * 催办方式
     * <p>
     * IM, 如果为空，默认是 IM
     */
    private final List<String> remindTypes;

    public CustomRemindTaskCmd(String terminalType, String processInstanceId, String taskDefinitionKey, List<String> remindTypes, SupportRefreshProperties refreshProperties) {
        this.terminalType = terminalType;
        this.processInstanceId = processInstanceId;
        this.taskDefinitionKey = taskDefinitionKey;
        this.remindTypes = CollectionUtils.isEmpty(remindTypes) ? Lists.newArrayList("IM") : remindTypes;
        this.refreshProperties = refreshProperties;
    }

    @Override
    public String paramToJsonString() {
        Map<String, Object> params = new HashMap<>();
        params.put("terminalType", terminalType);
        params.put("processInstanceId", processInstanceId);
        params.put("taskDefinitionKey", taskDefinitionKey);
        params.put("remindTypes", remindTypes);
        return JSON.toJSONString(params);
    }

    @Override
    public Void executeInternal(CommandContext commandContext) {
        ProcessEngineConfigurationImpl processEngineConfiguration =
                CommandContextUtil.getProcessEngineConfiguration(commandContext);
        RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
        ProcessInstance processInstance =
                runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (Objects.isNull(processInstance)) {
            throw new WorkflowEngineException(PROCESS_INSTANCE_CANT_REMIND, processInstanceId);
        }

        TaskService taskService = processEngineConfiguration.getTaskService();
        TaskQuery taskQuery = taskService.createTaskQuery().processInstanceId(processInstanceId);
        if (StringUtils.hasLength(taskDefinitionKey)) {
            taskQuery.taskDefinitionKey(taskDefinitionKey);
        }
        List<Task> list = taskQuery.active().list();
        if (CollectionUtils.isEmpty(list)) {
            throw new WorkflowEngineException(TASK_REMIND_ERROR_NOT_EXISTS);
        }
        if (!StringUtils.hasText(taskDefinitionKey)) {
            List<String> taskDefinitionKeys = list.stream().map(Task::getTaskDefinitionKey).distinct().collect(Collectors.toList());
            if (taskDefinitionKeys.isEmpty()) {
                throw new WorkflowEngineException(TASK_REMIND_ERROR_NOT_EXISTS);
            } else if (taskDefinitionKeys.size() > 1) {
                throw new WorkflowEngineException(REMIND_TASK_TOO_MANY);
            } else {
                taskDefinitionKey = taskDefinitionKeys.get(0);
            }
        }
        FlowableEventDispatcher eventDispatcher = processEngineConfiguration.getEventDispatcher();
        Process process = ProcessDefinitionUtil.getProcess(processInstance.getProcessDefinitionId());
        Optional<BpmnNoticeConf> noticeConfig =
                BpmnMetaParserHelper.getNoticeConfig(process);

        Map<String, Object> variables = runtimeService.getVariables(processInstanceId, list.stream()
                .map(i -> INTERNAL_TASK_RELATION_ASSIGNEE_INFO + i.getId()).collect(Collectors.toList()));
        List<BpmnTaskDelegateAssigner> assigners = variables.values().stream()
                .map(BpmnTaskDelegateAssigner::toObjectCompatible)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());


        // 过滤出未审批的任何,用选择的方式去发送消息
        remindTypes.forEach(type -> {
            list.forEach(task -> {
                assigners.stream().filter(i -> Objects.equals(task.getAssignee(), i.buildAssigneeId())).findAny().ifPresent(assigner -> {
                    MessagePushEventImpl event = MessagePushEventBuilder.createEvent(MessagePushEventType.valueOf(type),
                            Lists.newArrayList(assigner), noticeConfig.orElse(null), processInstance.getProcessInstanceId(),
                            processInstance.getProcessDefinitionKey(), processInstance.getTenantId(), task.getId(),
                            getActivitySignature(process.getFlowElement(taskDefinitionKey)),
                            refreshProperties.getImTemplateCode(), terminalType);
                    eventDispatcher.dispatchEvent(event, processEngineConfiguration.getEngineCfgKey());
                });
            });
        });
        return null;
    }
}
