package com.ankaibei.workFlow.webadmin.ankaibei.utils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSONObject;
import com.ankaibei.workFlow.common.core.annotation.DisableDataFilter;
import com.ankaibei.workFlow.common.core.constant.ErrorCodeEnum;
import com.ankaibei.workFlow.common.core.object.ResponseResult;
import com.ankaibei.workFlow.common.flow.constant.FlowConstant;
import com.ankaibei.workFlow.common.flow.dto.FlowTaskCommentDto;
import com.ankaibei.workFlow.common.flow.model.FlowEntry;
import com.ankaibei.workFlow.common.flow.model.FlowEntryPublish;
import com.ankaibei.workFlow.common.flow.model.FlowTaskComment;
import com.ankaibei.workFlow.common.flow.model.constant.FlowEntryStatus;
import com.ankaibei.workFlow.common.flow.online.service.FlowOnlineOperationService;
import com.ankaibei.workFlow.common.flow.service.FlowEntryService;
import com.ankaibei.workFlow.common.flow.util.FlowOperationHelper;
import com.ankaibei.workFlow.common.flow.vo.TaskInfoVo;
import com.ankaibei.workFlow.common.online.model.OnlineDatasource;
import com.ankaibei.workFlow.common.online.model.OnlineDatasourceRelation;
import com.ankaibei.workFlow.common.online.model.OnlineFormDatasource;
import com.ankaibei.workFlow.common.online.model.OnlineTable;
import com.ankaibei.workFlow.common.online.object.ColumnData;
import com.ankaibei.workFlow.common.online.service.OnlineFormService;
import com.ankaibei.workFlow.common.online.util.OnlineOperationHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

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

@Component
public class FlowTaskStart {

    @Autowired
    private FlowEntryService flowEntryService;
    @Autowired
    private OnlineFormService onlineFormService;
    @Autowired
    private FlowOperationHelper flowOperationHelper;
    @Autowired
    private OnlineOperationHelper onlineOperationHelper;
    @Autowired
    private FlowOnlineOperationService flowOnlineOperationService;


    /**
     * 根据指定流程的主版本，发起一个流程实例，同时作为第一个任务节点的执行人，执行第一个用户任务。
     * 该接口无需数据权限过滤，因此用DisableDataFilter注解标注。如果当前系统没有支持数据权限过滤，该注解不会有任何影响。
     *
     * @param processDefinitionKey 流程定义标识。
     * @param flowTaskCommentDto   审批意见。
     * @param taskVariableData     流程任务变量数据。
     * @param masterData           流程审批相关的主表数据。
     * @param slaveData            流程审批相关的多个从表数据。
     * @param copyData             传阅数据，格式为type和id，type的值参考FlowConstant中的常量值。
     * @return 应答结果对象。
     */
    @DisableDataFilter
    public ResponseResult<Map<String, String>> startAndTakeUserTaskSS(
            String processDefinitionKey, FlowTaskCommentDto flowTaskCommentDto, JSONObject taskVariableData,
            JSONObject masterData, JSONObject slaveData, JSONObject copyData) {
        String errorMessage;
        // 1. 验证流程数据的合法性。
        ResponseResult<FlowEntry> flowEntryResult = verifyAndGetFlowEntry(processDefinitionKey);
        if (!flowEntryResult.isSuccess()) {
            return ResponseResult.errorFrom(flowEntryResult);
        }
        // 2. 验证流程一个用户任务的合法性。
        FlowEntryPublish flowEntryPublish = flowEntryResult.getData().getMainFlowEntryPublish();
        if (!flowEntryPublish.getActiveStatus()) {
            errorMessage = "数据验证失败，当前流程发布对象已被挂起，不能启动新流程！";
            return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
        }
        ResponseResult<TaskInfoVo> taskInfoResult =
                flowOperationHelper.verifyAndGetInitialTaskInfo(flowEntryPublish, true);
        if (!taskInfoResult.isSuccess()) {
            return ResponseResult.errorFrom(taskInfoResult);
        }
        TaskInfoVo taskInfo = taskInfoResult.getData();
        // 3. 验证在线表单及其关联数据源的合法性。
        ResponseResult<OnlineDatasource> datasourceResult = this.verifyAndGetOnlineDatasource(taskInfo.getFormId());
        if (!datasourceResult.isSuccess()) {
            return ResponseResult.errorFrom(datasourceResult);
        }
        OnlineDatasource datasource = datasourceResult.getData();
        OnlineTable masterTable = datasource.getMasterTable();
        // 4. 为本次流程操作构建数据。
        ResponseResult<List<ColumnData>> columnDataListResult =
                onlineOperationHelper.buildTableData(masterTable, masterData, false, null);
        if (!columnDataListResult.isSuccess()) {
            return ResponseResult.errorFrom(columnDataListResult);
        }
        // 这里把传阅数据放到任务变量中，是为了避免给流程数据操作方法增加额外的方法调用参数。
        if (MapUtil.isNotEmpty(copyData)) {
            if (taskVariableData == null) {
                taskVariableData = new JSONObject();
            }
            taskVariableData.put(FlowConstant.COPY_DATA_KEY, copyData);
        }
        FlowTaskComment flowTaskComment = BeanUtil.copyProperties(flowTaskCommentDto, FlowTaskComment.class);
        // 5. 保存在线表单提交的数据，同时启动流程和自动完成第一个用户任务。
        //进入流程后获取到流程实例的id
        String processInstanceId = "";
        if (slaveData == null) {
            processInstanceId = flowOnlineOperationService.saveNewAndStartProcess(
                    flowEntryPublish.getProcessDefinitionId(),
                    flowTaskComment,
                    taskVariableData,
                    masterTable,
                    columnDataListResult.getData());
        } else {
            // 如果本次请求中包含从表数据，则一同插入。
            ResponseResult<Map<OnlineDatasourceRelation, List<List<ColumnData>>>> slaveDataListResult =
                    onlineOperationHelper.buildSlaveDataList(datasource.getDatasourceId(), slaveData);
            if (!slaveDataListResult.isSuccess()) {
                return ResponseResult.errorFrom(slaveDataListResult);
            }
            processInstanceId = flowOnlineOperationService.saveNewAndStartProcess(
                    flowEntryPublish.getProcessDefinitionId(),
                    flowTaskComment,
                    taskVariableData,
                    masterTable,
                    columnDataListResult.getData(),
                    slaveDataListResult.getData());
        }
        Map<String, String> map = new HashMap<>();
        map.put("processDefinitionId", flowEntryPublish.getProcessDefinitionId());
        map.put("processInstanceId", processInstanceId);
        return ResponseResult.success(map);
    }

    /**
     * 验证并获取流程对象。
     *
     * @param processDefinitionKey 流程引擎的流程定义标识。
     * @return 流程对象。
     */
    public ResponseResult<FlowEntry> verifyAndGetFlowEntry(String processDefinitionKey) {
        String errorMessage;
        FlowEntry flowEntry = flowEntryService.getFlowEntryFromCache(processDefinitionKey);
        if (flowEntry == null) {
            errorMessage = "数据验证失败，该流程并不存在，请刷新后重试！";
            return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage);
        }
        if (!flowEntry.getStatus().equals(FlowEntryStatus.PUBLISHED)) {
            errorMessage = "数据验证失败，该流程尚未发布，请刷新后重试！";
            return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
        }
        FlowEntryPublish flowEntryPublish =
                flowEntryService.getFlowEntryPublishFromCache(flowEntry.getMainEntryPublishId());
        flowEntry.setMainFlowEntryPublish(flowEntryPublish);
        return ResponseResult.success(flowEntry);
    }

    public ResponseResult<OnlineDatasource> verifyAndGetOnlineDatasource(String formId) {
        List<OnlineFormDatasource> formDatasourceList = onlineFormService.getFormDatasourceListFromCache(formId);
        if (CollUtil.isEmpty(formDatasourceList)) {
            String errorMessage = "数据验证失败，流程任务绑定的在线表单Id [" + formId + "] 不存在，请修改流程图！";
            return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
        }
        return onlineOperationHelper.verifyAndGetDatasource(formDatasourceList.get(0).getDatasourceId());
    }

//=========================================================================================================================================

}
