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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.iocoder.yudao.framework.common.enums.DeptTypeEnum;
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.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.payment.*;
import cn.iocoder.yudao.module.bpm.dal.dataobject.financialpayment.FinancialPaymentDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAPaymentDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOAPaymentMapper;
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.system.api.bank.BankApi;
import cn.iocoder.yudao.module.system.api.bank.dto.BankRespDTO;
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.project.ProjectApi;
import cn.iocoder.yudao.module.system.api.project.dto.ProjectDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import org.apache.commons.lang3.StringUtils;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.stereotype.Service;
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.convertList;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.OA_PAYMENT_FILES_NOT_NULL;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.OA_PAYMENT_NOT_EXISTS;

/**
 * OA 付款申请 Service 实现类
 *
 * @author 符溶馨
 */
@Service
@Validated
public class BpmOAPaymentServiceImpl extends BpmOABaseService implements BpmOAPaymentService{

    /**
     * OA 付款对应的流程定义 KEY
     */
    public static final String PROCESS_KEY = "oa_payment_2";

    @Resource
    private BpmOAPaymentMapper paymentMapper;

    @Resource
    private BpmProcessInstanceService processInstanceService;

    @Resource
    private BpmHistoryProcessInstanceService historyProcessInstanceService;

    @Resource
    private FinancialPaymentService financialPaymentService;

    @Resource
    private AdminUserApi userApi;

    @Resource
    private DeptApi deptApi;

    @Resource
    private BankApi bankApi;

    @Resource
    private ProjectApi projectApi;

    @Override
    public Long createPayment(Long userId, BpmOAPaymentCreateReqVO createReqVO) {

        //插入OA 付款申请
        BpmOAPaymentDO paymentDO = BeanUtils.toBean(createReqVO, BpmOAPaymentDO.class).setUserId(userId)
                .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
        if (createReqVO.getIsTickets() == 1) {
            paymentDO.setStatus(0); // 设置补票状态 为待补票
        }
        paymentMapper.insert(paymentDO);

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

        // 将工作流的编号，更新到 OA 付款申请单中
        paymentMapper.updateById(new BpmOAPaymentDO().setId(paymentDO.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) ;
        }
        return paymentDO.getId();
    }

    @Override
    public void updatePaymentResult(String processInstanceId, Long id, Integer result) {

        BpmOAPaymentDO paymentDO = validateLeaveExists(id);
        //审核通过 （最后节点）
        if (BpmProcessInstanceResultEnum.APPROVE.getResult().equals(result)) {

            ProcessInstance instance = processInstanceService.getProcessInstance(processInstanceId);

            if (instance.isEnded()) {

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

                // 从缓存中部门所属公司信息
                DeptRespDTO deptRespDTO = deptApi.getDept(paymentDO.getPaymentCompany()).getCheckedData();

                // 获取收款人信息
                BankRespDTO bankRespDTO = new BankRespDTO();
                if (paymentDO.getBankId() != null) {
                    bankRespDTO = bankApi.getBank(paymentDO.getBankId()).getCheckedData();
                }

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

        paymentMapper.updateById(new BpmOAPaymentDO().setId(id).setResult(result));
    }

    @Override
    public BpmOAPaymentDO getPayment(Long id) {

        return paymentMapper.selectById(id);
    }

    @Override
    public BpmOAPaymentDO getByProcessInstanceId(String processInstanceId) {

        return paymentMapper.selectOne(BpmOAPaymentDO::getProcessInstanceId, processInstanceId);
    }

    @Override
    public List<BpmOAPaymentRespVO> getPaymentList(Integer type) {

        int method = 0;
        // 获取发起人的部门信息
        AdminUserRespDTO userDTO = userApi.getUser(getLoginUserId()).getCheckedData();
        DeptRespDTO deptDTO = deptApi.getDept(userDTO.getDeptId()).getCheckedData();

        // 判断财务部门时，查询全部分批记录
        if (deptDTO != null && deptDTO.getType().equals(DeptTypeEnum.FINANCE_DEPT.getValue())) {
            method = 1;
        }

        return paymentMapper.selectPaymentList(type, userDTO.getDeptId(), method);
    }

    @Override
    public PageResult<BpmOAPaymentDO> getPaymentPage(BpmOAPaymentPageReqVO pageReqVO, Long userId) {

        return paymentMapper.selectTicketsPage(pageReqVO, userId);
    }

    @Override
    public void updateFiles(BpmOAPaymentUpdateReqVO updateReqVO) {
        // 校验付款申请是否存在
        BpmOAPaymentDO paymentDO = validateLeaveExists(updateReqVO.getId());

        //这里的逻辑，如果fileItems不为空，且有数据，那么说明是上传了附件的，则需要更工作流文件表对应的实例Id
        if (CollUtil.isNotEmpty(updateReqVO.getFileItems())) {
            uploadBpmFileProcessInstanceId(paymentDO.getProcessInstanceId(), updateReqVO.getFileItems()) ;
        }else {
            throw exception(OA_PAYMENT_FILES_NOT_NULL);
        }

        // 更新附件
        paymentMapper.updateById(new BpmOAPaymentDO()
                .setId(paymentDO.getId())
                .setLaterFileItems(updateReqVO.getFileItems())
                .setStatus(updateReqVO.getStatus()));
    }

    @Override
    public void updateStatus(BpmOAPaymentUpdateReqVO updateReqVO) {
        // 校验付款申请是否存在
        BpmOAPaymentDO paymentDO = validateLeaveExists(updateReqVO.getId());

        BpmOAPaymentDO update = BeanUtils.toBean(updateReqVO, BpmOAPaymentDO.class);
        if (update.getStatus() == 3) {
            List<UploadUserFile> fileItems = paymentDO.getFileItems() == null ? new ArrayList<>() : paymentDO.getFileItems();
            fileItems.addAll(paymentDO.getLaterFileItems());
            update.setFileItems(fileItems);
        }

        paymentMapper.updateById(update);
    }

    @Override
    public void deleteFiles(Long id, String url) {

        // 校验付款申请是否存在
        BpmOAPaymentDO paymentDO = validateLeaveExists(id);

        List<UploadUserFile> fileItems = paymentDO.getLaterFileItems();
        if (CollUtil.isNotEmpty(fileItems)) {
            fileItems.removeIf(item -> item.getUrl().equals(url));

            paymentMapper.updateById(new BpmOAPaymentDO()
                    .setId(id)
                    .setLaterFileItems(fileItems));
        }
    }

    @Override
    public BpmOAPaymentRespVO convertPayment(BpmOAPaymentDO payment) {

        BpmOAPaymentRespVO respVO = BeanUtils.toBean(payment, BpmOAPaymentRespVO.class);
        if (respVO != null) {

            // 获取部门信息Map
            List<Long> deptIds = new ArrayList<>();
            if (CollectionUtil.isNotEmpty(respVO.getFactoryDetail())) {
                deptIds.addAll(convertList(respVO.getFactoryDetail(), FactoryDetailVO::getDeptId));
            }else {
                deptIds.add(payment.getDeptId());
            }
            deptIds.add(payment.getPaymentCompany());
            Map<Long, DeptRespDTO> dtoMap = deptApi.getDeptMap(new HashSet<>(deptIds));

            // 设置部门名称
            respVO.setDeptName(dtoMap.get(payment.getDeptId()) != null ? dtoMap.get(payment.getDeptId()).getName() : null);
            // 设置付款公司名称
            respVO.setPaymentCompanyName(dtoMap.get(payment.getPaymentCompany()).getName());
            // 设置工厂名称
            respVO.getFactoryDetail().forEach(item -> {
                item.setFactoryName(dtoMap.get(item.getDeptId()).getName());
            });

            if (StringUtils.isNotEmpty(payment.getProjectNo())) {
                // 获取项目信息
                ProjectDTO project = projectApi.getProject(payment.getProjectNo()).getCheckedData();
                // 设置项目名称
                respVO.setProjectName(project.getName());
            }

            if (payment.getBankId() != null) {
                // 获取银行卡信息
                BankRespDTO bankResp = bankApi.getBank(payment.getBankId()).getCheckedData();
                // 设置银行卡信息
                respVO.setBankName(bankResp != null ?  bankResp.getBankName() : null);
                respVO.setBankNo(bankResp != null ? bankResp.getBankNo() :  null);
                respVO.setNickname(bankResp != null ? bankResp.getNickname() :  null);
            }
        }
        return respVO;
    }

    private BpmOAPaymentDO validateLeaveExists(Long id) {
        BpmOAPaymentDO paymentDO = paymentMapper.selectById(id);
        if (paymentDO == null) {
            throw exception(OA_PAYMENT_NOT_EXISTS);
        }
        return paymentDO;
    }
}
