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

import cn.hutool.core.collection.CollUtil;
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.module.bpm.api.oa.vo.receipt.BpmOAReceiptVO;
import cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.ReceiptSettlementDTO;
import cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.ReceiptSettlementVO;
import cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.ReceiptStatisticsDTO;
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.receipt.BpmOAReceiptCreateReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.receipt.BpmOAReceiptPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.receipt.BpmOAReceiptRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.receipt.ReceiptStatisticsVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAContractDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAReceiptDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOAContractMapper;
import cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOAReceiptMapper;
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.hrm.api.userlivetree.UserLiveTreeApi;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.OA_RECEIPT_NOT_EXISTS;

/**
 * OA 回款申请 Service 实现类
 *
 * @author 符溶馨
 */
@Service
@Validated
public class BpmOAReceiptServiceImpl extends BpmOABaseService implements BpmOAReceiptService {

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

    @Resource
    private BpmOAReceiptMapper receiptMapper;

    @Resource
    private BpmProcessInstanceApi processInstanceApi;

    @Resource
    private BpmHistoryProcessInstanceService historyProcessInstanceService;

    @Resource
    private BpmProcessInstanceService bpmProcessInstanceService;

    @Resource
    private UserLiveTreeApi userLiveTreeApi;

    @Resource
    private BpmOAContractMapper contractMapper;

    @Override
    public Long createReceipt(Long userId, BpmOAReceiptCreateReqVO createReqVO) {

        //插入OA 回款申请
        BpmOAReceiptDO receipt = BeanUtils.toBean(createReqVO, BpmOAReceiptDO.class)
                .setUserId(userId)
                .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
        receiptMapper.insert(receipt);

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

        // 将工作流的编号，更新到 OA 回款申请单中
        receiptMapper.updateById(new BpmOAReceiptDO().setId(receipt.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 receipt.getId();
    }

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

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

            ProcessInstance instance = bpmProcessInstanceService.getProcessInstance(processInstanceId);

            if (instance.isEnded()) {
                // 更新合同表的回款金额
                contractMapper.update(null, new LambdaUpdateWrapper<BpmOAContractDO>()
                        .setSql("return_money = return_money + " + receiptDO.getMoney())
                        .eq(BpmOAContractDO::getId, receiptDO.getContractId()));
            }
        }

        receiptMapper.updateById(new BpmOAReceiptDO().setId(id).setResult(result));
    }

    private BpmOAReceiptDO validateLeaveExists(Long id) {
        BpmOAReceiptDO receiptDO = receiptMapper.selectById(id);
        if (receiptDO == null) {
            throw exception(OA_RECEIPT_NOT_EXISTS);
        }
        return receiptDO;
    }

    @Override
    public BpmOAReceiptDO getReceipt(Long id) {

        return receiptMapper.selectById(id);
    }

    @Override
    public BpmOAReceiptDO getByProcessInstanceId(String processInstanceId) {

        return receiptMapper.selectOne(BpmOAReceiptDO::getProcessInstanceId, processInstanceId);
    }

    @Override
    public PageResult<BpmOAReceiptRespVO> getReceiptPage(BpmOAReceiptPageReqVO pageReqVO) {

        List<Long> userIds = new ArrayList<>();
        if ("my".equals(pageReqVO.getRelation())) {
            userIds.add(getLoginUserId());
        } else if ("sub".equals(pageReqVO.getRelation())) {
            // 查询当前用户 所有下级用户编号
            userIds.addAll(userLiveTreeApi.getItemIdsByUserId(getLoginUserId()).getCheckedData());
        }
        if (CollUtil.isEmpty(userIds)) {
            return PageResult.empty();
        }
        return receiptMapper.selectReceiptPage(pageReqVO, userIds);
    }

    @Override
    public ReceiptStatisticsVO getStatistics(Long userId, String relation) {
        ReceiptStatisticsVO respVO = receiptMapper.selectStatistics(userId, relation);

        // 设置数量、金额百分比
        respVO.setMoneyPercentage(calculatePercentageChange(respVO.getTodayAddMoney(), respVO.getYesterdayAddMoney()));
        return respVO;
    }

    @Override
    public List<BpmOAReceiptDO> getReceiptList(BpmOAReceiptVO respVO) {
        if (CollUtil.isEmpty(respVO.getUserId())) {
            return Collections.emptyList();
        }
        return receiptMapper.selectList(new LambdaQueryWrapperX<BpmOAReceiptDO>()
                .inIfPresent(BpmOAReceiptDO::getUserId, respVO.getUserId())
                .eq(BpmOAReceiptDO::getResult, BpmProcessInstanceResultEnum.APPROVE.getResult())
                .betweenIfPresent(BpmOAReceiptDO::getReturnTime, respVO.getStarTime(), respVO.getEndTime()));
    }

    @Override
    public List<ReceiptStatisticsDTO> getReceiptStatisticsListByUserIds(List<Long> userIds, LocalDateTime[] createTime) {

        return receiptMapper.selectReceiptStatisticsListByUserIds(userIds, createTime);
    }

    @Override
    public List<ReceiptSettlementDTO> getReceiptSettlement(ReceiptSettlementVO vo) {
        return receiptMapper.getReceiptSettlement(vo);
    }

    public static BigDecimal calculatePercentageChange(Object today, Object yesterday) {

        // 转换为 BigDecimal 进行高精度计算
        BigDecimal bigToday = new BigDecimal(today.toString());
        BigDecimal bigYesterday = new BigDecimal(yesterday.toString());

        if (bigYesterday.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }

        // 计算百分比变化
        BigDecimal difference = bigToday.subtract(bigYesterday);  // a - b
        BigDecimal percentageChange = difference.divide(bigYesterday, 2, RoundingMode.HALF_UP); // (a - b) / b

        // 转为百分比并返回
        return percentageChange.multiply(new BigDecimal(100));  // (result) * 100
    }
}
