package com.yhy.module.core.service.workbench.impl;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.yhy.module.core.enums.ErrorCodeConstants.SYNC_ALREADY_SYNCED;
import static com.yhy.module.core.enums.ErrorCodeConstants.SYNC_BOQ_NO_QUOTA;
import static com.yhy.module.core.enums.ErrorCodeConstants.SYNC_COMPILE_TREE_NOT_EXISTS;
import static com.yhy.module.core.enums.ErrorCodeConstants.SYNC_DIVISION_NOT_EXISTS;
import static com.yhy.module.core.enums.ErrorCodeConstants.SYNC_LIBRARY_DIVISION_NOT_EXISTS;
import static com.yhy.module.core.enums.ErrorCodeConstants.SYNC_NOT_SYNCED;
import static com.yhy.module.core.enums.ErrorCodeConstants.SYNC_ONLY_BOQ_OR_DIVISION;
import static com.yhy.module.core.enums.ErrorCodeConstants.SYNC_UNIFIED_FEE_NOT_ALLOWED;

import cn.hutool.core.collection.CollUtil;
import com.yhy.module.core.controller.admin.workbench.vo.sync.ApplySyncReqVO;
import com.yhy.module.core.controller.admin.workbench.vo.sync.SetSyncReqVO;
import com.yhy.module.core.controller.admin.workbench.vo.sync.SyncLibraryDivisionRespVO;
import com.yhy.module.core.controller.admin.workbench.vo.sync.SyncLibraryDivisionUpdateReqVO;
import com.yhy.module.core.controller.admin.workbench.vo.sync.SyncPendingRespVO;
import com.yhy.module.core.controller.admin.workbench.vo.sync.SyncSourceUnitRespVO;
import com.yhy.module.core.controller.admin.workbench.vo.sync.UnsetSyncReqVO;
import com.yhy.module.core.dal.dataobject.workbench.SyncBindingDO;
import com.yhy.module.core.dal.dataobject.workbench.SyncLibraryDO;
import com.yhy.module.core.dal.dataobject.workbench.SyncLibraryDivisionDO;
import com.yhy.module.core.dal.dataobject.workbench.SyncLibraryMarketMaterialDO;
import com.yhy.module.core.dal.dataobject.workbench.SyncLibraryResourceDO;
import com.yhy.module.core.dal.dataobject.workbench.WbBoqDivisionDO;
import com.yhy.module.core.dal.dataobject.workbench.WbBoqMarketMaterialDO;
import com.yhy.module.core.dal.dataobject.workbench.WbBoqResourceDO;
import com.yhy.module.core.dal.dataobject.workbench.WbCompileTreeDO;
import com.yhy.module.core.dal.mysql.workbench.SyncBindingMapper;
import com.yhy.module.core.dal.mysql.workbench.SyncLibraryDivisionMapper;
import com.yhy.module.core.dal.mysql.workbench.SyncLibraryMapper;
import com.yhy.module.core.dal.mysql.workbench.SyncLibraryMarketMaterialMapper;
import com.yhy.module.core.dal.mysql.workbench.SyncLibraryResourceMapper;
import com.yhy.module.core.dal.mysql.workbench.WbBoqDivisionMapper;
import com.yhy.module.core.dal.mysql.workbench.WbBoqMarketMaterialMapper;
import com.yhy.module.core.dal.mysql.workbench.WbBoqResourceMapper;
import com.yhy.module.core.dal.mysql.workbench.WbCompileTreeMapper;
import com.yhy.module.core.service.workbench.SyncLibraryService;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

/**
 * 同步库 Service 实现
 *
 * @author yhy
 */
@Service
@Validated
@Slf4j
public class SyncLibraryServiceImpl implements SyncLibraryService {

    @Resource
    private SyncLibraryMapper syncLibraryMapper;
    @Resource
    private SyncLibraryDivisionMapper syncLibraryDivisionMapper;
    @Resource
    private SyncLibraryResourceMapper syncLibraryResourceMapper;
    @Resource
    private SyncLibraryMarketMaterialMapper syncLibraryMarketMaterialMapper;
    @Resource
    private SyncBindingMapper syncBindingMapper;
    @Resource
    private WbBoqDivisionMapper wbBoqDivisionMapper;
    @Resource
    private WbBoqResourceMapper wbBoqResourceMapper;
    @Resource
    private WbBoqMarketMaterialMapper wbBoqMarketMaterialMapper;
    @Resource
    private WbCompileTreeMapper wbCompileTreeMapper;

    @Override
    public List<SyncLibraryDivisionRespVO> getTree(Long projectId) {
        // 查询项目下所有同步库
        List<SyncLibraryDO> libraries = syncLibraryMapper.selectListByProjectId(projectId);
        if (CollUtil.isEmpty(libraries)) {
            return Collections.emptyList();
        }
        // 收集所有同步库ID，批量查询所有分部分项节点
        List<Long> syncLibraryIds = libraries.stream()
                .map(SyncLibraryDO::getId)
                .collect(Collectors.toList());
        List<SyncLibraryDivisionDO> allNodes = syncLibraryDivisionMapper.selectListBySyncLibraryIds(syncLibraryIds);
        if (CollUtil.isEmpty(allNodes)) {
            return Collections.emptyList();
        }
        // 构建树结构（合并所有同步库的节点）
        Map<Long, List<SyncLibraryDivisionDO>> childrenMap = allNodes.stream()
                .filter(n -> n.getParentId() != null)
                .collect(Collectors.groupingBy(SyncLibraryDivisionDO::getParentId));
        // 找到根节点（parentId为null的）
        List<SyncLibraryDivisionDO> roots = allNodes.stream()
                .filter(n -> n.getParentId() == null)
                .collect(Collectors.toList());
        return roots.stream()
                .map(root -> buildTreeNode(root, childrenMap))
                .collect(Collectors.toList());
    }

    @Override
    public List<SyncSourceUnitRespVO> getSourceUnits(Long projectId) {
        // 查询项目下所有同步库
        List<SyncLibraryDO> libraries = syncLibraryMapper.selectListByProjectId(projectId);
        if (CollUtil.isEmpty(libraries)) {
            return Collections.emptyList();
        }
        // 收集所有同步库ID，批量查询绑定关系
        List<Long> syncLibraryIds = libraries.stream()
                .map(SyncLibraryDO::getId)
                .collect(Collectors.toList());
        List<SyncBindingDO> bindings = syncBindingMapper.selectListBySyncLibraryIds(syncLibraryIds);
        return bindings.stream().map(binding -> {
            SyncSourceUnitRespVO vo = new SyncSourceUnitRespVO();
            vo.setBindingId(String.valueOf(binding.getId()));
            vo.setDivisionId(String.valueOf(binding.getDivisionId()));
            vo.setCompileTreeId(String.valueOf(binding.getCompileTreeId()));
            vo.setSyncedVersion(binding.getSyncedVersion());
            // 获取单位工程名称
            WbCompileTreeDO compileTree = wbCompileTreeMapper.selectById(binding.getCompileTreeId());
            if (compileTree != null) {
                vo.setCompileTreeName(compileTree.getName());
            }
            return vo;
        }).collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateDivision(SyncLibraryDivisionUpdateReqVO reqVO) {
        SyncLibraryDivisionDO node = syncLibraryDivisionMapper.selectById(reqVO.getId());
        if (node == null) {
            throw exception(SYNC_LIBRARY_DIVISION_NOT_EXISTS);
        }
        // 更新节点字段
        SyncLibraryDivisionDO updateDO = new SyncLibraryDivisionDO();
        updateDO.setId(node.getId());
        if (reqVO.getCode() != null) updateDO.setCode(reqVO.getCode());
        if (reqVO.getName() != null) updateDO.setName(reqVO.getName());
        if (reqVO.getFeature() != null) updateDO.setFeature(reqVO.getFeature());
        if (reqVO.getUnit() != null) updateDO.setUnit(reqVO.getUnit());
        if (reqVO.getQty() != null) updateDO.setQty(reqVO.getQty());
        if (reqVO.getRate() != null) updateDO.setRate(reqVO.getRate());
        if (reqVO.getCostCode() != null) updateDO.setCostCode(reqVO.getCostCode());
        if (reqVO.getQuotaQtyFormula() != null) updateDO.setQuotaQtyFormula(reqVO.getQuotaQtyFormula());
        if (reqVO.getRemark() != null) updateDO.setRemark(reqVO.getRemark());
        if (reqVO.getAttributes() != null) updateDO.setAttributes(reqVO.getAttributes());
        syncLibraryDivisionMapper.updateById(updateDO);

        // 同步库版本号+1
        SyncLibraryDO library = syncLibraryMapper.selectById(node.getSyncLibraryId());
        if (library != null) {
            SyncLibraryDO updateLib = new SyncLibraryDO();
            updateLib.setId(library.getId());
            updateLib.setVersion(library.getVersion() + 1);
            syncLibraryMapper.updateById(updateLib);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void setSync(SetSyncReqVO reqVO) {
        WbBoqDivisionDO division = wbBoqDivisionMapper.selectById(reqVO.getDivisionId());
        if (division == null) {
            throw exception(SYNC_DIVISION_NOT_EXISTS);
        }
        // 校验节点类型
        if (!WbBoqDivisionDO.NODE_TYPE_BOQ.equals(division.getNodeType())
                && !WbBoqDivisionDO.NODE_TYPE_DIVISION.equals(division.getNodeType())) {
            throw exception(SYNC_ONLY_BOQ_OR_DIVISION);
        }
        // 校验是否已同步
        if (division.getSyncLibraryId() != null) {
            throw exception(SYNC_ALREADY_SYNCED);
        }
        // 清单节点必须有子定额
        if (WbBoqDivisionDO.NODE_TYPE_BOQ.equals(division.getNodeType())) {
            Long childCount = wbBoqDivisionMapper.selectCountByParentId(division.getId());
            if (childCount == 0) {
                throw exception(SYNC_BOQ_NO_QUOTA);
            }
        }
        // 排除统一取费节点
        if (WbBoqDivisionDO.NODE_TYPE_UNIFIED_FEE.equals(division.getNodeType())) {
            throw exception(SYNC_UNIFIED_FEE_NOT_ALLOWED);
        }

        // 获取项目ID
        WbCompileTreeDO compileTree = wbCompileTreeMapper.selectById(division.getCompileTreeId());
        if (compileTree == null) {
            throw exception(SYNC_COMPILE_TREE_NOT_EXISTS);
        }

        // 1. 创建同步库主表
        SyncLibraryDO syncLibrary = SyncLibraryDO.builder()
                .tenantId(division.getTenantId())
                .projectId(compileTree.getProjectId())
                .sourceDivisionId(division.getId())
                .sourceCompileTreeId(division.getCompileTreeId())
                .nodeType(division.getNodeType())
                .code(division.getCode())
                .name(division.getName())
                .feature(division.getFeature())
                .unit(division.getUnit())
                .qty(division.getQty())
                .rate(division.getRate())
                .attributes(division.getAttributes())
                .version(1)
                .build();
        syncLibraryMapper.insert(syncLibrary);

        // 2. 复制分部分项树到同步库（含子节点）
        List<WbBoqDivisionDO> allNodes = new ArrayList<>();
        allNodes.add(division);
        collectChildNodes(division.getId(), allNodes);

        // 过滤掉统一取费节点
        allNodes = allNodes.stream()
                .filter(n -> !WbBoqDivisionDO.NODE_TYPE_UNIFIED_FEE.equals(n.getNodeType()))
                .collect(Collectors.toList());

        // ID映射：原ID → 新ID
        Map<Long, Long> idMapping = new HashMap<>();
        for (WbBoqDivisionDO node : allNodes) {
            SyncLibraryDivisionDO syncDiv = SyncLibraryDivisionDO.builder()
                    .tenantId(node.getTenantId())
                    .syncLibraryId(syncLibrary.getId())
                    .nodeType(node.getNodeType())
                    .sourceType(node.getSourceType())
                    .sourceBoqCatalogId(node.getSourceBoqCatalogId())
                    .sourceBoqItemTreeId(node.getSourceBoqItemTreeId())
                    .sourceQuotaItemId(node.getSourceQuotaItemId())
                    .code(node.getCode())
                    .name(node.getName())
                    .feature(node.getFeature())
                    .unit(node.getUnit())
                    .qty(node.getQty())
                    .rate(node.getRate())
                    .costCode(node.getCostCode())
                    .quotaQtyFormula(node.getQuotaQtyFormula())
                    .sortOrder(node.getSortOrder())
                    .attributes(node.getAttributes())
                    .remark(node.getRemark())
                    .build();
            syncLibraryDivisionMapper.insert(syncDiv);
            idMapping.put(node.getId(), syncDiv.getId());
        }

        // 更新父节点ID映射
        for (WbBoqDivisionDO node : allNodes) {
            if (node.getParentId() != null && idMapping.containsKey(node.getParentId())) {
                Long newId = idMapping.get(node.getId());
                Long newParentId = idMapping.get(node.getParentId());
                SyncLibraryDivisionDO updateParent = new SyncLibraryDivisionDO();
                updateParent.setId(newId);
                updateParent.setParentId(newParentId);
                syncLibraryDivisionMapper.updateById(updateParent);
            }
            // 根节点（即被同步的节点本身）parentId设为null
        }

        // 3. 复制工料机到同步库
        List<Long> quotaNodeIds = allNodes.stream()
                .filter(n -> WbBoqDivisionDO.NODE_TYPE_QUOTA.equals(n.getNodeType()))
                .map(WbBoqDivisionDO::getId)
                .collect(Collectors.toList());
        if (CollUtil.isNotEmpty(quotaNodeIds)) {
            List<WbBoqResourceDO> resources = wbBoqResourceMapper.selectListByDivisionIds(quotaNodeIds);
            // 工料机ID映射（用于复合工料机的parentId映射）
            Map<Long, Long> resIdMapping = new HashMap<>();
            for (WbBoqResourceDO res : resources) {
                Long newDivisionId = idMapping.get(res.getDivisionId());
                if (newDivisionId == null) continue;
                SyncLibraryResourceDO syncRes = SyncLibraryResourceDO.builder()
                        .tenantId(res.getTenantId())
                        .syncDivisionId(newDivisionId)
                        .sourceResourceItemId(res.getSourceResourceItemId())
                        .sourceQuotaResourceId(res.getSourceQuotaResourceId())
                        .resourceType(res.getResourceType())
                        .code(res.getCode())
                        .name(res.getName())
                        .spec(res.getSpec())
                        .unit(res.getUnit())
                        .consumeQty(res.getConsumeQty())
                        .adjustConsumeQty(res.getAdjustConsumeQty())
                        .adjustRate(res.getAdjustRate())
                        .baseConsumeQty(res.getBaseConsumeQty())
                        .baseBasePrice(res.getBaseBasePrice())
                        .usageQty(res.getUsageQty())
                        .taxRate(res.getTaxRate())
                        .taxExclBasePrice(res.getTaxExclBasePrice())
                        .taxInclBasePrice(res.getTaxInclBasePrice())
                        .taxExclCompilePrice(res.getTaxExclCompilePrice())
                        .taxInclCompilePrice(res.getTaxInclCompilePrice())
                        .categoryId(res.getCategoryId())
                        .sourceType(res.getSourceType())
                        .sortOrder(res.getSortOrder())
                        .attributes(res.getAttributes())
                        .snapshotJson(res.getSnapshotJson())
                        .build();
                syncLibraryResourceMapper.insert(syncRes);
                resIdMapping.put(res.getId(), syncRes.getId());
            }
            // 更新复合工料机的parentId
            for (WbBoqResourceDO res : resources) {
                if (res.getParentId() != null && resIdMapping.containsKey(res.getParentId())) {
                    Long newResId = resIdMapping.get(res.getId());
                    Long newParentResId = resIdMapping.get(res.getParentId());
                    if (newResId != null && newParentResId != null) {
                        SyncLibraryResourceDO updateRes = new SyncLibraryResourceDO();
                        updateRes.setId(newResId);
                        updateRes.setParentId(newParentResId);
                        syncLibraryResourceMapper.updateById(updateRes);
                    }
                }
            }

            // 4. 复制市场主材设备到同步库
            List<WbBoqMarketMaterialDO> marketMaterials = wbBoqMarketMaterialMapper.selectListByDivisionIds(quotaNodeIds);
            for (WbBoqMarketMaterialDO mm : marketMaterials) {
                Long newDivisionId = idMapping.get(mm.getDivisionId());
                if (newDivisionId == null) continue;
                SyncLibraryMarketMaterialDO syncMm = SyncLibraryMarketMaterialDO.builder()
                        .tenantId(mm.getTenantId())
                        .syncDivisionId(newDivisionId)
                        .sourceResourceItemId(mm.getSourceResourceItemId())
                        .sourceMarketMaterialId(mm.getSourceMarketMaterialId())
                        .resourceType(mm.getResourceType())
                        .code(mm.getCode())
                        .name(mm.getName())
                        .spec(mm.getSpec())
                        .unit(mm.getUnit())
                        .consumeQty(mm.getConsumeQty())
                        .adjustConsumeQty(mm.getAdjustConsumeQty())
                        .taxRate(mm.getTaxRate())
                        .taxExclBasePrice(mm.getTaxExclBasePrice())
                        .taxInclBasePrice(mm.getTaxInclBasePrice())
                        .taxExclCompilePrice(mm.getTaxExclCompilePrice())
                        .taxInclCompilePrice(mm.getTaxInclCompilePrice())
                        .adjustRate(mm.getAdjustRate())
                        .categoryId(mm.getCategoryId())
                        .sourceType(mm.getSourceType())
                        .baseConsumeQty(mm.getBaseConsumeQty())
                        .baseBasePrice(mm.getBaseBasePrice())
                        .snapshotJson(mm.getSnapshotJson())
                        .usageQty(mm.getUsageQty())
                        .sortOrder(mm.getSortOrder())
                        .attributes(mm.getAttributes())
                        .build();
                syncLibraryMarketMaterialMapper.insert(syncMm);
            }
        }

        // 5. 更新原节点同步状态
        WbBoqDivisionDO updateDiv = new WbBoqDivisionDO();
        updateDiv.setId(division.getId());
        updateDiv.setSyncLibraryId(syncLibrary.getId());
        updateDiv.setIsSyncSource(true);
        wbBoqDivisionMapper.updateById(updateDiv);

        // 6. 创建绑定关系
        SyncBindingDO binding = SyncBindingDO.builder()
                .tenantId(division.getTenantId())
                .syncLibraryId(syncLibrary.getId())
                .divisionId(division.getId())
                .compileTreeId(division.getCompileTreeId())
                .syncedVersion(1)
                .build();
        syncBindingMapper.insert(binding);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void unsetSync(UnsetSyncReqVO reqVO) {
        WbBoqDivisionDO division = wbBoqDivisionMapper.selectById(reqVO.getDivisionId());
        if (division == null) {
            throw exception(SYNC_DIVISION_NOT_EXISTS);
        }
        if (division.getSyncLibraryId() == null) {
            throw exception(SYNC_NOT_SYNCED);
        }

        Long syncLibraryId = division.getSyncLibraryId();

        // 1. 删除绑定关系
        syncBindingMapper.deleteByDivisionId(division.getId());

        // 2. 更新原节点：清除同步状态（使用 UpdateWrapper 显式设置 null）
        wbBoqDivisionMapper.update(null, new com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper<WbBoqDivisionDO>()
                .eq(WbBoqDivisionDO::getId, division.getId())
                .set(WbBoqDivisionDO::getSyncLibraryId, null)
                .set(WbBoqDivisionDO::getIsSyncSource, false));

        // 3. 如果该同步库没有其他绑定，删除同步库
        Long bindingCount = syncBindingMapper.selectCountBySyncLibraryId(syncLibraryId);
        if (bindingCount == 0) {
            deleteSyncLibraryInternal(syncLibraryId);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void applySync(ApplySyncReqVO reqVO) {
        List<SyncBindingDO> bindings = syncBindingMapper.selectListByCompileTreeId(reqVO.getCompileTreeId());
        if (CollUtil.isEmpty(bindings)) {
            return;
        }

        for (SyncBindingDO binding : bindings) {
            SyncLibraryDO library = syncLibraryMapper.selectById(binding.getSyncLibraryId());
            if (library == null) continue;

            // 版本一致则跳过
            if (Objects.equals(binding.getSyncedVersion(), library.getVersion())) {
                continue;
            }

            WbBoqDivisionDO targetDivision = wbBoqDivisionMapper.selectById(binding.getDivisionId());
            if (targetDivision == null) continue;

            // 获取同步库的分部分项树
            List<SyncLibraryDivisionDO> syncNodes = syncLibraryDivisionMapper.selectListBySyncLibraryId(library.getId());

            // 删除原节点下的子节点（定额及其工料机）
            deleteChildNodesAndResources(targetDivision.getId());

            // 从同步库复制最新数据
            copyFromSyncLibrary(syncNodes, targetDivision, library);

            // 更新绑定的已同步版本
            SyncBindingDO updateBinding = new SyncBindingDO();
            updateBinding.setId(binding.getId());
            updateBinding.setSyncedVersion(library.getVersion());
            syncBindingMapper.updateById(updateBinding);
        }
    }

    @Override
    public List<SyncPendingRespVO> checkPending(Long compileTreeId) {
        List<SyncBindingDO> bindings = syncBindingMapper.selectListByCompileTreeId(compileTreeId);
        if (CollUtil.isEmpty(bindings)) {
            return Collections.emptyList();
        }

        List<SyncPendingRespVO> result = new ArrayList<>();
        for (SyncBindingDO binding : bindings) {
            SyncLibraryDO library = syncLibraryMapper.selectById(binding.getSyncLibraryId());
            if (library == null) continue;

            if (!Objects.equals(binding.getSyncedVersion(), library.getVersion())) {
                SyncPendingRespVO vo = new SyncPendingRespVO();
                vo.setSyncLibraryId(String.valueOf(library.getId()));
                vo.setSyncLibraryName(library.getName());
                vo.setSyncLibraryCode(library.getCode());
                vo.setCurrentVersion(library.getVersion());
                vo.setSyncedVersion(binding.getSyncedVersion());
                result.add(vo);
            }
        }
        return result;
    }


    // ==================== 私有方法 ====================

    /**
     * 递归收集子节点
     */
    private void collectChildNodes(Long parentId, List<WbBoqDivisionDO> result) {
        List<WbBoqDivisionDO> children = wbBoqDivisionMapper.selectListByParentId(parentId);
        for (WbBoqDivisionDO child : children) {
            result.add(child);
            collectChildNodes(child.getId(), result);
        }
    }

    /**
     * 构建树节点
     */
    private SyncLibraryDivisionRespVO buildTreeNode(SyncLibraryDivisionDO node,
                                                     Map<Long, List<SyncLibraryDivisionDO>> childrenMap) {
        SyncLibraryDivisionRespVO vo = new SyncLibraryDivisionRespVO();
        vo.setId(String.valueOf(node.getId()));
        vo.setSyncLibraryId(String.valueOf(node.getSyncLibraryId()));
        vo.setParentId(node.getParentId() != null ? String.valueOf(node.getParentId()) : null);
        vo.setNodeType(node.getNodeType());
        vo.setCode(node.getCode());
        vo.setName(node.getName());
        vo.setFeature(node.getFeature());
        vo.setUnit(node.getUnit());
        vo.setQty(node.getQty());
        vo.setRate(node.getRate());
        vo.setCostCode(node.getCostCode());
        vo.setQuotaQtyFormula(node.getQuotaQtyFormula());
        vo.setSortOrder(node.getSortOrder());
        vo.setAttributes(node.getAttributes());
        vo.setRemark(node.getRemark());

        List<SyncLibraryDivisionDO> children = childrenMap.get(node.getId());
        if (CollUtil.isNotEmpty(children)) {
            vo.setChildren(children.stream()
                    .map(child -> buildTreeNode(child, childrenMap))
                    .collect(Collectors.toList()));
        }
        return vo;
    }

    /**
     * 删除子节点及其工料机
     */
    private void deleteChildNodesAndResources(Long parentDivisionId) {
        List<WbBoqDivisionDO> children = wbBoqDivisionMapper.selectListByParentId(parentDivisionId);
        if (CollUtil.isEmpty(children)) return;

        for (WbBoqDivisionDO child : children) {
            // 递归删除子节点
            deleteChildNodesAndResources(child.getId());
            // 删除工料机
            wbBoqResourceMapper.deleteByDivisionId(child.getId());
            // 删除市场主材
            wbBoqMarketMaterialMapper.deleteByDivisionId(child.getId());
            // 删除节点本身
            wbBoqDivisionMapper.deleteById(child.getId());
        }
    }

    /**
     * 从同步库复制数据到工作台
     */
    private void copyFromSyncLibrary(List<SyncLibraryDivisionDO> syncNodes,
                                      WbBoqDivisionDO targetDivision,
                                      SyncLibraryDO library) {
        if (CollUtil.isEmpty(syncNodes)) return;

        // 找到同步库中的根节点（parentId为null的）
        List<SyncLibraryDivisionDO> rootNodes = syncNodes.stream()
                .filter(n -> n.getParentId() == null)
                .collect(Collectors.toList());

        // 同步库根节点的子节点才需要复制（根节点本身对应targetDivision）
        Map<Long, List<SyncLibraryDivisionDO>> childrenMap = syncNodes.stream()
                .filter(n -> n.getParentId() != null)
                .collect(Collectors.groupingBy(SyncLibraryDivisionDO::getParentId));

        // 更新目标节点的基本信息
        WbBoqDivisionDO updateTarget = new WbBoqDivisionDO();
        updateTarget.setId(targetDivision.getId());
        if (CollUtil.isNotEmpty(rootNodes)) {
            SyncLibraryDivisionDO rootNode = rootNodes.get(0);
            updateTarget.setCode(rootNode.getCode());
            updateTarget.setName(rootNode.getName());
            updateTarget.setFeature(rootNode.getFeature());
            updateTarget.setUnit(rootNode.getUnit());
            updateTarget.setQty(rootNode.getQty());
            updateTarget.setRate(rootNode.getRate());
        }
        wbBoqDivisionMapper.updateById(updateTarget);

        // 复制子节点
        for (SyncLibraryDivisionDO rootNode : rootNodes) {
            List<SyncLibraryDivisionDO> children = childrenMap.get(rootNode.getId());
            if (CollUtil.isNotEmpty(children)) {
                copySyncChildren(children, targetDivision.getId(), targetDivision.getCompileTreeId(),
                        targetDivision.getTenantId(), childrenMap, syncNodes);
            }
        }
    }

    /**
     * 递归复制同步库子节点到工作台
     */
    private void copySyncChildren(List<SyncLibraryDivisionDO> children, Long parentId,
                                   Long compileTreeId, Long tenantId,
                                   Map<Long, List<SyncLibraryDivisionDO>> childrenMap,
                                   List<SyncLibraryDivisionDO> allSyncNodes) {
        for (SyncLibraryDivisionDO syncChild : children) {
            // 创建工作台节点
            WbBoqDivisionDO newNode = WbBoqDivisionDO.builder()
                    .tenantId(tenantId)
                    .compileTreeId(compileTreeId)
                    .parentId(parentId)
                    .nodeType(syncChild.getNodeType())
                    .sourceType(syncChild.getSourceType())
                    .sourceBoqCatalogId(syncChild.getSourceBoqCatalogId())
                    .sourceBoqItemTreeId(syncChild.getSourceBoqItemTreeId())
                    .sourceQuotaItemId(syncChild.getSourceQuotaItemId())
                    .code(syncChild.getCode())
                    .name(syncChild.getName())
                    .feature(syncChild.getFeature())
                    .unit(syncChild.getUnit())
                    .qty(syncChild.getQty())
                    .rate(syncChild.getRate())
                    .costCode(syncChild.getCostCode())
                    .quotaQtyFormula(syncChild.getQuotaQtyFormula())
                    .sortOrder(syncChild.getSortOrder())
                    .attributes(syncChild.getAttributes())
                    .remark(syncChild.getRemark())
                    .build();
            wbBoqDivisionMapper.insert(newNode);

            // 如果是定额节点，复制工料机和市场主材
            if (WbBoqDivisionDO.NODE_TYPE_QUOTA.equals(syncChild.getNodeType())) {
                copySyncResources(syncChild.getId(), newNode.getId(), tenantId);
                copySyncMarketMaterials(syncChild.getId(), newNode.getId(), tenantId);
            }

            // 递归复制子节点
            List<SyncLibraryDivisionDO> grandChildren = childrenMap.get(syncChild.getId());
            if (CollUtil.isNotEmpty(grandChildren)) {
                copySyncChildren(grandChildren, newNode.getId(), compileTreeId, tenantId,
                        childrenMap, allSyncNodes);
            }
        }
    }

    /**
     * 从同步库复制工料机到工作台
     */
    private void copySyncResources(Long syncDivisionId, Long targetDivisionId, Long tenantId) {
        List<SyncLibraryResourceDO> syncResources = syncLibraryResourceMapper.selectListBySyncDivisionId(syncDivisionId);
        if (CollUtil.isEmpty(syncResources)) return;

        Map<Long, Long> resIdMapping = new HashMap<>();
        for (SyncLibraryResourceDO syncRes : syncResources) {
            WbBoqResourceDO newRes = WbBoqResourceDO.builder()
                    .tenantId(tenantId)
                    .divisionId(targetDivisionId)
                    .sourceResourceItemId(syncRes.getSourceResourceItemId())
                    .sourceQuotaResourceId(syncRes.getSourceQuotaResourceId())
                    .resourceType(syncRes.getResourceType())
                    .code(syncRes.getCode())
                    .name(syncRes.getName())
                    .spec(syncRes.getSpec())
                    .unit(syncRes.getUnit())
                    .consumeQty(syncRes.getConsumeQty())
                    .adjustConsumeQty(syncRes.getAdjustConsumeQty())
                    .adjustRate(syncRes.getAdjustRate())
                    .baseConsumeQty(syncRes.getBaseConsumeQty())
                    .baseBasePrice(syncRes.getBaseBasePrice())
                    .usageQty(syncRes.getUsageQty())
                    .taxRate(syncRes.getTaxRate())
                    .taxExclBasePrice(syncRes.getTaxExclBasePrice())
                    .taxInclBasePrice(syncRes.getTaxInclBasePrice())
                    .taxExclCompilePrice(syncRes.getTaxExclCompilePrice())
                    .taxInclCompilePrice(syncRes.getTaxInclCompilePrice())
                    .categoryId(syncRes.getCategoryId())
                    .sourceType(syncRes.getSourceType())
                    .sortOrder(syncRes.getSortOrder())
                    .attributes(syncRes.getAttributes())
                    .snapshotJson(syncRes.getSnapshotJson())
                    .build();
            wbBoqResourceMapper.insert(newRes);
            resIdMapping.put(syncRes.getId(), newRes.getId());
        }
        // 更新复合工料机parentId
        for (SyncLibraryResourceDO syncRes : syncResources) {
            if (syncRes.getParentId() != null && resIdMapping.containsKey(syncRes.getParentId())) {
                Long newResId = resIdMapping.get(syncRes.getId());
                Long newParentId = resIdMapping.get(syncRes.getParentId());
                if (newResId != null && newParentId != null) {
                    WbBoqResourceDO updateRes = new WbBoqResourceDO();
                    updateRes.setId(newResId);
                    updateRes.setParentId(newParentId);
                    wbBoqResourceMapper.updateById(updateRes);
                }
            }
        }
    }

    /**
     * 从同步库复制市场主材到工作台
     */
    private void copySyncMarketMaterials(Long syncDivisionId, Long targetDivisionId, Long tenantId) {
        List<SyncLibraryMarketMaterialDO> syncMaterials =
                syncLibraryMarketMaterialMapper.selectListBySyncDivisionId(syncDivisionId);
        if (CollUtil.isEmpty(syncMaterials)) return;

        for (SyncLibraryMarketMaterialDO syncMm : syncMaterials) {
            WbBoqMarketMaterialDO newMm = WbBoqMarketMaterialDO.builder()
                    .tenantId(tenantId)
                    .divisionId(targetDivisionId)
                    .sourceResourceItemId(syncMm.getSourceResourceItemId())
                    .sourceMarketMaterialId(syncMm.getSourceMarketMaterialId())
                    .resourceType(syncMm.getResourceType())
                    .code(syncMm.getCode())
                    .name(syncMm.getName())
                    .spec(syncMm.getSpec())
                    .unit(syncMm.getUnit())
                    .consumeQty(syncMm.getConsumeQty())
                    .adjustConsumeQty(syncMm.getAdjustConsumeQty())
                    .taxRate(syncMm.getTaxRate())
                    .taxExclBasePrice(syncMm.getTaxExclBasePrice())
                    .taxInclBasePrice(syncMm.getTaxInclBasePrice())
                    .taxExclCompilePrice(syncMm.getTaxExclCompilePrice())
                    .taxInclCompilePrice(syncMm.getTaxInclCompilePrice())
                    .adjustRate(syncMm.getAdjustRate())
                    .categoryId(syncMm.getCategoryId())
                    .sourceType(syncMm.getSourceType())
                    .baseConsumeQty(syncMm.getBaseConsumeQty())
                    .baseBasePrice(syncMm.getBaseBasePrice())
                    .snapshotJson(syncMm.getSnapshotJson())
                    .usageQty(syncMm.getUsageQty())
                    .sortOrder(syncMm.getSortOrder())
                    .attributes(syncMm.getAttributes())
                    .build();
            wbBoqMarketMaterialMapper.insert(newMm);
        }
    }

    /**
     * 内部删除同步库（含所有关联数据）
     */
    private void deleteSyncLibraryInternal(Long syncLibraryId) {
        // 获取所有分部分项节点ID
        List<SyncLibraryDivisionDO> divisions = syncLibraryDivisionMapper.selectListBySyncLibraryId(syncLibraryId);
        List<Long> divisionIds = divisions.stream().map(SyncLibraryDivisionDO::getId).collect(Collectors.toList());

        if (CollUtil.isNotEmpty(divisionIds)) {
            // 删除工料机
            syncLibraryResourceMapper.deleteBySyncDivisionIds(divisionIds);
            // 删除市场主材
            syncLibraryMarketMaterialMapper.deleteBySyncDivisionIds(divisionIds);
        }
        // 删除分部分项
        syncLibraryDivisionMapper.deleteBySyncLibraryId(syncLibraryId);
        // 删除绑定关系
        syncBindingMapper.deleteBySyncLibraryId(syncLibraryId);
        // 清理工作台分部分项树上的同步状态（防止脏数据）
        wbBoqDivisionMapper.clearSyncStatus(syncLibraryId);
        // 删除主表
        syncLibraryMapper.deleteById(syncLibraryId);
    }
}
