package cn.iocoder.yudao.module.pms.service.iotbom;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.pms.controller.admin.iotbom.vo.IotBomListReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotbom.vo.IotBomPageReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotbom.vo.IotBomRespVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotbom.vo.IotBomSaveReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotcommonbommaterial.vo.IotCommonBomMaterialSaveReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotdevicebom.vo.IotDeviceBomListReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotmaintenancebom.vo.IotMaintenanceBomPageReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotmainworkorderbom.vo.IotMainWorkOrderBomPageReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.vo.IotDevicePageReqVO;
import cn.iocoder.yudao.module.pms.dal.dataobject.IotDeviceDO;
import cn.iocoder.yudao.module.pms.dal.dataobject.IotProductClassifyDO;
import cn.iocoder.yudao.module.pms.dal.dataobject.iotbom.IotBomDO;
import cn.iocoder.yudao.module.pms.dal.dataobject.iotcommonbommaterial.IotCommonBomMaterialDO;
import cn.iocoder.yudao.module.pms.dal.dataobject.iotdevicebom.IotDeviceBomDO;
import cn.iocoder.yudao.module.pms.dal.dataobject.iotdevicematerial.IotDeviceMaterialDO;
import cn.iocoder.yudao.module.pms.dal.dataobject.iotmaintenancebom.IotMaintenanceBomDO;
import cn.iocoder.yudao.module.pms.dal.dataobject.iotmainworkorderbom.IotMainWorkOrderBomDO;
import cn.iocoder.yudao.module.pms.dal.mysql.IotDeviceMapper;
import cn.iocoder.yudao.module.pms.dal.mysql.iotbom.IotBomMapper;
import cn.iocoder.yudao.module.pms.dal.mysql.iotcommonbommaterial.IotCommonBomMaterialMapper;
import cn.iocoder.yudao.module.pms.dal.mysql.iotdevicebom.IotDeviceBomMapper;
import cn.iocoder.yudao.module.pms.dal.mysql.iotdevicematerial.IotDeviceMaterialMapper;
import cn.iocoder.yudao.module.pms.dal.mysql.iotmaintenancebom.IotMaintenanceBomMapper;
import cn.iocoder.yudao.module.pms.dal.mysql.iotmainworkorderbom.IotMainWorkOrderBomMapper;
import cn.iocoder.yudao.module.pms.service.IotProductClassifyService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap;
import static cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant.IOT_BOM_NOT_EXISTS;
import static cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant.IOT_PRODUCT_CLASSIFY_NOT_EXISTS;

/**
 * PMS BOM 关系 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Validated
public class IotBomServiceImpl implements IotBomService {

    @Resource
    private IotBomMapper iotBomMapper;
    @Resource
    private IotProductClassifyService iotProductClassifyService;
    @Resource
    private IotDeviceBomMapper iotDeviceBomMapper;
    @Resource
    private IotDeviceMapper iotDeviceMapper;
    @Resource
    private IotCommonBomMaterialMapper iotCommonBomMaterialMapper;
    @Resource
    private IotDeviceMaterialMapper iotDeviceMaterialMapper;

    @Resource
    private IotMainWorkOrderBomMapper iotMainWorkOrderBomMapper;
    @Resource
    private IotMaintenanceBomMapper iotMaintenanceBomMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createIotBom(IotBomSaveReqVO createReqVO) {
        // 插入
        IotBomDO iotBom = BeanUtils.toBean(createReqVO, IotBomDO.class);
        // 设置当前节点为 叶子
        iotBom.setLeafFlag(1);
        // 设置父节点为 非叶子节点
        if (!createReqVO.getParentId().equals(0L)) {
            IotBomDO parentBomNode = iotBomMapper.selectParentNode(createReqVO);
            parentBomNode.setLeafFlag(2);
            iotBomMapper.updateById(parentBomNode);
        }
        iotBomMapper.insert(iotBom);
        // 返回
        return iotBom.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateIotBom(IotBomSaveReqVO updateReqVO) {
        // 校验存在
        validateIotBomExists(updateReqVO.getId());
        // 更新
        IotBomDO updateObj = BeanUtils.toBean(updateReqVO, IotBomDO.class);
        iotBomMapper.updateById(updateObj);
        syncDeviceBomAndMaterials(updateObj.getDeviceCategoryId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateIotBomAlone(IotBomSaveReqVO updateReqVO) {
        // 校验存在
        // validateIotBomExists(updateReqVO.getId());
        // 更新
        // IotBomDO updateObj = BeanUtils.toBean(updateReqVO, IotBomDO.class);
        // iotBomMapper.updateById(updateObj);
        syncDeviceBomAndMaterialsAlone(updateReqVO.getDeviceCategoryId());
    }

    /**
     * 批量更新设备的 bom_sync_status = 1
     * @param ids
     */
    private void batchUpdateBomSyncStatus(List<Long> ids) {
        LambdaUpdateWrapper<IotDeviceDO> wrapper = new LambdaUpdateWrapper<>();
        wrapper.in(IotDeviceDO::getId, ids) // WHERE id IN (ids)
                .set(IotDeviceDO::getBomSyncStatus, 1); // SET bom_sync_status = 1
        iotDeviceMapper.update(wrapper);
    }

    @Override
    public void deleteIotBom(Long id) {
        // 校验存在
        IotBomDO bom = validateIotBomExists(id);
        // 删除
        iotBomMapper.deleteById(id);
        // 将设备分类BOM同步到 关联了当前设备分类的 设备 BOM
        syncDeviceBomAndMaterials(bom.getDeviceCategoryId());
    }

    /**
     * 将设备分类BOM及bom节点挂载的物料同步到 设备bom
     */
    public void syncDeviceBomAndMaterials (Long deviceCategoryId){
        // 如果设备BOM已经关联了保养计划 保养工单 则不必再同步设备BOM

        // 选择了相同设备分类的设备，如果未同步过设备分类BOM, 应该将设备分类BOM同步复制到设备BOM。
        // 设备分类BOM已经挂载的物料 也要拷贝到 设备BOM关联物料表
        // 1. 根据当前BOM节点关联的设备分类查询所有已经关联了此分类但是未同步过 设备分类BOM 的设备
        IotDevicePageReqVO reqVO = new IotDevicePageReqVO();
        reqVO.setAssetClass(deviceCategoryId);
        reqVO.setBomSyncStatus(0);
        List<IotDeviceDO> devices = iotDeviceMapper.selectList(reqVO);
        if (CollUtil.isNotEmpty(devices)) {
            Set<Long> originDeviceIds = new HashSet<>();
            devices.forEach(device -> {
                originDeviceIds.add(device.getId());
            });
            // 如果当前设备集合中的设备有 正在运行(启用+禁用 已经自动过滤了删除项) 的保养计划 保养工单 则不再同步设备BOM
            IotMaintenanceBomPageReqVO bomReqVO = new IotMaintenanceBomPageReqVO();
            bomReqVO.setDeviceIds(new ArrayList<>(originDeviceIds));
            List<IotMaintenanceBomDO> deviceBoms = iotMaintenanceBomMapper.selectList(bomReqVO);
            Set<Long> existDeviceIds = new HashSet<>();
            if (CollUtil.isNotEmpty(deviceBoms)) {
                deviceBoms.forEach(bom -> {
                    existDeviceIds.add(bom.getDeviceId());
                });
            }
            // 查询当前设备集合关联的 运行中(未保养完成) 的工单
            IotMainWorkOrderBomPageReqVO orderBomReqVO = new IotMainWorkOrderBomPageReqVO();
            orderBomReqVO.setDeviceIds(new ArrayList<>(originDeviceIds));
            orderBomReqVO.setStatus(0);
            List<IotMainWorkOrderBomDO> orderBoms = iotMainWorkOrderBomMapper.selectList(orderBomReqVO);
            Set<Long> existOrderDeviceIds = new HashSet<>();
            if (CollUtil.isNotEmpty(orderBoms)) {
                orderBoms.forEach(bom -> {
                    existOrderDeviceIds.add(bom.getDeviceId());
                });
            }
            List<IotDeviceDO> tobeUsedDevices = new ArrayList<>();
            devices.forEach(device -> {
                if (!existDeviceIds.contains(device.getId()) && !existOrderDeviceIds.contains(device.getId())) {
                    tobeUsedDevices.add(device);
                }
            });
            // 2. 批量将当前设备分类BOM复制到每个设备
            List<IotBomDO> bomList = deviceCategoryBom(deviceCategoryId);
            // 3. 构建树形结构并获取层级顺序
            List<IotBomDO> orderedBomS = buildHierarchyOrder(bomList);
            // 当前设备分类下所有bom节点id
            List<Long> bomIds = convertListByFlatMap(orderedBomS, bom -> Stream.of(bom.getId()));
            IotCommonBomMaterialSaveReqVO saveReqVO = new IotCommonBomMaterialSaveReqVO();
            saveReqVO.setBomNodeIds(bomIds);
            List<IotCommonBomMaterialDO> bomMaterials = iotCommonBomMaterialMapper.selectList(saveReqVO);
            Map<Long, List<IotCommonBomMaterialDO>> bomNodeMaterialMap = bomMaterials.stream()
                    .collect(Collectors.groupingBy(
                            IotCommonBomMaterialDO::getBomNodeId,  // 分组键：提取 bomNodeId
                            Collectors.toList()                      // 分组值：保存为列表
                    ));
            // 设备bom列表
            List<IotDeviceBomDO> resultDeviceBomList = new ArrayList<>();
            // 设备bom关联物料列表
            List<IotDeviceMaterialDO> deviceMaterialList = new ArrayList<>();
            List<Long> deviceIds = new ArrayList<>();
            tobeUsedDevices.forEach(device -> {
                deviceIds.add(device.getId());
                // 4. 生成ID映射关系
                Map<Long, Long> idMapping = new LinkedHashMap<>();
                orderedBomS.forEach(bom -> idMapping.put(bom.getId(), IdGenerator.nextId()));
                // 5. 转换目标对象
                List<IotDeviceBomDO> targetBoms = convertBoms(orderedBomS, idMapping, device.getId());
                // 转换 bom关联的物料 目标对象
                List<IotDeviceMaterialDO> targetDeviceMaterials = convertBomMaterials(idMapping, device.getAssetClass(), device.getId(), bomNodeMaterialMap);
                resultDeviceBomList.addAll(targetBoms);
                deviceMaterialList.addAll(targetDeviceMaterials);
            });
            if (CollUtil.isNotEmpty(resultDeviceBomList)) {
                // 先删除相关设备的BOM 及 BOM节点已经关联的物料 (如果存在)
                Integer count = iotDeviceBomMapper.deleteByDeviceIds(deviceIds);
                Integer materialCount = iotDeviceMaterialMapper.deleteByDeviceIds(deviceIds);
                // 批量插入设备BOM
                iotDeviceBomMapper.insertBatch(resultDeviceBomList);
                if (CollUtil.isNotEmpty(deviceMaterialList)) {
                    // 批量插入设备关联的物料
                    iotDeviceMaterialMapper.insertBatch(deviceMaterialList);
                }
                // 批量设置 设备 的BOM同步标识 bom_sync_status = 1
                // 单独修改设备BOM后再将 bom_sync_status 设置为 1
                // batchUpdateBomSyncStatus(deviceIds);
            }
        }
    }

    @Override
    public void copyCategoryBom(IotBomSaveReqVO updateReqVO) {
        // 校验 被拷贝的设备分类ID 与
        if (ObjUtil.isEmpty(updateReqVO.getDeviceCategoryId()) || ObjUtil.isEmpty(updateReqVO.getToDeviceCategoryId())) {
            throw exception(IOT_PRODUCT_CLASSIFY_NOT_EXISTS);
        }
        // 查询 deviceCategoryId 下关联的BOM
        List<IotBomDO> existBomList = deviceCategoryBom(updateReqVO.getDeviceCategoryId());
        if (CollUtil.isEmpty(existBomList)) {
            throw exception(IOT_BOM_NOT_EXISTS);
        }
        // 3. 构建树形结构并获取层级顺序
        List<IotBomDO> orderedBomS = buildHierarchyOrder(existBomList);
        // 当前设备分类下所有bom节点id
        List<Long> bomIds = convertListByFlatMap(orderedBomS, bom -> Stream.of(bom.getId()));
        IotCommonBomMaterialSaveReqVO saveReqVO = new IotCommonBomMaterialSaveReqVO();
        saveReqVO.setBomNodeIds(bomIds);
        List<IotCommonBomMaterialDO> bomMaterials = iotCommonBomMaterialMapper.selectList(saveReqVO);
        Map<Long, List<IotCommonBomMaterialDO>> bomNodeMaterialMap = bomMaterials.stream()
                .collect(Collectors.groupingBy(
                        IotCommonBomMaterialDO::getBomNodeId,  // 分组键：提取 bomNodeId
                        Collectors.toList()                      // 分组值：保存为列表
                ));

        // 新 bom 列表
        List<IotBomDO> resultCategoryBomList = new ArrayList<>();
        // 新 bom 关联物料列表
        List<IotCommonBomMaterialDO> bomMaterialList = new ArrayList<>();
        // 4. 生成ID映射关系
        Map<Long, Long> idMapping = new LinkedHashMap<>();
        orderedBomS.forEach(bom -> idMapping.put(bom.getId(), IdGenerator.nextId()));
        // 5. 转换目标对象
        List<IotBomDO> targetBoms = convertCategoryBoms(orderedBomS, updateReqVO.getToDeviceCategoryId(), idMapping);
        // 转换 bom关联的物料 目标对象
        List<IotCommonBomMaterialDO> targetCommonBomMaterials = convertCategoryBomMaterials(idMapping,
                updateReqVO.getToDeviceCategoryId(), bomNodeMaterialMap);
        resultCategoryBomList.addAll(targetBoms);
        bomMaterialList.addAll(targetCommonBomMaterials);
        // 先删除已有的 新 设备分类id 下的BOM 及关联的设备
        List<Long> deviceCategoryIds = new ArrayList<>();
        deviceCategoryIds.add(updateReqVO.getToDeviceCategoryId());
        iotBomMapper.deleteByDeviceCategoryIds(deviceCategoryIds);
        iotCommonBomMaterialMapper.deleteByDeviceCategoryIds(deviceCategoryIds);
        if (CollUtil.isNotEmpty(resultCategoryBomList)) {
            iotBomMapper.insertBatch(resultCategoryBomList);
            if (CollUtil.isNotEmpty(bomMaterialList)) {
                iotCommonBomMaterialMapper.insertBatch(bomMaterialList);
            }
        }
    }

    /**
     * 将设备分类BOM及bom节点挂载的物料同步到 设备bom
     */
    public void syncDeviceBomAndMaterialsAlone(Long deviceCategoryId){
        // 只同步没有BOM的设备
        // 查找当前分类 deviceCategoryId 下 没有BOM的设备
        IotDevicePageReqVO deviceReqVO = new IotDevicePageReqVO();
        deviceReqVO.setAssetClass(deviceCategoryId);
        List<IotDeviceDO> devicesUnderCategories = iotDeviceMapper.selectList(deviceReqVO);
        Set<Long> deviceIds = new HashSet<>();
        if (CollUtil.isNotEmpty(devicesUnderCategories)) {
            devicesUnderCategories.forEach(device -> {
                deviceIds.add(device.getId());
            });
        }
        // 查找设备BOM表 筛选出没有BOM的设备
        Map<Long, List<IotDeviceBomDO>> deviceBomPair = new HashMap<>();
        if (CollUtil.isNotEmpty(deviceIds)) {
            IotDeviceBomListReqVO reqVO = new IotDeviceBomListReqVO();
            reqVO.setDeviceIds(deviceIds);
            List<IotDeviceBomDO> deviceBoms = iotDeviceBomMapper.selectList(reqVO);
            if (CollUtil.isNotEmpty(deviceBoms)) {
                deviceBoms.forEach(bom -> {
                    if (deviceBomPair.containsKey(bom.getDeviceId())) {
                        List<IotDeviceBomDO> tempBoms = deviceBomPair.get(bom.getDeviceId());
                        tempBoms.add(bom);
                        deviceBomPair.put(bom.getDeviceId(), tempBoms);
                    } else {
                        List<IotDeviceBomDO> tempBoms = new ArrayList<>();
                        tempBoms.add(bom);
                        deviceBomPair.put(bom.getDeviceId(), tempBoms);
                    }
                });
            }
        }

        List<IotDeviceDO> tobeSyncBomDevices = new ArrayList<>();
        if (CollUtil.isNotEmpty(devicesUnderCategories)) {
            devicesUnderCategories.forEach(device -> {
                if (!deviceBomPair.containsKey(device.getId())) {
                    // 设备没有同步过分类BOM 可以更新同步 与已经同步过分类BOM的设备区分开
                    tobeSyncBomDevices.add(device);
                }
            });
        }

        // 选择了相同设备分类的设备，如果未同步过设备分类BOM, 应该将设备分类BOM同步复制到设备BOM。
        // 设备分类BOM已经挂载的物料 也要拷贝到 设备BOM关联物料表
        // 1. 根据当前BOM节点关联的设备分类查询所有已经关联了此分类但是未同步过 设备分类BOM 的设备
        if (CollUtil.isNotEmpty(tobeSyncBomDevices)) {
            // 2. 批量将当前设备分类BOM复制到每个设备
            List<IotBomDO> bomList = deviceCategoryBom(deviceCategoryId);
            // 3. 构建树形结构并获取层级顺序
            List<IotBomDO> orderedBomS = buildHierarchyOrder(bomList);
            // 当前设备分类下所有bom节点id
            List<Long> bomIds = convertListByFlatMap(orderedBomS, bom -> Stream.of(bom.getId()));
            IotCommonBomMaterialSaveReqVO saveReqVO = new IotCommonBomMaterialSaveReqVO();
            saveReqVO.setBomNodeIds(bomIds);
            List<IotCommonBomMaterialDO> bomMaterials = iotCommonBomMaterialMapper.selectList(saveReqVO);
            Map<Long, List<IotCommonBomMaterialDO>> bomNodeMaterialMap = bomMaterials.stream()
                    .collect(Collectors.groupingBy(
                            IotCommonBomMaterialDO::getBomNodeId,  // 分组键：提取 bomNodeId
                            Collectors.toList()                      // 分组值：保存为列表
                    ));
            // 设备bom列表
            List<IotDeviceBomDO> resultDeviceBomList = new ArrayList<>();
            // 设备bom关联物料列表
            List<IotDeviceMaterialDO> deviceMaterialList = new ArrayList<>();
            List<Long> tobeDelDeviceIds = new ArrayList<>();
            tobeSyncBomDevices.forEach(device -> {
                deviceIds.add(device.getId());
                // 4. 生成ID映射关系
                Map<Long, Long> idMapping = new LinkedHashMap<>();
                orderedBomS.forEach(bom -> idMapping.put(bom.getId(), IdGenerator.nextId()));
                // 5. 转换目标对象
                List<IotDeviceBomDO> targetBoms = convertBoms(orderedBomS, idMapping, device.getId());
                // 转换 bom关联的物料 目标对象
                List<IotDeviceMaterialDO> targetDeviceMaterials = convertBomMaterials(idMapping, device.getAssetClass(), device.getId(), bomNodeMaterialMap);
                resultDeviceBomList.addAll(targetBoms);
                deviceMaterialList.addAll(targetDeviceMaterials);
            });
            if (CollUtil.isNotEmpty(resultDeviceBomList)) {
                // 先删除相关设备的BOM 及 BOM节点已经关联的物料 (如果存在)
                if (CollUtil.isNotEmpty(tobeDelDeviceIds)) {
                    Integer count = iotDeviceBomMapper.deleteByDeviceIds(tobeDelDeviceIds);
                    Integer materialCount = iotDeviceMaterialMapper.deleteByDeviceIds(tobeDelDeviceIds);
                }
                // 批量插入设备BOM
                iotDeviceBomMapper.insertBatch(resultDeviceBomList);
                if (CollUtil.isNotEmpty(deviceMaterialList)) {
                    // 批量插入设备关联的物料
                    iotDeviceMaterialMapper.insertBatch(deviceMaterialList);
                }
                // 单独修改设备BOM后再将 bom_sync_status 设置为 1
            }
        }
    }

    private IotBomDO validateIotBomExists(Long id) {
        IotBomDO bom = iotBomMapper.selectById(id);
        if (ObjUtil.isEmpty(bom)) {
            throw exception(IOT_BOM_NOT_EXISTS);
        }
        return bom;
    }

    @Override
    public IotBomDO getIotBom(Long id) {
        return iotBomMapper.selectById(id);
    }

    @Override
    public PageResult<IotBomDO> getIotBomPage(IotBomPageReqVO pageReqVO) {
        return iotBomMapper.selectPage(pageReqVO);
    }

    @Override
    public List<IotBomDO> getBomList(IotBomListReqVO reqVO) {
        // 查询当前设备分类及子设备分类的所有BOM
        Set<Long> ids = new HashSet<>();
        if (Objects.nonNull(reqVO.getDeviceCategoryId())) {
            ids = iotProductClassifyService.getChildIotProductClassifyList(reqVO.getDeviceCategoryId());
            ids.add(reqVO.getDeviceCategoryId());
        }
        List<IotBomDO> list = iotBomMapper.selectList(reqVO, ids);
        return list;
    }

    @Override
    public PageResult<IotBomRespVO> getBomTreePage(IotBomListReqVO reqVO) {
        // 1. 查询所有符合条件的BOM节点（原逻辑）
        Set<Long> deviceCategoryIds = new HashSet<>();
        if (Objects.nonNull(reqVO.getDeviceCategoryId())) {
            deviceCategoryIds = iotProductClassifyService.getChildIotProductClassifyList(reqVO.getDeviceCategoryId());
            deviceCategoryIds.add(reqVO.getDeviceCategoryId());
        }
        List<IotBomDO> allNodes = iotBomMapper.selectList(reqVO, deviceCategoryIds);
        if (CollUtil.isEmpty(allNodes)) {
            return PageResult.empty();
        }

        // 2. 查询物料数量
        List<Long> bomNodeIds = allNodes.stream().map(IotBomDO::getId).collect(Collectors.toList());
        IotCommonBomMaterialSaveReqVO materialReq = new IotCommonBomMaterialSaveReqVO();
        materialReq.setBomNodeIds(bomNodeIds);
        List<IotCommonBomMaterialDO> materials = iotCommonBomMaterialMapper.selectList1(materialReq);
        Map<Long, Integer> materialCountMap = materials.stream()
                .collect(Collectors.groupingBy(IotCommonBomMaterialDO::getBomNodeId, Collectors.summingInt(x -> 1)));

        // 3. 查询设备分类名称（保持原有逻辑）
        Map<Long, IotProductClassifyDO> deviceCategoryMap = iotProductClassifyService.getIotProductClassifyMap(
                allNodes.stream().map(IotBomDO::getDeviceCategoryId).distinct().collect(Collectors.toList()));

        // 4. DO -> VO 并填充扩展字段
        List<IotBomRespVO> allVoList = allNodes.stream().map(bom -> {
            IotBomRespVO vo = BeanUtils.toBean(bom, IotBomRespVO.class);
            vo.setMaterials(materialCountMap.getOrDefault(bom.getId(), 0));
            Optional.ofNullable(bom.getDeviceCategoryId())
                    .map(deviceCategoryMap::get)
                    .ifPresent(c -> vo.setDeviceCategoryName(c.getName()));
            return vo;
        }).collect(Collectors.toList());

        // 5. 构建树结构（按 parent_id 分组）
        Map<Long, List<IotBomRespVO>> parentIdMap = allVoList.stream()
                .filter(vo -> vo.getParentId() != null)      // 防御 null，正常 parent_id 均有值
                .collect(Collectors.groupingBy(IotBomRespVO::getParentId));

        allVoList.forEach(vo -> {
            List<IotBomRespVO> children = parentIdMap.getOrDefault(vo.getId(), Collections.emptyList());
            children.sort(Comparator.comparingInt(IotBomRespVO::getSort)); // 子节点按 sort 升序
            vo.setChildren(children);
        });

        // 6. 提取顶级节点（parentId == 0 视为顶级，同时兼容 null）
        List<IotBomRespVO> rootNodes = allVoList.stream()
                .filter(vo -> vo.getParentId() != null && vo.getParentId() == 0)
                .sorted(Comparator.comparingInt(IotBomRespVO::getSort))
                .collect(Collectors.toList());

        // 7. 对顶级节点进行内存分页
        int total = rootNodes.size();
        int pageNo = reqVO.getPageNo();
        int pageSize = reqVO.getPageSize();
        int fromIndex = (pageNo - 1) * pageSize;
        int toIndex = Math.min(fromIndex + pageSize, total);
        if (fromIndex >= total) {
            return new PageResult<>(Collections.emptyList(), (long) total);
        }
        List<IotBomRespVO> pageRoots = rootNodes.subList(fromIndex, toIndex);

        return new PageResult<>(pageRoots, (long) total);
    }

    @Override
    public PageResult<IotBomDO> deviceAssociateBomPage(IotBomPageReqVO pageReqVO) {
        return iotBomMapper.deviceAssociateBomPage(pageReqVO);
    }

    @Override
    public List<IotBomDO> getBomList(Collection<Long> deviceCategoryIds) {
        if (CollUtil.isEmpty(deviceCategoryIds)) {
            return Collections.emptyList();
        }
        return iotBomMapper.getListByDeviceCategoryIds(deviceCategoryIds);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void copyBomByCategory(Long deviceCategoryId, Long deviceId) {
        // 1. 查询源数据（按创建时间排序保证基础顺序）
        List<IotBomDO> sourceBoms = deviceCategoryBom(deviceCategoryId);
        if (CollUtil.isNotEmpty(sourceBoms)) {
            // 2. 构建树形结构并获取层级顺序
            List<IotBomDO> orderedBoms = buildHierarchyOrder(sourceBoms);
            // 查询当前所有BOM节点关联的所有物料
            List<Long> bomIds = convertListByFlatMap(orderedBoms, bom -> Stream.of(bom.getId()));
            IotCommonBomMaterialSaveReqVO reqVO = new IotCommonBomMaterialSaveReqVO();
            reqVO.setBomNodeIds(bomIds);
            List<IotCommonBomMaterialDO> bomMaterials = iotCommonBomMaterialMapper.selectList(reqVO);
            Map<Long, List<IotCommonBomMaterialDO>> bomNodeMaterialMap = bomMaterials.stream()
                    .collect(Collectors.groupingBy(
                            IotCommonBomMaterialDO::getBomNodeId,  // 分组键：提取 bomNodeId
                            Collectors.toList()                      // 分组值：保存为列表
                    ));
            // 3. 生成ID映射关系
            Map<Long, Long> idMapping = new LinkedHashMap<>();
            orderedBoms.forEach(bom -> idMapping.put(bom.getId(), IdGenerator.nextId()));
            // 4. 转换目标对象
            List<IotDeviceBomDO> targetBoms = convertBoms(orderedBoms, idMapping, deviceId);
            // 转换 bom关联的物料 目标对象
            List<IotDeviceMaterialDO> targetDeviceMaterials = convertBomMaterials(idMapping, deviceCategoryId, deviceId, bomNodeMaterialMap);
            // 批量插入 设备bom关联的物料  // 设备分类BOM已经挂载的物料 也要拷贝到 设备BOM关联物料表
            iotDeviceMaterialMapper.insertBatch(targetDeviceMaterials);
            // 批量插入设备BOM
            iotDeviceBomMapper.insertBatch(targetBoms);
            // 拷贝BOM成功后 设置设备 bom_sync_status 字段 为 1 已同步
            // 只有在修改 设备BOM后才会将 关联设备的 bom_sync_status 设置为 1
            // iotDeviceMapper.updateBomSyncStatus(deviceId);
        }
    }

    /**
     * 查询某个设备分类下所有BOM节点
     */
    private List<IotBomDO> deviceCategoryBom(Long deviceCategoryId){
        // 1. 查询源数据（按创建时间排序保证基础顺序）
        List<IotBomDO> sourceBoms = iotBomMapper.selectList(new LambdaQueryWrapper<IotBomDO>()
                .eq(IotBomDO::getDeviceCategoryId, deviceCategoryId)
                .eq(IotBomDO::getDeleted, false)
                .orderByAsc(IotBomDO::getCreateTime));
        return sourceBoms;
    }

    /**
     * 转换目标对象
     * @param
     * @param idMapping
     * @return
     */
    private List<IotDeviceMaterialDO> convertBomMaterials(Map<Long, Long> idMapping, Long deviceCategoryId,
                                                          Long deviceId, Map<Long, List<IotCommonBomMaterialDO>> bomNodeMaterialMap) {
        List<IotDeviceMaterialDO> targetDeviceMaterials = new ArrayList<>();
        bomNodeMaterialMap.forEach((k,v) -> {
            // k bomNodeId    v bomNodeId关联的物料集合
            // 获取 bomNodeId 对应的 设备bom nodeId
            Long deviceBomNodeId = idMapping.get(k);
            v.forEach(bomMaterial -> {
                IotDeviceMaterialDO deviceMaterial = new IotDeviceMaterialDO();
                deviceMaterial.setDeviceCategoryId(deviceCategoryId);
                deviceMaterial.setDeviceId(deviceId);
                deviceMaterial.setBomNodeId(deviceBomNodeId);
                deviceMaterial.setName(bomMaterial.getName());
                deviceMaterial.setCode(bomMaterial.getCode());
                deviceMaterial.setQuantity(bomMaterial.getQuantity());
                deviceMaterial.setMaterialId(bomMaterial.getMaterialId());
                deviceMaterial.setCreator(bomMaterial.getCreator());
                deviceMaterial.setCreateTime(LocalDateTime.now());
                targetDeviceMaterials.add(deviceMaterial);
            });
        });
        return targetDeviceMaterials;
    }

    /**
     * 拷贝已有设备分类BOM 转换目标对象
     * @param
     * @param idMapping
     * @return
     */
    private List<IotCommonBomMaterialDO> convertCategoryBomMaterials(Map<Long, Long> idMapping, Long deviceCategoryId,
                                                          Map<Long, List<IotCommonBomMaterialDO>> bomNodeMaterialMap) {
        List<IotCommonBomMaterialDO> targetDeviceMaterials = new ArrayList<>();
        bomNodeMaterialMap.forEach((k,v) -> {
            // k bomNodeId    v bomNodeId关联的物料集合
            // 获取 bomNodeId 对应的 设备分类bom nodeId
            Long deviceBomNodeId = idMapping.get(k);
            v.forEach(bomMaterial -> {
                IotCommonBomMaterialDO commonBomMaterial = new IotCommonBomMaterialDO();
                commonBomMaterial.setDeviceCategoryId(deviceCategoryId);
                commonBomMaterial.setBomNodeId(deviceBomNodeId);
                commonBomMaterial.setName(bomMaterial.getName());
                commonBomMaterial.setCode(bomMaterial.getCode());
                commonBomMaterial.setQuantity(bomMaterial.getQuantity());
                commonBomMaterial.setMaterialId(bomMaterial.getMaterialId());
                commonBomMaterial.setCreator(bomMaterial.getCreator());
                commonBomMaterial.setCreateTime(LocalDateTime.now());
                targetDeviceMaterials.add(commonBomMaterial);
            });
        });
        return targetDeviceMaterials;
    }

    /**
     * 转换目标对象
     * @param orderedBoms
     * @param idMapping
     * @return
     */
    private List<IotDeviceBomDO> convertBoms(List<IotBomDO> orderedBoms, Map<Long, Long> idMapping, Long deviceId) {
        List<IotDeviceBomDO> targetBoms = new ArrayList<>();
        for (IotBomDO source : orderedBoms) {
            IotDeviceBomDO target = new IotDeviceBomDO();

            // 基础字段拷贝
            BeanUtils.copyProperties(source, target);

            // ID映射处理
            target.setId(idMapping.get(source.getId()));
            target.setParentId(idMapping.getOrDefault(source.getParentId(), 0L));

            // 特殊字段处理
            target.setDeviceId(deviceId);
            target.setCreateTime(LocalDateTime.now());
            target.setUpdateTime(LocalDateTime.now());

            targetBoms.add(target);
        }
        return targetBoms;
    }

    /**
     * 拷贝已有的设备分类的BOM
     * @param orderedBoms
     * @param idMapping
     * @return
     */
    private List<IotBomDO> convertCategoryBoms(List<IotBomDO> orderedBoms, Long deviceCategoryId, Map<Long, Long> idMapping) {
        List<IotBomDO> targetBoms = new ArrayList<>();
        for (IotBomDO source : orderedBoms) {
            IotBomDO target = new IotBomDO();

            // 基础字段拷贝
            BeanUtils.copyProperties(source, target);
            target.setDeviceCategoryId(deviceCategoryId);
            // ID映射处理
            target.setId(idMapping.get(source.getId()));
            target.setParentId(idMapping.getOrDefault(source.getParentId(), 0L));

            target.setCreateTime(LocalDateTime.now());
            target.setUpdateTime(LocalDateTime.now());

            targetBoms.add(target);
        }
        return targetBoms;
    }

    /**
     * 拷贝设备分类bom时构建层级结构
     * @param sourceBoms
     * @return
     */
    private List<IotBomDO> buildHierarchyOrder(List<IotBomDO> sourceBoms) {
        // 构建父子关系映射
        Map<Long, List<IotBomDO>> parentChildMap = new HashMap<>();
        Map<Long, IotBomDO> bomMap = new HashMap<>();
        for (IotBomDO bom : sourceBoms) {
            bomMap.put(bom.getId(), bom);
            parentChildMap.computeIfAbsent(bom.getParentId(), k -> new ArrayList<>()).add(bom);
        }
        // 层级遍历排序
        List<IotBomDO> orderedList = new ArrayList<>();
        Queue<IotBomDO> queue = new LinkedList<>(parentChildMap.getOrDefault(0L, Collections.emptyList()));
        while (!queue.isEmpty()) {
            IotBomDO current = queue.poll();
            orderedList.add(current);

            List<IotBomDO> children = parentChildMap.get(current.getId());
            if (children != null) {
                queue.addAll(children);
            }
        }
        return orderedList;
    }

    // 分布式ID生成器
    private static class IdGenerator {
        private static final Snowflake SNOWFLAKE = new Snowflake(1, 1);
        public static long nextId() {
            return SNOWFLAKE.nextId();
        }
    }

}