package cn.iocoder.yudao.module.bpm.service.oa;

import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.worktask.BpmOAWorkTaskPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.worktask.BpmOAWorkTaskReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.worktask.BpmOAWorkTaskRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.worktask.BpmOAWorkTaskTrackInfo;
import cn.iocoder.yudao.module.bpm.controller.admin.upload.UploadUserFile;
import cn.iocoder.yudao.module.bpm.convert.oa.BpmOAWorkTaskConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAWorkTaskDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOAWorkTaskMapper;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.OA_WORK_TASK_NOT_EXISTS;

/**
 * OA 请假申请 Service 实现类
 *
 * @author jason

 */
@Service
@Validated
public class BpmOAWorkTaskServiceImpl extends BpmOABaseService implements BpmOAWorkTaskService {

    /**
     * OA 请假对应的流程定义 KEY
     */
    public static final String PROCESS_KEY = "work_task";

    @Resource
    private BpmOAWorkTaskMapper workTaskMapper;

    @Resource
    private BpmProcessInstanceApi processInstanceApi;

    @Resource
    private HttpSession session;

    @Resource
    private AdminUserApi userApi;

    @Resource
    private DeptApi deptApi;

    @Resource
    @Lazy
    private BpmTaskService taskService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createWorkTask(Long userId, BpmOAWorkTaskReqVO createReqVO) {

        //设置 责任人Id
        session.setAttribute(userId.toString(), createReqVO.getUserId());

        // 获取当前登录用户得信息
        AdminUserRespDTO userRespDTO = userApi.getUser(userId).getCheckedData();

        BpmOAWorkTaskDO workTask = BpmOAWorkTaskConvert.INSTANCE.convert(createReqVO)
                .setFromUserId(userId)
                .setFromDeptId(userRespDTO.getDeptId())
                .setWorkTaskStatus(1) //默认待启动状态
                .setReceiverUserId(createReqVO.getUserId())
                .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
        workTaskMapper.insert(workTask);

        // 发起 BPM 流程
        Map<String, Object> processInstanceVariables = new HashMap<>();
        String processInstanceId = processInstanceApi.createProcessInstance(userId,
                new BpmProcessInstanceCreateReqDTO().setProcessDefinitionKey(PROCESS_KEY)
                        .setVariables(processInstanceVariables).setBusinessKey(String.valueOf(workTask.getId()))).getCheckedData();

        // 将工作流的编号，更新到 OA 请假单中
        workTaskMapper.updateById(new BpmOAWorkTaskDO().setId(workTask.getId()).setProcessInstanceId(processInstanceId));

        List<UploadUserFile> fileItems = createReqVO.getFileItems() ;
        //这里的逻辑，如果fileItems不为空，且有数据，那么说明是上传了附件的，则需要更工作流文件表对应的实例Id
        if (fileItems != null && !fileItems.isEmpty()) {
            uploadBpmFileProcessInstanceId(processInstanceId,fileItems) ;
        }
        return workTask.getId();
    }

    @Override
    public void updateWorkTaskResult(Long id, Integer result) {
        validateLeaveExists(id);
        //bpm流程设置成已完成，需要将任务自生的状态设置成已完成
        workTaskMapper.updateById(new BpmOAWorkTaskDO().setId(id).setResult(result).setWorkTaskStatus(3).setSchedule(100L));
    }

    private BpmOAWorkTaskDO validateLeaveExists(Long id) {

        BpmOAWorkTaskDO taskDO = workTaskMapper.selectById(id);
        if (taskDO == null) {
            throw exception(OA_WORK_TASK_NOT_EXISTS);
        }

        return taskDO;
    }

    @Override
    public BpmOAWorkTaskDO getWorkTask(Long id) {
        return workTaskMapper.selectById(id);
    }

    @Override
    public BpmOAWorkTaskDO getWorkTaskByProcessInstanceId(String processInstanceId) {

        return workTaskMapper.selectOne(BpmOAWorkTaskDO::getProcessInstanceId, processInstanceId);
    }

    @Override
    public void addTrackInfo(BpmOAWorkTaskReqVO updateReqVO) {

        //设置 跟踪任务创建时间
        BpmOAWorkTaskTrackInfo taskTrackInfo = updateReqVO.getTrackInfos();
        taskTrackInfo.setTrackTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

        BpmOAWorkTaskDO taskDO = validateLeaveExists(updateReqVO.getId());
        List<BpmOAWorkTaskTrackInfo> info = taskDO.getTrackInfo();
        if (info == null) {
            info = new ArrayList<>();
        }
        info.add(taskTrackInfo);

        BpmOAWorkTaskDO updateDo = new BpmOAWorkTaskDO();
        updateDo.setId(updateReqVO.getId());
        updateDo.setTrackInfo(info);
        workTaskMapper.updateById(updateDo);
    }

    @Override
    public void updateWorkTask(BpmOAWorkTaskReqVO createReqVO) {
        BpmOAWorkTaskDO taskDO = validateLeaveExists(createReqVO.getId());

        if (taskDO.getWorkTaskStatus() == 5 && createReqVO.getWorkTaskStatus() == 2) {

            Long daysDifference  = ChronoUnit.DAYS.between(taskDO.getStartTime(), taskDO.getEndTime());
            createReqVO.setShelveStartTime(LocalDate.now());
            createReqVO.setShelveEndTime(LocalDate.now().plusDays(daysDifference));
        }

        taskDO = BeanUtils.toBean(createReqVO, BpmOAWorkTaskDO.class);

        //更新任务进度  和  任务状态
        workTaskMapper.updateById(taskDO);
    }

    @Override
    public PageResult<BpmOAWorkTaskRespVO> getPage(Long loginUserId, BpmOAWorkTaskPageReqVO pageVO) {

        PageResult<BpmOAWorkTaskDO> pageResult = workTaskMapper.selectPage(loginUserId, pageVO);
        // 获得责任人 用户Id
        List<Long> userIds = pageResult.getList().stream().map(BpmOAWorkTaskDO::getReceiverUserId).collect(Collectors.toList());
        // 获得责任人用户信息列表
        List<AdminUserRespDTO> userRespDTOS = userApi.getUserList(userIds).getCheckedData();
        Map<Long, AdminUserRespDTO> userMap = convertMap(userRespDTOS, AdminUserRespDTO::getId);

        //获得部门信息map
        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(userRespDTOS.stream().map(AdminUserRespDTO::getDeptId).collect(Collectors.toSet()));

        //获得发起人用户信息列表
        List<Long> fromUserIds = pageResult.getList().stream().map(BpmOAWorkTaskDO::getFromUserId).collect(Collectors.toList());
        Map<Long, AdminUserRespDTO> fromUserMap = userApi.getUserMap(fromUserIds);

        PageResult<BpmOAWorkTaskRespVO> dataPage = BeanUtils.toBean(pageResult, BpmOAWorkTaskRespVO.class);
        dataPage.getList().forEach(data -> {

            data.setFromUserName(fromUserMap.get(data.getFromUserId()).getNickname());
            data.setUserName(userMap.get(data.getReceiverUserId()).getNickname());
            data.setDeptName(deptMap.get(userMap.get(data.getReceiverUserId()).getDeptId()).getName());
        });

        return dataPage;
    }

    @Override
    public PageResult<BpmOAWorkTaskRespVO> getMyPage(Long loginUserId, BpmOAWorkTaskPageReqVO pageVO) {

        PageResult<BpmOAWorkTaskDO> pageResult = workTaskMapper.selectMyPage(loginUserId, pageVO);

        //获得发起人用户信息列表
        List<Long> fromUserIds = pageResult.getList().stream().map(BpmOAWorkTaskDO::getFromUserId).collect(Collectors.toList());
        Map<Long, AdminUserRespDTO> fromUserMap = userApi.getUserMap(fromUserIds);

        //获得任务分配processInstanceId
        List<String> processInstanceIds = pageResult.getList().stream().map(BpmOAWorkTaskDO::getProcessInstanceId).collect(Collectors.toList());
        List<BpmTaskExtDO> taskExtDOS = taskService.getTaskByProcessInstanceIdAndResult(processInstanceIds, BpmProcessInstanceResultEnum.PROCESS.getResult());
        Map<String, BpmTaskExtDO> taskExtDOMap = convertMap(taskExtDOS, BpmTaskExtDO::getProcessInstanceId);

        PageResult<BpmOAWorkTaskRespVO> dataPage = BeanUtils.toBean(pageResult, BpmOAWorkTaskRespVO.class);
        dataPage.getList().forEach(data -> {

            data.setFromUserName(fromUserMap.get(data.getFromUserId()).getNickname());
            data.setTaskId(taskExtDOMap.get(data.getProcessInstanceId()) != null ? taskExtDOMap.get(data.getProcessInstanceId()).getTaskId() : null);
        });

        return dataPage;
    }
}
