package com.ruoyi.inventory.service.impl;

import java.util.*;
import java.util.stream.Collectors;

import com.ruoyi.common.annotation.SerialExecution;
import com.ruoyi.common.config.WarehouseConfig;
import com.ruoyi.common.core.domain.entity.Materials;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.inventory.domain.*;
import com.ruoyi.inventory.domain.vo.OutboundTemplateVO;
import com.ruoyi.inventory.mapper.InventoryMapper;
import com.ruoyi.inventory.mapper.OutboundOrderItemsMapper;
import com.ruoyi.inventory.mapper.OutboundOrderLogMapper;
import org.apache.commons.lang3.SystemUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.ruoyi.common.utils.StringUtils;
import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.inventory.mapper.OutboundOrdersMapper;
import com.ruoyi.inventory.service.IOutboundOrdersService;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.CollectionUtils;

/**
 * 出库单主Service业务层处理
 * 核心修正：
 * 1. 库存匹配Key统一为 物料ID_库位ID_库存类型（移除仓库维度）
 * 2. 确保inventoryType完整参与匹配
 * 3. 库存扣减后≤0时强制设置inventory_status=0
 * 4. 无库位库存不足时，自动扣减同物料同库存类型的有库位库存
 * 5. 导入场景下仅保留新增逻辑，移除更新相关处理
 * 6. 移除所有仓库相关逻辑，仅保留物料+库位维度匹配
 *
 * @author ruoyi
 * @date 2025-12-03
 */
@Service
public class OutboundOrdersServiceImpl implements IOutboundOrdersService
{
    @Autowired
    private OutboundOrdersMapper outboundOrdersMapper;

    @Autowired
    private OutboundOrderItemsMapper outboundOrderItemsMapper;
    @Autowired
    private OutboundOrderLogMapper outboundOrderLogMapper;

    @Autowired
    private OwnersServiceImpl ownersService;

    @Autowired
    private InventoryServiceImpl inventoryService;
    @Autowired
    private MaterialsServiceImpl materialsService;
    @Autowired
    private StorageLocationsServiceImpl storageLocationsService;

    @Autowired
    private InventoryMapper inventoryMapper;

    /**
     * 查询出库单主
     *
     * @param id 出库单主主键
     * @return 出库单主
     */
    @Override
    public OutboundOrders selectOutboundOrdersById(String id)
    {
        return outboundOrdersMapper.selectOutboundOrdersById(id);
    }

    /**
     * 查询出库单主列表
     *
     * @param outboundOrders 出库单主
     * @return 出库单主
     */
    @Override
    public List<OutboundOrders> selectOutboundOrdersList(OutboundOrders outboundOrders)
    {
        List<OutboundOrders> outboundOrders1 = outboundOrdersMapper.selectOutboundOrdersList(outboundOrders);
        return outboundOrders1;
    }

    /**
     * 新增出库单主
     *
     * @param outboundOrders 出库单主
     * @return 结果
     */
    @Transactional
    @Override
    public int insertOutboundOrders(OutboundOrders outboundOrders)
    {
        outboundOrders.setCreateTime(DateUtils.getNowDate());
        outboundOrders.setCreateUserCode(SystemUtils.getUserName());
        outboundOrders.setId(UUID.randomUUID().toString());

        int rows = outboundOrdersMapper.insertOutboundOrders(outboundOrders);
        insertOutboundOrderItems(outboundOrders);
        return rows;
    }

    /**
     * 修改出库单主（仅保留方法定义，移除更新逻辑）
     *
     * @param outboundOrders 出库单主
     * @return 结果
     */
    @Transactional
    @Override
    public int updateOutboundOrders(OutboundOrders outboundOrders)
    {
        throw new ServiceException("当前系统仅支持新增出库单，不支持修改操作");
    }

    /**
     * 批量删除出库单主
     *
     * @param ids 需要删除的出库单主主键
     * @return 结果
     */
    @Transactional
    @Override
    public int deleteOutboundOrdersByIds(String[] ids)
    {
        outboundOrdersMapper.deleteOutboundOrderItemsByOrderIds(ids);
        outboundOrderLogMapper.deleteOutboundOrderLogByOrdersIds(ids);
        return outboundOrdersMapper.deleteOutboundOrdersByIds(ids);
    }

    /**
     * 删除出库单主信息
     *
     * @param id 出库单主主键
     * @return 结果
     */
    @Transactional
    @Override
    public int deleteOutboundOrdersById(String id)
    {
        outboundOrdersMapper.deleteOutboundOrderItemsByOrderId(id);
        return outboundOrdersMapper.deleteOutboundOrdersById(id);
    }

    @SerialExecution(group = "inventoryRefresh", fair = true)
    @Override
    public int ship(OutboundOrders outboundOrders) {
        // 1. 查询当前出库单的所有明细
        OutboundOrderItems query = new OutboundOrderItems();
        query.setOutboundOrderId(outboundOrders.getId());
        query.setDivisor(null);
        List<OutboundOrderItems> outboundOrderItems = outboundOrderItemsMapper.selectOutboundOrderItemsList(query);

        // 2. 更新明细和订单状态
        OutboundOrderLog outboundOrderLog = new OutboundOrderLog();
        String updateUser = SystemUtils.getUserName();
        Date updateTime = DateUtils.getNowDate();
        for (OutboundOrderItems item : outboundOrderItems) {
            item.setItemStatus(3L);
            item.setUpdateBy(updateUser);
            item.setUpdateTime(updateTime);
            outboundOrderItemsMapper.updateOutboundOrderItems(item);

            outboundOrderLog.setId(item.getId());
            outboundOrderLog.setItemStatus(item.getItemStatus());
            outboundOrderLogMapper.updateOutboundOrderLog(outboundOrderLog);
        }

        outboundOrders.setOrderStatus(2L);
        outboundOrders.setUpdateTime(updateTime);
        outboundOrders.setUpdateUserCode(updateUser);
        outboundOrdersMapper.updateOutboundOrders(outboundOrders);

        // 3. 执行库存扣减（无库位不足时扣有库位）
        this.deductInventory(outboundOrderItems, updateUser, updateTime);

        return 1;
    }

    /**
     * 核心库存扣减逻辑
     * 1. 有库位：直接扣减指定库位库存
     * 2. 无库位：先扣无库位库存，不足则扣同物料同库存类型的有库位库存
     * 3. 扣减后数量≤0时设置inventory_status=0
     * 4. 无库位扣减有库位时，记录扣减的库位和数量（用于生成明细）
     * 5. 移除仓库维度，仅按物料+库位+库存类型匹配
     * @param outboundOrderItems 出库明细
     * @param updateUser 操作人
     * @param updateTime 操作时间
     * @return 无库位明细扣减的有库位库存记录 Map<无库位明细ID, List<扣减的库位库存信息Map>>
     */
    private Map<String, List<Map<String, Object>>> deductInventory(List<OutboundOrderItems> outboundOrderItems, String updateUser, Date updateTime) {
        if (CollectionUtils.isEmpty(outboundOrderItems)) {
            return Collections.emptyMap();
        }

        Map<String, List<Map<String, Object>>> deductRecordMap = new HashMap<>();
        Map<String, List<Inventory>> inventoryGroupMap = this.loadInventoryGroupMap();
        Map<String, Long> deductQtyMap = this.buildDeductQtyMap(outboundOrderItems);

        List<Inventory> needUpdateList = new ArrayList<>();

        for (Map.Entry<String, Long> entry : deductQtyMap.entrySet()) {
            String key = entry.getKey();
            Long totalDeductQty = entry.getValue();
            if (totalDeductQty <= 0) continue;

            String[] keyParts = key.split("_");
            String materialId = keyParts.length > 0 ? keyParts[0] : "";
            String locationId = keyParts.length > 1 ? keyParts[1] : "";
            String inventoryType = keyParts.length > 2 ? keyParts[2] : "";
            String groupKey = String.join("_", materialId, inventoryType);
            List<Inventory> inventoryList = inventoryGroupMap.get(groupKey);
            if (CollectionUtils.isEmpty(inventoryList)) {
                throw new ServiceException(String.format(
                        "物料[%s]库存类型[%s]无可用库存，无法扣减",
                        materialId, inventoryType));
            }

            Long remainDeductQty = totalDeductQty;
            List<Map<String, Object>> deductRecords = new ArrayList<>();
            String itemId = outboundOrderItems.stream()
                    .filter(item -> key.equals(buildDeductKey(item)))
                    .map(OutboundOrderItems::getId)
                    .findFirst()
                    .orElse(null);

            // 区分无库位/有库位逻辑
            if (StringUtils.isBlank(locationId)) {
                // 无库位：先扣无库位库存，再扣有库位库存
                // 第一步：扣无库位库存
                List<Inventory> noLocationInvList = inventoryList.stream()
                        .filter(inv -> StringUtils.isBlank(inv.getLocationId()))
                        .collect(Collectors.toList());
                for (Inventory inv : noLocationInvList) {
                    if (remainDeductQty <= 0) break;
                    Long currentQty = Optional.ofNullable(inv.getQuantity()).orElse(0L);
                    Long deductQty = Math.min(remainDeductQty, currentQty); // 不扣负数，只扣现有库存

                    inv.setQuantity(currentQty - deductQty);
                    if (inv.getQuantity() <= 0) {
                        inv.setInventoryStatus(0L);
                    }
                    inv.setUpdateBy(updateUser);
                    inv.setUpdateTime(updateTime);
                    needUpdateList.add(inv);

                    // 记录无库位扣减
                    deductRecords.add(buildDeductRecord(inv, inventoryType, deductQty));
                    remainDeductQty -= deductQty;
                }

                // 第二步：无库位不足，扣有库位库存
                if (remainDeductQty > 0) {
                    List<Inventory> hasLocationInvList = inventoryList.stream()
                            .filter(inv -> StringUtils.isNotBlank(inv.getLocationId()))
                            .collect(Collectors.toList());
                    for (Inventory inv : hasLocationInvList) {
                        if (remainDeductQty <= 0) break;
                        Long currentQty = Optional.ofNullable(inv.getQuantity()).orElse(0L);
                        Long deductQty = Math.min(remainDeductQty, currentQty); // 不扣负数

                        inv.setQuantity(currentQty - deductQty);
                        if (inv.getQuantity() <= 0) {
                            inv.setInventoryStatus(0L);
                        }
                        inv.setUpdateBy(updateUser);
                        inv.setUpdateTime(updateTime);
                        needUpdateList.add(inv);

                        // 记录有库位扣减
                        deductRecords.add(buildDeductRecord(inv, inventoryType, deductQty));
                        remainDeductQty -= deductQty;
                    }
                }

                // 最后仍有剩余（所有库存耗尽），允许扣最后一个库存为负数
                if (remainDeductQty > 0 && !inventoryList.isEmpty()) {
                    Inventory lastInv = inventoryList.get(inventoryList.size() - 1);
                    Long deductQty = remainDeductQty;
                    lastInv.setQuantity(Optional.ofNullable(lastInv.getQuantity()).orElse(0L) - deductQty);
                    lastInv.setInventoryStatus(0L);
                    lastInv.setUpdateBy(updateUser);
                    lastInv.setUpdateTime(updateTime);
                    needUpdateList.add(lastInv);
                    deductRecords.add(buildDeductRecord(lastInv, inventoryType, deductQty));
                    remainDeductQty = 0L;
                }
            } else {
                // 有库位：直接扣指定库位库存
                Inventory targetInv = inventoryList.stream()
                        .filter(inv -> locationId.equals(inv.getLocationId()))
                        .findFirst()
                        .orElseThrow(() -> new ServiceException(String.format(
                                "物料[%s]库位[%s]无库存", materialId, locationId)));

                Long currentQty = Optional.ofNullable(targetInv.getQuantity()).orElse(0L);
                Long deductQty = Math.min(remainDeductQty, currentQty);
                targetInv.setQuantity(currentQty - deductQty);
                if (targetInv.getQuantity() <= 0) {
                    targetInv.setInventoryStatus(0L);
                }
                targetInv.setUpdateBy(updateUser);
                targetInv.setUpdateTime(updateTime);
                needUpdateList.add(targetInv);
                deductRecords.add(buildDeductRecord(targetInv, inventoryType, deductQty));
                remainDeductQty -= deductQty;

                // 剩余部分扣为负数
                if (remainDeductQty > 0) {
                    Long finalDeduct = remainDeductQty;
                    targetInv.setQuantity(targetInv.getQuantity() - finalDeduct);
                    targetInv.setInventoryStatus(0L);
                    deductRecords.add(buildDeductRecord(targetInv, inventoryType, finalDeduct));
                    remainDeductQty = 0L;
                }
            }

            // 关联明细与扣减记录
            if (itemId != null && !deductRecords.isEmpty()) {
                deductRecordMap.put(itemId, deductRecords);
            }
        }

        // 批量更新库存
        if (!needUpdateList.isEmpty()) {
            inventoryMapper.batchUpdateInventory(needUpdateList);
            inventoryService.RefreshInventory(needUpdateList.stream().map(Inventory::getId).distinct().collect(Collectors.toList()));
        }

        return deductRecordMap;
    }

    private Map<String, Object> buildDeductRecord(Inventory inv, String inventoryType, Long deductQty) {
        Map<String, Object> record = new HashMap<>();
        record.put("inventoryId", inv.getId());
        record.put("locationId", inv.getLocationId());
        record.put("materialId", inv.getMaterialId());
        record.put("inventoryType", inventoryType);
        record.put("deductQty", deductQty);
        return record;
    }

    /**
     * 扣减单个库存的数量
     * @param inventory 库存对象
     * @param deductQty 待扣减数量
     * @param updateUser 操作人
     * @param updateTime 操作时间
     * @return 剩余未扣减数量
     */
    private Long deductSingleInventory(Inventory inventory, Long deductQty, String updateUser, Date updateTime) {
        Long currentQty = Optional.ofNullable(inventory.getQuantity()).orElse(0L);
        // 关键修改：不再限制扣减数量，直接扣减（允许库存变为负数）
        Long canDeductQty = deductQty; // 原逻辑：Math.min(deductQty, currentQty)

        // 扣减数量（允许负数）
        Long newQty = currentQty - canDeductQty;
        inventory.setQuantity(newQty);

        // 扣减后≤0，设置状态为0（即使是负数也设置）
        if (newQty <= 0) {
            inventory.setInventoryStatus(0L);
        }

        // 补充审计字段
        inventory.setUpdateBy(updateUser);
        inventory.setUpdateTime(updateTime);

        // 返回剩余未扣减数量（扣减全部，剩余为0）
        return 0L; // 原逻辑：deductQty - canDeductQty
    }

    /**
     * 预加载库存分组Map
     * Key=物料ID_库存类型  Value=该维度下所有库位的库存列表（含无库位）
     * 移除仓库维度
     */
    private Map<String, List<Inventory>> loadInventoryGroupMap() {
        Inventory query = new Inventory();
        query.setInventoryStatus(1L);
        query.setIsUsed(1L);
        List<Inventory> inventoryList = inventoryService.selectInventoryList(query);

        if (CollectionUtils.isEmpty(inventoryList)) {
            return Collections.emptyMap();
        }

        // 按「物料ID_库存类型」分组（移除仓库维度）
        return inventoryList.stream()
                .collect(Collectors.groupingBy(
                        inv -> String.join("_",
                                Optional.ofNullable(inv.getMaterialId()).orElse(""),
                                Optional.ofNullable(inv.getInventoryType()).map(String::valueOf).orElse("")
                        ),
                        HashMap::new,
                        Collectors.toList()
                ));
    }

    /**
     * 构建扣减数量Map
     * Key规则：物料ID_库位ID_库存类型（移除仓库维度）
     */
    private Map<String, Long> buildDeductQtyMap(List<OutboundOrderItems> items) {
        Map<String, Long> deductQtyMap = new HashMap<>();
        for (OutboundOrderItems item : items) {
            String key = buildDeductKey(item);
            // 累加扣减数量
            Long qty = Optional.ofNullable(item.getActualQuantity()).orElse(0L);
            deductQtyMap.put(key, deductQtyMap.getOrDefault(key, 0L) + qty);
        }
        return deductQtyMap;
    }

    /**
     * 构建明细的扣减Key
     * 移除仓库维度，Key规则：物料ID_库位ID_库存类型
     */
    private String buildDeductKey(OutboundOrderItems item) {
        return String.join("_",
                Optional.ofNullable(item.getMaterialId()).orElse(""),
                Optional.ofNullable(item.getLocationId()).orElse(""),
                Optional.ofNullable(item.getInventoryType()).map(String::valueOf).orElse("")
        );
    }

    @Override
    public List<Map<String,String>> outboundOrdersTopTenByQuantity() {
        return outboundOrdersMapper.SelectOutboundOrdersMaterialsTopTenByQuantity();
    }

    @Override
    public List<Map<String, String>> outboundOrdersTopTenByAmount() {
        return outboundOrdersMapper.SelectOutboundOrdersMaterialsTopTenByAmount();
    }

    @Override
    public String outboundOrdersCount() {
        return outboundOrdersMapper.outboundOrdersCount();
    }

    /**
     * 新增出库单明细信息
     *
     * @param outboundOrders 出库单主对象
     */
    public void insertOutboundOrderItems(OutboundOrders outboundOrders) {
        List<OutboundOrderItems> outboundOrderItemsList = outboundOrders.getOutboundOrderItemsList();
        String id = outboundOrders.getId();
        if (CollectionUtils.isEmpty(outboundOrderItemsList)) {
            return;
        }

        // 库存校验：失败时抛异常
        boolean isValid = inventoryService.inventoryLockValidation(outboundOrderItemsList);
        if (!isValid) {
            throw new RuntimeException("库存被修改请重新确认");
        }

        // 为明细设置订单ID和主键ID
        for (OutboundOrderItems items : outboundOrderItemsList) {
            items.setOutboundOrderId(id);
            items.setOrderId(outboundOrders.getOrderId());
            items.setId(UUID.randomUUID().toString().replace("-", ""));
        }

        // 批量插入出库单明细
        outboundOrdersMapper.batchOutboundOrderItems(outboundOrderItemsList);

        // 拷贝明细到日志列表
        List<String> inventoryIds = new ArrayList<>();
        List<OutboundOrderLog> outboundOrderLogs = new ArrayList<>();
        for (OutboundOrderItems items : outboundOrderItemsList) {
            OutboundOrderLog log = new OutboundOrderLog();
            BeanUtils.copyProperties(items, log);
            log.setOrderId(items.getOutboundOrderId());
            outboundOrderLogs.add(log);
            inventoryIds.add(log.getInventoryId());
        }

        // 插入日志 + 刷新库存
        if (!outboundOrderLogs.isEmpty()) {
            outboundOrderLogMapper.batchOutboundOrderLog(outboundOrderLogs);
        }
        if (!inventoryIds.isEmpty()) {
            inventoryService.RefreshInventory(inventoryIds);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public String importOutboundOrders(List<OutboundTemplateVO> inboundOrdersList, Boolean isUpdateSupport, String operName, Integer orderType) {
        // 1. 基础空值校验
        if (CollectionUtils.isEmpty(inboundOrdersList)) {
            throw new ServiceException("导入数据不能为空！");
        }

        // 2. 初始化变量
        int totalMainSuccess = 0;
        int totalMainFailure = 0;
        int totalItemSuccess = 0;
        int totalItemFailure = 0;
        StringBuilder successMsg = new StringBuilder();
        StringBuilder failureMsg = new StringBuilder();
        Date now = DateUtils.getNowDate();
        Long userId = SecurityUtils.getUserId();
        String operId = userId != null ? userId.toString() : "system";

        Map<String, OutboundOrders> validMainMap = new HashMap<>();
        Map<String, List<OutboundOrderItems>> validItemMap = new HashMap<>();
        boolean hasValidateError = false;

        // 3. 预加载映射缓存（移除仓库相关映射）
        Map<String, String> sapToMaterialIdMap = loadSapToMaterialIdMap();
        Map<String, String> locationNameToIdMap = loadLocationNameToIdMap();
        Map<String, String> ownerNameToIdMap = loadOwnerNameToIdMap();
        Map<String, AbstractMap.SimpleEntry<String, Long>> inventoryTOIdMap = loadInventoryTOIdMap();

        // 4. 按入库单号分组
        Map<String, List<OutboundTemplateVO>> orderGroupMap = inboundOrdersList.stream()
                .filter(vo -> StringUtils.isNotBlank(vo.getOrderId()))
                .collect(Collectors.groupingBy(OutboundTemplateVO::getOrderId));

        // 5. 数据验证（仅保留新增逻辑，检测到已存在则抛异常）
        for (Map.Entry<String, List<OutboundTemplateVO>> entry : orderGroupMap.entrySet()) {
            String orderId = entry.getKey();
            List<OutboundTemplateVO> voList = entry.getValue();
            OutboundOrders mainDO = null;
            List<OutboundOrderItems> itemDOList = new ArrayList<>();

            try {
                OutboundTemplateVO firstVO = voList.get(0);
                // 检查出库单是否已存在（仅新增，存在则抛异常）
                OutboundOrders outboundOrdersQuery = new OutboundOrders();
                outboundOrdersQuery.setOrderId(orderId);
                List<OutboundOrders> existMains = outboundOrdersMapper.selectOutboundOrdersList(outboundOrdersQuery);

                if (existMains != null && !existMains.isEmpty()) {
                    throw new ServiceException(String.format("入库单号【%s】已存在，当前系统仅支持新增，不支持更新", orderId));
                }

                // 仅新增逻辑：构建新出库单主数据
                mainDO = new OutboundOrders();
                BeanUtils.copyProperties(firstVO, mainDO,
                        "sapNo", "materialName", "plannedQuantity", "actualQuantity",
                        "plannedPackages", "materialUnit", "materialRemark", "warehouseName", "warehouseId");

                // 货主校验
                String ownerName = firstVO.getOwnerName();
                String ownerId = firstVO.getOwnerId();
                if (StringUtils.isNotBlank(ownerName)) {
                    String mappedOwnerId = ownerNameToIdMap.get(ownerName.trim());
                    if (StringUtils.isBlank(mappedOwnerId)) {
                        throw new ServiceException(String.format("业主【%s】不存在，无法新增入库单【%s】",
                                ownerName, orderId));
                    }
                    if (ownerId != null && !ownerId.equals(mappedOwnerId)) {
                        throw new ServiceException(String.format("入库单号【%s】的业主ID【%s】与业主名称【%s】不匹配",
                                orderId, ownerId, ownerName));
                    }
                    mainDO.setOwnerId(mappedOwnerId);
                } else if (ownerId != null) {
                    Owners ownerById = ownersService.selectOwnersById(ownerId);
                    if (ownerById == null) {
                        throw new ServiceException(String.format("入库单号【%s】的业主ID【%s】不存在",
                                orderId, ownerId));
                    }
                    mainDO.setOwnerId(ownerId);
                } else {
                    throw new ServiceException(String.format("入库单号【%s】的业主名称/ID不能为空", orderId));
                }

                // 填充主表必填字段
                mainDO.setId(UUID.randomUUID().toString());
                mainDO.setOrderId(orderId);
                mainDO.setOrderStatus(2L);
                mainDO.setCreateBy(operId);
                mainDO.setCreateTime(now);
                mainDO.setOrderTypeId(String.valueOf(orderType));
                mainDO.setCreateUserCode(operId);
                mainDO.setUpdateBy(operId);
                mainDO.setUpdateTime(now);
                mainDO.setUpdateUserCode(operId);
                mainDO.setSortNo(Optional.ofNullable(mainDO.getSortNo()).orElse(0L));

                // 明细校验
                for (int i = 0; i < voList.size(); i++) {
                    OutboundTemplateVO vo = voList.get(i);
                    int lineNo = i + 1;
                    OutboundOrderItems itemDO = new OutboundOrderItems();

                    BeanUtils.copyProperties(vo, itemDO,
                            "orderId", "systemNo", "orderTypeId", "batchId", "warehouseName", "warehouseId");

                    // 填充明细必填字段
                    itemDO.setId(UUID.randomUUID().toString());
                    itemDO.setOrderId(orderId);
                    itemDO.setBatchCode(Optional.ofNullable(mainDO.getBatchCode()).orElse(""));
                    itemDO.setOutboundOrderId(mainDO.getId());
                    itemDO.setCreateBy(operId);
                    itemDO.setCreateTime(now);
                    itemDO.setCreateUserCode(operId);
                    itemDO.setSortNo(0L);
                    itemDO.setItemStatus(3L); // 设置为已出库状态

                    // 物料SAP校验
                    String sapNo = vo.getSapNo() != null ? vo.getSapNo().trim() : "";
                    if (StringUtils.isBlank(sapNo)) {
                        throw new ServiceException(String.format("入库单号【%s】第%d条明细的物料SAP号不能为空",
                                orderId, lineNo));
                    }
                    String materialId = sapToMaterialIdMap.get(sapNo);
                    if (StringUtils.isBlank(materialId)) {
                        throw new ServiceException(String.format("入库单号【%s】第%d条明细的SAP号【%s】对应的物料不存在",
                                orderId, lineNo, sapNo));
                    }
                    itemDO.setMaterialId(materialId);

                    // 库位校验
                    String locationName = vo.getLocationName() != null ? vo.getLocationName().trim() : "";
                    String locationId = locationNameToIdMap.get(locationName);
                    if (StringUtils.isNotBlank(locationName)) {
                        if (StringUtils.isBlank(locationId)) {
                            throw new ServiceException(String.format("入库单号【%s】第%d条明细的库位【%s】不存在",
                                    orderId, lineNo, locationName));
                        }
                        itemDO.setLocationId(locationId);
                    }

                    // 库存类型设置
                    itemDO.setInventoryType(orderType);

                    // 实际出库数量（使用计划数量）
                    itemDO.setActualQuantity(Optional.ofNullable(vo.getActualQuantity()).orElse(0L));

                    // 库存校验（包含inventoryType）- 有库位才校验，无库位直接跳过
                    if (StringUtils.isNotBlank(locationName)) {
                        String inventoryTypeStr = Optional.ofNullable(orderType).map(String::valueOf).orElse("");
                        String inventoryMatchKey = String.join("_", materialId, locationId, inventoryTypeStr);
                        AbstractMap.SimpleEntry<String, Long> inventoryEntry = inventoryTOIdMap.get(inventoryMatchKey);
                        if (inventoryEntry == null) {
                            throw new ServiceException(String.format(
                                    "入库单号【%s】第%d条明细：物料【%s】+库位【%s】+库存类型【%s】组合的库存记录不存在",
                                    orderId, lineNo, vo.getMaterialName(), vo.getLocationName(), inventoryTypeStr));
                        }
                        itemDO.setInventoryId(inventoryEntry.getKey());
                    }
                    // 无库位时不校验库存，也不设置inventoryId

                    itemDOList.add(itemDO);
                }

                validMainMap.put(orderId, mainDO);
                validItemMap.put(orderId, itemDOList);

            } catch (Exception e) {
                hasValidateError = true;
                totalMainFailure++;
                totalItemFailure += voList.size();
                failureMsg.append(String.format("入库单号【%s】验证失败：%s；\n", orderId, e.getMessage()));
            }
        }

        // 6. 有验证失败直接抛异常
        if (hasValidateError) {
            throw new ServiceException(String.format("验证失败，导入终止！失败详情：%s", failureMsg.toString()));
        }

        // 7. 执行新增操作（仅新增，无更新）
        Map<String, List<OutboundOrderItems>> allItemListMap = new HashMap<>();
        for (Map.Entry<String, OutboundOrders> entry : validMainMap.entrySet()) {
            String orderId = entry.getKey();
            OutboundOrders mainDO = entry.getValue();
            List<OutboundOrderItems> itemDOList = validItemMap.get(orderId);

            // 仅新增主单
            outboundOrdersMapper.insertOutboundOrders(mainDO);
            totalMainSuccess++;
            successMsg.append(String.format("入库单号【%s】已新增；\n", orderId));

            // 插入明细（仅新增，无删除操作）
            if (!CollectionUtils.isEmpty(itemDOList)) {
                // 批量插入新明细
                int itemSuccess = outboundOrderItemsMapper.batchInsertOutboundOrderItems(itemDOList);
                totalItemSuccess += itemSuccess;
                int itemFail = itemDOList.size() - itemSuccess;
                totalItemFailure += itemFail;

                successMsg.append(String.format("入库单号【%s】成功导入%d条物料明细；\n", orderId, itemSuccess));
                if (itemFail > 0) {
                    failureMsg.append(String.format("入库单号【%s】有%d条物料明细导入失败；\n", orderId, itemFail));
                }

                allItemListMap.put(orderId, itemDOList);
            }
        }

        // 8. 执行库存扣减并处理无库位明细拆分
        for (Map.Entry<String, List<OutboundOrderItems>> entry : allItemListMap.entrySet()) {
            List<OutboundOrderItems> itemList = entry.getValue();
            // 执行库存扣减，获取无库位明细的扣减记录
            Map<String, List<Map<String, Object>>> deductRecordMap = deductInventory(itemList, operId, now);

            // 处理无库位明细拆分：整理有效明细后统一插入
            if (!deductRecordMap.isEmpty()) {
                handleNoLocationItemSplit(itemList, deductRecordMap, operId, now);
            }
        }

        // 9. 结果汇总
        if (totalMainFailure > 0 || totalItemFailure > 0) {
            String finalFailureMsg = String.format(
                    "导入结果：成功新增%d个入库单，失败%d个；成功导入%d条明细，失败%d条。失败详情：%s",
                    totalMainSuccess, totalMainFailure, totalItemSuccess, totalItemFailure, failureMsg.toString()
            );
            throw new ServiceException(finalFailureMsg);
        } else {
            String finalSuccessMsg = String.format(
                    "恭喜您，数据已全部导入成功！共新增%d个入库单，成功导入%d条物料明细。详情：%s",
                    totalMainSuccess, totalItemSuccess, successMsg.toString()
            );
            return finalSuccessMsg;
        }
    }

    /**
     * 处理无库位明细拆分：
     * 1. 过滤无效扣减（≤0），按库位合并有效扣减数量
     * 2. 仅新增场景：删除临时明细，插入整理后的有效明细
     * 3. 严格保留原逻辑，仅适配新增场景
     */
    private void handleNoLocationItemSplit(List<OutboundOrderItems> itemList,
                                           Map<String, List<Map<String, Object>>> deductRecordMap,
                                           String operId, Date now) {
        List<OutboundOrderItems> newValidItemList = new ArrayList<>();
        Set<String> orderIdSet = new HashSet<>();

        // 第一步：遍历扣减记录，整理有效明细（过滤≤0，合并同库位）
        for (OutboundOrderItems item : itemList) {
            String itemId = item.getId();
            List<Map<String, Object>> deductRecords = deductRecordMap.get(itemId);
            if (CollectionUtils.isEmpty(deductRecords)) continue;

            // 收集订单ID（用于删除临时明细）
            orderIdSet.add(item.getOutboundOrderId());

            // 按库位合并扣减数量，过滤≤0的无效记录
            Map<String, Long> locationQtyMap = new HashMap<>();
            for (Map<String, Object> rec : deductRecords) {
                String locId = (String) rec.get("locationId");
                Long deductQty = (Long) rec.get("deductQty");
                if (deductQty <= 0) continue; // 过滤无效扣减
                locationQtyMap.put(locId, locationQtyMap.getOrDefault(locId, 0L) + deductQty);
            }

            // 生成有效明细
            for (Map.Entry<String, Long> entry : locationQtyMap.entrySet()) {
                String locId = entry.getKey();
                Long validQty = entry.getValue();

                OutboundOrderItems newItem = new OutboundOrderItems();
                BeanUtils.copyProperties(item, newItem);
                newItem.setId(UUID.randomUUID().toString().replace("-", ""));
                newItem.setLocationId(locId);
                newItem.setActualQuantity(validQty);
                newItem.setInventoryId(deductRecords.stream()
                        .filter(r -> locId.equals(r.get("locationId")))
                        .findFirst()
                        .map(r -> (String) r.get("inventoryId"))
                        .orElse(""));
                newItem.setCreateBy(operId);
                newItem.setCreateTime(now);
                newItem.setUpdateBy(operId);
                newItem.setUpdateTime(now);
                newValidItemList.add(newItem);
            }
        }

        // 第二步：删除临时明细（导入时插入的原始明细）
        for (String orderId : orderIdSet) {
            outboundOrderItemsMapper.deleteOutboundOrderItemsByOrderId(orderId);
        }

        // 第三步：批量插入整理后的有效明细
        if (!newValidItemList.isEmpty()) {
            outboundOrderItemsMapper.batchInsertOutboundOrderItems(newValidItemList);

            // 同步生成日志（保留原日志逻辑）
            List<OutboundOrderLog> logList = new ArrayList<>();
            for (OutboundOrderItems newItem : newValidItemList) {
                OutboundOrderLog log = new OutboundOrderLog();
                BeanUtils.copyProperties(newItem, log);
                log.setOrderId(newItem.getOutboundOrderId());
                log.setItemStatus(3L);
                logList.add(log);
            }
            outboundOrderLogMapper.batchOutboundOrderLog(logList);
        }
    }

    // ========== 预加载映射辅助方法（移除仓库相关） ==========
    private Map<String, String> loadSapToMaterialIdMap() {
        List<Materials> materialsList = materialsService.selectMaterialsList(new Materials());
        if (CollectionUtils.isEmpty(materialsList)) {
            return Collections.emptyMap();
        }
        return materialsList.stream()
                .filter(m -> StringUtils.isNotBlank(m.getSapNo()))
                .collect(Collectors.toMap(
                        m -> m.getSapNo().trim(),
                        Materials::getId,
                        (k1, k2) -> k1
                ));
    }

    private Map<String, String> loadLocationNameToIdMap() {
        StorageLocations query = new StorageLocations();
        query.setIsUsed(1L);
        List<StorageLocations> locationList = storageLocationsService.selectStorageLocationsList(query);
        if (CollectionUtils.isEmpty(locationList)) {
            return Collections.emptyMap();
        }
        return locationList.stream()
                .filter(l -> StringUtils.isNotBlank(l.getLocationCode()))
                .collect(Collectors.toMap(
                        l -> l.getLocationCode().trim(),
                        StorageLocations::getId,
                        (k1, k2) -> k1
                ));
    }

    private Map<String, String> loadOwnerNameToIdMap() {
        List<Owners> ownerList = ownersService.selectOwnersList(new Owners());
        if (CollectionUtils.isEmpty(ownerList)) {
            return Collections.emptyMap();
        }
        return ownerList.stream()
                .filter(o -> StringUtils.isNotBlank(o.getOwnerName()))
                .collect(Collectors.toMap(
                        o -> o.getOwnerName().trim(),
                        Owners::getId,
                        (k1, k2) -> k1
                ));
    }

    /**
     * 加载库存映射Map（移除仓库维度）
     * Key=物料ID_库位ID_库存类型
     */
    private Map<String, AbstractMap.SimpleEntry<String, Long>> loadInventoryTOIdMap() {
        Inventory inventory = new Inventory();
        inventory.setInventoryStatus(1L);
        inventory.setIsUsed(1L);
        List<Inventory> inventoryList = inventoryService.selectInventoryList(inventory);

        if (CollectionUtils.isEmpty(inventoryList)) {
            return Collections.emptyMap();
        }

        Map<String, AbstractMap.SimpleEntry<String, Long>> emptyLocationMap = inventoryList.stream()
                .filter(inv -> StringUtils.isNotBlank(inv.getMaterialId())
                        && inv.getInventoryType() != null
                        && StringUtils.isNotBlank(inv.getId())
                        && StringUtils.isBlank(inv.getLocationId()))
                .collect(Collectors.toMap(
                        inv -> String.join("_",
                                inv.getMaterialId().trim(),
                                "", inv.getInventoryType().toString()),
                        inv -> new AbstractMap.SimpleEntry<>(inv.getId().trim(), Optional.ofNullable(inv.getQuantity()).orElse(0L)),
                        (k1, k2) -> k1,
                        HashMap::new
                ));

        Map<String, AbstractMap.SimpleEntry<String, Long>> nonEmptyLocationMap = inventoryList.stream()
                .filter(inv -> StringUtils.isNotBlank(inv.getMaterialId())
                        && StringUtils.isNotBlank(inv.getLocationId())
                        && StringUtils.isNotBlank(inv.getId())
                        && inv.getInventoryType() != null)
                .collect(Collectors.toMap(
                        inv -> String.join("_",
                                inv.getMaterialId().trim(),
                                inv.getLocationId().trim(),
                                inv.getInventoryType().toString()),
                        inv -> new AbstractMap.SimpleEntry<>(inv.getId().trim(), Optional.ofNullable(inv.getQuantity()).orElse(0L)),
                        (k1, k2) -> k1,
                        HashMap::new
                ));

        emptyLocationMap.putAll(nonEmptyLocationMap);
        return emptyLocationMap;
    }
}