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

import cn.iocoder.yudao.framework.common.pojo.UploadUserFile;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
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.evection.BpmOAEvectionCreateReqVO;
import cn.iocoder.yudao.module.bpm.convert.oa.BpmOAEvectionConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAEvectionDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOAEvectionMapper;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.module.bpm.service.task.BpmHistoryProcessInstanceService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.OA_EVECTION_IS_EXISTS;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.OA_EVECTION_NOT_EXISTS;

/**
 * OA 出差申请 Service 实现类
 *
 * @author 符溶馨

 */
@Service
@Validated
public class BpmOAEvectionServiceImpl extends BpmOABaseService implements BpmOAEvectionService {

    /**
     * OA 出差对应的流程定义 KEY
     */
    public static final String PROCESS_KEY = "oa_evection";

    @Resource
    private BpmOAEvectionMapper evectionMapper;

    @Resource
    private BpmProcessInstanceApi processInstanceApi;

    @Resource
    private BpmHistoryProcessInstanceService historyProcessInstanceService;

    @Resource
    private AdminUserApi userApi;

    @Override
    public Long createEvection(Long userId, BpmOAEvectionCreateReqVO createReqVO) {

        // 校验当前选择的时间段内 是否在出差中
        Long count = evectionMapper.selectIsExist(userId, createReqVO);
        if (count > 0L) {
            throw exception(OA_EVECTION_IS_EXISTS);
        }

        //插入OA 出差申请
        BpmOAEvectionDO evection = BpmOAEvectionConvert.INSTANCE.convert(createReqVO).setUserId(userId)
                .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
        evectionMapper.insert(evection) ;

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

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

        // 判断是否为重新发起的流程
        if (createReqVO.getProcessInstanceId() != null && createReqVO.getResult() == 3) {

            historyProcessInstanceService.createHistoryProcessInstance(processInstanceId, createReqVO.getProcessInstanceId());
        }

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

        // 发起出差申请后，判断是否是当天的出差
        if (evection.getStartTime().toLocalDate().isEqual(LocalDate.now())) {
            // 设置外勤打卡权限
            Set<Long> userIds = evection.getTogetherUserIds() == null ? new HashSet<>() : evection.getTogetherUserIds();
            userIds.add(userId);
            userApi.updateFieldworkType(userIds, 1);
        }

        return evection.getId();
    }

    @Override
    public void updateEvectionResult(Long id, Integer result) {

        BpmOAEvectionDO evectionDO = validateLeaveExists(id);
        evectionMapper.updateById(new BpmOAEvectionDO().setId(id).setResult(result));

        // -- 自己取消
        // -- 审核拒绝
        if (BpmProcessInstanceResultEnum.REJECT.getResult().equals(result)
                || BpmProcessInstanceResultEnum.CANCEL.getResult().equals(result)
                || BpmProcessInstanceResultEnum.BACK.getResult().equals(result)) {

            // 设置关闭外勤打卡权限
            Set<Long> userIds = evectionDO.getTogetherUserIds();
            userIds.add(evectionDO.getUserId());
            userApi.updateFieldworkType(userIds, 0);
        }
    }

    private BpmOAEvectionDO validateLeaveExists(Long id) {
        BpmOAEvectionDO evectionDO = evectionMapper.selectById(id);
        if (evectionDO == null) {
            throw exception(OA_EVECTION_NOT_EXISTS);
        }
        return evectionDO;
    }

    @Override
    public BpmOAEvectionDO getEvection(Long id) {

        return evectionMapper.selectById(id);
    }

    @Override
    public BpmOAEvectionDO getByProcessInstanceId(String processInstanceId) {

        return evectionMapper.selectOne(BpmOAEvectionDO::getProcessInstanceId, processInstanceId);
    }

    @Override
    public List<BpmOAEvectionDO> getEvectionListByStartTime(LocalDate now) {

        return evectionMapper.selectList(new LambdaQueryWrapperX<BpmOAEvectionDO>()
                .eq(BpmOAEvectionDO::getResult, BpmProcessInstanceResultEnum.APPROVE.getResult())
                .likeIfPresent(BpmOAEvectionDO::getStartTime, now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))));
    }

    @Override
    public List<BpmOAEvectionDO> getEvectionListByEndTime(LocalDate date) {

        return evectionMapper.selectList(new LambdaQueryWrapperX<BpmOAEvectionDO>()
                .eq(BpmOAEvectionDO::getResult, BpmProcessInstanceResultEnum.APPROVE.getResult())
                .likeIfPresent(BpmOAEvectionDO::getEndTime, date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))));
    }

    @Override
    public List<BpmOAEvectionDO> getEvectionList() {

        return evectionMapper.selectEvectionList(getLoginUserId());
    }
}
