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

import cn.axzo.workflow.common.exception.WorkflowEngineException;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.common.model.request.form.FormPermissionMetaInfo;
import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper;
import cn.axzo.workflow.core.common.utils.FormHelper;
import cn.axzo.workflow.core.repository.entity.ExtAxBpmnFormRelation;
import cn.axzo.workflow.core.repository.entity.ExtAxProcessLog;
import cn.axzo.workflow.core.service.BpmnProcessTaskForEsService;
import cn.axzo.workflow.core.service.ExtAxBpmnFormRelationService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
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.history.HistoricProcessInstance;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.form.api.FormDefinition;
import org.flowable.form.api.FormEngineConfigurationApi;
import org.flowable.form.api.FormInstanceInfo;
import org.flowable.form.api.FormRepositoryService;
import org.flowable.form.model.SimpleFormModel;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_ID_NOT_EXISTS;
import static cn.axzo.workflow.common.code.BpmnTaskRespCode.TASK_COMPLETE_FAIL_NOT_EXISTS;
import static cn.axzo.workflow.common.code.FormInstanceRespCode.FORM_PARAM_ERROR;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_STARTER;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.PROCESSING;

/**
 * 获取指定审批实例的表单内容以及字段权限
 *
 * @author wangli
 * @since 2024-11-13 14:24
 */
@Slf4j
public class GetFormInstanceAndPermissionCmd implements Command<FormInstanceInfo> {
    private final ExtAxBpmnFormRelationService bpmnFormRelationService;
    private final BpmnProcessTaskForEsService bpmnProcessTaskForEsService;
    private final BpmnTaskDelegateAssigner assigner;
    private String processInstanceId;
    private String taskId;
    private Boolean showOriginDefaultValue = false;

    /**
     * 全权通过实例查询一切
     *
     * @param processInstanceId
     */
    public GetFormInstanceAndPermissionCmd(ExtAxBpmnFormRelationService bpmnFormRelationService,
                                           BpmnProcessTaskForEsService bpmnProcessTaskForEsService,
                                           BpmnTaskDelegateAssigner assigner,
                                           String processInstanceId) {
        this.bpmnFormRelationService = bpmnFormRelationService;
        this.bpmnProcessTaskForEsService = bpmnProcessTaskForEsService;
        this.assigner = assigner;
        this.processInstanceId = processInstanceId;
    }

    /**
     * 通过实例和任务查询
     *
     * @param processInstanceId 可为空,则用 taskId 反查实例相关
     * @param taskId
     */
    public GetFormInstanceAndPermissionCmd(ExtAxBpmnFormRelationService bpmnFormRelationService,
                                           BpmnProcessTaskForEsService bpmnProcessTaskForEsService,
                                           BpmnTaskDelegateAssigner assigner,
                                           String processInstanceId,
                                           String taskId,
                                           Boolean showOriginDefaultValue) {
        this.bpmnFormRelationService = bpmnFormRelationService;
        this.bpmnProcessTaskForEsService = bpmnProcessTaskForEsService;
        this.assigner = assigner;
        this.processInstanceId = processInstanceId;
        this.taskId = taskId;
        this.showOriginDefaultValue = showOriginDefaultValue;
    }

    @Override
    public FormInstanceInfo execute(CommandContext commandContext) {
        preCheckParam();

        // 获取表单定义+字段内容
        FormInstanceInfo formInstanceInfo = getFormInstanceInfo(commandContext);
        if (Objects.isNull(formInstanceInfo)) {
            return null;
        }

        // 获取字段权限
        List<FormPermissionMetaInfo> fieldPermission = getFieldPermission(commandContext);

        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
        HistoryService historyService = processEngineConfiguration.getHistoryService();
        HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .includeProcessVariables()
                .singleResult();
        SimpleFormModel formModel = (SimpleFormModel) formInstanceInfo.getFormModel();
        Map<String, Object> processVariables = instance.getProcessVariables();

        FormHelper.populateFormModel(formModel, fieldPermission, processVariables, showOriginDefaultValue);
        return formInstanceInfo;
    }

    private List<FormPermissionMetaInfo> getFieldPermission(CommandContext commandContext) {
        List<ExtAxProcessLog> logs = bpmnProcessTaskForEsService.queryProcessLogByProcessInstanceId(processInstanceId);

        Map<String, ExtAxProcessLog> activityMap = logs.stream()
                .collect(Collectors.toMap(ExtAxProcessLog::getActivityId, Function.identity(), (s, t) -> s));

        // 待审批的节点
        List<ExtAxProcessLog> processingLogs = logs.stream()
                .filter(e -> (Objects.equals(PROCESSING.getStatus(), e.getStatus()) && Objects.isNull(e.getEndTime())))
                .collect(Collectors.toList());

        List<FormPermissionMetaInfo> list = binaryMerge(processingLogs, activityMap);
        if (!CollectionUtils.isEmpty(list)) {
            return list;
        }

        // 已结束的审批节点含开始节点
        List<ExtAxProcessLog> finishLogs = logs.stream()
                .filter(e -> !Objects.equals(PROCESSING.getStatus(), e.getStatus()) && Objects.nonNull(e.getEndTime()))
                .collect(Collectors.toList());

        list = binaryMerge(finishLogs, activityMap);
        if (!CollectionUtils.isEmpty(list)) {
            // 这里都是非审批的,统一将所有可见,必填的节点转成只读.    [完全忽略隐藏属性]
            list.forEach(FormPermissionMetaInfo::toReadonly);
            return list;
        }

        // 默认全字段都是隐藏
        // 假设通过分享功能，分享了审批，那么被分享人不应该看到表单
        ExtAxProcessLog starterLog = logs.stream().filter(e -> Objects.equals(NODE_STARTER.getType(), e.getActivityId()))
                .findFirst().orElse(new ExtAxProcessLog());
        ListUtils.emptyIfNull(starterLog.getFormFieldPermissionConf()).forEach(perm -> {
            // 防预编程, 强制设置为 hidden
            perm.setRequired(false);
            perm.setEditable(false);
            perm.setReadonly(false);
            perm.setHidden(true);
        });
        return CollectionUtils.isEmpty(starterLog.getFormFieldPermissionConf()) ? new ArrayList<>() : starterLog.getFormFieldPermissionConf();
    }

    @SuppressWarnings("unchecked")
    private List<FormPermissionMetaInfo> binaryMerge(List<ExtAxProcessLog> logs, Map<String, ExtAxProcessLog> activityMap) {
        List<List<FormPermissionMetaInfo>> permissions = new ArrayList<>();
        collectActivityIds(logs).forEach(id -> {
            ExtAxProcessLog log = activityMap.getOrDefault(id, null);
            if (Objects.nonNull(log)) {
                permissions.add(log.getFormFieldPermissionConf());
            }
        });
        return BpmnMetaParserHelper.mergeAllPermission(permissions.toArray(new List[0]));
    }

    private List<String> collectActivityIds(List<ExtAxProcessLog> logs) {
        List<String> activityIds = new ArrayList<>();
        if (Objects.isNull(assigner)) {
            return activityIds;
        }
        if (!CollectionUtils.isEmpty(logs)) {
            Map<String, List<BpmnTaskDelegateAssigner>> nodeUserMap = rebuildToNodeUserMap(logs);

            nodeUserMap.forEach((activityId, assigners) -> {
                assigners.forEach(u -> {
                    if (Objects.equals(u.getPersonId(), assigner.getPersonId())) {
                        activityIds.add(activityId);
                    }
                });
            });
        }
        return activityIds;
    }

    private static Map<String, List<BpmnTaskDelegateAssigner>> rebuildToNodeUserMap(List<ExtAxProcessLog> logs) {
        Map<String, List<BpmnTaskDelegateAssigner>> assignerMap = new HashMap<>();
        logs.forEach(e -> {
            if (assignerMap.containsKey(e.getActivityId())) {
                if (!CollectionUtils.isEmpty(e.getAssigneeFull())) {
                    assignerMap.get(e.getActivityId()).addAll(e.getAssigneeFull());
                }
            } else {
                assignerMap.put(e.getActivityId(), new ArrayList<>(ListUtils.emptyIfNull(e.getAssigneeFull())));
            }
        });
        return assignerMap;
    }

    private FormInstanceInfo getFormInstanceInfo(CommandContext commandContext) {
        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
        HistoryService historyService = processEngineConfiguration.getHistoryService();

        HistoricProcessInstance instance;
        if (StringUtils.hasText(processInstanceId)) {
            instance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        } else {
            HistoricTaskInstance task = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
            if (Objects.isNull(task)) {
                throw new WorkflowEngineException(TASK_COMPLETE_FAIL_NOT_EXISTS);
            }
            instance = historyService.createHistoricProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
        }
        if (Objects.isNull(instance)) {
            throw new WorkflowEngineException(PROCESS_INSTANCE_ID_NOT_EXISTS, processInstanceId);
        }
        processInstanceId = instance.getId();
        ExtAxBpmnFormRelation relation = bpmnFormRelationService.queryByBpmnDefinitionId(instance.getProcessDefinitionId());
        if (Objects.isNull(relation)) {
            // 没有配置表单
            return null;
        }

        FormEngineConfigurationApi formEngineConfiguration = CommandContextUtil.getFormEngineConfiguration(commandContext);
        FormRepositoryService formRepositoryService = formEngineConfiguration.getFormRepositoryService();
        FormDefinition formDefinition = formRepositoryService.createFormDefinitionQuery().deploymentId(relation.getFormDeploymentId()).singleResult();

        // 获取表单定义+字段内容
        return commandContext.getCommandExecutor().execute(new CustomGetFormInstanceModelCmd(null, null, formDefinition.getId(), taskId, processInstanceId, instance.getTenantId(), null, false));
    }

    private void preCheckParam() {
        if (!StringUtils.hasText(processInstanceId) && !StringUtils.hasText(taskId)) {
            throw new WorkflowEngineException(FORM_PARAM_ERROR);
        }
    }
}
