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

import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
import com.yhy.module.core.controller.admin.infoprice.vo.InfoPriceBookPageReqVO;
import com.yhy.module.core.controller.admin.infoprice.vo.InfoPriceBookRespVO;
import com.yhy.module.core.controller.admin.infoprice.vo.InfoPriceBookSaveReqVO;
import com.yhy.module.core.controller.admin.infoprice.vo.InfoPriceCategoryTreeRespVO;
import com.yhy.module.core.controller.admin.infoprice.vo.InfoPriceCategoryTreeSaveReqVO;
import com.yhy.module.core.controller.admin.infoprice.vo.InfoPriceResourceRespVO;
import com.yhy.module.core.controller.admin.infoprice.vo.InfoPriceResourceSaveReqVO;
import com.yhy.module.core.controller.admin.infoprice.vo.InfoPriceResourcePriceRespVO;
import com.yhy.module.core.controller.admin.infoprice.vo.InfoPriceResourcePriceSaveReqVO;
import com.yhy.module.core.dal.dataobject.infoprice.InfoPriceBookDO;
import com.yhy.module.core.dal.dataobject.infoprice.InfoPriceCategoryTreeDO;
import com.yhy.module.core.dal.dataobject.infoprice.InfoPriceResourceDO;
import com.yhy.module.core.dal.dataobject.infoprice.InfoPriceTreeDO;
import com.yhy.module.core.dal.dataobject.resource.ResourceCatalogItemDO;
import com.yhy.module.core.dal.mysql.infoprice.InfoPriceBookMapper;
import com.yhy.module.core.dal.mysql.infoprice.InfoPriceCategoryTreeMapper;
import com.yhy.module.core.dal.mysql.infoprice.InfoPriceResourceMapper;
import com.yhy.module.core.dal.mysql.infoprice.InfoPriceResourcePriceMapper;
import com.yhy.module.core.dal.mysql.infoprice.InfoPriceTreeMapper;
import com.yhy.module.core.enums.infoprice.InfoPricePublishStatusEnum;
import com.yhy.module.core.service.infoprice.InfoPriceBookService;
import com.yhy.module.core.service.infoprice.InfoPriceCategoryTreeService;
import com.yhy.module.core.service.infoprice.InfoPriceResourceService;
import com.yhy.module.core.service.infoprice.InfoPriceResourcePriceService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.yhy.module.core.enums.ErrorCodeConstants.*;

/**
 * 信息价册 Service 实现类
 *
 * @author yhy
 */
@Service
@Validated
@Slf4j
public class InfoPriceBookServiceImpl implements InfoPriceBookService {

    @Resource
    private InfoPriceBookMapper infoPriceBookMapper;

    @Resource
    private InfoPriceTreeMapper infoPriceTreeMapper;

    @Resource
    private InfoPriceCategoryTreeMapper infoPriceCategoryTreeMapper;

    @Resource
    private InfoPriceResourceMapper infoPriceResourceMapper;

    @Resource
    private InfoPriceResourcePriceMapper infoPriceResourcePriceMapper;

    @Resource
    private InfoPriceCategoryTreeService categoryTreeService;

    @Resource
    private InfoPriceResourceService infoPriceResourceService;

    @Resource
    private InfoPriceResourcePriceService infoPriceResourcePriceService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createInfoPriceBook(InfoPriceBookSaveReqVO createReqVO) {
        // 1. 验证树节点存在
        validateTreeNodeExists(createReqVO.getTreeNodeId());
        
        // 2. 验证时间范围（开始时间不能大于结束时间）
        validateTimeRange(createReqVO.getStartTime(), createReqVO.getEndTime());
        
        // 3. 验证时间段不与同一树节点下的其他信息价册重叠
        validateTimeOverlap(createReqVO.getTreeNodeId(), createReqVO.getStartTime(), createReqVO.getEndTime(), null);

        // 4. 验证发布状态
//        if (createReqVO.getPublishStatus() != null) {
//            validatePublishStatus(createReqVO.getPublishStatus());
//        }
        createReqVO.setPublishStatus(InfoPricePublishStatusEnum.UNCOMPLETED.getCode());

        // 5. 构建DO对象
        InfoPriceBookDO book = BeanUtils.toBean(createReqVO, InfoPriceBookDO.class);
        Long tenantId = cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder.getTenantId();
        book.setTenantId(tenantId);

        // 6. 插入数据库
        infoPriceBookMapper.insert(book);

        return book.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateInfoPriceBook(InfoPriceBookSaveReqVO updateReqVO) {
        // 1. 验证信息价册存在
        validateBookExists(updateReqVO.getId());

        // 2. 验证树节点存在
        validateTreeNodeExists(updateReqVO.getTreeNodeId());

        // 3. 验证时间范围（开始时间不能大于结束时间）
        validateTimeRange(updateReqVO.getStartTime(), updateReqVO.getEndTime());
        
        // 4. 验证时间段不与同一树节点下的其他信息价册重叠（排除自身）
        validateTimeOverlap(updateReqVO.getTreeNodeId(), updateReqVO.getStartTime(), updateReqVO.getEndTime(), updateReqVO.getId());

        // 5. 验证发布状态
        if (updateReqVO.getPublishStatus() != null) {
            validatePublishStatus(updateReqVO.getPublishStatus());
        }

        // 6. 更新数据
        InfoPriceBookDO updateObj = BeanUtils.toBean(updateReqVO, InfoPriceBookDO.class);
        
        // 如果发布状态为completed，设置完成时间
        if(updateReqVO.getPublishStatus() != null && updateReqVO.getPublishStatus().equals(InfoPricePublishStatusEnum.COMPLETED.getCode())){
            updateObj.setCompletedTime(java.time.LocalDateTime.now());
        }
        
        // 使用 LambdaUpdateWrapper 明确设置 publishTime 为 null
        if(updateReqVO.getPublishStatus() != null && updateReqVO.getPublishStatus().equals(InfoPricePublishStatusEnum.UNPUBLISHED.getCode())){
            com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper<InfoPriceBookDO> updateWrapper =
                    new com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper<>();
            updateWrapper.eq(InfoPriceBookDO::getId, updateReqVO.getId())
                    .set(InfoPriceBookDO::getPublishStatus, updateReqVO.getPublishStatus())
                    .set(InfoPriceBookDO::getPublishTime, null);  // 明确设置为 null

            infoPriceBookMapper.update(null, updateWrapper);
        }else{
            infoPriceBookMapper.updateById(updateObj);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteInfoPriceBook(Long id) {
        // 1. 验证信息价册存在
        validateBookExists(id);

        // 2. TODO: 验证是否有信息价条目（如果需要）
        // Long itemCount = infoPriceItemMapper.countByBookId(id);
        // if (itemCount > 0) {
        //     throw exception(INFO_PRICE_BOOK_HAS_ITEMS);
        // }

        // 3. 删除信息价册
        infoPriceBookMapper.deleteById(id);
    }

    @Override
    public InfoPriceBookRespVO getInfoPriceBook(Long id) {
        InfoPriceBookDO book = validateBookExists(id);
        InfoPriceBookRespVO respVO = BeanUtils.toBean(book, InfoPriceBookRespVO.class);
        
        // 计算虚拟字段"地区"
        respVO.setRegion(calculateRegion(book.getTreeNodeId()));
        
        return respVO;
    }

    @Override
    public PageResult<InfoPriceBookRespVO> getInfoPriceBookPage(InfoPriceBookPageReqVO pageReqVO) {
        // 1. 分页查询
        PageResult<InfoPriceBookDO> pageResult = infoPriceBookMapper.selectPage(pageReqVO);
        if (CollUtil.isEmpty(pageResult.getList())) {
            return PageResult.empty(pageResult.getTotal());
        }

        // 2. 转换为VO
        List<InfoPriceBookRespVO> voList = BeanUtils.toBean(pageResult.getList(), InfoPriceBookRespVO.class);

        // 3. 批量计算虚拟字段"地区"
        List<Long> treeNodeIds = voList.stream()
                .map(InfoPriceBookRespVO::getTreeNodeId)
                .distinct()
                .collect(Collectors.toList());

        Map<Long, String> regionMap = TenantUtils.executeIgnore(() ->
                calculateRegionBatch(treeNodeIds)
        );
        
        voList.forEach(vo -> vo.setRegion(regionMap.get(vo.getTreeNodeId())));

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

    @Override
    public PageResult<InfoPriceBookRespVO> getInfoPriceBookPageExcludeTenant(InfoPriceBookPageReqVO pageReqVO, Long excludeTenantId) {
        // 1. 分页查询（排除指定租户）
        PageResult<InfoPriceBookDO> pageResult = infoPriceBookMapper.selectPageExcludeTenant(pageReqVO, excludeTenantId);
        if (CollUtil.isEmpty(pageResult.getList())) {
            return PageResult.empty(pageResult.getTotal());
        }

        // 2. 转换为VO
        List<InfoPriceBookRespVO> voList = BeanUtils.toBean(pageResult.getList(), InfoPriceBookRespVO.class);

        // 3. 批量计算虚拟字段"地区"
        List<Long> treeNodeIds = voList.stream()
                .map(InfoPriceBookRespVO::getTreeNodeId)
                .distinct()
                .collect(Collectors.toList());
        
        Map<Long, String> regionMap = calculateRegionBatch(treeNodeIds);
        
        voList.forEach(vo -> vo.setRegion(regionMap.get(vo.getTreeNodeId())));

        // 4. 批量统计每个信息价册下的工料机总数
        List<Long> bookIds = voList.stream()
                .map(InfoPriceBookRespVO::getId)
                .collect(Collectors.toList());
        Map<Long, Long> resourceCountMap = calculateResourceCountBatch(bookIds);
        
        voList.forEach(vo -> vo.setResourceCount(resourceCountMap.getOrDefault(vo.getId(), 0L)));

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

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long copyInfoPriceBook(Long sourceId) {
        // 1. 查询源信息价册（忽略租户过滤，支持跨租户复制）
        InfoPriceBookDO sourceBook = cn.iocoder.yudao.framework.tenant.core.util.TenantUtils.executeIgnore(() -> 
            infoPriceBookMapper.selectById(sourceId)
        );
        
        if (sourceBook == null) {
            throw exception(INFO_PRICE_BOOK_NOT_EXISTS);
        }

        // 2. 验证树节点存在（在当前租户下）
        validateTreeNodeExists(sourceBook.getTreeNodeId());

        // 3. 验证时间段不与当前租户下的其他信息价册重叠
        validateTimeOverlap(sourceBook.getTreeNodeId(), sourceBook.getStartTime(), sourceBook.getEndTime(), null);

        // 4. 构建新的信息价册对象
        InfoPriceBookDO newBook = InfoPriceBookDO.builder()
                .treeNodeId(sourceBook.getTreeNodeId())
                .catalogVersion(sourceBook.getCatalogVersion())
                .name(sourceBook.getName())
                .startTime(sourceBook.getStartTime())
                .endTime(sourceBook.getEndTime())
                .publishStatus(InfoPricePublishStatusEnum.PUBLISHED.getCode())
                .publishTime(java.time.LocalDateTime.now())  // 发布时间设置为当前时间
                .attachment(sourceBook.getAttachment())
                .tenantId(cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder.getTenantId())
                .build();

        // 5. 插入数据库
        infoPriceBookMapper.insert(newBook);

        // 6. 获取源分类树后再创建新的树结构根据新的newBook.getId()
        List<InfoPriceCategoryTreeRespVO> sourceCategoryTrees = categoryTreeService.getCategoryTreeTree(sourceBook.getId());
        if (CollUtil.isNotEmpty(sourceCategoryTrees)) {
            copyCategoryTrees(sourceCategoryTrees, null, newBook.getId());
        }

        // 7. 获取源分类树后的所有id来查询全部信息价工料机信息InfoPriceResourceService把所有categoryTreeId修改为新的再创建复制内容
        List<InfoPriceCategoryTreeDO> newCategoryTrees = infoPriceCategoryTreeMapper.selectListByBookId(newBook.getId());
        if (CollUtil.isNotEmpty(newCategoryTrees)) {
            copyResources(sourceBook.getId(), newCategoryTrees);
        }
        // 8. 获取源对应信息价工料机信息关联id的InfoPriceResourcePriceService的信息价工料机价格历史，创建复制内容
        copyResourcePrices(sourceBook.getId(), newBook.getId());
        log.info("复制信息价册成功，源ID: {}, 新ID: {}", sourceId, newBook.getId());
        return newBook.getId();
    }

    @Override
    public List<InfoPriceBookRespVO> getInfoPriceBookAll(InfoPriceBookPageReqVO reqVO) {
        // 1. 查询该树节点下的所有信息价册
        cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX<InfoPriceBookDO> wrapper = 
                new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX<InfoPriceBookDO>()
                        .eq(InfoPriceBookDO::getPublishStatus,reqVO.getPublishStatus())
                        .eq(InfoPriceBookDO::getTreeNodeId, reqVO.getTreeNodeId())
                        .neIfPresent(InfoPriceBookDO::getId, reqVO.getExcludeBookId())
                        .orderByDesc(InfoPriceBookDO::getStartTime);
        
        List<InfoPriceBookDO> bookList = infoPriceBookMapper.selectList(wrapper);
        
        if (CollUtil.isEmpty(bookList)) {
            return java.util.Collections.emptyList();
        }

        // 2. 转换为VO
        List<InfoPriceBookRespVO> voList = BeanUtils.toBean(bookList, InfoPriceBookRespVO.class);

        // 3. 计算虚拟字段"地区"
        String region = calculateRegion(reqVO.getTreeNodeId());
        
        // 4. 批量查询所有信息价册的分类树（一次SQL查询）
        List<Long> bookIds = voList.stream()
                .map(InfoPriceBookRespVO::getId)
                .collect(Collectors.toList());
        Map<Long, List<InfoPriceCategoryTreeRespVO>> categoryTreeMap = categoryTreeService.getCategoryTreeTreeBatch(bookIds);
        
        // 5. 为每个信息价册填充分类树子节点
        for (InfoPriceBookRespVO vo : voList) {
            vo.setRegion(region);
            vo.setChildren(categoryTreeMap.getOrDefault(vo.getId(), java.util.Collections.emptyList()));
        }

        return voList;
    }

    @Override
    public List<InfoPriceBookRespVO> getInfoPriceBookListForWorkbench(Long treeNodeId) {
        // 查询该树节点下的所有信息价册（不过滤发布状态）
        cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX<InfoPriceBookDO> wrapper =
                new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX<InfoPriceBookDO>()
                        .eq(InfoPriceBookDO::getTreeNodeId, treeNodeId)
                        .orderByDesc(InfoPriceBookDO::getStartTime);

        List<InfoPriceBookDO> bookList = infoPriceBookMapper.selectList(wrapper);

        if (CollUtil.isEmpty(bookList)) {
            return java.util.Collections.emptyList();
        }

        return BeanUtils.toBean(bookList, InfoPriceBookRespVO.class);
    }

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

    /**
     * 验证信息价册存在
     */
    private InfoPriceBookDO validateBookExists(Long id) {
        InfoPriceBookDO book = TenantUtils.executeIsAdmin(() ->
                infoPriceBookMapper.selectById(id)
        );
        if (book == null) {
            throw exception(INFO_PRICE_BOOK_NOT_EXISTS);
        }
        return book;
    }

    /**
     * 验证树节点存在
     */
    private void validateTreeNodeExists(Long treeNodeId) {
        InfoPriceTreeDO tree = TenantUtils.executeIgnore(() ->
                infoPriceTreeMapper.selectById(treeNodeId)
        );
        if (tree == null) {
            throw exception(INFO_PRICE_TREE_NOT_EXISTS);
        }
    }

    /**
     * 验证时间范围（开始时间不能大于结束时间）
     */
    private void validateTimeRange(java.time.LocalDate startTime, java.time.LocalDate endTime) {
        if (startTime != null && endTime != null && endTime.isBefore(startTime)) {
            throw exception(INFO_PRICE_BOOK_TIME_INVALID);
        }
    }

    /**
     * 验证时间段不与同一树节点下的其他信息价册重叠
     *
     * @param treeNodeId 树节点ID
     * @param startTime  开始时间
     * @param endTime    结束时间
     * @param excludeId  排除的ID（更新时排除自身）
     */
    private void validateTimeOverlap(Long treeNodeId, java.time.LocalDate startTime, java.time.LocalDate endTime, Long excludeId) {
        if (treeNodeId == null || startTime == null || endTime == null) {
            return;
        }

        // 查询同一树节点下的所有信息价册
        List<InfoPriceBookDO> existingBooks = infoPriceBookMapper.selectList(
                new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX<InfoPriceBookDO>()
                        .eq(InfoPriceBookDO::getTreeNodeId, treeNodeId)
                        .neIfPresent(InfoPriceBookDO::getId, excludeId)
        );

        // 检查时间段是否有交集
        for (InfoPriceBookDO book : existingBooks) {
            if (book.getStartTime() == null || book.getEndTime() == null) {
                continue;
            }
            // 判断时间段是否有交集：!(新结束 < 旧开始 || 新开始 > 旧结束)
            // 即：新结束 >= 旧开始 && 新开始 <= 旧结束
            if (!endTime.isBefore(book.getStartTime()) && !startTime.isAfter(book.getEndTime())) {
                throw exception(INFO_PRICE_BOOK_TIME_OVERLAP);
            }
        }
    }

    /**
     * 验证发布状态
     */
    private void validatePublishStatus(String publishStatus) {
        InfoPricePublishStatusEnum statusEnum = InfoPricePublishStatusEnum.valueOfCode(publishStatus);
        if (statusEnum == null) {
            throw exception(INFO_PRICE_BOOK_INVALID_PUBLISH_STATUS);
        }
    }

    /**
     * 计算地区路径（单个）
     */
    private String calculateRegion(Long treeNodeId) {
        if (treeNodeId == null) {
            return "";
        }

        InfoPriceTreeDO tree = infoPriceTreeMapper.selectById(treeNodeId);
        if (tree == null) {
            return "";
        }

        // 获取路径上的所有节点
        List<Long> pathIds = new java.util.ArrayList<>();
        if (tree.getPath() != null && tree.getPath().length > 0) {
            for (String id : tree.getPath()) {
                pathIds.add(Long.parseLong(id));
            }
        }
        pathIds.add(treeNodeId);

        // 查询所有节点
        List<InfoPriceTreeDO> nodes = infoPriceTreeMapper.selectBatchIds(pathIds);
        Map<Long, String> nameMap = nodes.stream()
                .collect(Collectors.toMap(InfoPriceTreeDO::getId, InfoPriceTreeDO::getName));

        // 按路径顺序拼接名称
        return pathIds.stream()
                .map(nameMap::get)
                .filter(name -> name != null)
                .collect(Collectors.joining("*"));
    }

    /**
     * 批量计算地区路径
     */
    private Map<Long, String> calculateRegionBatch(List<Long> treeNodeIds) {
        if (CollUtil.isEmpty(treeNodeIds)) {
            return java.util.Collections.emptyMap();
        }

        // 查询所有树节点
        List<InfoPriceTreeDO> trees = infoPriceTreeMapper.selectBatchIds(treeNodeIds);
        
        // 收集所有需要查询的节点ID
        java.util.Set<Long> allNodeIds = new java.util.HashSet<>();
        for (InfoPriceTreeDO tree : trees) {
            if (tree.getPath() != null && tree.getPath().length > 0) {
                for (String id : tree.getPath()) {
                    allNodeIds.add(Long.parseLong(id));
                }
            }
            allNodeIds.add(tree.getId());
        }

        // 查询所有节点
        List<InfoPriceTreeDO> allNodes = infoPriceTreeMapper.selectBatchIds(new java.util.ArrayList<>(allNodeIds));
        Map<Long, String> nameMap = allNodes.stream()
                .collect(Collectors.toMap(InfoPriceTreeDO::getId, InfoPriceTreeDO::getName));

        // 为每个树节点计算地区路径
        return trees.stream().collect(Collectors.toMap(
                InfoPriceTreeDO::getId,
                tree -> {
                    List<Long> pathIds = new java.util.ArrayList<>();
                    if (tree.getPath() != null && tree.getPath().length > 0) {
                        for (String id : tree.getPath()) {
                            pathIds.add(Long.parseLong(id));
                        }
                    }
                    pathIds.add(tree.getId());

                    return pathIds.stream()
                            .map(nameMap::get)
                            .filter(name -> name != null)
                            .collect(Collectors.joining("*"));
                }
        ));
    }

    /**
     * 批量统计每个信息价册下的工料机总数
     * 
     * SQL逻辑：
     * 1. 根据 bookIds 查询所有分类树节点（yhy_info_price_category_tree）
     * 2. 根据分类树节点ID统计工料机数量（yhy_info_price_resource）
     * 3. 按 bookId 汇总工料机总数
     *
     * @param bookIds 信息价册ID列表
     * @return Map，key为bookId，value为工料机总数
     */
    private Map<Long, Long> calculateResourceCountBatch(List<Long> bookIds) {
        if (CollUtil.isEmpty(bookIds)) {
            return java.util.Collections.emptyMap();
        }

        // 1. 查询所有信息价册下的分类树节点
        List<InfoPriceCategoryTreeDO> categoryTrees = infoPriceCategoryTreeMapper.selectListByBookIds(bookIds);
        if (CollUtil.isEmpty(categoryTrees)) {
            return java.util.Collections.emptyMap();
        }

        // 2. 构建 categoryTreeId -> bookId 的映射
        Map<Long, Long> categoryTreeToBookMap = categoryTrees.stream()
                .collect(Collectors.toMap(InfoPriceCategoryTreeDO::getId, InfoPriceCategoryTreeDO::getBookId));

        // 3. 获取所有分类树节点ID
        List<Long> categoryTreeIds = categoryTrees.stream()
                .map(InfoPriceCategoryTreeDO::getId)
                .collect(Collectors.toList());

        // 4. 批量统计每个分类树节点下的工料机数量
        Map<Long, Long> categoryTreeResourceCountMap = infoPriceResourceMapper.countByCategoryTreeIds(categoryTreeIds);

        // 5. 按 bookId 汇总工料机总数
        Map<Long, Long> bookResourceCountMap = new java.util.HashMap<>();
        for (Map.Entry<Long, Long> entry : categoryTreeResourceCountMap.entrySet()) {
            Long categoryTreeId = entry.getKey();
            Long resourceCount = entry.getValue();
            Long bookId = categoryTreeToBookMap.get(categoryTreeId);
            if (bookId != null) {
                bookResourceCountMap.merge(bookId, resourceCount, Long::sum);
            }
        }

        return bookResourceCountMap;
    }

    /**
     * 递归复制分类树
     *
     * @param sourceTrees 源分类树列表
     * @param newParentId 新的父节点ID
     * @param newBookId 新的信息价册ID
     * @return 源节点ID到新节点ID的映射
     */
    private Map<Long, Long> copyCategoryTrees(List<InfoPriceCategoryTreeRespVO> sourceTrees, Long newParentId, Long newBookId) {
        Map<Long, Long> idMapping = new java.util.HashMap<>();
        
        for (InfoPriceCategoryTreeRespVO sourceTree : sourceTrees) {
            // 只处理顶级节点或指定父节点的子节点
//            if (newParentId == null && sourceTree.getParentId() != null) {
//                continue; // 跳过非顶级节点
//            }
//            if (newParentId != null && !newParentId.equals(sourceTree.getParentId())) {
//                continue; // 跳过不是指定父节点的子节点
//            }
            
            // 创建新的分类树节点
            InfoPriceCategoryTreeSaveReqVO createReqVO = new InfoPriceCategoryTreeSaveReqVO();
            createReqVO.setBookId(newBookId);
            createReqVO.setParentId(newParentId);
            createReqVO.setCode(sourceTree.getCode());
            createReqVO.setName(sourceTree.getName());
            createReqVO.setNodeType(sourceTree.getNodeType());
            createReqVO.setAttributes(sourceTree.getAttributes());
            
            Long newCategoryId = categoryTreeService.createCategoryTree(createReqVO);
            idMapping.put(sourceTree.getId(), newCategoryId);
            
            // 递归处理子节点
//            List<InfoPriceCategoryTreeRespVO> children = sourceTrees.stream()
//                    .filter(tree -> sourceTree.getId().equals(tree.getParentId()))
//                    .collect(Collectors.toList());
            if (CollUtil.isNotEmpty(sourceTree.getChildren())) {
                Map<Long, Long> childMapping = copyCategoryTrees(sourceTree.getChildren(), newCategoryId, newBookId);
                idMapping.putAll(childMapping);
            }
        }
        
        return idMapping;
    }

    /**
     * 复制工料机信息
     *
     * @param sourceBookId 源信息价册ID
     * @param newCategoryTrees 新的分类树节点列表
     */
    private void copyResources(Long sourceBookId, List<InfoPriceCategoryTreeDO> newCategoryTrees) {
        // 构建新分类树节点的ID映射（用于查找对应的源节点）
//        Map<String, Long> codeToNewIdMap = newCategoryTrees.stream()
//                .collect(Collectors.toMap(
//                        InfoPriceCategoryTreeDO::getCode,
//                        InfoPriceCategoryTreeDO::getId
//                ));
        
        // 获取源分类树节点（用于查找对应的工料机）
        List<InfoPriceCategoryTreeDO> sourceCategoryTrees = cn.iocoder.yudao.framework.tenant.core.util.TenantUtils.executeIgnore(() ->
                infoPriceCategoryTreeMapper.selectListByBookId(sourceBookId)
        );
        
        // 构建源分类树节点编码到ID的映射
        Map<String, Long> codeToSourceIdMap = sourceCategoryTrees.stream()
                .collect(Collectors.toMap(
                        InfoPriceCategoryTreeDO::getCode,
                        InfoPriceCategoryTreeDO::getId
                ));
        
        // 为每个新分类树节点复制工料机信息
        for (InfoPriceCategoryTreeDO newCategoryTree : newCategoryTrees) {
            String code = newCategoryTree.getCode();
            Long sourceCategoryId = codeToSourceIdMap.get(code);
            
            if (sourceCategoryId != null) {
                // 查询源分类树节点下的工料机信息
                List<InfoPriceResourceRespVO> sourceResources = cn.iocoder.yudao.framework.tenant.core.util.TenantUtils.executeIgnore(() ->
                        infoPriceResourceService.getResourceList(sourceCategoryId)
                );
                
                if (CollUtil.isNotEmpty(sourceResources)) {
                    // 转换为创建请求VO
                    List<InfoPriceResourceSaveReqVO> createReqVOList = sourceResources.stream()
                            .map(sourceResource -> {
                                InfoPriceResourceSaveReqVO createReqVO = new InfoPriceResourceSaveReqVO();
                                createReqVO.setCategoryTreeId(newCategoryTree.getId());
                                createReqVO.setCode(sourceResource.getCode()+"-copy");
                                createReqVO.setName(sourceResource.getName());
                                createReqVO.setSpec(sourceResource.getSpec());
                                createReqVO.setUnit(sourceResource.getUnit());
                                createReqVO.setPriceTaxExcl(sourceResource.getPriceTaxExcl());
                                createReqVO.setTaxRate(sourceResource.getTaxRate());
                                createReqVO.setPriceTaxIncl(sourceResource.getPriceTaxIncl());
                                createReqVO.setDrawingUrl(sourceResource.getDrawingUrl());
                                createReqVO.setCategoryId(sourceResource.getCategoryId());
                                createReqVO.setRemark(sourceResource.getRemark());
                                createReqVO.setAttributes(sourceResource.getAttributes());
                                return createReqVO;
                            })
                            .collect(Collectors.toList());
                    
                    // 批量创建工料机信息
                    infoPriceResourceService.createResourceBatch(createReqVOList);
                }
            }
        }
    }

    /**
     * 复制工料机价格历史
     *
     * @param sourceBookId 源信息价册ID
     * @param newBookId 新信息价册ID
     */
    private void copyResourcePrices(Long sourceBookId, Long newBookId) {
        // 获取源册的所有工料机信息
        List<InfoPriceResourceDO> sourceResources = cn.iocoder.yudao.framework.tenant.core.util.TenantUtils.executeIgnore(() ->
                infoPriceResourceMapper.selectList(
                        new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX<InfoPriceResourceDO>()
                                .in(InfoPriceResourceDO::getCategoryTreeId, 
                                    infoPriceCategoryTreeMapper.selectListByBookId(sourceBookId).stream()
                                            .map(InfoPriceCategoryTreeDO::getId)
                                            .collect(Collectors.toList()))
                )
        );

        if (CollUtil.isEmpty(sourceResources)) {
            return;
        }

        // 获取新册的所有工料机信息，建立编码映射
        List<InfoPriceResourceDO> newResources = infoPriceResourceMapper.selectList(
                new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX<InfoPriceResourceDO>()
                        .in(InfoPriceResourceDO::getCategoryTreeId, 
                            infoPriceCategoryTreeMapper.selectListByBookId(newBookId).stream()
                                    .map(InfoPriceCategoryTreeDO::getId)
                                    .collect(Collectors.toList()))
        );

        // 建立编码到新资源ID的映射
        Map<String, Long> codeToNewResourceIdMap = newResources.stream()
                .collect(Collectors.toMap(
                        InfoPriceResourceDO::getCode,
                        InfoPriceResourceDO::getId
                ));

        // 为每个源工料机复制价格历史
        for (InfoPriceResourceDO sourceResource : sourceResources) {
            String code = sourceResource.getCode();
            Long newResourceId = codeToNewResourceIdMap.get(code);
            
            if (newResourceId != null) {
                // 查询源工料机的价格历史
                List<InfoPriceResourcePriceRespVO> sourcePrices = cn.iocoder.yudao.framework.tenant.core.util.TenantUtils.executeIgnore(() ->
                        infoPriceResourcePriceService.getResourcePriceList(sourceResource.getId())
                );
                
                if (CollUtil.isNotEmpty(sourcePrices)) {
                    // 转换为创建请求VO列表
                    List<InfoPriceResourcePriceSaveReqVO> createReqVOList = sourcePrices.stream()
                            .map(sourcePrice -> {
                                InfoPriceResourcePriceSaveReqVO createReqVO = new InfoPriceResourcePriceSaveReqVO();
                                createReqVO.setResourceId(newResourceId);
                                createReqVO.setStartTime(sourcePrice.getStartTime());
                                createReqVO.setEndTime(sourcePrice.getEndTime());
                                createReqVO.setPriceTaxExcl(sourcePrice.getPriceTaxExcl());
                                createReqVO.setTaxRate(sourcePrice.getTaxRate());
                                createReqVO.setPriceTaxIncl(sourcePrice.getPriceTaxIncl());
                                createReqVO.setAttributes(sourcePrice.getAttributes());
                                return createReqVO;
                            })
                            .collect(Collectors.toList());
                    
                    // 批量创建价格历史
                    infoPriceResourcePriceService.createResourcePriceBatch(createReqVOList);
                }
            }
        }
    }
}
