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

import cn.axzo.workflow.common.model.dto.SignatureDTO;
import cn.axzo.workflow.core.common.utils.SpringContextUtils;
import cn.axzo.workflow.core.service.CategoryService;
import com.alibaba.fastjson.JSON;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.HistoryService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static cn.axzo.workflow.common.constant.BpmnConstants.BPM_MODEL_CATEGORY;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_INITIATOR;
import static cn.axzo.workflow.common.constant.BpmnConstants.SIGNATURE_COLLECTION;
import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_DEFINITION_KEY;
import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_END_TIME;
import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INITIATOR;
import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INSTANCE_ID;
import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_START_TIME;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_STARTER;

/**
 * 自定义获取流程实例中用于打印的变量命令实现
 *
 * @author wangli
 * @since 2025-01-21 11:47
 */
public class CustomGetProcessInstanceVariablesCmd extends AbstractCommand<Map<String, Object>> implements Serializable {

    private final String processInstanceId;
    private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");

    public CustomGetProcessInstanceVariablesCmd(String processInstanceId) {
        this.processInstanceId = processInstanceId;
    }

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

    @Override
    public Map<String, Object> executeInternal(CommandContext commandContext) {
        Map<String, Object> variables = new HashMap<>();
        variables.put(PRINT_VAR_PROCESS_INSTANCE_ID, processInstanceId);
        if (!StringUtils.hasText(processInstanceId)) {
            return variables;
        }

        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
        HistoryService historyService = processEngineConfiguration.getHistoryService();
        HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId).includeProcessVariables().singleResult();
        // 添加流程开始时间
        variables.put(PRINT_VAR_PROCESS_START_TIME, sdf.format(instance.getStartTime()));
        variables.put(PRINT_VAR_PROCESS_END_TIME, Objects.nonNull(instance.getEndTime()) ? sdf.format(instance.getEndTime()) : null);
        // 添加流程业务 ID
        addProcessDefinitionKey(variables, instance);

        Map<String, Object> processVariables = instance.getProcessVariables();
        // 发起人流程引擎内部模型，需要外部调用该命令时，再解析
        variables.put(PRINT_VAR_PROCESS_INITIATOR, processVariables.get(INTERNAL_INITIATOR));

        // 电子签名
        addSignature(variables, (List<SignatureDTO>) processVariables.get(SIGNATURE_COLLECTION));

        return variables;
    }

    private void addSignature(Map<String, Object> variables, List<SignatureDTO> signatures) {
        if (CollectionUtils.isEmpty(signatures)) {
            return;
        }
        signatures.forEach(sign -> {
            if (!Objects.equals(NODE_STARTER.getType(), sign.getActivityId())) {
                variables.put(sign.getActivityId(), sign.getSignatures());
            }
        });
    }

    private void addProcessDefinitionKey(Map<String, Object> variables, HistoricProcessInstance historicProcessInstance) {
        CategoryService categoryService = SpringContextUtils.getBean(CategoryService.class);
        categoryService.get(BPM_MODEL_CATEGORY, historicProcessInstance.getProcessDefinitionKey()).ifPresent(category -> {
            variables.put(PRINT_VAR_PROCESS_DEFINITION_KEY, category.getLabel() + "(" + category.getValue() + ")");
        });
    }

}
