package com.ankaibei.workFlow.webadmin.ankaibei.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.ankaibei.workFlow.common.core.annotation.DisableDataFilter;
import com.ankaibei.workFlow.common.core.object.CallResult;
import com.ankaibei.workFlow.common.core.object.ResponseResult;
import com.ankaibei.workFlow.common.flow.constant.FlowApprovalType;
import com.ankaibei.workFlow.common.flow.constant.FlowConstant;
import com.ankaibei.workFlow.common.flow.constant.FlowTaskStatus;
import com.ankaibei.workFlow.common.flow.exception.FlowOperationException;
import com.ankaibei.workFlow.common.flow.model.FlowTaskComment;
import com.ankaibei.workFlow.common.flow.model.FlowWorkOrder;
import com.ankaibei.workFlow.common.flow.online.controller.SubmitUserTaskVo;
import com.ankaibei.workFlow.common.flow.online.service.FlowOnlineOperationService;
import com.ankaibei.workFlow.common.flow.online.service.impl.FlowOnlineOperationServiceImpl;
import com.ankaibei.workFlow.common.flow.service.FlowApiService;
import com.ankaibei.workFlow.common.flow.service.FlowWorkOrderService;
import com.ankaibei.workFlow.common.flow.util.FlowOperationHelper;
import com.ankaibei.workFlow.common.flow.vo.TaskInfoVo;
import com.ankaibei.workFlow.common.log.annotation.OperationLog;
import com.ankaibei.workFlow.common.log.model.constant.SysOperationLogType;
import com.ankaibei.workFlow.common.online.model.OnlineDatasource;
import com.ankaibei.workFlow.common.online.model.OnlineDatasourceRelation;
import com.ankaibei.workFlow.common.online.model.OnlineTable;
import com.ankaibei.workFlow.common.online.object.ColumnData;
import com.ankaibei.workFlow.common.online.service.OnlineOperationService;
import com.ankaibei.workFlow.common.online.util.OnlineOperationHelper;
import com.ankaibei.workFlow.webadmin.ankaibei.enums.CodeEnum;
import com.ankaibei.workFlow.webadmin.ankaibei.enums.CostEnum;
import com.ankaibei.workFlow.webadmin.ankaibei.mapper.CommunicationMapper;
import com.ankaibei.workFlow.webadmin.ankaibei.mapper.CostInfoMapper;
import com.ankaibei.workFlow.webadmin.ankaibei.mapper.ManHourMapper;
import com.ankaibei.workFlow.webadmin.ankaibei.mapper.ProjectInfoMapper;
import com.ankaibei.workFlow.webadmin.ankaibei.mapper.PurchaseInfoMapper;
import com.ankaibei.workFlow.webadmin.ankaibei.mapper.RepastExpenseMapper;
import com.ankaibei.workFlow.webadmin.ankaibei.mapper.RestInfoMapper;
import com.ankaibei.workFlow.webadmin.ankaibei.mapper.TaskInfoMapper;
import com.ankaibei.workFlow.webadmin.ankaibei.mapper.TrafficExpenseMapper;
import com.ankaibei.workFlow.webadmin.ankaibei.mapper.TravelExpenseMapper;
import com.ankaibei.workFlow.webadmin.ankaibei.utils.FlowTaskStart;
import com.ankaibei.workFlow.webadmin.ankaibei.vo.ResMsg;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Slf4j
@RestController
@RequestMapping("/submitUserTask")
public class SubmitUserTaskController {

    @Autowired
    private FlowTaskStart flowTaskStart;
    @Autowired
    private CostInfoMapper costInfoMapper;
    @Autowired
    private FlowApiService flowApiService;
    @Autowired
    private FlowOperationHelper flowOperationHelper;
    @Autowired
    private FlowWorkOrderService flowWorkOrderService;
    @Autowired
    private OnlineOperationHelper onlineOperationHelper;
    @Autowired
    private OnlineOperationService onlineOperationService;
    @Autowired
    private FlowOnlineOperationService flowOnlineOperationService;
    @Autowired
    private FlowOnlineOperationServiceImpl flowOnlineOperationServiceImpl;
    @Resource
    private ManHourMapper manHourMapper;
    @Resource
    private RestInfoMapper restInfoMapper;
    @Resource
    private TaskInfoMapper taskInfoMapper;
    @Resource
    private ProjectInfoMapper projectInfoMapper;
    @Resource
    private PurchaseInfoMapper purchaseInfoMapper;
    @Resource
    private TravelExpenseMapper travelExpenseMapper;
    @Resource
    private CommunicationMapper communicationMapper;
    @Resource
    private RepastExpenseMapper repastExpenseMapper;
    @Resource
    private TrafficExpenseMapper trafficExpenseMapper;


    /**
     * 提交流程的用户任务。（项目管理后台调用）    (批量同意和拒绝)
     * 该接口无需数据权限过滤，因此用DisableDataFilter注解标注。如果当前系统没有支持数据权限过滤，该注解不会有任何影响。
     * <p>
     * processInstanceId  流程实例Id。
     * taskId             流程任务Id。
     * flowTaskCommentDto 流程审批数据。
     * taskVariableData   流程任务变量数据。
     * masterData         流程审批相关的主表数据。
     * slaveData          流程审批相关的多个从表数据。
     * copyData           传阅数据，格式为type和id，type的值参考FlowConstant中的常量值。
     *
     * @return 应答结果对象。
     */
    @DisableDataFilter
    @OperationLog(type = SysOperationLogType.SUBMIT_TASK)
    @PostMapping("/submitUserTaskBatch")
    public ResMsg submitUserTaskBatch(
            @RequestBody List<SubmitUserTaskVo> subVoList) {
        for (SubmitUserTaskVo taskVo : subVoList) {
            submitUserTaskOfAKB(taskVo);
        }
        return ResMsg.newInstance(CodeEnum.OK);
    }


    /**
     * 提交流程的用户任务。（项目管理后台调用）
     * 该接口无需数据权限过滤，因此用DisableDataFilter注解标注。如果当前系统没有支持数据权限过滤，该注解不会有任何影响。
     * <p>
     * processInstanceId  流程实例Id。
     * taskId             流程任务Id。
     * flowTaskCommentDto 流程审批数据。
     * taskVariableData   流程任务变量数据。
     * masterData         流程审批相关的主表数据。
     * slaveData          流程审批相关的多个从表数据。
     * copyData           传阅数据，格式为type和id，type的值参考FlowConstant中的常量值。
     *
     * @return 应答结果对象。
     */
    @DisableDataFilter
    @OperationLog(type = SysOperationLogType.SUBMIT_TASK)
    @PostMapping("/submitUserTaskOfAKB")
    public ResMsg submitUserTaskOfAKB(
            @RequestBody SubmitUserTaskVo subVo) {
        String errorMessage;
        // 验证流程任务的合法性。
        Task task = flowApiService.getProcessInstanceActiveTask(subVo.getProcessInstanceId(), subVo.getTaskId());
        ResponseResult<TaskInfoVo> taskInfoResult = flowOperationHelper.verifyAndGetRuntimeTaskInfo(task);
        if (!taskInfoResult.isSuccess()) {
            return ResMsg.newInstance(CodeEnum.FAIL, taskInfoResult);
        }
        TaskInfoVo taskInfo = taskInfoResult.getData();
        // 验证在线表单及其关联数据源的合法性。
        ResponseResult<OnlineDatasource> datasourceResult = flowTaskStart.verifyAndGetOnlineDatasource(taskInfo.getFormId());
        if (!datasourceResult.isSuccess()) {
            return ResMsg.newInstance(CodeEnum.FAIL, datasourceResult);
        }
        CallResult assigneeVerifyResult = flowApiService.verifyAssigneeOrCandidateAndClaim(task);
        if (!assigneeVerifyResult.isSuccess()) {
            return ResMsg.newInstance(CodeEnum.FAIL, assigneeVerifyResult);
        }
        OnlineDatasource datasource = datasourceResult.getData();
        OnlineTable masterTable = datasource.getMasterTable();
        Long datasourceId = datasource.getDatasourceId();
        ProcessInstance instance = flowApiService.getProcessInstance(subVo.getProcessInstanceId());
        String dataId = instance.getBusinessKey();
        // 这里把传阅数据放到任务变量中，是为了避免给流程数据操作方法增加额外的方法调用参数。
        if (MapUtil.isNotEmpty(subVo.getCopyDat())) {
            //if (taskVariableData == null) {
            //    taskVariableData = new JSONObject();
            //}
            subVo.getTaskVariableData().put(FlowConstant.COPY_DATA_KEY, subVo.getCopyDat());
        }
        FlowTaskComment flowTaskComment = BeanUtil.copyProperties(subVo.getFlowTaskCommentDto(), FlowTaskComment.class);
        if (StrUtil.isBlank(dataId)) {
            return this.submitNewTaskOther(subVo.getProcessInstanceId(), subVo.getTaskId(),
                    flowTaskComment, subVo.getTaskVariableData(), masterTable, subVo.getMasterData(), subVo.getSlaveData(), datasourceId);
        }
        try {
            if (StrUtil.equals(flowTaskComment.getApprovalType(), FlowApprovalType.TRANSFER)) {
                if (StrUtil.isBlank(flowTaskComment.getDelegateAssignee())) {
                    errorMessage = "数据验证失败，加签或转办任务指派人不能为空！！";
                    return ResMsg.newInstance(CodeEnum.DATA_VALIDATED_FAILED, errorMessage);
                }
            }
            updateAndTakeTask(
                    subVo, task, flowTaskComment, masterTable, dataId, datasourceId);
        } catch (FlowOperationException e) {
            log.error("Failed to call [FlowOnlineOperationService.updateAndTakeTask]", e);
            return ResMsg.newInstance(CodeEnum.DATA_VALIDATED_FAILED, e.getMessage());
        }
        return ResMsg.newInstance(CodeEnum.OK);
    }

    private ResMsg submitNewTaskOther(
            String processInstanceId,
            String taskId,
            FlowTaskComment flowTaskComment,
            JSONObject taskVariableData,
            OnlineTable masterTable,
            JSONObject masterData,
            JSONObject slaveData,
            Long datasourceId) {
        ResponseResult<List<ColumnData>> columnDataListResult =
                onlineOperationHelper.buildTableData(masterTable, masterData, false, null);
        if (!columnDataListResult.isSuccess()) {
            return ResMsg.newInstance(CodeEnum.FAIL, columnDataListResult);
        }
        // 保存在线表单提交的数据，同时启动流程和自动完成第一个用户任务。
        if (slaveData == null) {
            flowOnlineOperationService.saveNewAndTakeTask(
                    processInstanceId,
                    taskId,
                    flowTaskComment,
                    taskVariableData,
                    masterTable,
                    columnDataListResult.getData());
        } else {
            // 如果本次请求中包含从表数据，则一同插入。
            ResponseResult<Map<OnlineDatasourceRelation, List<List<ColumnData>>>> slaveDataListResult =
                    onlineOperationHelper.buildSlaveDataList(datasourceId, slaveData);
            if (!slaveDataListResult.isSuccess()) {
                return ResMsg.newInstance(CodeEnum.FAIL, slaveDataListResult);
            }
            saveNewAndTakeTask(
                    processInstanceId,
                    taskId,
                    flowTaskComment,
                    taskVariableData,
                    masterTable,
                    columnDataListResult.getData(),
                    slaveDataListResult.getData());
        }
        return ResMsg.newInstance(CodeEnum.OK);
    }

    public void saveNewAndTakeTask(
            String processInstanceId,
            String taskId,
            FlowTaskComment flowTaskComment,
            JSONObject taskVariableData,
            OnlineTable masterTable,
            List<ColumnData> masterColumnDataList,
            Map<OnlineDatasourceRelation, List<List<ColumnData>>> slaveColumnDataListMap) {
        Object dataId = onlineOperationService.saveNewAndSlaveRelation(
                masterTable, masterColumnDataList, slaveColumnDataListMap);
        Assert.notNull(dataId);
        Task task = flowApiService.getProcessInstanceActiveTask(processInstanceId, taskId);
        flowApiService.setBusinessKeyForProcessInstance(processInstanceId, dataId);
        Map<String, Object> variables =
                flowApiService.initAndGetProcessInstanceVariables(task.getProcessDefinitionId());
        if (taskVariableData == null) {
            taskVariableData = new JSONObject();
        }
        taskVariableData.putAll(variables);
        flowApiService.completeTask(task, flowTaskComment, taskVariableData);
        ProcessInstance instance = flowApiService.getProcessInstance(processInstanceId);
        FlowWorkOrder flowWorkOrder =
                flowWorkOrderService.getFlowWorkOrderByProcessInstanceId(instance.getProcessInstanceId());
        if (flowWorkOrder == null) {
            flowWorkOrderService.saveNew(instance, dataId, masterTable.getTableId(), null);
        } else {
            flowWorkOrder.setBusinessKey(dataId.toString());
            flowWorkOrder.setUpdateTime(new Date());
            flowWorkOrder.setFlowStatus(FlowTaskStatus.SUBMITTED);
            flowWorkOrderService.updateById(flowWorkOrder);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateAndTakeTask(
            SubmitUserTaskVo subVo,
            Task task,
            FlowTaskComment flowTaskComment,
            OnlineTable masterTable,
            String masterDataId,
            Long datasourceId) {
        int flowStatus = FlowTaskStatus.APPROVING;
        if (flowTaskComment.getApprovalType().equals(FlowApprovalType.REFUSE)) {
            flowStatus = FlowTaskStatus.REFUSED;
        } else if (flowTaskComment.getApprovalType().equals(FlowApprovalType.STOP)) {
            flowStatus = FlowTaskStatus.FINISHED;
        }
        flowWorkOrderService.updateFlowStatusByProcessInstanceId(task.getProcessInstanceId(), flowStatus);
        this.handleMasterTableData(masterTable, subVo.getMasterData(), masterDataId);
        if (subVo.getSlaveData() != null) {
            for (Map.Entry<String, Object> relationEntry : subVo.getSlaveData().entrySet()) {
                Long relationId = Long.parseLong(relationEntry.getKey());
                flowOnlineOperationServiceImpl.handleSlaveTableData(
                        relationId, relationEntry.getValue(), datasourceId, masterTable, subVo.getMasterData(), masterDataId);
            }
        }
        if (flowTaskComment.getApprovalType().equals(FlowApprovalType.STOP)) {
            Integer s = MapUtil.getInt(subVo.getTaskVariableData(), FlowConstant.LATEST_APPROVAL_STATUS_KEY);
            flowWorkOrderService.updateLatestApprovalStatusByProcessInstanceId(task.getProcessInstanceId(), s);
            CallResult stopResult = flowApiService.stopProcessInstance(
                    task.getProcessInstanceId(), flowTaskComment.getTaskComment(), flowStatus);
            if (!stopResult.isSuccess()) {
                throw new FlowOperationException(stopResult.getErrorMessage());
            }
        } else {
            flowApiService.completeTask(task, flowTaskComment, subVo.getTaskVariableData());
        }
        int flowTaskStatus = flowWorkOrderService.getFlowWorkOrderByProcessInstanceId(subVo.getProcessInstanceId()).getFlowStatus();
        switch (CostEnum.getEnumByCode(subVo.getCostType())) {

            case PROJECTADD:
                projectInfoMapper.updateProjectFlowStatus(flowTaskStatus + "", subVo.getApplyDataId());
            case MANHOUR:
                manHourMapper.updateManHourFlowStatus(flowTaskStatus + "", subVo.getApplyDataId());
            case PROJECTTASKADD:
                taskInfoMapper.updateTaskFlowStatus(flowTaskStatus + "", subVo.getApplyDataId());
            case COMMUNICATION:
                communicationMapper.updateCommunicationFlowStatus(flowTaskStatus + "", subVo.getApplyDataId());
                costInfoMapper.updateCostInfoFlowStatus(flowTaskStatus + "", subVo.getApplyDataId(), "通讯费");
            case PURCHASE:
                purchaseInfoMapper.updatePurchaseInfoFlowStatus(flowTaskStatus + "", subVo.getApplyDataId());
                costInfoMapper.updateCostInfoFlowStatus(flowTaskStatus + "", subVo.getApplyDataId(), "采购费");
            case REST:
                restInfoMapper.updateRestsFlowStatus(flowTaskStatus + "", subVo.getApplyDataId());
                costInfoMapper.updateCostInfoFlowStatus(flowTaskStatus + "", subVo.getApplyDataId(), "其他费用");
            case TRANSPORTATION:
                trafficExpenseMapper.updateTrafficFlowStatus(flowTaskStatus + "", subVo.getApplyDataId());
                costInfoMapper.updateCostInfoFlowStatus(flowTaskStatus + "", subVo.getApplyDataId(), "交通费");
            case REPAST:
                repastExpenseMapper.updateRepastFlowStatus(flowTaskStatus + "", subVo.getApplyDataId());
                costInfoMapper.updateCostInfoFlowStatus(flowTaskStatus + "", subVo.getApplyDataId(), "餐饮费");
            case TRAVEL:
                travelExpenseMapper.updateTravelFlowStatus(flowTaskStatus + "", subVo.getApplyDataId());
                costInfoMapper.updateCostInfoFlowStatus(flowTaskStatus + "", subVo.getApplyDataId(), "差旅费");
            default:
        }
    }

    private void handleMasterTableData(OnlineTable masterTable, JSONObject masterData, String dataId) {
        // 如果存在主表数据，就执行主表数据的更新。
        if (masterData != null) {
            Map<String, Object> originalMasterData =
                    onlineOperationService.getMasterData(masterTable, null, null, dataId);
            for (Map.Entry<String, Object> entry : originalMasterData.entrySet()) {
                masterData.putIfAbsent(entry.getKey(), entry.getValue());
            }
            ResponseResult<List<ColumnData>> columnDataListResult =
                    onlineOperationHelper.buildTableData(masterTable, masterData, true, null);
            if (!columnDataListResult.isSuccess()) {
                throw new FlowOperationException(columnDataListResult.getErrorMessage());
            }
            if (!onlineOperationService.update(masterTable, columnDataListResult.getData())) {
                throw new FlowOperationException("主表数据不存在！");
            }
        }
    }
}
