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

import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.common.util.string.DTO.IdCardDO;
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
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.entry.BpmOAEntryCreateReqVO;
import cn.iocoder.yudao.framework.common.pojo.UploadUserFile;
import cn.iocoder.yudao.module.bpm.convert.oa.BpmOAEntryConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAEntryDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOAEntryMapper;
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.infra.api.file.FileApi;
import cn.iocoder.yudao.module.infra.api.file.dto.UserFileUpdateReqDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.UserSaveRespDTO;
import org.flowable.engine.runtime.ProcessInstance;
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 java.time.LocalDate;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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_ENTRY_NOT_EXISTS;

/**
 * OA 入职申请 Service 实现类
 *
 * @author 符溶馨
 */
@Service
@Validated
public class BpmOAEntryServiceImpl implements BpmOAEntryService{

    /**
     * OA 入职申请对应的流程定义 KEY
     */
    public static final String PROCESS_KEY = "oa_entry_2";

    @Resource
    private BpmOAEntryMapper entryMapper;

    @Resource
    private BpmProcessInstanceApi processInstanceApi;

    @Resource
    @Lazy
    private BpmProcessInstanceService bpmProcessInstanceService;

    @Resource
    private AdminUserApi userApi;

    @Resource
    private FileApi fileApi;

    @Resource
    private BpmHistoryProcessInstanceService historyProcessInstanceService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createEntry(BpmOAEntryCreateReqVO createReqVO) {

        //插入OA 入职申请
        BpmOAEntryDO entry = BpmOAEntryConvert.INSTANCE.convert(createReqVO)
                .setUserId(getLoginUserId())
                .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
        entryMapper.insert(entry) ;

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

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

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

        // 将工作流的编号，更新到 OA 入职申请单中
        entryMapper.updateById(new BpmOAEntryDO().setId(entry.getId()).setProcessInstanceId(processInstanceId));

        return entry.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateEntryResult(String processInstanceId, Long id, Integer result) {

        BpmOAEntryDO entry = validateLeaveExists(id);

        BpmOAEntryDO updateVO = new BpmOAEntryDO().setId(id).setResult(result);
        //审核通过 （最后节点）
        if (BpmProcessInstanceResultEnum.APPROVE.getResult().equals(result)) {

            ProcessInstance instance = bpmProcessInstanceService.getProcessInstance(processInstanceId);

            if (instance.isEnded()) {

                // 新增入职申请中的用户信息
                UserSaveRespDTO saveRespDTO = BeanUtils.toBean(entry, UserSaveRespDTO.class);
                saveRespDTO.setId(null);
                saveRespDTO.setDeptId(entry.getEntryDeptId());
                saveRespDTO.setPositionId(entry.getEntryPositionId());
                saveRespDTO.setPostIds(Collections.singleton(entry.getEntryPostId()));
                saveRespDTO.setPassword("123456");
                saveRespDTO.setStatus(1);  // 默认设置关闭

                // 解析身份证号码
                IdCardDO idCardDO = StrUtils.IdCardAnalysis(saveRespDTO.getIdcard());
                // 设置性别、年龄、生日
                saveRespDTO.setSex(idCardDO.getSex());
                saveRespDTO.setAge(idCardDO.getAge());
                saveRespDTO.setBirthdayDay(idCardDO.getDate());

                // 插入用户 获得用户编号
                Long userId = userApi.createUser(saveRespDTO).getCheckedData();
                updateVO.setEntryUserId(userId);

                // 同步修改用户文件数据
                List<UploadUserFile> uploadUserFiles = BeanUtils.toBean(entry.getFileItems(), UploadUserFile.class);
                UserFileUpdateReqDTO updateReqDTO = new UserFileUpdateReqDTO()
                        .setUrls(convertList(uploadUserFiles, UploadUserFile::getUrl))
                        .setUserId(userId);
                fileApi.updateUserFileUserId(updateReqDTO);
            }
        }

        // 更新入职申请状态
        entryMapper.updateById(updateVO);
    }

    private BpmOAEntryDO validateLeaveExists(Long id) {

        BpmOAEntryDO entry = entryMapper.selectById(id);
        if (entry == null) {
            throw exception(OA_ENTRY_NOT_EXISTS);
        }

        return entry;
    }

    @Override
    public BpmOAEntryDO getEntry(Long id) {

        return entryMapper.selectById(id);
    }

    @Override
    public BpmOAEntryDO getEntryByProcessInstanceId(String processInstanceId) {

        return entryMapper.selectOne(BpmOAEntryDO::getProcessInstanceId, processInstanceId);
    }

    @Override
    public List<BpmOAEntryDO> getEntryListByTime(LocalDate entryDate) {

        return entryMapper.selectList(new LambdaQueryWrapperX<BpmOAEntryDO>()
                .eq(BpmOAEntryDO::getEntryDate, entryDate)
                .eq(BpmOAEntryDO::getResult, BpmProcessInstanceResultEnum.APPROVE.getResult()));
    }
}
