package com.yunzhupaas.flowable.controller;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.google.common.collect.ImmutableList;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import com.yunzhupaas.base.ActionResult;
import com.yunzhupaas.base.controller.SuperController;
import com.yunzhupaas.base.vo.ListVO;
import com.yunzhupaas.base.vo.PaginationVO;
import com.yunzhupaas.constant.MsgCode;
import com.yunzhupaas.exception.WorkFlowException;
import com.yunzhupaas.flowable.entity.OperatorEntity;
import com.yunzhupaas.flowable.entity.RecordEntity;
import com.yunzhupaas.flowable.entity.TaskEntity;
import com.yunzhupaas.flowable.enums.OperatorStateEnum;
import com.yunzhupaas.flowable.enums.TaskStatusEnum;
import com.yunzhupaas.flowable.model.candidates.CandidateCheckFo;
import com.yunzhupaas.flowable.model.candidates.CandidateCheckVo;
import com.yunzhupaas.flowable.model.candidates.CandidateUserFo;
import com.yunzhupaas.flowable.model.candidates.CandidateUserVo;
import com.yunzhupaas.flowable.model.operator.FlowBatchModel;
import com.yunzhupaas.flowable.model.operator.OperatorVo;
import com.yunzhupaas.flowable.model.operator.ReducePagination;
import com.yunzhupaas.flowable.model.operator.ReduceUserModel;
import com.yunzhupaas.flowable.model.task.AuditModel;
import com.yunzhupaas.flowable.model.task.FlowModel;
import com.yunzhupaas.flowable.model.task.TaskPagination;
import com.yunzhupaas.flowable.model.templatenode.BackNodeModel;
import com.yunzhupaas.flowable.model.util.FlowNature;
import com.yunzhupaas.flowable.service.CirculateService;
import com.yunzhupaas.flowable.service.OperatorService;
import com.yunzhupaas.flowable.service.RecordService;
import com.yunzhupaas.flowable.service.TaskService;
import com.yunzhupaas.flowable.util.*;
import com.yunzhupaas.permission.entity.UserEntity;
import com.yunzhupaas.util.JsonUtil;
import com.yunzhupaas.util.RedisUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 类的描述
 *
 * @author YUNZHUPAASYUNZHUPAAS开发组
 * @version 5.0.x
 * @since 2024/4/25 14:48
 */
@Tag(name = "经办", description = "OperatorController")
@RestController
@RequestMapping("/api/workflow/operator")
@RequiredArgsConstructor
public class OperatorController extends SuperController<OperatorService, OperatorEntity> {
    private final OperatorService operatorService;
    private final TaskService taskService;
    @Autowired
    private ServiceUtil serviceUtil;
    @Autowired
    private RecordService recordService;
    @Autowired
    private CirculateService circulateService;
    @Autowired
    private OperatorUtil operatorUtil;
    @Autowired
    private TaskUtil taskUtil;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private RedisLock redisLock;

    /**
     * 判断候选人
     *
     * @param id 经办主键
     * @param fo 参数类
     */
    @Operation(summary = "判断候选人")
    @PostMapping("/CandidateNode/{id}")
    public ActionResult candidates(@PathVariable("id") String id, @RequestBody CandidateCheckFo fo)
            throws WorkFlowException {
        return ActionResult.success(taskService.checkCandidates(id, fo));
    }

    /**
     * 获取候选人
     *
     * @param fo 参数类
     */
    @Operation(summary = "获取候选人")
    @PostMapping("/CandidateUser/{id}")
    public ActionResult candidateUser(@PathVariable("id") String id, @RequestBody CandidateUserFo fo)
            throws WorkFlowException {
        List<CandidateUserVo> candidates = taskService.getCandidateUser(id, fo);
        PaginationVO paginationVO = JsonUtil.getJsonToBean(fo, PaginationVO.class);
        return ActionResult.page(candidates, paginationVO);
    }

    /**
     * 列表
     *
     * @param category   列表标识
     * @param pagination 参数
     */
    @Operation(summary = "列表")
    @GetMapping("/List/{category}")
    public ActionResult list(@PathVariable("category") String category, TaskPagination pagination) {
        List<OperatorVo> list = new ArrayList<>();
        pagination.setCategory(category);
        pagination.setDelegateType(true);
        switch (category) {
            case "0": // 待签
            case "1": // 待办
            case "2": // 在办
            case "5": // 批量在办
                list = operatorService.getList(pagination);
                break;
            case "3": // 已办
                list = recordService.getList(pagination);
                break;
            case "4": // 抄送
                list = circulateService.getList(pagination);
        }
        List<OperatorVo> vos = new ArrayList<>();
        List<UserEntity> userList = serviceUtil
                .getUserName(list.stream().map(OperatorVo::getCreatorUserId).collect(Collectors.toList()));
        for (OperatorVo operatorVo : list) {
            OperatorVo vo = JsonUtil.getJsonToBean(operatorVo, OperatorVo.class);
            UserEntity userEntity = userList.stream().filter(t -> t.getId().equals(vo.getCreatorUserId())).findFirst()
                    .orElse(null);
            vo.setCreatorUser(userEntity != null ? userEntity.getRealName() + "/" + userEntity.getAccount() : "");
            if (ObjectUtil.equals(operatorVo.getIsProcessing(), 1)
                    && ObjectUtil.equals(operatorVo.getStatus(), OperatorStateEnum.Transfer.getCode())) {
                vo.setStatus(OperatorStateEnum.TransferProcessing.getCode());
            }
            vos.add(vo);
        }
        PaginationVO paginationVO = JsonUtil.getJsonToBean(pagination, PaginationVO.class);
        return ActionResult.page(vos, paginationVO);
    }

    /**
     * 签收或退签
     *
     * @param flowModel 参数类，ids 主键集合、type 0 签收 1 退签
     */
    @Operation(summary = "签收或退签")
    @PostMapping("/Sign")
    public ActionResult<String> sign(@RequestBody FlowModel flowModel) throws WorkFlowException {
        operatorService.sign(flowModel);
        return ActionResult.success(MsgCode.SU005.get());
    }

    /**
     * 开始办理
     *
     * @param flowModel 参数类，ids 主键集合
     */
    @Operation(summary = "开始办理")
    @PostMapping("/Transact")
    public ActionResult<String> startHandle(@RequestBody FlowModel flowModel) throws WorkFlowException {
        operatorService.startHandle(flowModel);
        return ActionResult.success(MsgCode.SU005.get());
    }

    /**
     * 保存草稿
     *
     * @param id        经办主键
     * @param flowModel 参数
     */
    @Operation(summary = "保存草稿")
    @PostMapping("/SaveAudit/{id}")
    public ActionResult<String> saveAudit(@PathVariable("id") String id, @RequestBody FlowModel flowModel)
            throws WorkFlowException {
        operatorService.saveAudit(id, flowModel);
        return ActionResult.success(MsgCode.SU002.get());
    }

    /**
     * 同意拒绝
     *
     * @param id        经办主键
     * @param flowModel 参数
     */
    @Operation(summary = "同意拒绝")
    @PostMapping("/Audit/{id}")
    public ActionResult audit(@PathVariable("id") String id, @RequestBody FlowModel flowModel) throws Exception {
        OperatorEntity operator = operatorUtil.checkOperator(id);
        TaskEntity taskEntity = taskService.getInfo(operator.getTaskId());
        if (null == taskEntity) {
            throw new WorkFlowException(MsgCode.FA001.get());
        }
        boolean lock = redisLock.lock(taskEntity.getId() + operator.getNodeId(), operator.getId(), 5, TimeUnit.SECONDS);
        if (!lock) {
            throw new WorkFlowException("当前节点正在审批中，请稍后再试");
        }
        Integer handleStatus = flowModel.getHandleStatus();
        try {
            operatorService.auditWithCheck(id, flowModel);
            List<OperatorEntity> list = flowModel.getList();
            if (CollectionUtil.isNotEmpty(list)) {
                TimeUtil.timeModel(list, flowModel, redisUtil);
            }
        } catch (Exception e) {
            operatorUtil.compensate(taskEntity);
            throw e;
        } finally {
            redisLock.unlock(taskEntity.getId() + operator.getNodeId(), operator.getId());
        }
        operatorUtil.handleEvent();
        taskEntity = flowModel.getTaskEntity();
        if (taskEntity.getRejectDataId() == null) {
            operatorUtil.autoAudit(flowModel);
            operatorUtil.handleEvent();
        }
        TaskEntity task = taskService.getById(taskEntity.getId());
        taskEntity = task == null ? taskEntity : task;
        AuditModel model = taskUtil.getAuditModel(taskEntity.getId(), flowModel, operator);
        String msg = Objects.equals(handleStatus, FlowNature.RejectCompletion) ? MsgCode.WF065.get()
                : MsgCode.WF066.get();
        if (ObjectUtil.equals(operator.getIsProcessing(), 1)) {
            msg = MsgCode.WF148.get();
        }
        return ActionResult.success(msg, model);
    }

    /**
     * 加签
     *
     * @param id        经办主键
     * @param flowModel 参数
     */
    @Operation(summary = "加签")
    @PostMapping("/AddSign/{id}")
    public ActionResult<String> freeApprover(@PathVariable("id") String id, @RequestBody FlowModel flowModel)
            throws Exception {
        operatorService.addSign(id, flowModel);
        operatorUtil.autoAudit(flowModel);
        operatorUtil.handleEvent();
        return ActionResult.success(MsgCode.WF004.get());
    }

    /**
     * 获取加签的人
     *
     * @param id         经办主键
     * @param pagination 参数
     */
    @Operation(summary = "获取加签的人")
    @PostMapping("/AddSignUserIdList/{id}")
    public ActionResult getAddSignUserIdList(@PathVariable("id") String id, @RequestBody ReducePagination pagination)
            throws WorkFlowException {
        List<ReduceUserModel> reduceList = operatorService.getReduceList(id, pagination);
        PaginationVO paginationVO = JsonUtil.getJsonToBean(pagination, PaginationVO.class);
        return ActionResult.page(reduceList, paginationVO);
    }

    /**
     * 减签
     *
     * @param flowModel 参数
     * @param id        记录主键
     */
    @Operation(summary = "减签")
    @PostMapping("/ReduceApprover/{id}")
    public ActionResult<String> reduceApprover(@RequestBody FlowModel flowModel, @PathVariable("id") String id)
            throws WorkFlowException {
        operatorService.reduce(id, flowModel);
        return ActionResult.success(MsgCode.WF069.get());
    }

    /**
     * 获取退回的节点
     *
     * @param id 经办主键
     */
    @Operation(summary = "获取退回的节点")
    @GetMapping("/SendBackNodeList/{id}")
    public ActionResult getFallbacks(@PathVariable("id") String id) throws WorkFlowException {
        ListVO<BackNodeModel> vo = new ListVO<>();
        vo.setList(operatorService.getFallbacks(id));
        return ActionResult.success(vo);
    }

    /**
     * 退回
     *
     * @param id        经办主键
     * @param flowModel 参数
     */
    @Operation(summary = "退回")
    @PostMapping("/SendBack/{id}")
    public ActionResult<String> reject(@PathVariable("id") String id, @RequestBody FlowModel flowModel)
            throws Exception {
        OperatorEntity operator = operatorUtil.checkOperator(id);
        TaskEntity taskEntity = taskService.getInfo(operator.getTaskId());
        if (null == taskEntity) {
            throw new WorkFlowException(MsgCode.FA001.get());
        }
        try {
            operatorService.back(id, flowModel);
        } catch (Exception e) {
            operatorUtil.compensate(taskEntity);
            throw e;
        }
        operatorUtil.event(flowModel, 9);
        return ActionResult.success(MsgCode.WF002.get());
    }

    /**
     * 撤回
     *
     * @param id        记录主键
     * @param flowModel 参数
     */
    @Operation(summary = "撤回")
    @PostMapping("/Recall/{taskRecordId}")
    public ActionResult<String> recall(@PathVariable("taskRecordId") String id, @RequestBody FlowModel flowModel)
            throws Exception {
        RecordEntity record = recordService.getInfo(id);
        if (null == record) {
            throw new WorkFlowException(MsgCode.FA001.get());
        }
        if (record.getStatus().equals(FlowNature.Invalid)) {
            throw new WorkFlowException(MsgCode.WF005.get());
        }
        OperatorEntity operator = operatorService.getInfo(record.getOperatorId());
        if (null == operator) {
            throw new WorkFlowException(MsgCode.FA001.get());
        }
        TaskEntity taskEntity = taskService.getInfo(operator.getTaskId());
        if (null == taskEntity) {
            throw new WorkFlowException(MsgCode.FA001.get());
        }

        flowModel.setRecordEntity(record);
        flowModel.setOperatorEntity(operator);
        try {
            operatorService.recall(id, flowModel);
        } catch (Exception e) {
            operatorUtil.compensate(taskEntity);
            throw e;
        }
        operatorUtil.event(flowModel, 6);
        return ActionResult.success(MsgCode.WF008.get());
    }

    /**
     * 转审
     *
     * @param id        经办主键
     * @param flowModel 参数
     */
    @Operation(summary = "转审")
    @PostMapping("/Transfer/{id}")
    public ActionResult<String> transfer(@PathVariable("id") String id, @RequestBody FlowModel flowModel)
            throws Exception {
        operatorService.transfer(id, flowModel);
        operatorUtil.autoAudit(flowModel);
        operatorUtil.handleEvent();
        OperatorEntity operator = operatorService.getById(id);
        return ActionResult
                .success(ObjectUtil.equals(operator.getIsProcessing(), 1) ? MsgCode.WF003.get() : MsgCode.WF152.get());
    }

    /**
     * 协办
     *
     * @param id        主键
     * @param flowModel 参数
     */
    @Operation(summary = "协办")
    @PostMapping("/Assist/{id}")
    public ActionResult<String> assist(@PathVariable("id") String id, @RequestBody FlowModel flowModel)
            throws WorkFlowException {
        operatorService.assist(id, flowModel);
        return ActionResult.success(MsgCode.WF067.get());
    }

    /**
     * 协办保存
     *
     * @param id        主键
     * @param flowModel 参数
     */
    @Operation(summary = "协办保存")
    @PostMapping("/AssistSave/{id}")
    public ActionResult<String> assistSave(@PathVariable("id") String id, @RequestBody FlowModel flowModel)
            throws WorkFlowException {
        operatorService.assistSave(id, flowModel);
        return ActionResult.success(MsgCode.WF068.get());
    }

    /**
     * 批量审批流程分类列表
     */
    @Operation(summary = "批量审批流程分类列表")
    @GetMapping("/BatchFlowSelector")
    public ActionResult<List<FlowBatchModel>> batchFlowSelector() {
        List<FlowBatchModel> list = operatorService.batchFlowSelector();
        return ActionResult.success(list);
    }

    /**
     * 批量审批流程版本列表
     *
     * @param templateId 流程定义主键
     */
    @Operation(summary = "批量审批流程版本列表")
    @GetMapping("/BatchVersionSelector/{templateId}")
    public ActionResult<List<FlowBatchModel>> batchVersionSelector(@PathVariable("templateId") String templateId) {
        List<FlowBatchModel> list = operatorService.batchVersionSelector(templateId);
        return ActionResult.success(list);
    }

    /**
     * 批量审批节点列表
     *
     * @param flowId 定义版本主键
     */
    @Operation(summary = "批量审批节点列表")
    @GetMapping("/BatchNodeSelector/{flowId}")
    public ActionResult<List<FlowBatchModel>> batchNodeSelector(@PathVariable("flowId") String flowId) {
        List<FlowBatchModel> list = operatorService.batchNodeSelector(flowId);
        return ActionResult.success(list);
    }

    /**
     * 批量审批节点属性
     *
     * @param flowModel 参数
     */
    @Operation(summary = "批量审批节点属性")
    @GetMapping("/BatchNode")
    public ActionResult batchNode(FlowModel flowModel) throws WorkFlowException {
        Map<String, Object> nodeMap = operatorService.batchNode(flowModel);
        return ActionResult.success(nodeMap);
    }

    /**
     * 批量获取候选人
     *
     * @param flowId     版本主键
     * @param operatorId 经办主键
     * @param batchType  类型，0.同意 1.拒绝
     */
    @Operation(summary = "批量获取候选人")
    @GetMapping("/BatchCandidate")
    public ActionResult batchCandidate(String flowId, String operatorId, Integer batchType) throws WorkFlowException {
        CandidateCheckVo vo = operatorService.batchCandidates(flowId, operatorId, batchType);
        return ActionResult.success(vo);
    }

    /**
     * 批量审批
     *
     * @param flowModel 参数
     */
    @Operation(summary = "批量审批")
    @PostMapping("/BatchOperation")
    public ActionResult batchOperation(@RequestBody FlowModel flowModel) throws Exception {
        try {
            operatorService.batch(flowModel);
        } catch (Exception e) {
            List<TaskEntity> taskList = flowModel.getTaskList();
            if (CollectionUtil.isNotEmpty(taskList)) {
                for (TaskEntity taskEntity : taskList) {
                    operatorUtil.compensate(taskEntity);
                }
            }
            throw e;
        }
        return ActionResult.success(MsgCode.WF011.get());
    }

    /**
     * 消息跳转工作流
     *
     * @param id 经办或抄送主键
     */
    @Operation(summary = "消息跳转工作流")
    @GetMapping("/{id}/Info")
    public ActionResult checkInfo(@PathVariable("id") String id,
            @RequestParam(value = "opType", required = false) String opType) throws WorkFlowException {
        Map<String, String> map = new HashMap<>();
        List<String> list = ImmutableList.of(FlowNature.LaunchDetail, FlowNature.LaunchCreate);
        if (list.contains(opType)) {
            String type = FlowNature.LaunchDetail;
            TaskEntity task = taskService.getById(id);
            if (null != task) {
                taskUtil.checkTemplateHide(task.getTemplateId());
                if (ObjectUtil.equals(task.getStatus(), TaskStatusEnum.TO_BE_SUBMIT.getCode())) {
                    type = FlowNature.LaunchCreate;
                }
            }
            map.put("opType", type);
            return ActionResult.success(map);
        }
        String type = taskService.checkInfo(id);
        map.put("opType", type);
        return ActionResult.success(map);
    }

    /**
     * 进度节点记录列表
     *
     * @param taskId 任务主键
     * @param nodeId 节点id
     */
    @Operation(summary = "进度节点记录列表")
    @GetMapping("/RecordList")
    public ActionResult getRecordList(@RequestParam("taskId") String taskId, @RequestParam("nodeId") String nodeId) {
        return ActionResult.success(recordService.getList(taskId, nodeId));
    }
}
