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

import cn.axzo.workflow.common.model.request.bpmn.BpmnNoticeConf;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.common.model.request.bpmn.task.ExtHiTaskSearchDTO;
import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper;
import cn.axzo.workflow.core.engine.cmd.CustomCarbonCopyUserSelectorCmd;
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 cn.axzo.workflow.core.repository.entity.ExtAxHiTaskInst;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import cn.axzo.workflow.core.service.converter.BpmnHistoricTaskInstanceConverter;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.ServiceTask;
import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher;
import org.flowable.common.engine.impl.interceptor.CommandExecutor;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;

import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getCarbonCopyConfigs;

/**
 * 抄送功能的具体实现
 * <p>
 * 只负责完成抄送自身功能即可, 关于审批日志中需要记录抄送节点的功能,由 CustomServiceTaskDelegateExpressionActivityBehavior 完成
 *
 * @author wangli
 * @since 08/03/2024 17:17
 */
@Component
@RefreshScope
@Slf4j
public class EngineCarbonCopyEventListener implements JavaDelegate {
    @Value("${workflow.carbonCopyTemplateCode}")
    private String carbonCopyTemplateCode;
    @Resource
    private EngineExecutionStartListener engineExecutionStartListener;
    @Resource
    private SpringProcessEngineConfiguration springProcessEngineConfiguration;
    @Resource
    private BpmnHistoricTaskInstanceConverter historicTaskInstanceConverter;
    @Resource
    private ExtAxHiTaskInstService extAxHiTaskInstService;
    @Resource
    private String serviceVersion;

    @Override
    public void execute(DelegateExecution execution) {
        log.info("EngineCarbonCopyEventListener exec...");
        String currentActivityId = execution.getCurrentActivityId();
        String processDefinitionId = execution.getProcessDefinitionId();
        String processInstanceId = execution.getProcessInstanceId();
        CommandExecutor commandExecutor = springProcessEngineConfiguration.getCommandExecutor();
        Process mainProcess = ProcessDefinitionUtil.getBpmnModel(processDefinitionId).getMainProcess();

        ServiceTask serviceTask = (ServiceTask) mainProcess.getFlowElement(currentActivityId);

        List<BpmnTaskDelegateAssigner> carbonUsers = new ArrayList<>();
        getCarbonCopyConfigs(serviceTask).ifPresent(carbons -> {
            carbonUsers.addAll(commandExecutor.execute(new CustomCarbonCopyUserSelectorCmd(processInstanceId,
                    carbons, serviceTask, engineExecutionStartListener,
                    historicTaskInstanceConverter, serviceVersion, execution)));
        });

        RuntimeService runtimeService =
                springProcessEngineConfiguration.getProcessEngineConfiguration().getRuntimeService();
        runtimeService.setVariable(processInstanceId,
                INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + currentActivityId, carbonUsers);

        // 发送抄送事件
        invokeCarbonCopy(carbonUsers, BpmnMetaParserHelper.getNoticeConfig(mainProcess), execution);
    }

    private void invokeCarbonCopy(List<BpmnTaskDelegateAssigner> carbonUsers,
                                  Optional<BpmnNoticeConf> noticeConf,
                                  DelegateExecution execution) {
        if (CollectionUtils.isEmpty(carbonUsers)) {
            return;
        }

        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
        FlowableEventDispatcher eventDispatcher = processEngineConfiguration.getEventDispatcher();

        BpmnNoticeConf bpmnNoticeConf = noticeConf.map(conf -> {
            if (!StringUtils.hasText(conf.getCarbonCopy().getCarbonCopyMessageId())) {
                conf.getCarbonCopy().setCarbonCopyMessageId(carbonCopyTemplateCode);
            }
            return conf;
        }).orElse(null);

        MessagePushEventImpl event = MessagePushEventBuilder.createEvent(MessagePushEventType.CARBON_COPY,
                carbonUsers, bpmnNoticeConf, execution.getProcessInstanceId(),
                execution.getTenantId(), getCarbonTaskId(execution));
        eventDispatcher.dispatchEvent(event, processEngineConfiguration.getEngineCfgKey());
    }

    private String getCarbonTaskId(DelegateExecution execution) {
        AtomicReference<String> taskId = new AtomicReference<>(null);
        ExtHiTaskSearchDTO searchDTO = new ExtHiTaskSearchDTO();
        searchDTO.setProcessInstanceId(execution.getProcessInstanceId());
        searchDTO.setTaskDefinitionKey(execution.getCurrentActivityId());
        extAxHiTaskInstService.queryList(searchDTO)
                .stream()
                .max(Comparator.comparing(ExtAxHiTaskInst::getCreateAt))
                .ifPresent(i -> taskId.set(i.getTaskId()));
        return taskId.get();
    }


}
