package com.casic.missiles.service.Impl.flowable;

import cn.hutool.core.lang.Assert;
import com.casic.missiles.core.model.auth.AuthUser;
import com.casic.missiles.core.shiro.ShiroKit;
import com.casic.missiles.dto.ReturnDTO;
import com.casic.missiles.dto.ReturnUtil;
import com.casic.missiles.dto.flowable.*;
import com.casic.missiles.enums.system.ApprovalStatusEnum;
import com.casic.missiles.enums.BusinessExceptionEnum;
import com.casic.missiles.exception.BusinessException;
import com.casic.missiles.modular.system.dao.DeptMapper;
import com.casic.missiles.modular.system.dao.UserMapper;
import com.casic.missiles.modular.system.model.Dept;
import com.casic.missiles.modular.system.model.User;
import com.casic.missiles.service.flowable.ApprovalOperateService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description:
 * @Author: wangpeng
 * @Date: 2022/12/23 14:04
 */
@Slf4j
@Service
public class ApprovalOperateServiceImpl implements ApprovalOperateService {
    @Resource
    private HistoryService historyService;
    @Resource
    protected TaskService taskService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private UserMapper userMapper;
    @Resource
    private DeptMapper deptMapper;

    @Override
    public List<List<ApprovalLogResponse>> approvalLog(String processId) {
        //一个task只能有一个处理人assignee，可以有多个候选人candidate users
        //实际办理人，并行会签时是多个人，串行会签时是一个人，或签时是多个人
        List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processId)
                .orderByHistoricActivityInstanceStartTime()
                .desc().list();
        List<ApprovalLogResponse> hisFlowList = new ArrayList<>();
        for (HistoricActivityInstance histIns : list) {
            //histIns.getTaskId()对应每个用户生成的任务id，暂时仅用于判断 非任务活动节点taskId为null
            if (StringUtils.isNotBlank(histIns.getTaskId())) {
                ApprovalLogResponse flowTask = new ApprovalLogResponse();
                //activityId对应用户任务节点的taskId（ACT_RU_TASK表的TASK_DEF_KEY_，即流程定义中自定义生成的分taskId），用于对审批人按任务节点分组
                flowTask.setTaskId(histIns.getActivityId());
                flowTask.setTaskName(histIns.getActivityName());
                flowTask.setCreateTime(histIns.getStartTime());
                flowTask.setFinishTime(histIns.getEndTime());
                //实际办理人,最后一个节点是应办理人
                if (StringUtils.isNotBlank(histIns.getAssignee())) {
                    //json转bpmn中assignee是用户id
                    User user = userMapper.selectById(Long.parseLong(histIns.getAssignee()));
                    if (!Objects.isNull(user)) {
                        flowTask.setAssigneeId(user.getId());
                        flowTask.setAssigneeName(user.getName());
                        Dept dept = deptMapper.selectById(user.getDeptId());
                        flowTask.setDeptId(user.getDeptId());
                        flowTask.setDeptName(dept.getSimpleName());
                    }
                }

                flowTask.setDuration(histIns.getDurationInMillis() == null || histIns.getDurationInMillis() == 0 ? null : getDate(histIns.getDurationInMillis()));
                // 获取意见评论内容
                List<Comment> commentList = taskService.getProcessInstanceComments(histIns.getProcessInstanceId());
                commentList.forEach(comment -> {
                    if (histIns.getTaskId().equals(comment.getTaskId())) {
                        flowTask.setComment(ApprovalLogResponse.FlowCommentDto.builder().type(comment.getType()).comment(comment.getFullMessage()).build());
                    }
                });
                //覆盖已有的root，因为驳回会再产生一个发起人任务
                int flag = 0;
                ApprovalLogResponse oldValue = null;
                if(histIns.getActivityId().contains("root")){
                    for (ApprovalLogResponse his : hisFlowList) {
                        if(his.getTaskId().contains("root")){
                            oldValue = his;
                            flag = 1;
                            break;
                        }
                    }
                }
                if(flag > 0){
                    hisFlowList.remove(oldValue);
                }
                hisFlowList.add(flowTask);
            }
        }
        Map<String, List<ApprovalLogResponse>> resultMap = new HashMap<>();
        //结果按taskId分组，并保证审批顺序
        if (!CollectionUtils.isEmpty(hisFlowList)) {
            resultMap = hisFlowList.stream().collect(Collectors.groupingBy(ApprovalLogResponse::getTaskId, LinkedHashMap::new, Collectors.toCollection(ArrayList::new)));
        }
        //对分组的taskId的列表进行整合，taskId是流程定义中自定义的，多实例的任务每个人的taskId相同
        Map<String, List<ApprovalLogResponse>> finalResultMap = resultMap;
        List<List<ApprovalLogResponse>> resultList = new ArrayList<>();
        for (String key : resultMap.keySet()) {
            List<ApprovalLogResponse> approvalLogList = finalResultMap.get(key);
            for (ApprovalLogResponse approvalLog : approvalLogList) {
                if (approvalLog.getTaskId().contains("root")) {
                    HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                            .processInstanceId(processId)
                            .singleResult();
                    String startUserId = historicProcessInstance.getStartUserId();
                    approvalLog.setAssigneeId(Long.valueOf(startUserId));
                    User user = userMapper.selectById(Long.valueOf(startUserId));
                    approvalLog.setApprovalStatus(approvalLog.getTaskName());//发起人
                    if(!Objects.isNull(user)){
                        approvalLog.setAssigneeName(user.getName());
                        Dept dept = deptMapper.selectById(user.getDeptId());
                        approvalLog.setDeptId(user.getDeptId());
                        approvalLog.setDeptName(dept.getSimpleName());
                    }
                } else if (Objects.isNull(approvalLog.getFinishTime())) {
                    approvalLog.setApprovalStatus("审批中");
                } else if (!Objects.isNull(approvalLog.getFinishTime())) {
                    approvalLog.setApprovalStatus("审批完成");//可能是通过/拒绝/驳回，审批意见中可见
                }
            }
            resultList.add(approvalLogList);
        }

        return resultList;
    }

    @Override
    @Transactional
    public ReturnDTO agree(ApprovalAgreeRequest request) {
        AuthUser user = ShiroKit.getUser();
        Assert.isFalse(Objects.isNull(user), () -> {
            throw new BusinessException(BusinessExceptionEnum.LOGIN_USER_ACQUIRE_FAILED);
        });
        //设置同意发起人
        Authentication.setAuthenticatedUserId(String.valueOf(user.getId()));

        Task task = taskService.createTaskQuery().taskId(request.getTaskId()).singleResult();
        if (StringUtils.isNotBlank(request.getComments())) {
            //1正常意见
            taskService.addComment(task.getId(), task.getProcessInstanceId(), "1", request.getComments());
        }
        taskService.complete(task.getId());
        return ReturnUtil.success();
    }

    @Override
    @Transactional
    public ReturnDTO reject(ApprovalRejectRequest request) {
        AuthUser user = ShiroKit.getUser();
        Assert.isFalse(Objects.isNull(user), () -> {
            throw new BusinessException(BusinessExceptionEnum.LOGIN_USER_ACQUIRE_FAILED);
        });
        //设置驳回发起人
        Authentication.setAuthenticatedUserId(String.valueOf(user.getId()));

        Task task = taskService.createTaskQuery().taskId(request.getTaskId()).singleResult();
        if (StringUtils.isNotBlank(request.getComments())) {
            //3驳回意见
            taskService.addComment(task.getId(), task.getProcessInstanceId(), "3", request.getComments());
        }
        //设置驳回状态为未通过
        runtimeService.updateBusinessStatus(task.getProcessInstanceId(), ApprovalStatusEnum.FAILED_REJECT);

        //驳回至指定节点，现驳回到起始节点
        HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(task.getProcessInstanceId())
                .orderByHistoricTaskInstanceStartTime()
                .asc().list().get(0);

        runtimeService.createChangeActivityStateBuilder()
                .processInstanceId(task.getProcessInstanceId())
                .moveActivityIdTo(task.getTaskDefinitionKey(), historicTaskInstance.getTaskDefinitionKey())
                .changeState();
        return ReturnUtil.success();
    }

    @Override
    @Transactional
    public ReturnDTO refuse(ApprovalRefuseRequest request) {
        AuthUser user = ShiroKit.getUser();
        Assert.isFalse(Objects.isNull(user), () -> {
            throw new BusinessException(BusinessExceptionEnum.LOGIN_USER_ACQUIRE_FAILED);
        });
        //设置拒绝发起人
        Authentication.setAuthenticatedUserId(String.valueOf(user.getId()));

        Task task = taskService.createTaskQuery().taskId(request.getTaskId()).singleResult();
        if (StringUtils.isNotBlank(request.getComments())) {
            //2拒绝意见
            taskService.addComment(task.getId(), task.getProcessInstanceId(), "2", request.getComments());
        }
        //设置拒绝状态
        runtimeService.updateBusinessStatus(task.getProcessInstanceId(), ApprovalStatusEnum.FAILED);

        runtimeService.deleteProcessInstance(task.getProcessInstanceId(), "拒绝");
        return ReturnUtil.success();
    }

    @Override
    @Transactional
    public ReturnDTO revoke(ApprovalRevokeRequest request) {
        AuthUser user = ShiroKit.getUser();
        Assert.isFalse(Objects.isNull(user), () -> {
            throw new BusinessException(BusinessExceptionEnum.LOGIN_USER_ACQUIRE_FAILED);
        });

        //设置发起人
        Authentication.setAuthenticatedUserId(String.valueOf(user.getId()));

//        HistoricTaskInstance task = historyService.createHistoricTaskInstanceQuery().taskId(request.getTaskId()).singleResult();
//        Task task = taskService.createTaskQuery().taskId(request.getTaskId()).singleResult();

        //只有发起者才能撤回
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(request.getProcessInstanceId())
                .startedBy(String.valueOf(user.getId()))
                .or()
                .unfinished() //审批中的流程才能撤回
                .processInstanceBusinessStatus(ApprovalStatusEnum.IN_APPROVED) //查询审批中状态
                .endOr()
                .singleResult();
        if (Objects.isNull(historicProcessInstance)) {
            log.error("该用户无审批中的流程，userId:{}，processInstanceId:{}", user.getId(), request.getProcessInstanceId());
            return ReturnUtil.failed("该用户无审批中的流程");
        }

        //设置取消状态
        runtimeService.updateBusinessStatus(request.getProcessInstanceId(), ApprovalStatusEnum.CANCELED);
//        runtimeService.deleteProcessInstance(request.getProcessInstanceId(), "取消");
        return ReturnUtil.success();
    }

    @Override
    @Transactional
    public ReturnDTO delete(ApprovalDeleteRequest request) {
        AuthUser user = ShiroKit.getUser();
        Assert.isFalse(Objects.isNull(user), () -> {
            throw new BusinessException(BusinessExceptionEnum.LOGIN_USER_ACQUIRE_FAILED);
        });

        //设置发起人
        Authentication.setAuthenticatedUserId(String.valueOf(user.getId()));

        Task task = taskService.createTaskQuery().taskId(request.getTaskId()).singleResult();

        runtimeService.updateBusinessStatus(task.getProcessInstanceId(), ApprovalStatusEnum.DELETED);
        runtimeService.deleteProcessInstance(task.getProcessInstanceId(), "删除");

        return ReturnUtil.success();
    }

    /**
     * 流程完成时间处理
     *
     * @param ms
     * @return
     */
    private String getDate(long ms) {

        long day = ms / (24 * 60 * 60 * 1000);
        long hour = (ms / (60 * 60 * 1000) - day * 24);
        long minute = ((ms / (60 * 1000)) - day * 24 * 60 - hour * 60);
        long second = (ms / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60);

        if (day > 0) {
            return day + "天" + hour + "小时" + minute + "分钟";
        }
        if (hour > 0) {
            return hour + "小时" + minute + "分钟";
        }
        if (minute > 0) {
            return minute + "分钟";
        }
        if (second > 0) {
            return second + "秒";
        } else {
            return 0 + "秒";
        }
    }
}
