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

import cn.iocoder.yudao.framework.common.Constants;
import cn.iocoder.yudao.framework.common.pojo.UploadUserFile;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.replacementCard.BpmOAReplacementCardCreateReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.replacementCard.ReplacementCardItemVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAReplacementCardDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOAReplacementCardMapper;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.module.bpm.service.task.BpmHistoryProcessInstanceService;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import cn.iocoder.yudao.module.system.api.attendance.AttendanceApi;
import cn.iocoder.yudao.module.system.api.group.AttendanceGroupApi;
import com.alibaba.nacos.common.utils.StringUtils;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.OA_REPLACEMENT_CARD_NOT_EXISTS;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.INSUFFICIENT_NUMBER_OF_CARD_REFILLS;

/**
 * OA 补卡申请 Service 实现类
 *
 * @author 符溶馨
 */
@Service
@Validated
public class BpmOAReplacementCardServiceImpl extends BpmOABaseService implements BpmOAReplacementCardService{

    /**
     * OA 补卡申请对应的流程定义 KEY
     */
    public static final String PROCESS_KEY = "oa_replacementCard_2";

    @Resource
    private BpmOAReplacementCardMapper replacementCardMapper;

    @Resource
    private BpmProcessInstanceService bpmProcessInstanceService;

    @Resource
    private BpmHistoryProcessInstanceService historyProcessInstanceService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private AttendanceGroupApi attendanceGroupApi;

    @Resource
    private AttendanceApi attendanceApi;

    @Override
    public Long createReplacementCard(Long userId, BpmOAReplacementCardCreateReqVO createReqVO) {

        // 获取用户可补卡次数
        Integer count = attendanceApi.getReplacementCardNum().getCheckedData();
        if (count <= 0) {
            throw exception(INSUFFICIENT_NUMBER_OF_CARD_REFILLS);
        }

        //插入OA 补卡申请
        BpmOAReplacementCardDO replacementCard = BeanUtils.toBean(createReqVO, BpmOAReplacementCardDO.class)
                .setUserId(userId)
                .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
        replacementCardMapper.insert(replacementCard);

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

        // 将工作流的编号，更新到 OA 补卡申请单中
        replacementCardMapper.updateById(new BpmOAReplacementCardDO().setId(replacementCard.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) ;
        }

        // 发起补卡申请后， 先扣除用户补卡次数
        String key = "ReplacementCardNum" + "_" + userId + "_" + LocalDateTime.now().format(Constants.YEAR_MONTH_FORMAT);
        String value = stringRedisTemplate.opsForValue().get(key);
        if (StringUtils.isNotEmpty(value)) {
            if (Integer.parseInt(value) > 0) {
                stringRedisTemplate.opsForValue().increment(key, -(createReqVO.getApplicationItem().size()));
            } else {
                throw exception(INSUFFICIENT_NUMBER_OF_CARD_REFILLS);
            }
        }else {
            // 如果redis不存在，则从数据库获取
            Integer num = attendanceGroupApi.getReplacementCardNum(userId).getCheckedData();
            if (num > 0) {
                num = num - createReqVO.getApplicationItem().size();
                stringRedisTemplate.opsForValue().set(key, num.toString(), 32, TimeUnit.DAYS);
            } else {
                throw exception(INSUFFICIENT_NUMBER_OF_CARD_REFILLS);
            }
        }

        return replacementCard.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateReplacementCardResult(String processInstanceId, Long id, Integer result) {
        BpmOAReplacementCardDO replacementCard = validateLeaveExists(id);
        replacementCardMapper.updateById(new BpmOAReplacementCardDO().setId(id).setResult(result));

        //审核通过 （最后节点）
        if (BpmProcessInstanceResultEnum.APPROVE.getResult().equals(result)) {

            ProcessInstance instance = bpmProcessInstanceService.getProcessInstance(processInstanceId);

            if (instance.isEnded()) {

                List<ReplacementCardItemVO> respVO = replacementCard.getApplicationItem();
                //直接从数据库取出来的List<Reimbursement> 实际上是List<LinkedHashMap>类型 所以不能直接遍历
                //将list再次转为json串，然后由json串再转为list
                String json = JsonUtils.toJsonString(respVO);
                respVO = JsonUtils.parseArray(json, ReplacementCardItemVO.class);

                // 获取需补卡的打卡记录编号
                List<Long> punchRecordIds = convertList(respVO, ReplacementCardItemVO::getPunchRecordId);

                // 审核通过， 则进行批量补卡操作
                attendanceApi.replacementCard(punchRecordIds);
            }
        }

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

            // 恢复redis 中的补卡次数
            String key = "ReplacementCardNum" + "_" + replacementCard.getUserId() + "_" + LocalDateTime.now().format(Constants.YEAR_MONTH_FORMAT);
            stringRedisTemplate.opsForValue().increment(key, replacementCard.getApplicationItem().size());
        }
    }

    private BpmOAReplacementCardDO validateLeaveExists(Long id) {
        BpmOAReplacementCardDO replacementCard = replacementCardMapper.selectById(id);
        if (replacementCard == null) {
            throw exception(OA_REPLACEMENT_CARD_NOT_EXISTS);
        }

        return replacementCard;
    }

    @Override
    public BpmOAReplacementCardDO getReplacementCard(Long id) {

        return replacementCardMapper.selectById(id);
    }

    @Override
    public BpmOAReplacementCardDO getByProcessInstanceId(String processInstanceId) {

        return replacementCardMapper.selectOne(BpmOAReplacementCardDO::getProcessInstanceId, processInstanceId);
    }

    @Override
    public List<BpmOAReplacementCardDO> getListByResult() {

        return replacementCardMapper.selectList(new LambdaQueryWrapperX<BpmOAReplacementCardDO>()
                .eq(BpmOAReplacementCardDO::getUserId, getLoginUserId())
                .eq(BpmOAReplacementCardDO::getResult, BpmProcessInstanceResultEnum.PROCESS.getResult())
                .like(BpmOAReplacementCardDO::getCreateTime, LocalDate.now().format(Constants.YEAR_MONTH_FORMAT)));
    }
}
