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

import cn.hutool.core.collection.CollectionUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.UploadUserFile;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
import cn.iocoder.yudao.module.bpm.api.oa.vo.loan.BpmOALoanSumDTO;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.loan.BpmOALoanCreateReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.loan.BpmOALoanPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.loan.BpmOAReturnVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.financialpayment.FinancialPaymentDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOALoanDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOALoanMapper;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.module.bpm.service.financialpayment.FinancialPaymentService;
import cn.iocoder.yudao.module.bpm.service.task.BpmHistoryProcessInstanceService;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import cn.iocoder.yudao.module.smartfactory.api.factoryInfo.FactoryInfoApi;
import cn.iocoder.yudao.module.smartfactory.api.factoryInfo.dto.FactoryInfoDTO;
import cn.iocoder.yudao.module.smartfactory.api.staff.StaffApi;
import cn.iocoder.yudao.module.smartfactory.api.staff.dto.StaffDTO;
import cn.iocoder.yudao.module.system.api.bank.BankApi;
import cn.iocoder.yudao.module.system.api.bank.dto.BankRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.Collection;
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.module.bpm.enums.ErrorCodeConstants.OA_LOAN_NOT_CREATE;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.OA_LOAN_NOT_EXISTS;

/**
 * OA 加班申请 Service 实现类
 *
 * @author 符溶馨
 */
@Service
@Validated
public class BpmOALoanServiceImpl extends BpmOABaseService implements BpmOALoanService {

    /**
     * OA 加班对应的流程定义 KEY
     */
    public static final String PROCESS_KEY = "oa_loan_2";

    @Resource
    private BpmOALoanMapper loanMapper;

    @Resource
    private BpmProcessInstanceService processInstanceService;

    @Resource
    private BpmHistoryProcessInstanceService historyProcessInstanceService;

    @Resource
    private AdminUserApi userApi;

    @Resource
    private FinancialPaymentService financialPaymentService;

    @Resource
    private StaffApi staffApi;

    @Resource
    private BankApi bankApi;

    @Resource
    private FactoryInfoApi factoryInfoApi;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createLoan(Long userId, BpmOALoanCreateReqVO vo) {

        // 提交是费用借支时
        if (vo.getLoanType() == 2) {

            // 校验创建
            StaffDTO staffDTO = validateCreate(userId, vo);
            vo.setSfUserId(staffDTO.getId());
            vo.setFactoryId(staffDTO.getFactoryId());
        }

        //插入OA 借支申请
        BpmOALoanDO loan = BeanUtils.toBean(vo, BpmOALoanDO.class)
                .setUserId(userId)
                .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
        loanMapper.insert(loan);



        // 发起 BPM 流程
        Map<String, Object> processInstanceVariables = new HashMap<>();
        // 提交是费用借支时
        if (vo.getLoanType() == 2) {

            // 获取借支人所属厂区信息
            FactoryInfoDTO factoryInfoDTO = factoryInfoApi.getFactoryInfo(loan.getFactoryId()).getCheckedData();
            processInstanceVariables.put("factoryType", factoryInfoDTO.getType());
        }

        processInstanceVariables.put("loanType", loan.getLoanType());
        String processInstanceId = processInstanceService.createProcessInstance(userId,
                new BpmProcessInstanceCreateReqDTO().setProcessDefinitionKey(PROCESS_KEY)
                        .setVariables(processInstanceVariables).setBusinessKey(String.valueOf(loan.getId())));

        // 将工作流的编号，更新到 OA 借支申请单中
        loanMapper.updateById(new BpmOALoanDO().setId(loan.getId()).setProcessInstanceId(processInstanceId));

        // 判断是否为重新发起的流程
        if (vo.getProcessInstanceId() != null && vo.getResult() == 3) {
            historyProcessInstanceService.createHistoryProcessInstance(processInstanceId, vo.getProcessInstanceId());
        }
        List<UploadUserFile> fileItems = vo.getFileItems();
        //这里的逻辑，如果fileItems不为空，且有数据，那么说明是上传了附件的，则需要更工作流文件表对应的实例Id
        if (fileItems != null && !fileItems.isEmpty()) {
            uploadBpmFileProcessInstanceId(processInstanceId, fileItems);
        }
        return loan.getId();
    }

    private StaffDTO validateCreate(Long userId, BpmOALoanCreateReqVO vo) {

        StaffDTO staffDTO = staffApi.getStaffByUserId(userId).getCheckedData();
        if (staffDTO == null) {
            throw exception(OA_LOAN_NOT_CREATE);
        }

        return staffDTO;
    }

    @Override
    public void updateLoanResult(String processInstanceId, Long id, Integer result) {
        BpmOALoanDO loanDO = validateLoanExists(id);
        loanMapper.updateById(new BpmOALoanDO().setId(id).setResult(result));

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

            ProcessInstance instance = processInstanceService.getProcessInstance(processInstanceId);
            if (instance.isEnded()) {

                BpmProcessInstanceExtDO processInstance = processInstanceService.getProcessInstanceDO(processInstanceId);
                // -- 插入到财务支付表中
                CommonResult<AdminUserRespDTO> user = userApi.getUser(loanDO.getUserId());

                // 获取收款人信息
                BankRespDTO bankRespDTO = bankApi.getBank(loanDO.getBankId()).getCheckedData();

                financialPaymentService.save(new FinancialPaymentDO()
                        .setUserId(loanDO.getUserId())
                        .setDeptId(user.getData() == null ? null : user.getData().getDeptId())
                        .setProcessInstanceId(loanDO.getProcessInstanceId())
                        .setReason(loanDO.getReason())
                        .setObjectId(id)
                        .setType(7)
                        .setStatus(0)
                        .setAmountPayable(loanDO.getTotalMoney())
                        .setProcessInstanceName(processInstance.getName())
                        .setBeginTime(processInstance.getCreateTime())
                        .setEndTime(processInstance.getEndTime())
                        .setRecipientName(bankRespDTO != null ? bankRespDTO.getNickname() + "," + bankRespDTO.getBankNo() + "," + bankRespDTO.getBankName() : "")
                );
            }
        }
    }

    private BpmOALoanDO validateLoanExists(Long id) {
        BpmOALoanDO loanDO = loanMapper.selectById(id);
        if (loanDO == null) {
            throw exception(OA_LOAN_NOT_EXISTS);
        }
        return loanDO;
    }

    @Override
    public BpmOALoanDO getLoan(Long id) {

        return loanMapper.selectById(id);
    }

    @Override
    public BpmOALoanDO getByProcessInstanceId(String processInstanceId) {

        List<BpmOALoanDO> bpmOALoanDOS = loanMapper.selectList(BpmOALoanDO::getProcessInstanceId, processInstanceId);
        if (!CollectionUtil.isEmpty(bpmOALoanDOS)) {
            return bpmOALoanDOS.get(0);
        }
        return null;
    }

    @Override
    public PageResult<BpmOALoanDO> getListByStaffId(BpmOALoanPageReqVO pageReqVO) {
        return loanMapper.selectPage(pageReqVO, new LambdaQueryWrapperX<BpmOALoanDO>()
                .eq(BpmOALoanDO::getSfUserId, pageReqVO.getStaffId())
                .eq(BpmOALoanDO::getLoanType, pageReqVO.getLoanType())
                .eq(BpmOALoanDO::getResult, BpmProcessInstanceResultEnum.APPROVE.getResult()));
    }

    @Override
    public List<BpmOALoanSumDTO> getListByStaffId(Collection<Long> staffId, String month) {
        return loanMapper.selectSumByStaffId(staffId, month);
    }

    @Override
    public PageResult<BpmOAReturnVO> getReturnList(BpmOALoanPageReqVO pageReqVO) {

        // 过滤之前老数据
        if (pageReqVO.getLoanType() == null) {
            return PageResult.empty();
        }
        IPage<BpmOAReturnVO> page = loanMapper.selectReturnList(pageReqVO, MyBatisUtils.buildPage(pageReqVO));
        return new PageResult<>(page.getRecords(), page.getTotal());
    }
}
