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

import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
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.procure.BpmOAProcureListEditReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.reimbursement.BpmOAReimbursementCreateReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.reimbursement.BpmOAReimbursementRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.reimbursement.Reimbursement;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.reimbursement.ReimbursementDTO;
import cn.iocoder.yudao.module.bpm.controller.admin.upload.UploadUserFile;
import cn.iocoder.yudao.module.bpm.convert.oa.BpmOAReimbursementConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAImprestDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAProcureDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAReimbursementDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOAImprestMapper;
import cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOAReimbursementMapper;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
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.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.util.*;

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

/**
 * 功能描述
 * OA 报销申请 Service 实现类
 *
 * @author: yj
 * @date: 2024年02月09日 15:33
 */
@Service
@Validated
public class BpmOAReimbursementServiceImpl extends BpmOABaseService implements BpmOAReimbursementService{

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

    @Resource
    private BpmOAReimbursementMapper reimbursementMapper ;

    @Resource
    private BpmProcessInstanceApi processInstanceApi;

    @Resource
    @Lazy // 解决循环依赖
    private BpmProcessInstanceService bpmProcessInstanceService;

    @Resource
    private BpmOAImprestService bpmOAImprestService;

    @Resource
    private BpmOAImprestMapper bpmOAImprestMapper;

    @Resource
    private BpmOAProcureService bpmOAProcureService;

    @Resource
    private DeptApi deptApi;

    @Resource
    private AdminUserApi userApi;

    @Override
    public Long createReimbursement(Long userId, BpmOAReimbursementCreateReqVO createReqVO) {
        BpmOAReimbursementDO reimbursement = BpmOAReimbursementConvert.INSTANCE.convert(createReqVO).setUserId(userId)
                .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
        reimbursementMapper.insert(reimbursement) ;

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

        // 将工作流的编号，更新到 OA 报销表单中
        reimbursementMapper.updateById(new BpmOAReimbursementDO().setId(reimbursement.getId()).setProcessInstanceId(processInstanceId));

        //如果是备用金报销，则更新备用金流程status,并存入当前报销流程ID
        if (createReqVO.getDifference() != null) {
            BpmOAImprestDO bpmOAImprestDO = bpmOAImprestService.getImprestByUserId(userId);
            bpmOAImprestMapper.updateById(new BpmOAImprestDO().setId(bpmOAImprestDO.getId()).setReimbursementId(reimbursement.getId()).setStatus(BpmOAImprestDO.IN_PROGRESS));
        }

        //判断是否有 采购报销
        List<Long> procureIds = new ArrayList<>();
        for (Reimbursement data : reimbursement.getReimbursements()) {

            //报销类别为 采购费时
            if ("4".equals(data.getType())) {

                BpmOAProcureDO procureDO = bpmOAProcureService.getOaProcure(data.getProcureId());
                if (procureDO != null) {

                    procureIds.add(procureDO.getId());
                }
            }
        }

        if (!procureIds.isEmpty()) {

            //所有关联的采购申请改为 进行中状态
            bpmOAProcureService.updatePayFlagByIds(
                    new BpmOAProcureListEditReqVO()
                            .setPayFlag(BpmOAProcureDO.IN_PROGRESS)
                            .setProcureIds(procureIds)
            );
        }

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

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

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

            ProcessInstance instance = bpmProcessInstanceService.getProcessInstance(processInstanceId);

            if (instance.isEnded()) {

                //获得备用金信息
                BpmOAImprestDO bpmOAImprestDO = bpmOAImprestMapper.selectOne(BpmOAImprestDO::getReimbursementId, id);
                if (bpmOAImprestDO != null) {

                    //将相应备用金申请状态改为 已报销
                    bpmOAImprestMapper.updateById(new BpmOAImprestDO().setId(bpmOAImprestDO.getId()).setStatus(BpmOAImprestDO.FLAG_TRUE));
                }

                //判断是否有采购报销
                List<Long> procureIds = new ArrayList<>();
                BpmOAReimbursementDO bpmOAReimbursementDO = getReimbursement(id);
                List<Reimbursement> reimbursements = bpmOAReimbursementDO.getReimbursements();

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

                for (Reimbursement reimbursement : reimbursements) {

                    //报销类别为 采购费时
                    if ("4".equals(reimbursement.getType())) {

                        BpmOAProcureDO procureDO = bpmOAProcureService.getOaProcure(reimbursement.getProcureId());
                        if (procureDO != null) {

                            procureIds.add(procureDO.getId());
                        }
                    }
                }

                if (!procureIds.isEmpty()) {

                    //所有关联的采购申请改为 已支付状态
                    bpmOAProcureService.updatePayFlagByIds(
                            new BpmOAProcureListEditReqVO()
                                    .setPayFlag(BpmOAProcureDO.FLAG_TRUE)
                                    .setProcureIds(procureIds)
                    );
                }
            }
        }

        // -- 自己取消
        // -- 审核拒绝
        //所有关联的采购申请改为 未支付状态
        if (BpmProcessInstanceResultEnum.REJECT.getResult().equals(result)
                || BpmProcessInstanceResultEnum.CANCEL.getResult().equals(result)
                || BpmProcessInstanceResultEnum.BACK.getResult().equals(result)) {

            BpmOAImprestDO bpmOAImprestDO = bpmOAImprestMapper.selectOne(BpmOAImprestDO::getReimbursementId, id);

            if (bpmOAImprestDO != null) {

                //将相应备用金申请状态改为 未报销
                bpmOAImprestMapper.updateById(new BpmOAImprestDO().setId(bpmOAImprestDO.getId()).setStatus(BpmOAImprestDO.FLAG_FALSE));
            }

            //判断是否有采购报销
            List<Long> procureIds = new ArrayList<>();
            BpmOAReimbursementDO bpmOAReimbursementDO = getReimbursement(id);
            List<Reimbursement> reimbursements = bpmOAReimbursementDO.getReimbursements();

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

            for (Reimbursement reimbursement : reimbursements) {

                //报销类别为 采购费时
                if ("4".equals(reimbursement.getType())) {

                    BpmOAProcureDO procureDO = bpmOAProcureService.getOaProcure(reimbursement.getProcureId());
                    if (procureDO != null) {

                        procureIds.add(procureDO.getId());
                    }
                }
            }

            if (!procureIds.isEmpty()) {

                //所有关联的采购申请改为 未支付状态
                bpmOAProcureService.updatePayFlagByIds(
                        new BpmOAProcureListEditReqVO()
                                .setPayFlag(BpmOAProcureDO.FLAG_FALSE)
                                .setProcureIds(procureIds)
                );
            }
        }

        reimbursementMapper.updateById(new BpmOAReimbursementDO().setId(id).setResult(result));
    }

    private void validateLeaveExists(Long id) {
        if (reimbursementMapper.selectById(id) == null) {
            throw exception(OA_REIMBURSEMENT_NOT_EXISTS);
        }
    }

    @Override
    public BpmOAReimbursementDO getReimbursement(Long id) {

        return reimbursementMapper.selectById(id);
    }

    @Override
    public BpmOAReimbursementRespVO convert(BpmOAReimbursementDO reimbursementDO) {

        List<Reimbursement> reimbursement = reimbursementDO.getReimbursements();

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

        //针对之前的数据， 没有设置明细所属部门的  手动设置发起人的部门编号
        AdminUserRespDTO userRespDTO = userApi.getUser(reimbursementDO.getUserId()).getCheckedData();
        for (Reimbursement data : reimbursement) {

            if (data.getDeptId() != null) {
                break;
            }

            data.setDeptId(userRespDTO.getDeptId());
        }

        //获取部门信息map
        Map<Long, DeptRespDTO> deptMap =  deptApi.getDeptMap(convertSet(reimbursement, Reimbursement::getDeptId));

        List<ReimbursementDTO> reimbursementDTOS = BeanUtils.toBean(reimbursement, ReimbursementDTO.class);

        BpmOAReimbursementRespVO bpmOAReimbursementRespVO = BeanUtils.toBean(reimbursementDO, BpmOAReimbursementRespVO.class);
        bpmOAReimbursementRespVO.setReimbursements(BpmOAReimbursementConvert.INSTANCE.convertList(reimbursementDTOS, deptMap));  //拼接数据

        return bpmOAReimbursementRespVO;
    }
}