package cn.iocoder.foodnexus.module.erp.service.sale;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.foodnexus.framework.common.exception.ServiceException;
import cn.iocoder.foodnexus.framework.common.pojo.PageResult;
import cn.iocoder.foodnexus.framework.common.util.CommonUtil;
import cn.iocoder.foodnexus.framework.common.util.number.MoneyUtils;
import cn.iocoder.foodnexus.framework.common.util.object.BeanUtils;
import cn.iocoder.foodnexus.framework.redis.utils.RedisUtils;
import cn.iocoder.foodnexus.module.erp.dal.dataobject.purchase.ErpPurchaseReturnDO;
import cn.iocoder.foodnexus.module.erp.dal.dataobject.purchase.ErpPurchaseReturnItemDO;
import cn.iocoder.foodnexus.module.erp.dal.dataobject.sale.ErpSaleOutDO;
import cn.iocoder.foodnexus.module.erp.dal.mysql.purchase.ErpPurchaseReturnMapper;
import cn.iocoder.foodnexus.module.erp.dal.mysql.sale.ErpSaleOrderMapper;
import cn.iocoder.foodnexus.module.erp.enums.SaleOrderPickUpStatus;
import cn.iocoder.foodnexus.module.erp.controller.admin.purchase.vo.returns.ErpPurchaseReturnSaveReqVO;
import cn.iocoder.foodnexus.module.erp.controller.admin.sale.vo.returns.ErpSaleReturnPageReqVO;
import cn.iocoder.foodnexus.module.erp.controller.admin.sale.vo.returns.ErpSaleReturnSaveReqVO;
import cn.iocoder.foodnexus.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO;
import cn.iocoder.foodnexus.module.erp.dal.dataobject.purchase.ErpPurchaseOrderItemDO;
import cn.iocoder.foodnexus.module.erp.dal.dataobject.sale.ErpSaleOrderDO;
import cn.iocoder.foodnexus.module.erp.dal.dataobject.sale.ErpSaleReturnDO;
import cn.iocoder.foodnexus.module.erp.dal.dataobject.sale.ErpSaleReturnItemDO;
import cn.iocoder.foodnexus.module.erp.dal.mysql.purchase.ErpPurchaseOrderItemMapper;
import cn.iocoder.foodnexus.module.erp.dal.mysql.purchase.ErpPurchaseOrderMapper;
import cn.iocoder.foodnexus.module.erp.dal.mysql.sale.ErpSaleReturnItemMapper;
import cn.iocoder.foodnexus.module.erp.dal.mysql.sale.ErpSaleReturnMapper;
import cn.iocoder.foodnexus.module.erp.dal.redis.no.ErpNoRedisDAO;
import cn.iocoder.foodnexus.module.erp.api.enums.ErpAuditStatus;
import cn.iocoder.foodnexus.module.erp.enums.stock.ErpStockRecordBizTypeEnum;
import cn.iocoder.foodnexus.module.erp.service.finance.ErpAccountService;
import cn.iocoder.foodnexus.module.erp.service.purchase.ErpPurchaseReturnService;
import cn.iocoder.foodnexus.module.erp.service.stock.ErpStockRecordService;
import cn.iocoder.foodnexus.module.erp.service.stock.bo.ErpStockRecordCreateReqBO;
import cn.iocoder.foodnexus.module.order.api.CustomerOrderApi;
import cn.iocoder.foodnexus.module.order.dto.CustomerOrderDTO;
import cn.iocoder.foodnexus.module.order.dto.CustomerOrderItemDTO;
import cn.iocoder.foodnexus.module.order.dto.DeliveryOrderUpdateReqVO;
import cn.iocoder.foodnexus.module.product.dal.dataobject.spu.ProductSpuDO;
import cn.iocoder.foodnexus.module.product.service.spu.ProductSpuService;
import cn.iocoder.foodnexus.module.system.api.user.AdminUserApi;
import cn.iocoder.foodnexus.module.system.controller.admin.vo.AuditCommonReqVO;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;

import static cn.iocoder.foodnexus.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.foodnexus.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.foodnexus.module.erp.enums.ErrorCodeConstants.*;
import static cn.iocoder.foodnexus.module.erp.enums.SaleOrderPickUpStatus.*;
import static cn.iocoder.foodnexus.module.erp.service.sale.ErpSaleOrderService.SALE_ORDER_LOCK;

// TODO 芋艿：记录操作日志

/**
 * ERP 销售退货 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Validated
public class ErpSaleReturnServiceImpl implements ErpSaleReturnService {

    @Resource
    private ErpSaleReturnMapper saleReturnMapper;
    @Resource
    private ErpSaleReturnItemMapper saleReturnItemMapper;

    @Resource
    private ErpNoRedisDAO noRedisDAO;

    @Resource
    private ProductSpuService productService;
    @Resource
    @Lazy // 延迟加载，避免循环依赖
    private ErpSaleOrderService saleOrderService;
    @Autowired
    private ErpSaleOrderMapper saleOrderMapper;
    @Resource
    private ErpAccountService accountService;
    @Resource
    private ErpStockRecordService stockRecordService;

    @Resource
    private AdminUserApi adminUserApi;

    @Autowired
    private ErpPurchaseReturnService purchaseReturnService;

    @Autowired
    private ErpPurchaseReturnMapper purchaseReturnMapper;

    @Autowired
    private ErpPurchaseOrderMapper purchaseOrderMapper;

    @Autowired
    private ErpPurchaseOrderItemMapper purchaseOrderItemMapper;

    @Autowired
    private CustomerOrderApi customerOrderApi;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createSaleReturn(ErpSaleReturnSaveReqVO createReqVO) {
        // 1.1 校验销售订单已审核
        ErpSaleOrderDO saleOrder = saleOrderService.validateSaleOrder(createReqVO.getOrderId());
        // 1.2 校验退货项的有效性
        List<ErpSaleReturnItemDO> saleReturnItems = validateSaleReturnItems(createReqVO.getItems());
        /*// 1.3 校验结算账户
        accountService.validateAccount(createReqVO.getAccountId());
        // 1.4 校验销售人员
        if (createReqVO.getSaleUserId() != null) {
            adminUserApi.validateUser(createReqVO.getSaleUserId());
        }*/
        // 1.5 生成退货单号，并校验唯一性
        String no = noRedisDAO.generate(ErpNoRedisDAO.SALE_RETURN_NO_PREFIX);
        if (saleReturnMapper.selectByNo(no) != null) {
            throw exception(SALE_RETURN_NO_EXISTS);
        }

        // 2.1 插入退货
        ErpSaleReturnDO saleReturn = BeanUtils.toBean(createReqVO, ErpSaleReturnDO.class, in -> in
                .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus()))
                .setReturnsStatus(SaleOrderPickUpStatus.RETURNS_PROCESS.getType())
                .setOrderNo(saleOrder.getNo()).setCustomerId(saleOrder.getCustomerId());
        calculateTotalPrice(saleReturn, saleReturnItems);
        saleReturnMapper.insert(saleReturn);
        // 2.2 插入退货项
        saleReturnItems.forEach(o -> o.setReturnId(saleReturn.getId()));
        saleReturnItemMapper.insertBatch(saleReturnItems);

        // 3. 更新销售订单的退货数量
        updateSaleOrderReturnCount(createReqVO.getOrderId());
        return saleReturn.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateSaleReturn(ErpSaleReturnSaveReqVO updateReqVO) {
        // 1.1 校验存在
        ErpSaleReturnDO saleReturn = validateSaleReturnExists(updateReqVO.getId());
        if (ErpAuditStatus.APPROVE.getStatus().equals(saleReturn.getStatus())) {
            throw exception(SALE_RETURN_UPDATE_FAIL_APPROVE, saleReturn.getNo());
        }
        // 1.2 校验销售订单已审核
        ErpSaleOrderDO saleOrder = saleOrderService.validateSaleOrder(updateReqVO.getOrderId());
        // 1.3 校验结算账户
        accountService.validateAccount(updateReqVO.getAccountId());
        // 1.4 校验销售人员
        if (updateReqVO.getSaleUserId() != null) {
            adminUserApi.validateUser(updateReqVO.getSaleUserId());
        }
        // 1.5 校验订单项的有效性
        List<ErpSaleReturnItemDO> saleReturnItems = validateSaleReturnItems(updateReqVO.getItems());

        // 2.1 更新退货
        ErpSaleReturnDO updateObj = BeanUtils.toBean(updateReqVO, ErpSaleReturnDO.class)
                .setOrderNo(saleOrder.getNo()).setCustomerId(saleOrder.getCustomerId());
        calculateTotalPrice(updateObj, saleReturnItems);
        saleReturnMapper.updateById(updateObj);
        // 2.2 更新退货项
        updateSaleReturnItemList(updateReqVO.getId(), saleReturnItems);

        // 3.1 更新销售订单的出库数量
        updateSaleOrderReturnCount(updateObj.getOrderId());
        // 3.2 注意：如果销售订单编号变更了，需要更新“老”销售订单的出库数量
        if (ObjectUtil.notEqual(saleReturn.getOrderId(), updateObj.getOrderId())) {
            updateSaleOrderReturnCount(saleReturn.getOrderId());
        }
    }

    private void calculateTotalPrice(ErpSaleReturnDO saleReturn, List<ErpSaleReturnItemDO> saleReturnItems) {
        saleReturn.setTotalCount(getSumValue(saleReturnItems, ErpSaleReturnItemDO::getCount, BigDecimal::add));
        saleReturn.setTotalProductPrice(getSumValue(saleReturnItems, ErpSaleReturnItemDO::getTotalPrice, Integer::sum, 0));
        saleReturn.setTotalTaxPrice(getSumValue(saleReturnItems, ErpSaleReturnItemDO::getTaxPrice, BigDecimal::add, BigDecimal.ZERO));
        saleReturn.setTotalPrice(saleReturn.getTotalProductPrice() /*+ (saleReturn.getTotalTaxPrice())*/);
        // 计算优惠价格
        if (saleReturn.getDiscountPercent() == null) {
            saleReturn.setDiscountPercent(BigDecimal.ZERO);
        }
        // saleReturn.setDiscountPrice(MoneyUtils.priceMultiplyPercent(saleReturn.getTotalPrice(), saleReturn.getDiscountPercent()));
        saleReturn.setTotalPrice(saleReturn.getTotalPrice()/*.subtract(saleReturn.getDiscountPrice().add(saleReturn.getOtherPrice()))*/);
    }

    private void updateSaleOrderReturnCount(Long orderId) {
        // 1.1 查询销售订单对应的销售出库单列表
        List<ErpSaleReturnDO> saleReturns = saleReturnMapper.selectListByOrderId(orderId);
        // 1.2 查询对应的销售订单项的退货数量
        Map<Long, BigDecimal> returnCountMap = saleReturnItemMapper.selectOrderItemCountSumMapByReturnIds(
                convertList(saleReturns, ErpSaleReturnDO::getId));
        // 2. 更新销售订单的出库数量
        saleOrderService.updateSaleOrderReturnCount(orderId, returnCountMap);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateSaleReturnStatus(AuditCommonReqVO reqVO) {
        Integer status = Integer.parseInt(reqVO.getAuditStatus());
        Long id = reqVO.getId();
        boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status);
        // 1.1 校验存在
        ErpSaleReturnDO saleReturn = validateSaleReturnExists(id);
        // 1.2 校验状态
        if (saleReturn.getStatus().equals(status)) {
            throw exception(approve ? SALE_RETURN_APPROVE_FAIL : SALE_RETURN_PROCESS_FAIL);
        }
        // 1.3 校验已退款
        if (!approve && saleReturn.getRefundPrice().compareTo(BigDecimal.ZERO) > 0) {
            throw exception(SALE_RETURN_PROCESS_FAIL_EXISTS_REFUND);
        }

        // 2. 更新状态
        int updateCount = saleReturnMapper.updateByIdAndStatus(id, saleReturn.getStatus(),
                new ErpSaleReturnDO().setStatus(status).setAuditReason(reqVO.getAuditReason()).setAuditTime(LocalDateTime.now()).setReturnsStatus(approve ? RETURNS_APPROVE.getType() : RETURNS_REJECT.getType()));
        if (updateCount == 0) {
            throw exception(approve ? SALE_RETURN_APPROVE_FAIL : SALE_RETURN_PROCESS_FAIL);
        }

        // 3. 变更库存
        List<ErpSaleReturnItemDO> saleReturnItems = saleReturnItemMapper.selectListByReturnId(id);

        // 更新客户订单退款字段
        List<CustomerOrderItemDTO> customerORderItems = customerOrderApi.queryItemsByOrderId(saleReturn.getCustomerOrderId());
        Map<Long, CustomerOrderItemDTO> customerOrderItemMap = CommonUtil.listConvertMap(customerORderItems, CustomerOrderItemDTO::getId);

        List<CustomerOrderItemDTO> updateItems = new ArrayList<>();
        Integer returnsTotal = 0;
        for (ErpSaleReturnItemDO item : saleReturnItems) {
            Long orderItemId = item.getCustomerOrderItemId();
            BigDecimal returnsCount = item.getCount();
            if (!customerOrderItemMap.containsKey(orderItemId)) {
                throw exception(SALERETURN_ERROR_ORDER_ITEM);
            }
            CustomerOrderItemDTO orderItem = customerOrderItemMap.get(orderItemId);
            CustomerOrderItemDTO updateItem = new CustomerOrderItemDTO();
            updateItem.setId(orderItem.getId());
            updateItem.setReturnsQuantity(returnsCount);
            updateItem.setReturnsTotal(MoneyUtils.multiplyAndRoundToInt(returnsCount, orderItem.getOrderItemPrice()));
            if (orderItem.getSignedQuantity().compareTo(CommonUtil.getEls(updateItem.getReturnsQuantity(), BigDecimal.ZERO)) < 0) {
                throw exception(SALE_RETURN_COUNT_ERROR, orderItem.getProductName());
            }
            /*updateItem.setSignedQuantity(orderItem.getSignedQuantity() - returnsCount);
            updateItem.setSignedTotal(orderItem.getSignedTotal() - updateItem.getReturnsTotal());*/
            updateItems.add(updateItem);

            returnsTotal += updateItem.getReturnsTotal();
        }

        customerOrderApi.updateActualAmount(saleReturn.getCustomerOrderId(), - returnsTotal);
        customerOrderApi.updateItems(updateItems);

        // 销售订单状态变更
        ErpSaleOrderDO updateSaleOrder = new ErpSaleOrderDO();
        updateSaleOrder.setId(saleReturn.getOrderId());
        updateSaleOrder.setPickUpStatus(approve ? RETURNS_APPROVE : RETURNS_REJECT);
        saleOrderService.update(updateSaleOrder, null);

        // TODO 退款确认后才进行库存变更
        /*Integer bizType = approve ? ErpStockRecordBizTypeEnum.SALE_RETURN.getType()
                : ErpStockRecordBizTypeEnum.SALE_RETURN_CANCEL.getType();
        saleReturnItems.forEach(saleReturnItem -> {
            Integer count = approve ? saleReturnItem.getCount() : - saleReturnItem.getCount();
            stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO(
                    saleReturnItem.getProductId(), saleReturnItem.getWarehouseId(), BigDecimal.valueOf(count),
                    bizType, saleReturnItem.getReturnId(), saleReturnItem.getId(), saleReturn.getNo()));
        });*/

        // 将订单根据供应商拆分成采购退货单
        Map<Long, List<ErpSaleReturnItemDO>> returnItemMap = CommonUtil.listConvertListMap(saleReturnItems, ErpSaleReturnItemDO::getSupplierId);
        returnItemMap.forEach((supplierId, saleReturnsItems) -> {
            List<ErpPurchaseReturnSaveReqVO.Item> purchaseReturnitems = new ArrayList<>();
            ErpPurchaseReturnSaveReqVO purchaseReturnSave = new ErpPurchaseReturnSaveReqVO();
            purchaseReturnSave.setReturnTime(saleReturn.getReturnTime());
            purchaseReturnSave.setSupplierId(supplierId);
            ErpPurchaseOrderDO purchaseOrder = purchaseOrderMapper.selectOne(Wrappers.<ErpPurchaseOrderDO>lambdaQuery()
                    .eq(ErpPurchaseOrderDO::getCustomerOrderId, saleReturn.getCustomerOrderId())
                    .eq(ErpPurchaseOrderDO::getSupplierId, supplierId)
                    .last("LIMIT 1"));
            Optional.ofNullable(purchaseOrder).orElseThrow(() -> new ServiceException(PURCHASE_ORDER_NOT_EXISTS));
            List<ErpPurchaseOrderItemDO> purchaseOrderItems = purchaseOrderItemMapper.selectListByOrderId(purchaseOrder.getId());
            Optional.ofNullable(purchaseOrderItems).orElseThrow(() -> new ServiceException(PURCHASE_ORDER_NOT_EXISTS));
            purchaseReturnSave.setOrderId(purchaseOrder.getId());
            purchaseReturnSave.setFilesUrl(saleReturn.getFilesUrl());
            purchaseReturnSave.setRemark(saleReturn.getRemark());
            purchaseReturnSave.setReturnsStatus(approve ? RETURNS_APPROVE.getType() : RETURNS_REJECT.getType());
            purchaseReturnSave.setAddressId(saleReturn.getAddressId());
            purchaseReturnSave.setAddressInfo(saleReturn.getAddressInfo());
            purchaseReturnSave.setWarehouseId(saleReturn.getWarehouseId());
            purchaseReturnSave.setWarehouseAreaId(saleReturn.getWarehouseAreaId());
            purchaseReturnSave.setWarehouseInfo(saleReturn.getWarehouseInfo());
            purchaseReturnSave.setDeliveryStaffId(saleReturn.getDeliveryStaffId());
            purchaseReturnSave.setCustomerOrderId(saleReturn.getCustomerOrderId());
            purchaseReturnSave.setSaleOrderId(saleReturn.getOrderId());
            purchaseReturnSave.setCustomerId(saleReturn.getCustomerId());
            Map<Long, ErpPurchaseOrderItemDO> purchaseOrderMap = CommonUtil.listConvertMap(purchaseOrderItems, ErpPurchaseOrderItemDO::getCustomerOrderItemId);
            for (ErpSaleReturnItemDO saleReturnsItem : saleReturnsItems) {
                ErpPurchaseReturnSaveReqVO.Item item = new ErpPurchaseReturnSaveReqVO.Item();
                if (!purchaseOrderMap.containsKey(saleReturnsItem.getCustomerOrderItemId())) {
                    throw exception(PURCHASE_ORDER_NOT_EXISTS);
                }
                item.setOrderItemId(purchaseOrderMap.get(saleReturnsItem.getCustomerOrderItemId()).getId());
                item.setCustomerOrderItemId(saleReturnsItem.getCustomerOrderItemId());
                item.setWarehouseId(saleReturnsItem.getWarehouseId());
                item.setProductId(saleReturnsItem.getProductId());
                item.setProductUnit(saleReturnsItem.getProductUnit());
                item.setProductPrice(saleReturnsItem.getProductPrice());
                item.setCount(saleReturnsItem.getCount());
                item.setRemark(saleReturnsItem.getRemark());
                item.setProductInfo(saleReturnsItem.getProductInfo());
                purchaseReturnitems.add(item);
            }
            purchaseReturnSave.setItems(purchaseReturnitems);
            Long purchaseReturnOrderId = purchaseReturnService.createPurchaseReturn(purchaseReturnSave);
            saleReturnItemMapper.update(Wrappers.<ErpSaleReturnItemDO>lambdaUpdate()
                    .eq(ErpSaleReturnItemDO::getReturnId, saleReturn.getId())
                    .eq(ErpSaleReturnItemDO::getSupplierId, supplierId)
                    .set(ErpSaleReturnItemDO::getPurchaseReturnOrderId, purchaseReturnOrderId));
        });

    }

    @Override
    public void updateSaleReturnRefundPrice(Long id, BigDecimal refundPrice) {
        ErpSaleReturnDO saleReturn = saleReturnMapper.selectById(id);
        if (saleReturn.getRefundPrice().equals(refundPrice)) {
            return;
        }
        if (refundPrice.compareTo(BigDecimal.valueOf(saleReturn.getTotalPrice())) > 0) {
            throw exception(SALE_RETURN_FAIL_REFUND_PRICE_EXCEED, refundPrice, saleReturn.getTotalPrice());
        }
        saleReturnMapper.updateById(new ErpSaleReturnDO().setId(id).setRefundPrice(refundPrice));
    }

    private List<ErpSaleReturnItemDO> validateSaleReturnItems(List<ErpSaleReturnSaveReqVO.Item> list) {
        // 1. 校验产品存在
        List<ProductSpuDO> productList = productService.getSpuList(
                convertSet(list, ErpSaleReturnSaveReqVO.Item::getProductId));
        Map<Long, ProductSpuDO> productMap = convertMap(productList, ProductSpuDO::getId);
        // 2. 转化为 ErpSaleReturnItemDO 列表
        return convertList(list, o -> BeanUtils.toBean(o, ErpSaleReturnItemDO.class, item -> {
            item.setProductUnit(productMap.get(item.getProductId()).getUnitName());
            item.setTotalPrice(MoneyUtils.multiplyAndRoundToInt(item.getCount(), item.getProductPrice()));
            /*if (item.getTotalPrice() == null) {
                return;
            }*/
            /*if (item.getTaxPercent() != null) {
                item.setTaxPrice(MoneyUtils.priceMultiplyPercent(item.getTotalPrice(), item.getTaxPercent()));
            }*/
        }));
    }

    private void updateSaleReturnItemList(Long id, List<ErpSaleReturnItemDO> newList) {
        // 第一步，对比新老数据，获得添加、修改、删除的列表
        List<ErpSaleReturnItemDO> oldList = saleReturnItemMapper.selectListByReturnId(id);
        List<List<ErpSaleReturnItemDO>> diffList = diffList(oldList, newList, // id 不同，就认为是不同的记录
                (oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));

        // 第二步，批量添加、修改、删除
        if (CollUtil.isNotEmpty(diffList.get(0))) {
            diffList.get(0).forEach(o -> o.setReturnId(id));
            saleReturnItemMapper.insertBatch(diffList.get(0));
        }
        if (CollUtil.isNotEmpty(diffList.get(1))) {
            saleReturnItemMapper.updateBatch(diffList.get(1));
        }
        if (CollUtil.isNotEmpty(diffList.get(2))) {
            saleReturnItemMapper.deleteByIds(convertList(diffList.get(2), ErpSaleReturnItemDO::getId));
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteSaleReturn(List<Long> ids) {
        // 1. 校验不处于已审批
        List<ErpSaleReturnDO> saleReturns = saleReturnMapper.selectByIds(ids);
        if (CollUtil.isEmpty(saleReturns)) {
            return;
        }
        saleReturns.forEach(saleReturn -> {
            if (ErpAuditStatus.APPROVE.getStatus().equals(saleReturn.getStatus())) {
                throw exception(SALE_RETURN_DELETE_FAIL_APPROVE, saleReturn.getNo());
            }
        });

        // 2. 遍历删除，并记录操作日志
        saleReturns.forEach(saleReturn -> {
            // 2.1 删除订单
            saleReturnMapper.deleteById(saleReturn.getId());
            // 2.2 删除订单项
            saleReturnItemMapper.deleteByReturnId(saleReturn.getId());

            // 2.3 更新销售订单的出库数量
            updateSaleOrderReturnCount(saleReturn.getOrderId());
        });

    }

    private ErpSaleReturnDO validateSaleReturnExists(Long id) {
        ErpSaleReturnDO saleReturn = saleReturnMapper.selectById(id);
        if (saleReturn == null) {
            throw exception(SALE_RETURN_NOT_EXISTS);
        }
        return saleReturn;
    }

    @Override
    public ErpSaleReturnDO getSaleReturn(Long id) {
        return saleReturnMapper.selectById(id);
    }

    @Override
    public ErpSaleReturnDO validateSaleReturn(Long id) {
        ErpSaleReturnDO saleReturn = validateSaleReturnExists(id);
        if (ObjectUtil.notEqual(saleReturn.getStatus(), ErpAuditStatus.APPROVE.getStatus())) {
            throw exception(SALE_RETURN_NOT_APPROVE);
        }
        return saleReturn;
    }

    @Override
    public ErpSaleReturnDO validateBySaleOrderId(Long saleOrderId) {
        ErpSaleReturnDO saleReturn = saleReturnMapper.selectOne(Wrappers.<ErpSaleReturnDO>lambdaQuery()
                .eq(ErpSaleReturnDO::getOrderId, saleOrderId)
                .last("LIMIT 1"));
        if (saleReturn == null) {
            throw exception(SALE_RETURN_NOT_EXISTS);
        }
        if (ObjectUtil.notEqual(saleReturn.getStatus(), ErpAuditStatus.APPROVE.getStatus())) {
            throw exception(SALE_RETURN_NOT_APPROVE);
        }
        return saleReturn;
    }

    @Override
    public PageResult<ErpSaleReturnDO> getSaleReturnPage(ErpSaleReturnPageReqVO pageReqVO) {
        return saleReturnMapper.selectPage(pageReqVO);
    }

    // ==================== 销售退货项 ====================

    @Override
    public List<ErpSaleReturnItemDO> getSaleReturnItemListByReturnId(Long returnId) {
        return saleReturnItemMapper.selectListByReturnId(returnId);
    }

    @Override
    public List<ErpSaleReturnItemDO> getSaleReturnItemListByReturnIds(Collection<Long> returnIds) {
        if (CollUtil.isEmpty(returnIds)) {
            return Collections.emptyList();
        }
        return saleReturnItemMapper.selectListByReturnIds(returnIds);
    }

    /**
     * 根据客户订单id查询
     *
     * @param customerOrderId
     * @return
     */
    @Override
    public ErpSaleReturnDO queryByCustomerOrderId(Long customerOrderId) {
        return saleReturnMapper.selectOne(Wrappers.<ErpSaleReturnDO>lambdaQuery()
                .eq(ErpSaleReturnDO::getCustomerOrderId, customerOrderId)
                .last("LIMIT 1"));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void accept(Long saleOrderId, Long deliveryStaffId) {
        ErpSaleOrderDO saleOrder = saleOrderService.validateSaleOrder(saleOrderId);
        ErpSaleReturnDO saleReturn = validateBySaleOrderId(saleOrderId);

        if (!saleReturn.getReturnsStatus().equals(RETURNS_APPROVE.getType()) || !saleOrder.getPickUpStatus().equals(RETURNS_APPROVE)) {
            throw exception(SALE_ORDER_PICKUP_STATUS_FAIL);
        }

        String key = String.format(SALE_ORDER_LOCK, saleOrder.getId());
        RedisUtils.tryLockRds(key);
        try {
            // 更新退货单和销售订单的状态
            saleOrderMapper.update(Wrappers.<ErpSaleOrderDO>lambdaUpdate()
                    .set(ErpSaleOrderDO::getPickUpStatus, SaleOrderPickUpStatus.RETURNS_STAFF_ACCEPT.getType())
                    .set(ErpSaleOrderDO::getReturnsDeliveryStaffId, deliveryStaffId)
                    .eq(ErpSaleOrderDO::getId, saleOrder.getId()));

            saleReturnMapper.update(Wrappers.<ErpSaleReturnDO>lambdaUpdate()
                    .set(ErpSaleReturnDO::getAcceptTime, LocalDateTime.now())
                    .set(ErpSaleReturnDO::getReturnsStatus, SaleOrderPickUpStatus.RETURNS_STAFF_ACCEPT.getType())
                    .set(ErpSaleReturnDO::getDeliveryStaffId, deliveryStaffId)
                    .eq(ErpSaleReturnDO::getId, saleReturn.getId()));

            purchaseReturnMapper.update(Wrappers.<ErpPurchaseReturnDO>lambdaUpdate()
                    .set(ErpPurchaseReturnDO::getDeliveryStaffId, deliveryStaffId)
                    .set(ErpPurchaseReturnDO::getReturnsStatus, SaleOrderPickUpStatus.RETURNS_STAFF_ACCEPT.getType())
                    .eq(ErpPurchaseReturnDO::getSaleOrderId, saleOrder.getId()));

        } finally {
            RedisUtils.unLockRds(key);
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delivery(Long saleOrderId, Long deliveryStaffId) {
        ErpSaleOrderDO saleOrder = saleOrderService.validateSaleOrder(saleOrderId);
        ErpSaleReturnDO saleReturn = validateBySaleOrderId(saleOrderId);

        if (!saleReturn.getReturnsStatus().equals(RETURNS_STAFF_ACCEPT.getType()) || !saleOrder.getPickUpStatus().equals(RETURNS_STAFF_ACCEPT)) {
            throw exception(SALE_ORDER_PICKUP_STATUS_FAIL);
        }

        String key = String.format(SALE_ORDER_LOCK, saleOrder.getId());
        RedisUtils.tryLockRds(key);
        try {
            // 更新退货单和销售订单的状态
            saleOrderMapper.update(Wrappers.<ErpSaleOrderDO>lambdaUpdate()
                    .set(ErpSaleOrderDO::getPickUpStatus, SaleOrderPickUpStatus.RETURNS_STAFF_ALREADY.getType())
                    .set(ErpSaleOrderDO::getReturnsDeliveryStaffId, deliveryStaffId)
                    .eq(ErpSaleOrderDO::getId, saleOrder.getId()));

            saleReturnMapper.update(Wrappers.<ErpSaleReturnDO>lambdaUpdate()
                    .set(ErpSaleReturnDO::getDeliveryTime, LocalDateTime.now())
                    .set(ErpSaleReturnDO::getReturnsStatus, SaleOrderPickUpStatus.RETURNS_STAFF_ALREADY.getType())
                    .set(ErpSaleReturnDO::getDeliveryStaffId, deliveryStaffId)
                    .eq(ErpSaleReturnDO::getId, saleReturn.getId()));

            purchaseReturnMapper.update(Wrappers.<ErpPurchaseReturnDO>lambdaUpdate()
                    .set(ErpPurchaseReturnDO::getDeliveryStaffId, deliveryStaffId)
                    .set(ErpPurchaseReturnDO::getReturnsStatus, SaleOrderPickUpStatus.RETURNS_STAFF_ALREADY.getType())
                    .eq(ErpPurchaseReturnDO::getSaleOrderId, saleOrder.getId()));

        } finally {
            RedisUtils.unLockRds(key);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void arrival(DeliveryOrderUpdateReqVO reqVO, Long deliveryStaffId) {
        Long orderId = reqVO.getId();
        ErpSaleReturnDO saleReturn = saleReturnMapper.selectOne(Wrappers.<ErpSaleReturnDO>lambdaQuery()
                .eq(ErpSaleReturnDO::getCustomerOrderId, orderId)
                .last("LIMIT 1"));
        if (CommonUtil.isEmpty(saleReturn)) {
            throw exception(SALE_RETURN_NO_EXISTS);
        }
        ErpSaleOrderDO saleOrder = saleOrderService.validateSaleOrder(saleReturn.getOrderId());
        if (!SaleOrderPickUpStatus.RETURNS_STAFF_ALREADY.equals(saleOrder.getPickUpStatus())) {
            throw exception(SALE_ORDER_PICKUP_STATUS_FAIL);
        }

        String key = String.format(SALE_ORDER_LOCK, saleOrder.getId());
        RedisUtils.tryLockRds(key);
        try {

            // 更新退货单和销售订单的状态
            saleOrderMapper.update(Wrappers.<ErpSaleOrderDO>lambdaUpdate()
                    .set(ErpSaleOrderDO::getPickUpStatus, RETURNS_FINISH.getType())
                    .eq(ErpSaleOrderDO::getId, saleOrder.getId()));

            saleReturnMapper.update(Wrappers.<ErpSaleReturnDO>lambdaUpdate()
                    .set(ErpSaleReturnDO::getDeliveryTime, LocalDateTime.now())
                    .set(ErpSaleReturnDO::getReturnsStatus, RETURNS_FINISH.getType())
                    .eq(ErpSaleReturnDO::getId, saleReturn.getId()));

            purchaseReturnMapper.update(Wrappers.<ErpPurchaseReturnDO>lambdaUpdate()
                    .set(ErpPurchaseReturnDO::getReturnsStatus, RETURNS_FINISH.getType())
                    .eq(ErpPurchaseReturnDO::getSaleOrderId, saleOrder.getId()));

            List<CustomerOrderItemDTO> customerORderItems = customerOrderApi.queryItemsByOrderId(orderId);
            Map<Long, CustomerOrderItemDTO> customerOrderItemMap = CommonUtil.listConvertMap(customerORderItems, CustomerOrderItemDTO::getId);

            List<CustomerOrderItemDTO> updateItems = new ArrayList<>();
            for (DeliveryOrderUpdateReqVO.DeliveryOrderUpdateItems item : reqVO.getOrderItems()) {
                Long orderItemId = item.getId();
                BigDecimal returnsCount = item.getSignedQuantity();
                if (!customerOrderItemMap.containsKey(orderItemId)) {
                    throw exception(SALERETURN_ERROR_ORDER_ITEM);
                }
                CustomerOrderItemDTO orderItem = customerOrderItemMap.get(orderItemId);
                if (returnsCount.compareTo(orderItem.getSignedQuantity()) > 0) {
                    throw exception(SALE_RETURN_COUNT_ERROR, orderItem.getProductName());
                }
                CustomerOrderItemDTO updateItem = new CustomerOrderItemDTO();
                updateItem.setId(orderItem.getId());
                updateItem.setReturnsQuantity(returnsCount);
                updateItem.setReturnsTotal(MoneyUtils.multiplyAndRoundToInt(returnsCount, orderItem.getOrderItemPrice()));
                updateItems.add(updateItem);
            }

            customerOrderApi.updateItems(updateItems);

            // 3. 变更库存
            List<ErpSaleReturnItemDO> saleReturnItems = saleReturnItemMapper.selectListByReturnId(saleReturn.getId());
            Integer bizType = ErpStockRecordBizTypeEnum.SALE_RETURN.getType();
            saleReturnItems.forEach(saleReturnItem -> {
                BigDecimal count = saleReturnItem.getCount();
                stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO(
                        saleReturnItem.getProductId(), saleReturnItem.getWarehouseId(), count,
                        bizType, saleReturnItem.getReturnId(), saleReturnItem.getId(), saleReturn.getNo()));
            });

            // 变更库存
            List<ErpPurchaseReturnDO> purchaseReturnDOS = purchaseReturnMapper.selectList(ErpPurchaseReturnDO::getSaleOrderId, saleOrder.getId());
            for (ErpPurchaseReturnDO item : purchaseReturnDOS) {
                purchaseReturnService.updatePurchaseReturnStatus(item.getId(), ErpAuditStatus.APPROVE.getStatus());
            }

        } finally {
            RedisUtils.unLockRds(key);
        }
    }

    @Override
    public ErpSaleReturnDO getSaleReturnByCustomerOrderId(Long customerOrderId) {
        return saleReturnMapper.selectOne(Wrappers.<ErpSaleReturnDO>lambdaQuery()
                .eq(ErpSaleReturnDO::getCustomerOrderId, customerOrderId)
                        .orderByDesc(ErpSaleReturnDO::getId)
                .last("LIMIT 1"));
    }

}
