package cn.iocoder.foodnexus.module.order.service.customerorder;

import cn.iocoder.foodnexus.framework.common.enums.UserSystemEnum;
import cn.iocoder.foodnexus.framework.common.util.CommonUtil;
import cn.iocoder.foodnexus.framework.common.util.number.MoneyUtils;
import cn.iocoder.foodnexus.framework.common.util.spring.SpringUtils;
import cn.iocoder.foodnexus.framework.redis.utils.RedisUtils;
import cn.iocoder.foodnexus.framework.security.core.LoginUser;
import cn.iocoder.foodnexus.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.foodnexus.module.customerpermission.core.annotation.CustomerVisible;
import cn.iocoder.foodnexus.module.erp.api.PurchaseOrderSplitEvent;
import cn.iocoder.foodnexus.module.erp.api.enums.ErpAuditStatus;
import cn.iocoder.foodnexus.module.erp.api.enums.ErpDeliveryStatus;
import cn.iocoder.foodnexus.module.erp.api.service.ErpCustomerApi;
import cn.iocoder.foodnexus.module.erp.api.service.ErpSupplierApi;
import cn.iocoder.foodnexus.module.erp.api.service.ErpWarehouseApi;
import cn.iocoder.foodnexus.module.erp.controller.admin.sale.vo.returns.ErpSaleReturnSaveReqVO;
import cn.iocoder.foodnexus.module.erp.dal.dataobject.customerwarehouse.CustomerWarehouseDO;
import cn.iocoder.foodnexus.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO;
import cn.iocoder.foodnexus.module.erp.dal.dataobject.sale.*;
import cn.iocoder.foodnexus.module.erp.dal.mysql.purchase.ErpPurchaseOrderMapper;
import cn.iocoder.foodnexus.module.erp.dal.mysql.sale.ErpSaleOrderMapper;
import cn.iocoder.foodnexus.module.erp.enums.SaleOrderPickUpStatus;
import cn.iocoder.foodnexus.module.erp.service.customerwarehouse.CustomerWarehouseService;
import cn.iocoder.foodnexus.module.erp.service.purchase.ErpPurchaseOrderServiceImpl;
import cn.iocoder.foodnexus.module.erp.service.sale.ErpSaleOrderService;
import cn.iocoder.foodnexus.module.erp.service.sale.ErpSaleOutService;
import cn.iocoder.foodnexus.module.erp.service.sale.ErpSaleReturnService;
import cn.iocoder.foodnexus.module.operations.dal.dataobject.inquiresupplierpush.InquireSupplierPushDO;
import cn.iocoder.foodnexus.module.operations.dal.dataobject.scoringweight.ScoringWeightDO;
import cn.iocoder.foodnexus.module.operations.service.inquiresupplierpush.InquireSupplierPushService;
import cn.iocoder.foodnexus.module.operations.service.scoringweight.ScoringWeightService;
import cn.iocoder.foodnexus.module.order.api.CustomerOrderApi;
import cn.iocoder.foodnexus.module.order.api.CustomerOrderRecordApi;
import cn.iocoder.foodnexus.module.order.controller.admin.orderScore.vo.OrderScoreSaveReqVO;
import cn.iocoder.foodnexus.module.order.controller.app.customerOrder.vo.*;
import cn.iocoder.foodnexus.module.order.dal.dataobject.shoppingcart.ShoppingCartDO;
import cn.iocoder.foodnexus.module.order.dto.*;
import cn.iocoder.foodnexus.module.order.dto.CustomerOrderRemark;
import cn.iocoder.foodnexus.module.order.dal.dataobject.customerorderitem.CustomerOrderItemDO;
import cn.iocoder.foodnexus.module.order.dal.mysql.customerorderitem.CustomerOrderItemMapper;
import cn.iocoder.foodnexus.module.order.enums.CustomerOrderStatus;
import cn.iocoder.foodnexus.module.order.service.customeraddress.CustomerAddressService;
import cn.iocoder.foodnexus.module.order.service.customerorderitem.CustomerOrderItemService;
import cn.iocoder.foodnexus.module.order.service.orderScore.OrderScoreService;
import cn.iocoder.foodnexus.module.order.service.shoppingcart.ShoppingCartService;
import cn.iocoder.foodnexus.module.product.api.InquireCustomerApi;
import cn.iocoder.foodnexus.module.product.api.dto.CustomerVisibleProductRespDTO;
import cn.iocoder.foodnexus.module.product.api.dto.ProductInfo;
import cn.iocoder.foodnexus.module.product.dal.dataobject.category.ProductCategoryDO;
import cn.iocoder.foodnexus.module.product.dal.dataobject.spu.ProductSpuDO;
import cn.iocoder.foodnexus.module.product.service.category.ProductCategoryService;
import cn.iocoder.foodnexus.module.product.service.spu.ProductSpuService;
import cn.iocoder.foodnexus.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.foodnexus.module.system.service.user.AdminUserService;
import cn.iocoder.foodnexus.module.system.util.GenCodeUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import cn.iocoder.foodnexus.module.order.controller.admin.customerorder.vo.*;
import cn.iocoder.foodnexus.module.order.dal.dataobject.customerorder.CustomerOrderDO;
import cn.iocoder.foodnexus.framework.common.pojo.PageResult;
import cn.iocoder.foodnexus.framework.common.util.object.BeanUtils;

import cn.iocoder.foodnexus.module.order.dal.mysql.customerorder.CustomerOrderMapper;

import static cn.iocoder.foodnexus.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.foodnexus.module.order.enums.ErrorCodeConstants.*;

/**
 * 客户总订单 Service 实现类
 *
 * @author 超级管理员
 */
@Service
@Validated
@Slf4j
public class CustomerOrderServiceImpl implements CustomerOrderService, CustomerOrderApi {

    @Resource
    private CustomerOrderMapper customerOrderMapper;

    @Autowired
    private CustomerOrderItemService customerOrderItemService;

    @Autowired
    private CustomerOrderItemMapper customerOrderItemMapper;

    @Autowired
    private CustomerWarehouseService customerWarehouseService;

    @Autowired
    private InquireCustomerApi inquireCustomerApi;

    @Autowired
    private ErpCustomerApi customerApi;

    @Autowired
    private ErpWarehouseApi warehouseApi;

    @Autowired
    private CustomerAddressService addressService;

    @Autowired
    private InquireSupplierPushService inquireSupplierPushService;

    @Autowired
    private ErpSupplierApi supplierApi;

    @Autowired
    @Lazy
    private CustomerOrderRecordApi orderRecordApi;

    @Autowired
    private ProductSpuService productSpuService;

    @Autowired
    private ProductCategoryService productCategoryService;

    @Autowired
    @Lazy
    private ErpSaleOutService saleOutService;

    @Autowired
    @Lazy
    private ErpSaleReturnService saleReturnService;

    @Autowired
    @Lazy
    private ErpSaleOrderService saleOrderService;

    @Autowired
    private ErpSaleOrderMapper saleOrderMapper;

    @Autowired
    private ErpPurchaseOrderMapper purchaseOrderMapper;

    @Autowired
    private ScoringWeightService scoringWeightService;

    @Autowired
    private OrderScoreService orderScoreService;

    @Autowired
    private AdminUserService userService;

    @Autowired
    private ShoppingCartService shoppingCartService;

    @Autowired
    private GenCodeUtils genCodeUtils;

    @Override
    public Long createCustomerOrder(CustomerOrderSaveReqVO createReqVO) {
        // 插入
        CustomerOrderDO customerOrder = BeanUtils.toBean(createReqVO, CustomerOrderDO.class);
        customerOrderMapper.insert(customerOrder);

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

    @Override
    public void updateCustomerOrder(CustomerOrderSaveReqVO updateReqVO) {
        // 校验存在
        validateCustomerOrderExists(updateReqVO.getId());
        // 更新
        CustomerOrderDO updateObj = BeanUtils.toBean(updateReqVO, CustomerOrderDO.class);
        customerOrderMapper.updateById(updateObj);
    }

    @Override
    public void deleteCustomerOrder(Long id) {
        // 校验存在
        validateCustomerOrderExists(id);
        // 删除
        customerOrderMapper.deleteById(id);
    }

    @Override
        public void deleteCustomerOrderListByIds(List<Long> ids) {
        // 删除
        customerOrderMapper.deleteByIds(ids);
        }


    private void validateCustomerOrderExists(Long id) {
        if (customerOrderMapper.selectById(id) == null) {
            throw exception(CUSTOMER_ORDER_NOT_EXISTS);
        }
    }

    @Override
    public CustomerOrderDO getCustomerOrder(Long id) {
        return customerOrderMapper.selectById(id);
    }

    @Override
    public PageResult<CustomerOrderDO> getCustomerOrderPage(CustomerOrderPageReqVO pageReqVO) {
        return customerOrderMapper.selectPage(pageReqVO);
    }

    /**
     * 客户创建订单
     *
     * @param createReqVO
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @CustomerVisible
    public Long appCreateCustomerOrder(AppCustomerOrderSaveReqVO createReqVO) {
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        Long customerId = customerApi.queryCustomerIdByUserId(loginUser.getId());
        List<CustomerWarehouseDO> customerWarehouse = customerWarehouseService.getCustomerWarehouseByCustomerId(customerId);
        if (CommonUtil.isEmpty(customerWarehouse) || CommonUtil.isEmpty(customerWarehouse.get(0))) {
            log.error("客户订单下单失败,1_019_100_004, \"仓库不存在\", req:{}", createReqVO);
            throw exception(CUSTOMER_ORDER_ADDRESS_NOEXISTS);
        } else {
            // TODO 理论上客户只对应一个库区
            createReqVO.setWarehouseId(customerWarehouse.get(0).getWarehouseId());
            createReqVO.setWarehouseAreaId(customerWarehouse.get(0).getWarehouseAreaId());
        }
        this.validCustomerOrder(createReqVO, customerId);
        Long customerOrderId;

        String lock = String.format(ORDER_LOCK, loginUser.getId());
        RedisUtils.tryLockRds(lock);
        try {
            CustomerVisibleProductRespDTO dto = inquireCustomerApi.queryCustomerIdByCustomerId(customerId);
            Map<Long, CustomerVisibleProductRespDTO.CustomerProduct> customerProductMap = CommonUtil.listConvertMap(dto.getItems(), CustomerVisibleProductRespDTO.CustomerProduct::getProductId);
            Set<Long> supplierSet = new HashSet<>();
            Set<Long> procuctSet = new HashSet<>();
            for (AppCustomerOrderItemSaveReqVO item : createReqVO.getOrderItems()) {
                if (customerProductMap.containsKey(item.getProductId())) {
                    CustomerVisibleProductRespDTO.CustomerProduct customerProduct = customerProductMap.get(item.getProductId());
                    item.setOrderItemPrice(customerProduct.getSupplierQuote());
                    item.setOrderItemTotal(customerProduct.getSupplierQuote() * item.getOrderItemQuantity());
                    item.setMarketPrice(customerProduct.getMarketPrice());
                    item.setDiscount(customerProduct.getFloatingRate());
                    InquireSupplierPushDO inquireSupplierPush = inquireSupplierPushService.getInquireSupplierPush(customerProduct.getInquireSupplierId());
                    procuctSet.add(inquireSupplierPush.getProductId());

                    item.setSupplierId(inquireSupplierPush.getSupplierId());
                    item.setSupplierName(supplierApi.queryNameById(inquireSupplierPush.getSupplierId()));
                    supplierSet.add(inquireSupplierPush.getSupplierId());
                }
            }

            // 数据补全
            CustomerOrderSaveReqVO createOrder = BeanUtils.toBean(createReqVO, CustomerOrderSaveReqVO.class);
            createOrder.setCode(genCodeUtils.createAmBatch("XS"));
            createOrder.setOrderStatus(CustomerOrderStatus.ORDER_SUCCESS);
            createOrder.setCustomerId(customerId);
            AdminUserDO user = userService.getUser(loginUser.getId());
            createOrder.setCustomerDeptId(user.getDeptId());
            createOrder.setWarehouseInfo(warehouseApi.getInfoByWarehouseAreaId(createOrder.getWarehouseAreaId()));
            createOrder.setAddressInfo(BeanUtils.toBean(addressService.getCustomerAddress(createReqVO.getAddressId()), CustomerAddressInfo.class));
            createOrder.setSupplierCount(supplierSet.size());
            createOrder.setProductCount(procuctSet.size());
            // 购物车信息
            List<Long> productIds = CommonUtil.listConvert(createReqVO.getOrderItems(), AppCustomerOrderItemSaveReqVO::getProductId);
            Map<Long, ShoppingCartDO> shoppingMap = shoppingCartService.getMap(loginUser.getId(), productIds);
            // 子订单补全
            AtomicReference<Integer> orderAmount = new AtomicReference<>(0);
            List<CustomerOrderItemDO> collect = createReqVO.getOrderItems().stream().map(orderItem -> {
                CustomerOrderItemDO item = BeanUtils.toBean(orderItem, CustomerOrderItemDO.class);
                item.setCustomerId(customerId);
                item.setProductInfo(BeanUtils.toBean(productSpuService.getSpu(item.getProductId()),
                        ProductInfo.class, product ->
                                product.setCategoryName(productCategoryService.queryNameById(product.getCategoryId()))));
                item.setProductName(item.getProductInfo().getName());
                item.setSignedQuantity(BigDecimal.ZERO);
                item.setSignedTotal(0);
                if (shoppingMap.containsKey(orderItem.getProductId())) {
                    item.setCustomerRemark(shoppingMap.get(orderItem.getProductId()).getRemark());
                }
                orderAmount.updateAndGet(v -> v + item.getOrderItemTotal());
                return item;
            }).collect(Collectors.toList());

            createOrder.setOrderAmount(orderAmount.get());

            customerOrderId = this.createCustomerOrder(createOrder);
            customerOrderItemService.createBatch(collect, customerOrderId);

            // 删除购物车中的同样商品的信息
            shoppingCartService.deleteByProductId(loginUser.getId(), productIds);

            // 拆分采购订单
            orderSplitPurchase(customerOrderId);

            // 订单记录
            CustomerOrderRecordEvent event = new CustomerOrderRecordEvent();
            event.setOrderStatus(CustomerOrderStatus.ORDER_SUCCESS);
            event.setCustomerOrderId(customerOrderId);
            event.setCopyWriter(CommonUtil.asList(String.valueOf((double) createOrder.getOrderAmount() / 100),
                    String.valueOf(createOrder.getProductCount())));
            orderRecordApi.recordEvent(event);
        } finally {
            RedisUtils.unLockRds(lock);
        }

        return customerOrderId;
    }

    /**
     * 客户修改订单
     *
     * @param updateReqVO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @CustomerVisible
    public void appUpdateCustomerOrder(AppCustomerOrderSaveReqVO updateReqVO) {
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        CustomerOrderDO customerOrder = getCustomerOrder(updateReqVO.getId());
        if (CommonUtil.isEmpty(customerOrder)) {
            throw exception(CUSTOMER_ORDER_NOT_EXISTS);
        }
        // TODO 具体修改什么待定

        // TODO 采购订单拆分更新

    }

    /**
     * 取消订单
     *
     * @param reqVO
     */
    @Override
    public void cancel(AppCustomerOrderRemarkReqVO reqVO) {
        CustomerOrderDO customerOrder = getCustomerOrder(reqVO.getId());
        if (CommonUtil.isEmpty(customerOrder)) {
            throw exception(CUSTOMER_ORDER_NOT_EXISTS);
        }
        String lock = String.format(ORDER_LOCK, reqVO.getId());
        RedisUtils.tryLockRds(lock);
        try {
            CustomerOrderStatus orderStatus = customerOrder.getOrderStatus();
            if (orderStatus.equals(CustomerOrderStatus.ORDER_SUCCESS) ||
                    orderStatus.equals(CustomerOrderStatus.ORDER_MATCH) ||
            orderStatus.equals(CustomerOrderStatus.SUPPLIER_ACCEPT_ORDER)) {
                CustomerOrderRemark orderRemark = new CustomerOrderRemark();
                orderRemark.setOperTime(new Date());
                orderRemark.setRemark(CommonUtil.getEls(reqVO.getRemark(), ""));
                orderRemark.setRemarkFiles(reqVO.getRemarkFiles());

                CustomerOrderDO updateInfo = new CustomerOrderDO();
                updateInfo.setId(reqVO.getId());
                updateInfo.setOrderRemark(orderRemark);
                customerOrderMapper.updateById(updateInfo);

                // 标记销售订单状态取消
                saleOrderMapper.update(Wrappers.<ErpSaleOrderDO>lambdaUpdate()
                        .set(ErpSaleOrderDO::getPickUpStatus, SaleOrderPickUpStatus.CANCEL.getType())
                        .eq(ErpSaleOrderDO::getCustomerOrderId, customerOrder.getId()));

                purchaseOrderMapper.update(Wrappers.<ErpPurchaseOrderDO>lambdaUpdate()
                        .set(ErpPurchaseOrderDO::getDeliveryStatus, ErpDeliveryStatus.CANCEL.getStatus())
                        .eq(ErpPurchaseOrderDO::getCustomerOrderId, customerOrder.getId()));

                // 订单记录
                CustomerOrderRecordEvent event = new CustomerOrderRecordEvent();
                event.setOrderStatus(CustomerOrderStatus.CANCEL);
                event.setCustomerOrderId(reqVO.getId());
                orderRecordApi.recordEvent(event);
            } else {
                throw exception(CUSTOMER_ORDER_STATUS_ERROR);
            }
        } finally {
            RedisUtils.unLockRds(lock);
        }
    }

    /**
     * 签收订单
     *
     * @param reqVO
     */
    @Override
    public void receipt(AppCustomerOrderRemarkReqVO reqVO) {
        // TODO .. 校验订单
        CustomerOrderDO customerOrder = getCustomerOrder(reqVO.getId());
        if (CommonUtil.isEmpty(customerOrder)) {
            throw exception(CUSTOMER_ORDER_NOT_EXISTS);
        }
        if (!customerOrder.getOrderStatus().equals(CustomerOrderStatus.ARRIVAL)) {
            throw exception(CUSTOMER_ORDER_STATUS_ERROR);
        }
        List<CustomerOrderItemDO> customerOrderItems = customerOrderItemMapper.selectList(CustomerOrderItemDO::getOrderId, reqVO.getId());
        ErpSaleOutDO saleOut = saleOutService.getSaleOutByCustomerOrderId(reqVO.getId());
        if (CommonUtil.isEmpty(saleOut)) {
            throw exception(CUSTOMER_ORDER_OUT_ERROR);
        }
        String lock = String.format(ORDER_LOCK, reqVO.getId());
        RedisUtils.tryLockRds(lock);
        try {
            List<ErpSaleOutItemDO> saleOutItems = saleOutService.getSaleOutItemListByOutId(saleOut.getId());
            Map<Long, ErpSaleOutItemDO> productMap = CommonUtil.listConvertMap(saleOutItems, ErpSaleOutItemDO::getProductId);

            List<CustomerOrderItemDO> updateBatch = new ArrayList<>();
            AtomicReference<Integer> actualAmount = new AtomicReference<>(0);
            customerOrderItems.forEach(customerOrderItem -> {
                CustomerOrderItemDO updateItem = new CustomerOrderItemDO();
                updateItem.setId(customerOrderItem.getId());
                if (productMap.containsKey(customerOrderItem.getProductId())) {
                    ErpSaleOutItemDO saleOutItem = productMap.get(customerOrderItem.getProductId());
                    updateItem.setSignedQuantity(saleOutItem.getCount());
                    CustomerOrderRemark remark = new CustomerOrderRemark();
                    remark.setOperTime(Date.from(saleOutItem.getCreateTime()
                            .atZone(ZoneId.systemDefault())  // 绑定系统默认时区
                            .toInstant()));
                    remark.setRemarkFiles(saleOutItem.getRemarkFiles());
                    remark.setRemark(saleOutItem.getRemark());
                    updateItem.setOrderRemark(remark);
                } else {
                    updateItem.setSignedQuantity(BigDecimal.valueOf(customerOrderItem.getOrderItemQuantity()));
                }
                updateItem.setSignedTotal(MoneyUtils.multiplyAndRoundToInt(updateItem.getSignedQuantity(), customerOrderItem.getOrderItemPrice()));
                actualAmount.updateAndGet(v -> v + updateItem.getSignedTotal());
                updateBatch.add(updateItem);
            });

            customerOrderItemMapper.updateBatch(updateBatch);
            // 更新实际支付金额
            CustomerOrderDO updateOrder = new CustomerOrderDO();
            updateOrder.setId(customerOrder.getId());
            updateOrder.setActualAmount(actualAmount.get());
            updateOrder.setReceiptFile(saleOut.getReceiptFile());
            customerOrderMapper.updateById(updateOrder);

            // 更新销售单和销售出库单的状态
            saleOrderService.receiptByCustomerOrderId(reqVO.getId(), CommonUtil.listConvert(customerOrderItems, CustomerOrderItemDO::getId));
            saleOutService.receiptByCustomerOrderId(reqVO.getId(), CommonUtil.listConvert(customerOrderItems, CustomerOrderItemDO::getId));

            // 订单记录
            CustomerOrderRecordEvent event = new CustomerOrderRecordEvent();
            event.setOrderStatus(CustomerOrderStatus.SIGN_RECEIPT);
            event.setCustomerOrderId(reqVO.getId());
            event.setCopyWriter(CommonUtil.asList(CommonUtil.getEls(saleOut.getReceiptFile(), "")));
            orderRecordApi.recordEvent(event);
        } finally {
            RedisUtils.unLockRds(lock);
        }
    }

    @Override
    public List<AppCustomerOrderReceiptRespVO> queryReceipt(Long id) {
        CustomerOrderDO customerOrder = getCustomerOrder(id);
        if (CommonUtil.isEmpty(customerOrder)) {
            throw exception(CUSTOMER_ORDER_NOT_EXISTS);
        }
        if (!customerOrder.getOrderStatus().equals(CustomerOrderStatus.ARRIVAL)) {
            throw exception(CUSTOMER_ORDER_STATUS_ERROR);
        }
        List<CustomerOrderItemDO> customerOrderItems = customerOrderItemMapper.selectList(CustomerOrderItemDO::getOrderId, id);
        ErpSaleOutDO saleOut = saleOutService.getSaleOutByCustomerOrderId(id);
        if (CommonUtil.isEmpty(saleOut)) {
            throw exception(CUSTOMER_ORDER_OUT_ERROR);
        }
        List<ErpSaleOutItemDO> saleOutItems = saleOutService.getSaleOutItemListByOutId(saleOut.getId());
        Map<Long, ErpSaleOutItemDO> productMap = CommonUtil.listConvertMap(saleOutItems, ErpSaleOutItemDO::getProductId);

        return CommonUtil.listConvert(customerOrderItems, customerOrderItem -> {
            AppCustomerOrderReceiptRespVO result = new AppCustomerOrderReceiptRespVO();
            result.setProductId(customerOrderItem.getProductId());
            result.setReceiptFile(saleOut.getReceiptFile());
            if (productMap.containsKey(customerOrderItem.getProductId())) {
                ErpSaleOutItemDO saleOutItem = productMap.get(customerOrderItem.getProductId());
                result.setSignedQuantity(saleOutItem.getCount());
                result.setSignedTotal(MoneyUtils.multiplyAndRoundToInt(saleOutItem.getCount(), saleOutItem.getProductPrice()));
                result.setRemark(saleOutItem.getRemark());
                result.setRemarkFiles(saleOutItem.getRemarkFiles());
            } else {
                result.setSignedQuantity(BigDecimal.valueOf(customerOrderItem.getOrderItemQuantity()));
                result.setSignedTotal(customerOrderItem.getOrderItemQuantity() * customerOrderItem.getOrderItemPrice());
            }
            return result;
        });
    }

    /**
     * 订单退货
     *
     * @param reqVO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void orderReturn(AppCustomerReturnOrderReqVO reqVO) {
        Long id = reqVO.getCustomerOrderId();
        CustomerOrderDO customerOrder = getCustomerOrder(id);
        if (CommonUtil.isEmpty(customerOrder)) {
            throw exception(CUSTOMER_ORDER_NOT_EXISTS);
        }
        if (!customerOrder.getOrderStatus().equals(CustomerOrderStatus.SIGN_RECEIPT)) {
            throw exception(CUSTOMER_ORDER_STATUS_ERROR);
        }

        String lock = String.format(ORDER_LOCK, reqVO.getCustomerOrderId());
        RedisUtils.tryLockRds(lock);
        try {

            // 修改客户订单状态
            CustomerOrderRecordEvent event = new CustomerOrderRecordEvent();
            event.setOrderStatus(CustomerOrderStatus.RETURN);
            event.setCustomerOrderId(id);
            event.setCopyWriter(null);
            orderRecordApi.recordEvent(event);

            // 添加销售退货单
            List<CustomerOrderItemDO> customerOrderItems = customerOrderItemMapper.selectList(CustomerOrderItemDO::getOrderId, id);
            Map<Long, CustomerOrderItemDO> orderItemMap = CommonUtil.listConvertMap(customerOrderItems, CustomerOrderItemDO::getId);

            ErpSaleReturnSaveReqVO returnSaveReqVO = new ErpSaleReturnSaveReqVO();
            returnSaveReqVO.setReturnTime(LocalDateTime.now());
            returnSaveReqVO.setCustomerId(customerOrder.getCustomerId());
            // 查询对应的销售订单
            ErpSaleOrderDO saleOrderDO = saleOrderService.queryByCustomerOrderId(customerOrder.getId());
            returnSaveReqVO.setOrderId(saleOrderDO.getId());
            returnSaveReqVO.setCustomerOrderId(customerOrder.getId());
            returnSaveReqVO.setRemark(reqVO.getRemark());
            returnSaveReqVO.setFilesUrl(reqVO.getRemarkFiles());
            returnSaveReqVO.setWarehouseId(saleOrderDO.getWarehouseId());
            returnSaveReqVO.setWarehouseAreaId(saleOrderDO.getWarehouseAreaId());
            returnSaveReqVO.setWarehouseInfo(saleOrderDO.getWarehouseInfo());
            returnSaveReqVO.setAddressId(saleOrderDO.getAddressId());
            returnSaveReqVO.setAddressInfo(saleOrderDO.getAddressInfo());

            // 更新销售订单
            ErpSaleOrderDO updateSaleOrder = new ErpSaleOrderDO();
            updateSaleOrder.setId(saleOrderDO.getId());
            updateSaleOrder.setPickUpStatus(SaleOrderPickUpStatus.RETURNS_PROCESS);

            List<ErpSaleReturnSaveReqVO.Item> items = new ArrayList<>(reqVO.getOrderItems().size());
            List<ErpSaleOrderItemDO> updateSaleOrderItems = new ArrayList<>(reqVO.getOrderItems().size());
            for (AppCustomerReturnOrderReqVO.ReturnItems item : reqVO.getOrderItems()) {
                CustomerOrderItemDO customerOrderItemDO = orderItemMap.get(item.getCustomerOrderItemId());
                if (!Objects.equals(customerOrderItemDO.getProductId(), item.getProductId())) {
                    throw exception(CUSTOMER_ORDER_RETURN_PRODUCT_ERROR);
                }
                ErpSaleReturnSaveReqVO.Item returnItem = new ErpSaleReturnSaveReqVO.Item();
                ErpSaleOrderItemDO saleOrderItemDO = saleOrderService.queryItemByCustomerOrderItemId(customerOrderItemDO.getId());
                ErpSaleOrderItemDO updateSaleOrderitem = new ErpSaleOrderItemDO();
                updateSaleOrderitem.setId(saleOrderItemDO.getId());
                updateSaleOrderitem.setPickUpStatus(SaleOrderPickUpStatus.RETURNS_PROCESS);

                BigDecimal returnCount = item.getReturnCount(); // 待退数量（Integer转BigDecimal）
                BigDecimal actualSigned = CommonUtil.getEls(customerOrderItemDO.getSignedQuantity(), BigDecimal.ZERO);
                BigDecimal actualReturned = CommonUtil.getEls(customerOrderItemDO.getReturnsQuantity(), BigDecimal.ZERO);

                // (已签 - 已退) < 待退
                if (actualSigned.subtract(actualReturned).compareTo(returnCount) < 0) {
                    throw exception(CUSTOMER_ORDER_RETURN_COUNT_ERROR, customerOrderItemDO.getProductName());
                }
                returnItem.setOrderItemId(saleOrderItemDO.getId());
                returnItem.setCustomerOrderItemId(customerOrderItemDO.getId());
                returnItem.setWarehouseId(customerOrder.getWarehouseAreaId());
                returnItem.setProductId(item.getProductId());
                returnItem.setProductUnit(customerOrderItemDO.getProductInfo().getUnitName());
                returnItem.setProductPrice(customerOrderItemDO.getOrderItemPrice());
                returnItem.setProductInfo(customerOrderItemDO.getProductInfo());
                returnItem.setCount(item.getReturnCount());
                returnItem.setRemark(item.getRemark());
                returnItem.setSupplierId(customerOrderItemDO.getSupplierId());
                items.add(returnItem);

                updateSaleOrderItems.add(updateSaleOrderitem);
            }
            returnSaveReqVO.setItems(items);
            saleReturnService.createSaleReturn(returnSaveReqVO);

            saleOrderService.update(updateSaleOrder, updateSaleOrderItems);
        } finally {
            RedisUtils.unLockRds(lock);
        }
    }

    /**
     * 查询订单评价
     *
     * @param id
     * @return
     */
    @Override
    public AppCustomerOrderScoreRespVO queryScore(Long id) {
        CustomerOrderDO customerOrder = getCustomerOrder(id);
        if (CommonUtil.isEmpty(customerOrder)) {
            throw exception(CUSTOMER_ORDER_NOT_EXISTS);
        }
        AppCustomerOrderScoreRespVO result = new AppCustomerOrderScoreRespVO();
        result.setOrderRemark(customerOrder.getOrderRemark());
        List<ScoringWeightDO> scoringWeightDOS = scoringWeightService.queryByUserSystem(UserSystemEnum.CUSTOMER);
        result.setItems(CommonUtil.listConvert(scoringWeightDOS, item -> {
            AppCustomerOrderScoreRespVO.Item resultItem = new AppCustomerOrderScoreRespVO.Item();
            resultItem.setScoreId(item.getId());
            resultItem.setScoreName(item.getScoreName());
            resultItem.setScore(orderScoreService.queryScore(id, item.getId()));
            return resultItem;
        }));
        return result;
    }

    /**
     * 评价订单
     *
     * @param reqVO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void score(AppCustomerOrderScoreReqVO reqVO) {
        log.info("评价订单：{}", reqVO);
        Long id = reqVO.getOrderId();
        CustomerOrderDO customerOrder = getCustomerOrder(id);
        if (CommonUtil.isEmpty(customerOrder)) {
            throw exception(CUSTOMER_ORDER_NOT_EXISTS);
        }
        if (CommonUtil.isNotEmpty(customerOrder.getHasScore()) && customerOrder.getHasScore()) {
            throw exception(CUSTOMER_ORDER_HAS_RECEIPT);
        }
        String lock = String.format(ORDER_LOCK, reqVO.getOrderId());
        RedisUtils.tryLockRds(lock);
        try {
            if (CommonUtil.isNotEmpty(reqVO.getItems())) {
                reqVO.getItems().forEach(item -> {
                    if (!orderScoreService.exists(id, item.getScoreId())) {
                        ScoringWeightDO scoringWeight = scoringWeightService.getScoringWeight(item.getScoreId());
                        Set<Long> supplierIds = CommonUtil.listConvertSet(customerOrderItemMapper.selectList(CustomerOrderItemDO::getOrderId, id), CustomerOrderItemDO::getSupplierId);
                        for (Long supplierId : supplierIds) {
                            OrderScoreSaveReqVO saveReqVO = new OrderScoreSaveReqVO();
                            saveReqVO.setOrderId(id);
                            saveReqVO.setScoreId(item.getScoreId());
                            saveReqVO.setScoreName(scoringWeight.getScoreName());
                            saveReqVO.setSupplierId(supplierId);
                            saveReqVO.setCustomerId(customerOrder.getCustomerId());
                            saveReqVO.setScore(item.getScore());
                            saveReqVO.setSort(scoringWeight.getSort());
                            orderScoreService.createScore(saveReqVO);
                        }
                    } else {
                        throw exception(SCORE_EXISTS);
                    }
                });
            }
            CustomerOrderDO updateScore = new CustomerOrderDO();
            if (CommonUtil.isNotEmpty(reqVO.getOrderRemark())) {
                reqVO.getOrderRemark().setOperTime(new Date());
                updateScore.setOrderRemark(reqVO.getOrderRemark());
            }

            updateScore.setId(id);
            updateScore.setHasScore(Boolean.TRUE);
            customerOrderMapper.updateById(updateScore);
        } finally {
            RedisUtils.unLockRds(lock);
        }
    }

    @Override
    public Map<String, Long> queryStatusCount(Long loginUserId) {
        Map<String, Long> result = new HashMap<>();
        List<Map<String, Object>> maps = customerOrderMapper.queryStatusCount(loginUserId);
        Map<String, Long> statusMap = CommonUtil.listConvertMap(maps, key -> String.valueOf(key.get("order_status")),
                value -> Long.valueOf(String.valueOf(value.getOrDefault("status_count", "0"))));
        for (CustomerOrderStatus em : CustomerOrderStatus.values()) {
            result.put(em.getKey(), statusMap.getOrDefault(em.getKey(), 0L));
        }
        // TODO 待评价
        result.put("TO_BE_SCORE", customerOrderMapper.selectCount(Wrappers.<CustomerOrderDO>lambdaQuery()
                .eq(CustomerOrderDO::getCreator, loginUserId)
                .eq(CustomerOrderDO::getHasScore, Boolean.FALSE)
                .in(CustomerOrderDO::getOrderStatus, CommonUtil.asList(CustomerOrderStatus.SIGN_RECEIPT.getKey(),
                        CustomerOrderStatus.FINISH.getKey()))));
        return result;
    }

    /**
     * 分页获取月度账单
     *
     * @param reqVO
     * @return
     */
    @Override
    public AppCustomerMonthOrderRespVO reconciliationPageYear(AppCustomerYearOrderPageReqVO reqVO) {
        AppCustomerMonthOrderRespVO respVO = customerOrderMapper.reconciliationPageYearTotal(reqVO.getYear(), reqVO.getDeptIds(), reqVO.getCreator());
        if (CommonUtil.isNotEmpty(respVO)) {
            respVO.setPageResult(customerOrderMapper.reconciliationPageYearPage(reqVO));
        }
        return respVO;
    }

    /**
     * 月订单汇总
     *
     * @param yearMonth
     * @return
     */
    @Override
    public AppCustomerMonthOrderTotalRespVO monthTotal(String yearMonth, Set<Long> deptIds, Long loginUserId) {
        return customerOrderMapper.reconciliationMonthTotal(yearMonth, deptIds, loginUserId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void reconciliationConfirm(AppCustomerOrderReconciliationReqVO reqVO) {
        reqVO.getIds().forEach(id -> {
            CustomerOrderDO customerOrder = getCustomerOrder(id);
            if (CommonUtil.isEmpty(customerOrder)) {
                throw exception(CUSTOMER_ORDER_NOT_EXISTS);
            }
            /*if (customerOrder.getHasFinish()) {
                throw exception(CUSTOMER_ORDER_HAS_FINISH, customerOrder.getCode());
            }*/
            if (CustomerOrderStatus.SIGN_RECEIPT.equals(customerOrder.getOrderStatus()) ||
                    CustomerOrderStatus.RETURN.equals(customerOrder.getOrderStatus())) {
                // this.updateOrderStatus(id, CustomerOrderStatus.FINISH);
                // 订单记录
                CustomerOrderDO updateDo = new CustomerOrderDO();
                updateDo.setId(id);
                if (CustomerOrderStatus.SIGN_RECEIPT.equals(customerOrder.getOrderStatus())) {
                    updateDo.setOrderStatus(CustomerOrderStatus.FINISH);
                }
                updateDo.setHasFinish(Boolean.TRUE);
                customerOrderMapper.updateById(updateDo);
                /*CustomerOrderRecordEvent event = new CustomerOrderRecordEvent();
                event.setOrderStatus(CustomerOrderStatus.FINISH);
                event.setCustomerOrderId(id);
                orderRecordApi.recordEvent(event);*/
            }
        });
    }

    /**
     * 查询退款
     *
     * @param id
     * @return
     */
    @Override
    public AppCustomerOrderReturnRespVO queryReturn(Long id) {
        AppCustomerOrderReturnRespVO result = new AppCustomerOrderReturnRespVO();
        CustomerOrderDO customerOrder = getCustomerOrder(id);
        if (CommonUtil.isEmpty(customerOrder)) {
            throw exception(CUSTOMER_ORDER_NOT_EXISTS);
        }
        if (!customerOrder.getOrderStatus().equals(CustomerOrderStatus.RETURN)) {
            throw exception(CUSTOMER_ORDER_STATUS_ERROR);
        }
        ErpSaleReturnDO saleReturn = saleReturnService.queryByCustomerOrderId(id);
        if (CommonUtil.isEmpty(saleReturn)) {
            return result;
        }
        result.setSaleReturnId(saleReturn.getId());
        result.setSaleReturnNo(saleReturn.getNo());
        result.setTotalReturnPrice(saleReturn.getTotalPrice());
        result.setRemark(saleReturn.getRemark());
        result.setRemarkFiles(saleReturn.getFilesUrl());
        result.setReturnsStatus(saleReturn.getReturnsStatus());
        result.setAuditStatus(saleReturn.getStatus());
        result.setAuditReason(saleReturn.getAuditReason());
        List<AppCustomerOrderReturnRespVO.ReturnsRecords> transform = this.transform(saleReturn);
        result.setReturnsRecords(transform);
        Optional<AppCustomerOrderReturnRespVO.ReturnsRecords> first = Optional.ofNullable(transform).orElse(Lists.newArrayList())
                .stream().filter(obj -> obj != null && obj.getReturnsStatus().equals(saleReturn.getReturnsStatus())).findFirst();
        first.ifPresent(result::setCurrentReturnsRecords);
        List<ErpSaleReturnItemDO> returnItems = saleReturnService.getSaleReturnItemListByReturnId(saleReturn.getId());
        List<CustomerOrderItemDO> customerOrderItems = customerOrderItemMapper.selectList(CustomerOrderItemDO::getOrderId, id);
        Map<Long, CustomerOrderItemDO> orderItemMap = CommonUtil.listConvertMap(customerOrderItems, CustomerOrderItemDO::getId);
        result.setReturnItems(CommonUtil.listConvert(returnItems, item -> {
            CustomerOrderItemDO customerOrderItemDO = orderItemMap.get(item.getCustomerOrderItemId());
            AppCustomerOrderReturnRespVO.ReturnItems returnItem = new AppCustomerOrderReturnRespVO.ReturnItems();
            returnItem.setProductId(item.getProductId());
            returnItem.setCustomerOrderItemId(customerOrderItemDO.getId());
            returnItem.setProductInfo(customerOrderItemDO.getProductInfo());
            returnItem.setOrderItemPrice(customerOrderItemDO.getOrderItemPrice());
            returnItem.setReturnCount(item.getCount());
            returnItem.setReturnPrice(item.getProductPrice());
            returnItem.setTotalReturnPrice(item.getTotalPrice());
            returnItem.setRemark(item.getRemark());
            return returnItem;
        }));
        return result;
    }

    @Override
    public PageResult<CustomerOrderReconPageRespVO> pageReconciliation(CustomerOrderReconPageReqVO pageReqVO) {
        return customerOrderMapper.pageReconciliation(pageReqVO);
    }

    private List<AppCustomerOrderReturnRespVO.ReturnsRecords> transform(ErpSaleReturnDO saleReturn) {
        List<AppCustomerOrderReturnRespVO.ReturnsRecords> collect = Arrays.stream(SaleOrderPickUpStatus.values()).filter(SaleOrderPickUpStatus::isReturns).map(em -> {
            AppCustomerOrderReturnRespVO.ReturnsRecords returnsRecords = new AppCustomerOrderReturnRespVO.ReturnsRecords();
            switch (em) {
                case RETURNS_PROCESS:
                    returnsRecords.setRecordTime(saleReturn.getCreateTime());
                    returnsRecords.setReturnsStatus(em.getType());
                    break;
                case RETURNS_APPROVE:
                    if (ErpAuditStatus.APPROVE.getStatus().equals(saleReturn.getStatus())) {
                        returnsRecords.setRecordTime(saleReturn.getAuditTime());
                        returnsRecords.setReturnsStatus(em.getType());
                    }
                    break;
                case RETURNS_REJECT:
                    if (ErpAuditStatus.REJECT.getStatus().equals(saleReturn.getStatus())) {
                        returnsRecords.setRecordTime(saleReturn.getAuditTime());
                        returnsRecords.setReturnsStatus(em.getType());
                    }
                    break;
                case RETURNS_STAFF_ACCEPT:
                    returnsRecords.setRecordTime(saleReturn.getAcceptTime());
                    returnsRecords.setReturnsStatus(em.getType());
                    break;
                case RETURNS_STAFF_ALREADY:
                    returnsRecords.setRecordTime(saleReturn.getDeliveryTime());
                    returnsRecords.setReturnsStatus(em.getType());
                    break;
                case RETURNS_FINISH:
                    returnsRecords.setRecordTime(saleReturn.getArrivalTime());
                    returnsRecords.setReturnsStatus(em.getType());
                    break;
                default:
                    break;
            }
            return returnsRecords;
        }).toList();
        return collect.stream().filter(item -> CommonUtil.isNotEmpty(item.getRecordTime())).collect(Collectors.toList());
    }

    /**
     * {@link ErpPurchaseOrderServiceImpl#orderSplit(PurchaseOrderSplitEvent)}
     */
    private void orderSplitPurchase(Long customerOrderId) {
        PurchaseOrderSplitEvent event = new PurchaseOrderSplitEvent();
        event.setCustomerOrderId(customerOrderId);
        SpringUtils.getApplicationContext().publishEvent(event);
    }

    private void validCustomerOrder(AppCustomerOrderSaveReqVO createReqVO, Long customerId) {
        if (!warehouseApi.existsById(createReqVO.getWarehouseId())) {
            throw exception(CUSTOMER_ORDER_WAREHOUSE_NOEXISTS);
        }
        if (!warehouseApi.existsById(createReqVO.getWarehouseAreaId())) {
            throw exception(CUSTOMER_ORDER_WAREHOUSE_NOEXISTS);
        }
        if (!customerWarehouseService.exists(createReqVO.getWarehouseAreaId(), customerId)) {
            throw exception(CUSTOMER_WAREHOUSE_NOT_BIND);
        }
        if (!addressService.existsById(createReqVO.getAddressId())) {
            throw exception(CUSTOMER_ORDER_ADDRESS_NOEXISTS);
        }
    }

    @Override
    public CustomerOrderDTO queryById(Long customerOrderId) {
        return BeanUtils.toBean(customerOrderMapper.selectById(customerOrderId), CustomerOrderDTO.class);
    }

    @Override
    public List<CustomerOrderItemDTO> queryItemsByOrderId(Long customerOrderId) {
        return BeanUtils.toBean(customerOrderItemMapper.selectList(CustomerOrderItemDO::getOrderId, customerOrderId),
                CustomerOrderItemDTO.class);
    }

    @Override
    public void updateOrderStatus(Long customerOrderId, CustomerOrderStatus updateOrderStatus) {
        customerOrderMapper.update(Wrappers.<CustomerOrderDO>lambdaUpdate()
                .set(CustomerOrderDO::getOrderStatus, updateOrderStatus)
                .eq(CustomerOrderDO::getId, customerOrderId));
    }

    @Override
    public Map<Long, CustomerOrderDTO> getOrderMap(Collection<Long> orderIds) {
        if (CommonUtil.isEmpty(orderIds)) {
            return new HashMap<>();
        }
        List<CustomerOrderDO> customerOrders = customerOrderMapper.selectByIds(orderIds);
        return CommonUtil.listConvertMap(BeanUtils.toBean(customerOrders, CustomerOrderDTO.class), CustomerOrderDTO::getId);
    }

    @Override
    public List<CustomerOrderItemDTO> queryItemsByIds(Collection<Long> orderItems) {
        if (CommonUtil.isEmpty(orderItems)) {
            return Lists.newArrayList();
        }
        return BeanUtils.toBean(customerOrderItemMapper.selectByIds(orderItems), CustomerOrderItemDTO.class);
    }

    @Override
    public void updateItems(List<CustomerOrderItemDTO> updateItems) {
        if (CommonUtil.isNotEmpty(updateItems)) {
            customerOrderItemMapper.updateById(BeanUtils.toBean(updateItems, CustomerOrderItemDO.class));
        }
    }

    @Override
    public Map<Long, String> getCodeMap(Collection<Long> ids) {
        if (CommonUtil.isEmpty(ids)) {
            return new HashMap<>();
        }
        return CommonUtil.listConvertMap(customerOrderMapper.selectByIds(ids), CustomerOrderDO::getId, CustomerOrderDO::getCode);
    }

    @Override
    public void updateActualAmount(Long id, int addAmount) {
        String lock = String.format(ORDER_LOCK, id);
        RedisUtils.tryLockRds(lock);
        try {
            CustomerOrderDO customerOrder = this.getCustomerOrder(id);
            if (CommonUtil.isNotEmpty(customerOrder)) {
                int actualAmount = customerOrder.getActualAmount() + addAmount;
                actualAmount = Math.max(actualAmount, 0);
                customerOrderMapper.update(Wrappers.<CustomerOrderDO>lambdaUpdate()
                        .set(CustomerOrderDO::getActualAmount, actualAmount)
                        .eq(CustomerOrderDO::getId, id));
            }
        } finally {
            RedisUtils.unLockRds(lock);
        }
    }

    @Override
    public void updatePurchaseAnnex(Long customerOrderId, Long supplierId, Long productId, String annex) {
        customerOrderItemMapper.update(Wrappers.<CustomerOrderItemDO>lambdaUpdate()
                .set(CustomerOrderItemDO::getPurchaseAnnex, annex)
                .eq(CustomerOrderItemDO::getOrderId, customerOrderId)
                .eq(CustomerOrderItemDO::getSupplierId, supplierId)
                .eq(CustomerOrderItemDO::getProductId, productId));
    }

}