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

import cn.axzo.workflow.common.enums.CarbonCopyObjectType;
import cn.axzo.workflow.common.model.request.bpmn.BpmnCarbonCopyConf;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnHistoricTaskInstanceVO;
import cn.axzo.workflow.core.engine.listener.EngineExecutionStartListener;
import cn.axzo.workflow.core.service.converter.BpmnHistoricTaskInstanceConverter;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import org.flowable.bpmn.model.CustomProperty;
import org.flowable.bpmn.model.ServiceTask;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.runtime.Execution;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.springframework.util.CollectionUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_INITIATOR;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_INFO;
import static cn.axzo.workflow.common.constant.BpmnConstants.NO_ASSIGNEE;
import static cn.axzo.workflow.common.constant.BpmnConstants.OLD_INTERNAL_TASK_RELATION_ASSIGNEE_INFO_SNAPSHOT;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.removeDuplicateByPersonId;

/**
 * 抄送节点,计算全量被抄送人,并主动去重
 *
 * @author wangli
 * @since 18/03/2024 11:33
 */
public class CustomCarbonCopyUserSelectorCmd implements Command<List<BpmnTaskDelegateAssigner>>, Serializable {
    private static final long serialVersionUID = 1L;

    private final String processInstanceId;
    private final List<BpmnCarbonCopyConf> carbons;
    private final ServiceTask serviceTask;
    private final EngineExecutionStartListener engineExecutionStartListener;
    private final BpmnHistoricTaskInstanceConverter historicTaskInstanceConverter;
    private final String serviceVersion;
    /**
     * 模型中发起节点立马接着抄送节点,此时无法通过 runtimeService 得到正运行的 execution,所以由外部传递
     */
    private DelegateExecution execution;

    public CustomCarbonCopyUserSelectorCmd(String processInstanceId, List<BpmnCarbonCopyConf> carbons, ServiceTask serviceTask, EngineExecutionStartListener engineExecutionStartListener, BpmnHistoricTaskInstanceConverter historicTaskInstanceConverter, String serviceVersion) {
        this.processInstanceId = processInstanceId;
        this.carbons = carbons;
        this.serviceTask = serviceTask;
        this.engineExecutionStartListener = engineExecutionStartListener;
        this.historicTaskInstanceConverter = historicTaskInstanceConverter;
        this.serviceVersion = serviceVersion;
    }

    public CustomCarbonCopyUserSelectorCmd(String processInstanceId, List<BpmnCarbonCopyConf> carbons,
                                           ServiceTask serviceTask,
                                           EngineExecutionStartListener engineExecutionStartListener,
                                           BpmnHistoricTaskInstanceConverter historicTaskInstanceConverter,
                                           String serviceVersion,
                                           DelegateExecution execution) {
        this.processInstanceId = processInstanceId;
        this.carbons = carbons;
        this.serviceTask = serviceTask;
        this.engineExecutionStartListener = engineExecutionStartListener;
        this.historicTaskInstanceConverter = historicTaskInstanceConverter;
        this.serviceVersion = serviceVersion;
        this.execution = execution;
    }

    @Override
    public List<BpmnTaskDelegateAssigner> execute(CommandContext commandContext) {
        if (CollectionUtils.isEmpty(carbons)) {
            return Collections.emptyList();
        }
        ProcessEngineConfigurationImpl processEngineConfiguration =
                CommandContextUtil.getProcessEngineConfiguration(commandContext);
        RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
        List<Execution> list = new ArrayList<>();
        if (Objects.isNull(execution)) {
            list.addAll(runtimeService.createExecutionQuery().processInstanceId(processInstanceId).list());
        } else {
            list.add((Execution) execution);
        }
        List<BpmnTaskDelegateAssigner> assigners = new ArrayList<>();
        carbons.forEach(carbon -> {
            CarbonCopyObjectType carbonCopyObjectType = carbon.getCarbonCopyObjectType();
            switch (carbonCopyObjectType) {
                case ent_role:
                case ent_identity:
                case ent_position:
                case project_role:
                case project_identity:
                case project_position:
                    CustomProperty property = new CustomProperty();
                    property.setName(carbonCopyObjectType.getType());
                    property.setSimpleValue(carbon.getSpecifyValue());
                    serviceTask.setCustomProperties(Lists.newArrayList(property));
                    assigners.addAll(engineExecutionStartListener.approverSelect(carbonCopyObjectType.getSimpleType(),
                            serviceTask, (DelegateExecution) list.get(0), false));
                    break;
                case approver_relation:
                    assigners.addAll(getApproverRelationUser(carbon, carbonCopyObjectType,
                            (DelegateExecution) list.get(0), processEngineConfiguration, processInstanceId));
                    break;
                case specify_user:
                    assigners.addAll(JSON.parseArray(carbon.getSpecifyValue(), BpmnTaskDelegateAssigner.class));
                    break;
                default:
                    break;
            }
        });
        return removeDuplicateByPersonId(assigners);
    }

    private List<BpmnTaskDelegateAssigner> getApproverRelationUser(BpmnCarbonCopyConf carbon,
                                                                   CarbonCopyObjectType carbonCopyObjectType,
                                                                   DelegateExecution execution,
                                                                   ProcessEngineConfigurationImpl processEngineConfiguration,
                                                                   String processInstanceId) {
        List<BpmnTaskDelegateAssigner> assigners = new ArrayList<>();
        List<String> values = JSON.parseArray(carbon.getSpecifyValue(), String.class)
                .stream().map(JSON::parseObject)
                .map(i -> i.getString("value"))
                .collect(Collectors.toList());
        HistoryService historyService = processEngineConfiguration.getHistoryService();
        values.forEach(value -> {
            CarbonCopyObjectType approverRelationConf = CarbonCopyObjectType.valueOfType(value);
            switch (approverRelationConf) {
                case initiator:
                    HistoricVariableInstance historicVariableInstance =
                            historyService.createHistoricVariableInstanceQuery()
                                    .processInstanceId(processInstanceId)
                                    .variableName(INTERNAL_INITIATOR)
                                    .singleResult();
                    if (Objects.isNull(historicVariableInstance)) {
                        RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
                        BpmnTaskDelegateAssigner assigner = BpmnTaskDelegateAssigner.toObjectCompatible(runtimeService.getVariable(processInstanceId, INTERNAL_INITIATOR));
                        assigners.add(assigner);
                    } else {
                        assigners.add(BpmnTaskDelegateAssigner.toObjectCompatible(historicVariableInstance.getValue()));
                    }
                    break;
                case approver:
                    List<HistoricTaskInstance> taskInstances = historyService.createHistoricTaskInstanceQuery()
                            .processInstanceId(processInstanceId)
                            //                            .includeProcessVariables()
                            .orderByHistoricTaskInstanceStartTime()
                            .desc().list();
                    List<BpmnHistoricTaskInstanceVO> vos =
                            historicTaskInstanceConverter.toVosSkipSystemOperation(taskInstances,
                                    serviceVersion);

                    Map<String, HistoricVariableInstance> variableInstanceMap =
                            // 不能使用框架提供的历史变量 API 查询,有 BUG
                            historyService.createNativeHistoricVariableInstanceQuery()
                                    .sql("select * from ACT_HI_VARINST t where t.proc_inst_id_= #{processInstanceId}")
                                    .parameter("processInstanceId", processInstanceId)
                                    .list().stream()
                                    .collect(Collectors.toMap(HistoricVariableInstance::getVariableName,
                                            Function.identity(), (s, t) -> s));
                    vos.forEach(vo -> {
                        HistoricVariableInstance assginerSnapshot =
                                variableInstanceMap.getOrDefault(INTERNAL_TASK_RELATION_ASSIGNEE_INFO + vo.getTaskId(),
                                        null);
                        if (Objects.isNull(assginerSnapshot)) {
                            assginerSnapshot =
                                    variableInstanceMap.getOrDefault(OLD_INTERNAL_TASK_RELATION_ASSIGNEE_INFO_SNAPSHOT + vo.getTaskId(), null);
                        }
                        if (Objects.nonNull(assginerSnapshot)) {
                            BpmnTaskDelegateAssigner assigner = BpmnTaskDelegateAssigner.toObjectCompatible(assginerSnapshot.getValue());
                            if (Objects.nonNull(assigner) && !Objects.equals(assigner.buildAssigneeId(), NO_ASSIGNEE)) {
                                assigners.add(assigner);
                            }
                        }
                    });
                    // 历史真正有同意和驳回动作的人
                    break;
                case ent_initiator_leader:
                case project_initiator_leader:
                    CustomProperty property = new CustomProperty();
                    property.setName(approverRelationConf.getType());
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("value", value);
                    property.setSimpleValue(JSONUtil.toJsonStr(Lists.newArrayList(jsonObject)));
                    serviceTask.setCustomProperties(Lists.newArrayList(property));
                    assigners.addAll(engineExecutionStartListener.approverSelect(carbonCopyObjectType.getSimpleType(),
                            serviceTask, execution, false));
                default:
                    break;
            }
        });
        return assigners;
    }


}
