package com.zt.plat.module.qms.resource.material.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zt.plat.framework.common.biz.system.permission.PermissionCommonApi;
import com.zt.plat.framework.common.exception.ServiceException;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.zt.plat.framework.security.core.util.SecurityFrameworkUtils;
import com.zt.plat.module.infra.api.businessfile.dto.BusinessFileWithUrlRespDTO;
import com.zt.plat.module.qms.business.config.dal.dataobject.ConfigWarehouseLocationDO;
import com.zt.plat.module.qms.business.config.service.ConfigWarehouseLocationService;
import com.zt.plat.module.qms.common.service.BusinessFileService;
import com.zt.plat.module.qms.core.constant.DataTypeConstant;
import com.zt.plat.module.qms.enums.QmsPermissionConstant;
import com.zt.plat.module.qms.resource.material.controller.vo.MaterialProductPageReqVO;
import com.zt.plat.module.qms.resource.material.controller.vo.MaterialProductRespVO;
import com.zt.plat.module.qms.resource.material.controller.vo.MaterialProductSaveReqVO;
import com.zt.plat.module.qms.resource.material.controller.vo.query.MaterialProductQueryVO;
import com.zt.plat.module.qms.resource.material.dal.dataobject.MaterialProductDO;
import com.zt.plat.module.qms.resource.material.dal.mapper.MaterialProductMapper;
import com.zt.plat.module.qms.resource.material.enums.MaterialInventoryAlarmLevel;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated;

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

import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.zt.plat.module.qms.enums.ErrorCodeConstants.*;

/**
 * 物料大类 Service 实现类
 *
 * @author 后台管理
 */
@Slf4j
@Service
@Validated
public class MaterialProductServiceImpl implements MaterialProductService {

    @Resource
    private MaterialProductMapper materialProductMapper;

    @Autowired
    private BusinessFileService businessFileService;

    @Autowired
    private ConfigWarehouseLocationService configWarehouseLocationService;
    @Autowired
    private PermissionCommonApi permissionCommonApi;

    /**
     * 保存分类
     *
     */
    @Transactional
    @Override
    public MaterialProductRespVO saveMaterialCategory(MaterialProductSaveReqVO createReqVO) {
        Long reqId = createReqVO.getId();

        MaterialProductDO mtrl = BeanUtils.toBean(createReqVO, MaterialProductDO.class);

        LambdaQueryWrapperX<MaterialProductDO> queryWrapperX = new LambdaQueryWrapperX<MaterialProductDO>()
                .eqIfPresent(MaterialProductDO::getParentId, createReqVO.getParentId())
                .eqIfPresent(MaterialProductDO::getName, createReqVO.getName());
        // 新增
        if (reqId == null) {
            // 检查分类名称是否重复
            boolean exists = materialProductMapper.exists(queryWrapperX);
            if (exists) throw exception(DICTIONARY_BUSINESS_CLASSIFY_DUPLICATE);

            mtrl.setNodeType(DataTypeConstant.DATA_TYPE_CATEGORY);

            if (mtrl.getParentId() != 0) {
                // 从父分类继承 是否危险品、是否标准物质、是否标准溶液
                MaterialProductDO prnCtg = materialProductMapper.selectById(mtrl.getParentId());
                if (prnCtg == null) throw exception(MATERIAL_PRODUCT_PARENT_NOT_EXISTS);
                mtrl.setHazardous(prnCtg.getHazardous())
                        .setStandardMaterialFlag(prnCtg.getStandardMaterialFlag())
                        .setStandardSolutionFlag(prnCtg.getStandardSolutionFlag());
            }

            materialProductMapper.insert(mtrl);

            String newIdPath = getIdPath(mtrl);
            mtrl.setIdPath(newIdPath);
        }
        // 修改
        else {
            // 校验存在
            MaterialProductDO origDO = materialProductMapper.selectById(reqId);
            if (origDO == null) throw exception(MATERIAL_PRODUCT_NOT_EXISTS);
            // 检查分类名称是否重复
            queryWrapperX.ne(MaterialProductDO::getId, reqId);
            boolean exists = materialProductMapper.exists(queryWrapperX);
            if (exists) throw exception(DICTIONARY_BUSINESS_CLASSIFY_DUPLICATE);

            Long parentId = mtrl.getParentId();
            // 当前节点的父节点不能是当前节点
            if (parentId.equals(reqId))
                throw new ServiceException(DATA_COLLECTION_CLASSIFY_PARENT_ERROR);

            // 父节点没有变更
            if (origDO.getParentId().equals(parentId)) {
                materialProductMapper.updateById(mtrl);
                return BeanUtils.toBean(mtrl, MaterialProductRespVO.class);
            }
            // 检查是否存在父子节点反向嵌套冲突
            if (parentId != 0) {
                MaterialProductDO prnDO = materialProductMapper.selectById(parentId);
                if (prnDO != null && prnDO.getIdPath().contains("/" + reqId + "/"))
                    throw new ServiceException(DATA_COLLECTION_CLASSIFY_PARENT_ERROR);
            }

            String oldIdPath = origDO.getIdPath();
            String newIdPath = getIdPath(mtrl);
            // 更新后代节点路径
            updateChrIdPath(reqId, oldIdPath, newIdPath);
            mtrl.setIdPath(newIdPath);

        }
        materialProductMapper.updateById(mtrl);
        return BeanUtils.toBean(mtrl, MaterialProductRespVO.class);

    }

    /**
     * 获取分类树
     *
     * @return 树列表
     */
    @Override
    public List<MaterialProductRespVO> getMaterialCategoryTree() {
        List<MaterialProductDO> list = materialProductMapper.selectList(Wrappers.lambdaQuery(MaterialProductDO.class)
                .eq(MaterialProductDO::getNodeType, DataTypeConstant.DATA_TYPE_CATEGORY)
                .orderByAsc(MaterialProductDO::getSortNo));
        if (CollUtil.isEmpty(list)) return List.of();

        List<MaterialProductRespVO> voList = list.stream().map(m -> BeanUtils.toBean(m, MaterialProductRespVO.class)).toList();
        // 返回分类树
        return listTransTree(voList, 0L);
    }

    private void updateChrIdPath(Long reqId, String oldIdPath, String newIdPath) {
        List<MaterialProductDO> children = materialProductMapper.selectList(Wrappers.lambdaQuery(MaterialProductDO.class)
                .like(MaterialProductDO::getIdPath, "/" + reqId + "/")
                .ne(MaterialProductDO::getId, reqId));
        if (CollUtil.isNotEmpty(children)) {
            List<MaterialProductDO> toUpdateChildren = children.stream().peek(m -> {
                String idPath = m.getIdPath().replace(oldIdPath, newIdPath);
                m.setIdPath(idPath);
            }).toList();
            materialProductMapper.updateBatch(toUpdateChildren);
        }
    }

    private String getIdPath(MaterialProductDO mtrl) {
        String idPath = "";
        Long parentId = mtrl.getParentId();
        if (parentId == 0) {
            idPath = "/" + 0 + "/" + mtrl.getId() + "/";
        } else {
            MaterialProductDO mtrlPrn = materialProductMapper.selectById(parentId);
            if (mtrlPrn != null) idPath = mtrlPrn.getIdPath() + mtrl.getId() + "/" ;
        }
        return idPath;
    }

    /**
     * 组装分类树
     *
     */
    private List<MaterialProductRespVO> listTransTree(List<MaterialProductRespVO> mtrlVOList, Long parentId) {

        // 获取父级节点
        List<MaterialProductRespVO> parentMtrlVOs = mtrlVOList.stream().filter(mtrlVo ->
                mtrlVo.getParentId().equals(parentId)).collect(Collectors.toList());
        // 设置响应的子节点
        parentMtrlVOs.forEach(mtrlVO -> mtrlVO.setChildren(listTransTree(mtrlVOList, mtrlVO.getId())));

        return parentMtrlVOs;
    }

    @Transactional
    @Override
    public MaterialProductRespVO saveMaterialProduct(MaterialProductSaveReqVO createReqVO) {
        Long reqId = createReqVO.getId();
        MaterialProductDO mtrl = BeanUtils.toBean(createReqVO, MaterialProductDO.class);
        mtrl.setNodeType(DataTypeConstant.DATA_TYPE_DATA);

        // 编码 + 规格 做重复检查
        LambdaQueryWrapperX<MaterialProductDO> queryWrapperX = new LambdaQueryWrapperX<MaterialProductDO>()
                .eqIfPresent(MaterialProductDO::getCode, createReqVO.getCode())
                .eqIfPresent(MaterialProductDO::getSpecification, createReqVO.getSpecification())
                .eqIfPresent(MaterialProductDO::getNodeType, DataTypeConstant.DATA_TYPE_DATA);
        // 新增
        if (reqId == null) {
            boolean exists = materialProductMapper.exists(queryWrapperX);
            if (exists) throw exception(MATERIAL_PRODUCT_CODE_SPECIFICATION_EXISTED);
            // 从直接父分类获取是否危险品、是否标准物质、是否标准溶液
            MaterialProductDO prnCtg = materialProductMapper.selectById(mtrl.getParentId());
            if (prnCtg != null) {
                mtrl.setHazardous(prnCtg.getHazardous())
                        .setStandardMaterialFlag(prnCtg.getStandardMaterialFlag())
                        .setStandardSolutionFlag(prnCtg.getStandardSolutionFlag());
            }
            if (mtrl.getInventoryAlarmFlag() != null && mtrl.getInventoryAlarmFlag() == 1)
                mtrl.setInventoryAlarmLevel(MaterialInventoryAlarmLevel.danger.name());
            materialProductMapper.insert(mtrl);
            // 更新 idPath

        } else {
            // 修改
            MaterialProductDO origDO = materialProductMapper.selectById(reqId);
            if (origDO == null) throw exception(MATERIAL_PRODUCT_NOT_EXISTS);
            queryWrapperX.neIfPresent(MaterialProductDO::getId, reqId);
            boolean exists = materialProductMapper.exists(queryWrapperX);
            if (exists) throw exception(MATERIAL_PRODUCT_CODE_SPECIFICATION_EXISTED);

            businessFileService.deleteBusinessFileList(createReqVO.getDeleteFileIdList());

            Long newParentId = createReqVO.getParentId();
            if (!origDO.getParentId().equals(newParentId)) {
                // 父节点有变更，更新大类属性
                MaterialProductDO productDO = materialProductMapper.selectById(newParentId);
                MaterialProductDO topParent;
                if (productDO == null) throw new ServiceException(1_032_160_000, "父级分类不存在");
                if (productDO.getParentId().equals(0L)) {
                    topParent = productDO;
                } else {
                    String[] pIds = productDO.getIdPath().split("/");
                    topParent = materialProductMapper.selectById(pIds[1]);
                }
                if (topParent == null) throw new ServiceException(1_032_160_000, "顶级分类不存在");
                mtrl.setHazardous(topParent.getHazardous())
                        .setStandardMaterialFlag(topParent.getStandardMaterialFlag())
                        .setStandardSolutionFlag(topParent.getStandardSolutionFlag());
            }
            // 父节点有变更时更新 idPath
        }
        String newIdPath = getIdPath(mtrl);
        mtrl.setIdPath(newIdPath);
        materialProductMapper.updateById(mtrl);
        // 处理库存预警级别
        handleInventoryAlarmLevel(mtrl, reqId);
        return BeanUtils.toBean(mtrl, MaterialProductRespVO.class);
    }

    private void handleInventoryAlarmLevel(MaterialProductDO mtrl, Long reqId) {
        if (mtrl.getInventoryAlarmFlag() != null && mtrl.getInventoryAlarmFlag() == 1) {
            // 根据实际库存数量计算准确的预警级别
            this.updateMaterialProductAlarmLevel(Collections.singletonList(reqId));
        } else if (mtrl.getInventoryAlarmFlag() != null && mtrl.getInventoryAlarmFlag() == 0) {
            // 明确禁用预警，清空预警级别
            materialProductMapper.update(Wrappers.lambdaUpdate(MaterialProductDO.class)
                    .eq(MaterialProductDO::getId, reqId)
                    .set(MaterialProductDO::getInventoryAlarmLevel, null));
        }
    }

    @Override
    public void updateMaterialProduct(MaterialProductSaveReqVO updateReqVO) {
        // 校验存在
        validateMaterialProductExists(updateReqVO.getId());
        // 更新
        MaterialProductDO updateObj = BeanUtils.toBean(updateReqVO, MaterialProductDO.class);
        materialProductMapper.updateById(updateObj);
    }

    @Override
    public void deleteMaterialProduct(Long id) {
        MaterialProductDO pdtDo = materialProductMapper.selectById(id);
        if (pdtDo == null) throw exception(MATERIAL_PRODUCT_NOT_EXISTS);
        // 分类
        if (DataTypeConstant.DATA_TYPE_CATEGORY.equals(pdtDo.getNodeType())) {
            // 分类下有子分类或大类时不可删除
            boolean exists = materialProductMapper.exists(Wrappers.lambdaQuery(MaterialProductDO.class)
                    .eq(MaterialProductDO::getParentId, id));
            if (exists) throw exception(MATERIAL_CATEGORY_EXISTS_CHILDREN);
        }
        // 大类
        else {
            checkIsDelete(Collections.singletonList(id));
        }
        materialProductMapper.deleteById(id);
    }

    private void checkIsDelete(List<Long> pdtIds) {
        // 大类下有批次时不可删除
        boolean existsBatch = materialProductMapper.checkIsExistsBatchByPdt(pdtIds);
        if (existsBatch) throw exception(MATERIAL_PRODUCT_EXISTS_BATCH);
        // 大类下有溶液配置申请或溶液配置时不可删除
        boolean existsSolution = materialProductMapper.checkIsExistsSolutionByPdt(pdtIds);
        if (existsSolution) throw new ServiceException(1_032_160_000, "大类下有溶液配置申请或已配置的溶液, 不可删除");
        // 大类下有物料实例后不可删除（主要争对危化品配置）
        boolean existsInf = materialProductMapper.checkIsExistsInfByPdt(pdtIds);
        if (existsInf) throw new ServiceException(1_032_160_000, "大类下已有物料实例, 暂不可删除");
    }

    @Override
        public void deleteMaterialProductListByIds(List<Long> ids) {
        // 校验存在
        List<MaterialProductDO> list = materialProductMapper.selectByIds(ids);
        if (CollUtil.isEmpty(list) || list.size() != ids.size()) {
            throw exception(MATERIAL_PRODUCT_NOT_EXISTS);
        }
        // 检查物料大类中是否含有分类数据
        List<MaterialProductDO> mtCtgList = list.stream().filter(m -> DataTypeConstant.DATA_TYPE_CATEGORY.equals(m.getNodeType())).toList();
        if (CollUtil.isNotEmpty(mtCtgList)) throw exception(MATERIAL_PRODUCTS_EXISTS_CATEGORY);
        // 检查是否可删除 大类下有批次时不可删除
        checkIsDelete(ids);
        // 删除
        materialProductMapper.deleteByIds(ids);
        }

    private void validateMaterialProductExists(List<Long> ids) {
        List<MaterialProductDO> list = materialProductMapper.selectByIds(ids);
        if (CollUtil.isEmpty(list) || list.size() != ids.size()) {
            throw exception(MATERIAL_PRODUCT_NOT_EXISTS);
        }
    }

    private void validateMaterialProductExists(Long id) {
        if (materialProductMapper.selectById(id) == null) {
            throw exception(MATERIAL_PRODUCT_NOT_EXISTS);
        }
    }

    @Override
    public MaterialProductDO getMaterialProduct(Long id) {
        return materialProductMapper.selectById(id);
    }

    @Override
    public PageResult<MaterialProductDO> getMaterialProductPage(MaterialProductPageReqVO pageReqVO) {
        return materialProductMapper.selectPage(pageReqVO);
    }

    @Override
    public MaterialProductDO getMaterialProductInfo(Long id) {
        MaterialProductDO mtrlDo = this.getMaterialProduct(id);
        if (mtrlDo == null) return null;
        MaterialProductDO prnMtrl = materialProductMapper.selectById(mtrlDo.getParentId());
        if (prnMtrl == null) return mtrlDo;
        mtrlDo.setCategoryName(prnMtrl.getName());
        return mtrlDo;
    }


    @Override
    public List<MaterialProductRespVO> getCategoryAndData(MaterialProductQueryVO queryVO) {
        LambdaQueryWrapper<MaterialProductDO> wrapperX = new LambdaQueryWrapperX<MaterialProductDO>()
                .eqIfPresent(MaterialProductDO::getNodeType, queryVO.getNodeType())
                .eqIfPresent(MaterialProductDO::getHazardous, queryVO.getHazardous())
                .eqIfPresent(MaterialProductDO::getStandardSolutionFlag, queryVO.getStandardSolutionFlag())
                .eqIfPresent(MaterialProductDO::getStandardMaterialFlag, queryVO.getStandardMaterialFlag())
                .likeIfPresent(MaterialProductDO::getName, queryVO.getName())
                .and(StrUtil.isNotEmpty(queryVO.getCategoryCustomConfig()),wrapper ->
                        wrapper.eq(MaterialProductDO::getNodeType, DataTypeConstant.DATA_TYPE_CATEGORY)
                        .like(MaterialProductDO::getCustomConfig, queryVO.getCategoryCustomConfig()))
                .orderByAsc(MaterialProductDO::getSortNo)
                .orderByAsc(MaterialProductDO::getId);
        List<MaterialProductDO> mtrlDos = materialProductMapper.selectList(wrapperX);

        if (CollUtil.isEmpty(mtrlDos)) return List.of();

        if (StrUtil.isNotEmpty(queryVO.getCategoryCustomConfig()) && StrUtil.isEmpty(queryVO.getNodeType())) {
            List<Long> ctgIds = mtrlDos.stream().map(MaterialProductDO::getId).toList();
            List<MaterialProductDO> pdts = materialProductMapper.selectList(Wrappers.lambdaQuery(MaterialProductDO.class)
                    .in(MaterialProductDO::getParentId, ctgIds));
            mtrlDos.addAll(pdts);
        }

        return BeanUtils.toBean(mtrlDos, MaterialProductRespVO.class);
    }

    @Override
    public PageResult<MaterialProductRespVO> getMaterialInventoryPage(MaterialProductPageReqVO pageReqVO) {
        List<Long> warehouseIds = pageReqVO.getWarehouseIds();
        if (CollUtil.isNotEmpty(warehouseIds)) {
            List<ConfigWarehouseLocationDO> locations = configWarehouseLocationService.getLocationsByWarehouseIds(warehouseIds);
            if (CollUtil.isNotEmpty(locations)) {
                pageReqVO.setLocationIds(locations.stream().map(ConfigWarehouseLocationDO::getId).toList());
            }
        }
        PageResult<MaterialProductDO> pageResult = materialProductMapper.selectPage(pageReqVO);
        List<MaterialProductDO> mtrlDos = pageResult.getList();
        if (CollUtil.isEmpty(mtrlDos)) return new PageResult<>(new ArrayList<>(), pageResult.getTotal());
        List<Long> mtrlIds = mtrlDos.stream().map(MaterialProductDO::getId).toList();
        List<MaterialProductRespVO> voList = mtrlDos.stream().map(m -> BeanUtils.toBean(m, MaterialProductRespVO.class)).toList();
        // 一次性获取大类下的库存数量
        Map<Long, Long> stockQuantityMap = this.getStockQuantityByPdtIds(mtrlIds, warehouseIds);
        for (MaterialProductRespVO vo : voList) {
            Long val = stockQuantityMap.get(vo.getId());
            vo.setInventoryQuantity(BigDecimal.valueOf(val == null ? 0L : val));
        }

        return new PageResult<>(voList, pageResult.getTotal());
    }

    @Override
    public List<MaterialProductDO> getMaterialProductsByLikeIdPath(Long pdtId) {

        return materialProductMapper.selectList(Wrappers.lambdaQuery(MaterialProductDO.class)
                .like(MaterialProductDO::getIdPath, "/" + pdtId + "/")
                .eq(MaterialProductDO::getNodeType, DataTypeConstant.DATA_TYPE_DATA));
    }

    @Override
    public List<MaterialProductDO> getListByIds(List<Long> pdtIds) {

        return materialProductMapper.selectByIds(pdtIds);
    }

    @Transactional
    @Override
    public void updateMaterialProductAlarmLevel(List<Long> pdtIds) {
        if (CollUtil.isEmpty(pdtIds)) return;
        List<MaterialProductDO> pdtDOs = materialProductMapper.selectByIds(pdtIds);
        if (CollUtil.isEmpty(pdtDOs)) return;
        List<MaterialProductDO> alarmPdts = pdtDOs.stream().filter(m -> m.getInventoryAlarmFlag() == 1).toList();
        if (CollUtil.isEmpty(alarmPdts)) return;
        Map<Long, Long> stockQuantityMap = materialProductMapper.getStockQuantityByPdtIds(pdtIds, false);
        List<MaterialProductDO> updateAlarmLevelPdts = alarmPdts.stream().map(m -> {
            String customConfig = m.getCustomConfig();
            String alarmLevel = null;
            if (StrUtil.isNotEmpty(customConfig)) {
                JSONObject alarmRange = JSONUtil.parseObj(customConfig).getJSONObject("alarmRange");
                if (alarmRange != null) {
                    // BigDecimal[] safeRange = (BigDecimal[]) alarmRange.get(MaterialInventoryAlarmLevel.safe.name());
                    List<BigDecimal> dangerRange = alarmRange.getJSONArray(MaterialInventoryAlarmLevel.danger.name()).toList(BigDecimal.class);
                    List<BigDecimal> warnRange = alarmRange.getJSONArray(MaterialInventoryAlarmLevel.warn.name()).toList(BigDecimal.class);
                    Long qty = stockQuantityMap.get(m.getId());
                    BigDecimal inventoryQuantity = BigDecimal.valueOf(qty == null ? 0L : qty);
                    if (inventoryQuantity.compareTo(dangerRange.get(1)) <= 0) {
                        alarmLevel = MaterialInventoryAlarmLevel.danger.name();
                    } else if (inventoryQuantity.compareTo(warnRange.get(1)) <= 0) {
                        alarmLevel = MaterialInventoryAlarmLevel.warn.name();
                    } else {
                        alarmLevel = MaterialInventoryAlarmLevel.safe.name();
                    }
                }
            }
            return new MaterialProductDO().setId(m.getId()).setInventoryAlarmLevel(alarmLevel);
        }).toList();

        materialProductMapper.updateBatch(updateAlarmLevelPdts);
    }

    @Override
    public MaterialProductRespVO getMaterialProductInfoWithFiles(Long id) {
        MaterialProductDO productDO = materialProductMapper.selectById(id);
        MaterialProductRespVO respVO = BeanUtils.toBean(productDO, MaterialProductRespVO.class);
        CommonResult<List<BusinessFileWithUrlRespDTO>> result = businessFileService.getBusinessFileList(id);
        respVO.setBusinessFileRet(result.getData());
        return respVO;
    }

    @Override
    public Map<Long, Long> getStockQuantityByPdtIds(List<Long> mtrlIds) {
        return materialProductMapper.getStockQuantityByPdtIds(mtrlIds, false);
    }

    @Override
    public Map<Long, Long> getStockQuantityByPdtIds(List<Long> mtrlIds, List<Long> warehouseIds) {
        // 可能需要进行部门过滤
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        CommonResult<Boolean> hasAdminRoleRes = permissionCommonApi.hasAnyRoles(loginUserId, QmsPermissionConstant.ADMIN_ROLE);
        CommonResult<Boolean> hasMaterialAdminRoleRes = permissionCommonApi.hasAnyRoles(loginUserId, QmsPermissionConstant.MATERIAL_ADMIN_ROLE);
        // 如果不是管理员，需要按部门过滤
        Boolean dataPermissionByBlgDept = !hasAdminRoleRes.getData() && !hasMaterialAdminRoleRes.getData();
        if (CollUtil.isEmpty(warehouseIds)) return materialProductMapper.getStockQuantityByPdtIds(mtrlIds, dataPermissionByBlgDept);
        return materialProductMapper.getStockQuantityByPdtIds(mtrlIds, warehouseIds);

    }

    @Override
    public List<MaterialProductDO> getTopCategoriesByPdtIds(List<Long> pdtIds) {
        List<MaterialProductDO> productDOS = materialProductMapper.selectByIds(pdtIds);
        // 获取顶级分类ids ipPath格式：/0/2015987059211448321/2018922665117827074/2018932101769150466/
        Set<Long> topCategoryIds = productDOS.stream()
                .map(MaterialProductDO::getIdPath)
                .filter(StrUtil::isNotEmpty)
                .map(path -> path.split("/"))
                .filter(segments -> segments.length > 2)
                .map(segments -> segments[2])
                .map(Long::parseLong)
                .collect(Collectors.toSet());
        return materialProductMapper.selectByIds(topCategoryIds);
    }

    @Override
    public Boolean checkIsVerifyCategoryByPdtId(Long productId) {

        List<MaterialProductDO> topCategories = this.getTopCategoriesByPdtIds(Collections.singletonList(productId));
        if (CollUtil.isEmpty(topCategories)) return false;
        MaterialProductDO topCtg = topCategories.get(0);
        JSONObject customConfig = JSONUtil.parseObj(topCtg.getCustomConfig());
        Object verifyCalibrateFlag = customConfig.get("verifyCalibrateFlag");
        if (verifyCalibrateFlag == null) return false;
        return "1".equals(verifyCalibrateFlag);
    }

    @Override
    public List<MaterialProductDO> getProductListByPdtCodes(List<String> pdtCodes) {

        return materialProductMapper.selectList(Wrappers.lambdaQuery(MaterialProductDO.class)
                .in(MaterialProductDO::getCode, pdtCodes));
    }

    @Override
    public List<MaterialProductDO> getProductListByCodeAndSpec(List<String> codes, List<String> specifications) {
        if (CollUtil.isEmpty(codes) || CollUtil.isEmpty(specifications)) {
            return Collections.emptyList();
        }

        return materialProductMapper.selectList(Wrappers.lambdaQuery(MaterialProductDO.class)
                .in(MaterialProductDO::getCode, codes)
                .in(MaterialProductDO::getSpecification, specifications));
    }

    @Override
    public List<MaterialProductDO> selectListWithCustomSql(MaterialProductPageReqVO reqVO) {
        return materialProductMapper.selectListWithCustomSql(reqVO);
    }

    @Override
    public List<Long> getIdListByIdPath(Long id) {
        List<MaterialProductDO> productDOList = this.listByIdPath(id, DataTypeConstant.DATA_TYPE_DATA);
        List<Long> productIds = productDOList.stream().map(MaterialProductDO::getId).toList();
        return productIds;
    }

    @Override
    public List<MaterialProductDO> listByIdPath(Long id, String nodeType) {
        LambdaQueryWrapper<MaterialProductDO> query = new LambdaQueryWrapper<>();
        query.like(MaterialProductDO::getIdPath, id);
        if(!ObjectUtils.isEmpty(nodeType))
            query.eq(MaterialProductDO::getNodeType, nodeType);
        query.orderByAsc(MaterialProductDO::getSortNo);
        return materialProductMapper.selectList(query);
    }
}