package cn.iocoder.yudao.module.system.service.rental;

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.oa.BpmOARefundApi;
import cn.iocoder.yudao.module.infra.api.file.FileApi;
import cn.iocoder.yudao.module.system.controller.admin.rental.vo.itemsrecord.RentalItemsCountReqVO;
import cn.iocoder.yudao.module.system.controller.admin.rental.vo.rentaldepositrecord.RentalDepositAmountReqVO;
import cn.iocoder.yudao.module.system.controller.admin.rental.vo.rentaldepositrecord.RentalDepositRecordPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.rental.vo.rentaldepositrecord.RentalDepositRecordSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.rental.RentalDepositRecordDO;
import cn.iocoder.yudao.module.system.dal.dataobject.rental.RentalOrderDO;
import cn.iocoder.yudao.module.system.dal.mysql.rental.RentalDepositRecordMapper;
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.math.BigDecimal;
import java.util.List;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;

/**
 * 租赁订单押金记录 Service 实现类
 *
 * @author 符溶馨
 */
@Service
@Validated
public class RentalDepositRecordServiceImpl implements RentalDepositRecordService {

    @Resource
    private RentalDepositRecordMapper rentalDepositRecordMapper;

    @Resource
    @Lazy
    private RentalOrderService rentalOrderService;

    @Resource
    private FileApi fileApi;

    @Resource
    private BpmOARefundApi refundApi;

    @Resource
    private RentalCustomerService customerService;

    @Resource
    private RentalItemsRecordService itemsRecordService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createRentalDepositRecord(RentalDepositRecordSaveReqVO createReqVO) {

        // 获取当前订单的 剩余押金金额
        BigDecimal depositAmount = rentalOrderService.getOrderAmount(createReqVO.getOrderNo());

        // 获取当前订单的金额
        RentalDepositAmountReqVO reqVO = getRentalDepositRecordAmount(createReqVO.getOrderNo(), true);
        // 已收金额 - 退款金额
        BigDecimal amount = reqVO.getReceivedAmount().subtract(reqVO.getRefundAmount());

        // 客户剩余押金 金额
        BigDecimal customerDepositAmount = amount;
        switch (createReqVO.getType()) {
            case 1:
                // 押金收款
                // 判断实际已收的金额 + 本次收款金额 大于 当前订单的剩余押金金额时
                if (amount.add(createReqVO.getAmount()).compareTo(depositAmount) > 0) {
                    throw exception(RENTAL_RECEIVED_AMOUNT_EXCESS);
                }
                customerDepositAmount = amount.add(createReqVO.getAmount());
                break;
            case 2:
                RentalOrderDO updateOrder = new RentalOrderDO();
                // 押金退款
                // 判断当前退款金额 + 扣款金额 大于 剩余已收金额时
                BigDecimal refundAmount = createReqVO.getAmount().add(createReqVO.getChargebacksAmount() == null ? BigDecimal.ZERO : createReqVO.getChargebacksAmount());
                if (amount.compareTo(refundAmount) < 0) {
                    throw exception(RENTAL_REFUND_AMOUNT_EXCESS);
                } else if (amount.compareTo(refundAmount) == 0) { // 判断当前退款金额 是否等于 实际已收的金额 - 扣款金额时
                    // 获取租赁物品信息
                    List<RentalItemsCountReqVO> itemList = itemsRecordService.getRentalItemsCount(createReqVO.getOrderNo(), false);
                    // 判断租赁物品是否已经归还完毕
                    boolean isReturned = itemList.stream().allMatch(item -> item.getNumber() == 0);
                    if (!isReturned) {
                        throw exception(RENTAL_ITEMS_NOT_REFUND);
                    }
                    // 更新订单状态为 已退款
                    updateOrder.setOrderNo(createReqVO.getOrderNo());
                    updateOrder.setStatus(RentalOrderDO.REFUNDED);
                }else { // 判断当前退款金额 是否小于 实际已收的金额 - 扣款金额时
                    // 更新订单状态为 租赁中
                    updateOrder.setOrderNo(createReqVO.getOrderNo());
                    updateOrder.setStatus(RentalOrderDO.ON_LEASE);
                }
                customerDepositAmount = customerDepositAmount.subtract(refundAmount);
                // 同步更新 退款申请流程中的 退款状态
                refundApi.updateStatus(createReqVO.getOrderNo());
                // 同步更新 租赁订单状态
                rentalOrderService.update(updateOrder);
                break;
        }

        // 插入
        RentalDepositRecordDO rentalDepositRecord = BeanUtils.toBean(createReqVO, RentalDepositRecordDO.class);
        rentalDepositRecordMapper.insert(rentalDepositRecord);

        // 更新交易凭证附件 业务编号
        UpdateBusinessFile(rentalDepositRecord);

        // 获取客户编号
        Long customerId = rentalOrderService.getCustomerIdByOrderNo(createReqVO.getOrderNo());
        if (customerId != null) {
            // 同步更新客户剩余押金 金额
            customerService.updateRentalCustomerAmount(customerId, customerDepositAmount);
        }

        // 返回
        return rentalDepositRecord.getId();
    }

    @Override
    public void createRentalDepositRecordBatch(List<RentalDepositRecordSaveReqVO> createReqVO) {
        // 插入
        List<RentalDepositRecordDO> rentalDepositRecord = BeanUtils.toBean(createReqVO, RentalDepositRecordDO.class);
        rentalDepositRecordMapper.insertBatch(rentalDepositRecord);

        for (RentalDepositRecordDO record : rentalDepositRecord) {
            // 更新交易凭证附件 业务编号
            UpdateBusinessFile(record);
        }
    }

    private void UpdateBusinessFile(RentalDepositRecordDO updateReqVO) {

        List<String> urls = updateReqVO.getFileItems().stream().map(UploadUserFile::getUrl).collect(Collectors.toList());
        fileApi.updateBusinessFile(urls, updateReqVO.getId().toString());
    }

    @Override
    public void updateRentalDepositRecord(RentalDepositRecordSaveReqVO updateReqVO) {
        // 校验存在
        validateRentalDepositRecordExists(updateReqVO.getId());
        // 更新
        RentalDepositRecordDO updateObj = BeanUtils.toBean(updateReqVO, RentalDepositRecordDO.class);
        rentalDepositRecordMapper.updateById(updateObj);
    }

    @Override
    public void updateRentalDepositRecordType(List<RentalDepositRecordDO> updateReqVO) {

        // 更新
        rentalDepositRecordMapper.updateBatch(updateReqVO);
    }

    private void validateRentalDepositRecordExists(Long id) {
        if (rentalDepositRecordMapper.selectById(id) == null) {
            throw exception(RENTAL_DEPOSIT_RECORD_NOT_EXISTS);
        }
    }

    @Override
    public RentalDepositRecordDO getRentalDepositRecord(Long id) {
        return rentalDepositRecordMapper.selectById(id);
    }

    @Override
    public PageResult<RentalDepositRecordDO> getRentalDepositRecordPage(RentalDepositRecordPageReqVO pageReqVO) {
        return rentalDepositRecordMapper.selectPage(pageReqVO);
    }

    @Override
    public RentalDepositAmountReqVO getRentalDepositRecordAmount(String orderNo, Boolean isUpdate) {
        return rentalDepositRecordMapper.selectAmount(orderNo, isUpdate);
    }
}