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

import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum;
import cn.axzo.workflow.common.exception.WorkflowEngineException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.form.api.FormInstance;
import org.flowable.form.api.FormService;
import org.flowable.form.engine.FormEngineConfiguration;
import org.flowable.form.engine.impl.cmd.GetFormInstanceValuesCmd;
import org.flowable.form.engine.impl.util.CommandContextUtil;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.joda.time.LocalDate;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_ID_NOT_EXISTS;
import static cn.axzo.workflow.common.code.FormInstanceRespCode.FORM_INSTANCE_DATA_NOT_FOUND;
import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_SUBMIT_FORM_VARIABLE;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.PROCESSING;

/**
 * 自定义的获取表单实例数据的命令
 *
 * @author wangli
 * @since 2025-01-21 10:24
 */
public class CustomGetFormInstanceLatestValuesCmd extends GetFormInstanceValuesCmd {
    private static final long serialVersionUID = -1017881786777839488L;
    private String processInstanceId;
    private Boolean throwException;

    public CustomGetFormInstanceLatestValuesCmd(String processInstanceId) {
        this(null, processInstanceId, true);
    }

    public CustomGetFormInstanceLatestValuesCmd(String processInstanceId, Boolean throwException) {
        this(null, processInstanceId, throwException);
    }

    public CustomGetFormInstanceLatestValuesCmd(String formInstanceId, String processInstanceId,
                                                Boolean throwException) {
        super(formInstanceId);
        this.processInstanceId = processInstanceId;
        this.throwException = throwException;
    }

    @Override
    public byte[] execute(CommandContext commandContext) {
        if (StringUtils.hasText(formInstanceId) && !StringUtils.hasText(processInstanceId)) {
            return super.execute(commandContext);
        }
        ProcessEngineConfigurationImpl processEngineConfiguration =
                org.flowable.engine.impl.util.CommandContextUtil.getProcessEngineConfiguration(commandContext);

        HistoricProcessInstance instance =
                processEngineConfiguration.getHistoryService().createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (Objects.isNull(instance)) {
            throw new WorkflowEngineException(PROCESS_INSTANCE_ID_NOT_EXISTS, processInstanceId);
        }
        Map<String, Object> variableLocal = new HashMap<>();
        if (Objects.equals(PROCESSING, BpmnProcessInstanceResultEnum.valueOfStatus(instance.getBusinessStatus()))) {
            Map taskSubmitFormVariables =
                    processEngineConfiguration.getRuntimeService().getVariableLocal(processInstanceId,
                            TASK_SUBMIT_FORM_VARIABLE, Map.class);
            if (!CollectionUtils.isEmpty(taskSubmitFormVariables)) {
                variableLocal.putAll(taskSubmitFormVariables);
            }
        } else {
            HistoricVariableInstance historicVariableInstance = processEngineConfiguration.getHistoryService()
                    .createHistoricVariableInstanceQuery()
                    .processInstanceId(processInstanceId)
                    .variableName(TASK_SUBMIT_FORM_VARIABLE)
                    .singleResult();
            if (Objects.nonNull(historicVariableInstance)) {
                variableLocal.putAll((Map<String, Object>) historicVariableInstance.getValue());
            }
        }
        FormEngineConfiguration formEngineConfiguration = CommandContextUtil.getFormEngineConfiguration();
        if (CollectionUtils.isEmpty(variableLocal)) {

            FormService formService = formEngineConfiguration.getFormService();
            List<FormInstance> formInstances =
                    formService.createFormInstanceQuery().processInstanceId(processInstanceId)
                    .orderBySubmittedDate().desc().listPage(0, 1);
            if (!CollectionUtils.isEmpty(formInstances)) {
                // {
                //  "values" : {
                //    "form_mutil_text1" : "表单变量 2",
                //    "form_text1" : "表单变量 1",
                //    "form_image1" : "[{\"fileUrl\":\"http://gips2.baidu.com/it/u=195724436,3554684702&fm=3028&app=3028&f=JPEG&fmt=auto?w=1280&h=960\"}]"
                //  }
                //}
                return formInstances.get(0).getFormValueBytes();
            }
            if (throwException) {
                throw new WorkflowEngineException(FORM_INSTANCE_DATA_NOT_FOUND, processInstanceId);
            } else {
                return null;
            }
        } else {
            ObjectMapper objectMapper = formEngineConfiguration.getObjectMapper();
            ObjectNode submittedFormValuesJson = objectMapper.createObjectNode();

            ObjectNode valuesNode = submittedFormValuesJson.putObject("values");
            variableLocal.forEach((k, variableValue) -> {
                if (variableValue == null) {
                    valuesNode.putNull(k);
                } else if (variableValue instanceof Long) {
                    valuesNode.put(k, (Long) variableValue);

                } else if (variableValue instanceof Double) {
                    valuesNode.put(k, (Double) variableValue);

                } else if (variableValue instanceof Boolean) {
                    valuesNode.put(k, (Boolean) variableValue);

                } else if (variableValue instanceof LocalDate) {
                    valuesNode.put(k, ((LocalDate) variableValue).toString());

                } else {
                    valuesNode.put(k, variableValue.toString());
                }
            });
            try {
                return objectMapper.writeValueAsBytes(submittedFormValuesJson);
            } catch (JsonProcessingException e) {
                if (throwException) {
                    throw new WorkflowEngineException(FORM_INSTANCE_DATA_NOT_FOUND, processInstanceId);
                } else {
                    return null;
                }
            }
        }

    }
}
