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

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_CATALOG_ITEM_NOT_EXISTS;
import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_VARIABLE_SETTING_CALC_BASE_INVALID;
import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_VARIABLE_SETTING_CALC_BASE_VARIABLE_NOT_EXISTS;
import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_VARIABLE_SETTING_CODE_DUPLICATE;
import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_VARIABLE_SETTING_CODE_DUPLICATE_ACROSS_TABS;
import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_VARIABLE_SETTING_CODE_FORMAT_INVALID;
import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_VARIABLE_SETTING_INVALID_CATEGORY;
import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_VARIABLE_SETTING_INVALID_NODE_TYPE;
import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_VARIABLE_SETTING_NOT_EXISTS;

import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import com.yhy.module.core.controller.admin.quota.vo.QuotaVariableSettingRespVO;
import com.yhy.module.core.controller.admin.quota.vo.QuotaVariableSettingSaveReqVO;
import com.yhy.module.core.dal.dataobject.quota.QuotaCatalogItemDO;
import com.yhy.module.core.dal.dataobject.quota.QuotaFeeItemDO;
import com.yhy.module.core.dal.dataobject.quota.QuotaVariableSettingDO;
import com.yhy.module.core.dal.dataobject.resource.ResourceCategoryDO;
import com.yhy.module.core.dal.mysql.quota.QuotaCatalogItemMapper;
import com.yhy.module.core.dal.mysql.quota.QuotaFeeItemMapper;
import com.yhy.module.core.dal.mysql.quota.QuotaVariableSettingMapper;
import com.yhy.module.core.dal.mysql.resource.ResourceCategoryMapper;
import com.yhy.module.core.service.quota.QuotaVariableSettingService;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
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 实现类
 */
@Service
@Validated
@Slf4j
public class QuotaVariableSettingServiceImpl implements QuotaVariableSettingService {

    private static final Set<String> VALID_CATEGORIES = new HashSet<>(Arrays.asList(
            QuotaVariableSettingDO.CategoryEnum.DIVISION,
            QuotaVariableSettingDO.CategoryEnum.MEASURE,
            QuotaVariableSettingDO.CategoryEnum.OTHER,
            QuotaVariableSettingDO.CategoryEnum.UNIT_SUMMARY
    ));

    @Resource
    private QuotaVariableSettingMapper quotaVariableSettingMapper;

    @Resource
    private QuotaCatalogItemMapper quotaCatalogItemMapper;

    @Resource
    private ResourceCategoryMapper resourceCategoryMapper;

    @Resource
    private QuotaFeeItemMapper quotaFeeItemMapper;

    @Resource
    private com.yhy.module.core.service.workbench.WbBoqDivisionService wbBoqDivisionService;

    @Resource
    private com.yhy.module.core.service.workbench.WbBoqResourceService wbBoqResourceService;

    @Resource
    private com.yhy.module.core.dal.mysql.workbench.WbBoqResourceMapper wbBoqResourceMapper;

    @Resource
    private com.yhy.module.core.service.workbench.WbSnapshotReadService wbSnapshotReadService;

    @Resource
    private com.yhy.module.core.dal.mysql.resource.ResourceItemMapper resourceItemMapper;

    @Resource
    private com.yhy.module.core.service.workbench.QuotaPriceCalculatorService quotaPriceCalculatorService;

    @Resource
    private com.yhy.module.core.dal.mysql.workbench.WbUnitInfoMapper wbUnitInfoMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createVariableSetting(QuotaVariableSettingSaveReqVO createReqVO) {
        // 1. 验证定额专业节点
        validateSpecialtyNode(createReqVO.getCatalogItemId());

        // 2. 验证类别
        validateCategory(createReqVO.getCategory());

        // 3. 验证代号格式和唯一性
        if (StrUtil.isNotBlank(createReqVO.getCode())) {
            validateCodeFormat(createReqVO.getCode());
            validateCodeUnique(null, createReqVO.getCatalogItemId(), createReqVO.getCategory(), createReqVO.getCode());
            // 跨标签页唯一性校验：四个标签页不允许出现相同的费用代号
            validateCodeUniqueAcrossCategories(null, createReqVO.getCatalogItemId(), createReqVO.getCategory(), createReqVO.getCode());
        }

        // 4. 设置排序值（支持基于参考节点的插入位置）
        Integer sortOrder = createReqVO.getSortOrder();
        if (sortOrder == null) {
            Long referenceNodeId = createReqVO.getReferenceNodeId();
            String insertPosition = createReqVO.getInsertPosition();
            
            if (referenceNodeId != null && insertPosition != null) {
                // 基于参考节点计算排序值
                QuotaVariableSettingDO referenceNode = quotaVariableSettingMapper.selectById(referenceNodeId);
                if (referenceNode != null) {
                    int refSortOrder = referenceNode.getSortOrder();
                    if ("above".equals(insertPosition)) {
                        // 在参考节点上方插入：使用参考节点的排序值，并将参考节点及其后的节点排序值+1
                        sortOrder = refSortOrder;
                        quotaVariableSettingMapper.incrementSortOrderFrom(
                                createReqVO.getCatalogItemId(), createReqVO.getCategory(), refSortOrder);
                    } else if ("below".equals(insertPosition)) {
                        // 在参考节点下方插入：使用参考节点排序值+1，并将后续节点排序值+1
                        sortOrder = refSortOrder + 1;
                        quotaVariableSettingMapper.incrementSortOrderFrom(
                                createReqVO.getCatalogItemId(), createReqVO.getCategory(), refSortOrder + 1);
                    } else {
                        // 默认追加到末尾
                        sortOrder = quotaVariableSettingMapper.selectMaxSortOrder(
                                createReqVO.getCatalogItemId(), createReqVO.getCategory()) + 1;
                    }
                } else {
                    sortOrder = quotaVariableSettingMapper.selectMaxSortOrder(
                            createReqVO.getCatalogItemId(), createReqVO.getCategory()) + 1;
                }
            } else {
                sortOrder = quotaVariableSettingMapper.selectMaxSortOrder(
                        createReqVO.getCatalogItemId(), createReqVO.getCategory()) + 1;
            }
        }
        createReqVO.setSortOrder(sortOrder);

        // 5. 验证计算基数
        if (createReqVO.getCalcBase() != null) {
            validateCalcBase(createReqVO.getCalcBase(), createReqVO.getCatalogItemId(), createReqVO.getCategory());
        }

        // 6. 插入数据
        QuotaVariableSettingDO variableSetting = BeanUtils.toBean(createReqVO, QuotaVariableSettingDO.class);
        quotaVariableSettingMapper.insert(variableSetting);

        return variableSetting.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateVariableSetting(QuotaVariableSettingSaveReqVO updateReqVO) {
        // 1. 验证存在
        QuotaVariableSettingDO existingItem = quotaVariableSettingMapper.selectById(updateReqVO.getId());
        if (existingItem == null) {
            throw exception(QUOTA_VARIABLE_SETTING_NOT_EXISTS);
        }

        // 2. 验证定额专业节点
        validateSpecialtyNode(updateReqVO.getCatalogItemId());

        // 3. 验证类别
        validateCategory(updateReqVO.getCategory());

        // 4. 验证代号格式和唯一性
        if (StrUtil.isNotBlank(updateReqVO.getCode())) {
            validateCodeFormat(updateReqVO.getCode());
            validateCodeUnique(updateReqVO.getId(), updateReqVO.getCatalogItemId(), 
                    updateReqVO.getCategory(), updateReqVO.getCode());
            // 跨标签页唯一性校验：四个标签页不允许出现相同的费用代号
            validateCodeUniqueAcrossCategories(updateReqVO.getId(), updateReqVO.getCatalogItemId(),
                    updateReqVO.getCategory(), updateReqVO.getCode());
        }

        // 5. 验证计算基数
        if (updateReqVO.getCalcBase() != null) {
            validateCalcBase(updateReqVO.getCalcBase(), updateReqVO.getCatalogItemId(), updateReqVO.getCategory());
        }

        // 6. 更新数据
        QuotaVariableSettingDO updateObj = BeanUtils.toBean(updateReqVO, QuotaVariableSettingDO.class);
        quotaVariableSettingMapper.updateById(updateObj);
    }
    
    /**
     * 验证计算基数
     * @param calcBase 计算基数
     * @param catalogItemId 模式节点ID
     * @param category 类别
     */
    private void validateCalcBase(Map<String, Object> calcBase, Long catalogItemId, String category) {
        if (calcBase == null) {
            return;
        }
        
        String formula = (String) calcBase.get("formula");
        // 公式可以为空，表示清空计算基数
        if (StrUtil.isBlank(formula)) {
            return;
        }
        
        // 验证公式语法：只允许字母、数字、运算符和括号
        String validPattern = "^[A-Za-z0-9_+\\-*/().\\s]+$";
        if (!formula.matches(validPattern)) {
            throw exception(QUOTA_VARIABLE_SETTING_CALC_BASE_INVALID);
        }
        
        // 验证公式中的变量是否有效
        validateFormulaVariables(formula, catalogItemId, category);
    }
    
    /**
     * 验证公式中的变量是否有效
     * 参考定额费率的验证逻辑，验证类别价格代码（如 DCLF、DRGF 等）
     * @param formula 公式
     * @param catalogItemId 模式节点ID
     * @param category 类别
     */
    private void validateFormulaVariables(String formula, Long catalogItemId, String category) {
        // 1. 提取公式中的所有变量（变量由字母、数字、下划线组成，但必须以字母开头）
        java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("[A-Za-z][A-Za-z0-9_]*");
        java.util.regex.Matcher matcher = pattern.matcher(formula);
        List<String> potentialCodes = new java.util.ArrayList<>();
        while (matcher.find()) {
            potentialCodes.add(matcher.group());
        }
        
        if (potentialCodes.isEmpty()) {
            return; // 没有变量，只有数字和运算符
        }
        
        // 2. 获取所有有效的变量代码
        Set<String> validCodes = new HashSet<>();
        
        // 2.1 获取同一定额专业节点下同一类别的所有有效费用代号（手动配置）
        List<QuotaVariableSettingDO> settings = quotaVariableSettingMapper.selectListByCatalogItemIdAndCategory(catalogItemId, category);
        for (QuotaVariableSettingDO setting : settings) {
            if (StrUtil.isNotBlank(setting.getCode())) {
                validCodes.add(setting.getCode());
            }
        }
        
        // 2.2 获取定额专业节点下所有费率模式子节点的定额取费变量代号
        List<Long> rateModeIds = quotaCatalogItemMapper.selectChildIds(catalogItemId);
        List<QuotaFeeItemDO> feeItems = quotaFeeItemMapper.selectVariableListByCatalogItemIds(rateModeIds);
        for (QuotaFeeItemDO feeItem : feeItems) {
            if (StrUtil.isNotBlank(feeItem.getCode())) {
                validCodes.add(feeItem.getCode());
            }
        }
        
        // 2.3 获取所有类别价格代码（从机类字典获取，如 DCLF、DRGF 等）
        List<ResourceCategoryDO> categories = resourceCategoryMapper.selectList();
        for (ResourceCategoryDO cat : categories) {
            // 添加4个价格代码
            if (StrUtil.isNotBlank(cat.getTaxExclBaseCode())) {
                validCodes.add(cat.getTaxExclBaseCode());
            }
            if (StrUtil.isNotBlank(cat.getTaxInclBaseCode())) {
                validCodes.add(cat.getTaxInclBaseCode());
            }
            if (StrUtil.isNotBlank(cat.getTaxExclCompileCode())) {
                validCodes.add(cat.getTaxExclCompileCode());
            }
            if (StrUtil.isNotBlank(cat.getTaxInclCompileCode())) {
                validCodes.add(cat.getTaxInclCompileCode());
            }
        }
        
        // 3. 检查公式中的变量是否都有效
        List<String> invalidCodes = new java.util.ArrayList<>();
        for (String code : potentialCodes) {
            if (StrUtil.isNotBlank(code) && !validCodes.contains(code)) {
                invalidCodes.add(code);
            }
        }
        
        if (!invalidCodes.isEmpty()) {
            throw exception(QUOTA_VARIABLE_SETTING_CALC_BASE_VARIABLE_NOT_EXISTS, String.join(", ", invalidCodes));
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteVariableSetting(Long id) {
        // 1. 验证存在
        QuotaVariableSettingDO variableSetting = quotaVariableSettingMapper.selectById(id);
        if (variableSetting == null) {
            throw exception(QUOTA_VARIABLE_SETTING_NOT_EXISTS);
        }

        // 2. 删除
        quotaVariableSettingMapper.deleteById(id);
    }

    @Override
    public QuotaVariableSettingDO getVariableSetting(Long id) {
        return quotaVariableSettingMapper.selectById(id);
    }

    @Override
    public List<QuotaVariableSettingDO> getVariableSettingList(Long catalogItemId, String category) {
        return quotaVariableSettingMapper.selectListByCatalogItemIdAndCategory(catalogItemId, category);
    }

    @Override
    public List<QuotaVariableSettingRespVO> getVariableSettingListWithFeeItems(Long catalogItemId, String category) {
        List<QuotaVariableSettingRespVO> result = new ArrayList<>();
        
        // 1. 获取手动配置的变量设置（现在绑定到定额专业节点）
        List<QuotaVariableSettingDO> manualSettings = quotaVariableSettingMapper.selectListByCatalogItemIdAndCategory(catalogItemId, category);
        for (QuotaVariableSettingDO setting : manualSettings) {
            QuotaVariableSettingRespVO vo = BeanUtils.toBean(setting, QuotaVariableSettingRespVO.class);
            vo.setSource("manual");
            result.add(vo);
        }
        
        // 2. 获取定额专业节点下所有费率模式子节点的ID
        List<Long> rateModeIds = quotaCatalogItemMapper.selectChildIds(catalogItemId);
        
        // 3. 获取所有费率模式下 variable=true 的定额取费数据
        List<QuotaFeeItemDO> feeItems = quotaFeeItemMapper.selectVariableListByCatalogItemIds(rateModeIds);
        
        // 收集已有的代号，避免重复
        Set<String> existingCodes = new HashSet<>();
        for (QuotaVariableSettingDO setting : manualSettings) {
            if (StrUtil.isNotBlank(setting.getCode())) {
                existingCodes.add(setting.getCode());
            }
        }
        
        // 将定额取费数据转换为变量设置格式
        for (QuotaFeeItemDO feeItem : feeItems) {
            // 跳过已存在相同代号的数据（手动配置优先）
            if (StrUtil.isNotBlank(feeItem.getCode()) && existingCodes.contains(feeItem.getCode())) {
                continue;
            }
            
            QuotaVariableSettingRespVO vo = new QuotaVariableSettingRespVO();
            vo.setId(feeItem.getId());
            vo.setCatalogItemId(catalogItemId); // 使用定额专业节点ID
            vo.setCategory(category);
            vo.setName(feeItem.getName());
            vo.setCode(feeItem.getCode());
            vo.setCalcBase(feeItem.getCalcBase());
            vo.setSortOrder(feeItem.getSortOrder() != null ? feeItem.getSortOrder() + 10000 : 10000); // 排在手动配置之后
            vo.setSource("fee_item");
            result.add(vo);
        }
        
        // 4. 按 sortOrder 排序
        result.sort((a, b) -> {
            int sortA = a.getSortOrder() != null ? a.getSortOrder() : 0;
            int sortB = b.getSortOrder() != null ? b.getSortOrder() : 0;
            return Integer.compare(sortA, sortB);
        });
        
        return result;
    }

    @Override
    public List<QuotaVariableSettingDO> getVariableSettingListAll(Long catalogItemId) {
        return quotaVariableSettingMapper.selectListByCatalogItemId(catalogItemId);
    }

    @Override
    public List<QuotaVariableSettingRespVO> getVariableSettingListAllWithFeeItems(Long catalogItemId) {
        List<QuotaVariableSettingRespVO> result = new ArrayList<>();
        
        // 遍历所有类别，分别调用 getVariableSettingListWithFeeItems 获取数据
        // 这样可以确保每个类别的定额取费数据都能正确获取
        for (String category : VALID_CATEGORIES) {
            List<QuotaVariableSettingRespVO> categorySettings = getVariableSettingListWithFeeItems(catalogItemId, category);
            log.info("[getVariableSettingListAllWithFeeItems] 定额专业 {} 类别 {} 的变量设置数量: {}", 
                catalogItemId, category, categorySettings.size());
            result.addAll(categorySettings);
        }
        
        log.info("[getVariableSettingListAllWithFeeItems] 定额专业 {} 的变量设置总数: {}, 类别分布: {}", 
            catalogItemId, result.size(),
            result.stream().collect(java.util.stream.Collectors.groupingBy(
                s -> s.getCategory() != null ? s.getCategory() : "null", 
                java.util.stream.Collectors.counting())));
        
        // 按 category 和 sortOrder 排序
        result.sort((a, b) -> {
            // 先按 category 排序
            int categoryCompare = (a.getCategory() != null ? a.getCategory() : "").compareTo(b.getCategory() != null ? b.getCategory() : "");
            if (categoryCompare != 0) {
                return categoryCompare;
            }
            // 再按 sortOrder 排序
            int sortA = a.getSortOrder() != null ? a.getSortOrder() : 0;
            int sortB = b.getSortOrder() != null ? b.getSortOrder() : 0;
            return Integer.compare(sortA, sortB);
        });
        
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void swapSort(Long nodeId1, Long nodeId2) {
        QuotaVariableSettingDO item1 = quotaVariableSettingMapper.selectById(nodeId1);
        QuotaVariableSettingDO item2 = quotaVariableSettingMapper.selectById(nodeId2);

        if (item1 == null || item2 == null) {
            throw exception(QUOTA_VARIABLE_SETTING_NOT_EXISTS);
        }

        // 交换排序值
        Integer tempSort = item1.getSortOrder();
        item1.setSortOrder(item2.getSortOrder());
        item2.setSortOrder(tempSort);

        quotaVariableSettingMapper.updateById(item1);
        quotaVariableSettingMapper.updateById(item2);
    }

    @Override
    public void validateVariableSettingExists(Long id) {
        if (quotaVariableSettingMapper.selectById(id) == null) {
            throw exception(QUOTA_VARIABLE_SETTING_NOT_EXISTS);
        }
    }

    @Override
    public List<QuotaVariableSettingRespVO> getVariableSettingListAllWithSummary(
            Long catalogItemId, 
            Long compileTreeId, 
            List<Long> baseNumberRangeIds) {
        // 1. 获取变量设置列表（包含取费项）
        List<QuotaVariableSettingRespVO> result = getVariableSettingListAllWithFeeItems(catalogItemId);
        
        // 2. 如果没有编制树ID，直接返回（不计算汇总值）
        if (compileTreeId == null) {
            return result;
        }
        
        // 3. 获取分部分项树数据（含价格计算）
        List<com.yhy.module.core.controller.admin.workbench.vo.WbBoqDivisionRespVO> divisionTree = 
            wbBoqDivisionService.getTreeWithPrice(compileTreeId);
        
        // 4. 获取工料机消耗数据，按分类和价格字段汇总
        // 变量代号如 DRGF = 除税人工费，需要通过 calcBase.variables 中的 categoryId 和 priceField 来计算
        Map<String, java.math.BigDecimal> variableValueMap = buildVariableValueMap(
            compileTreeId,
            divisionTree, 
            baseNumberRangeIds != null ? new HashSet<>(baseNumberRangeIds) : null
        );
        
        log.info("汇总值计算 - 变量值映射: {}", variableValueMap);
        
        // 5. 为每个变量计算汇总值
        for (QuotaVariableSettingRespVO vo : result) {
            java.math.BigDecimal summaryValue = calculateSummaryValue(vo.getCalcBase(), variableValueMap);
            vo.setSummaryValue(summaryValue);
        }
        
        return result;
    }

    /**
     * 构建变量值映射（基数设置弹窗汇总值计算）
     * 根据 calcBase.variables 中的配置，从工料机消耗表汇总各变量的值
     * 变量代号格式：D/H + RGF/CLF/JXF/SBF/QT 等
     * D = 除税(tax_excl), H = 含税(tax_incl)
     * RGF = 人工费(categoryId=1), CLF = 材料费(categoryId=3), JXF = 机械费(categoryId=4)
     *
     * 合价计算规则：
     * - 用量 = 定额工程量(quotaQty) × 消耗量(consumeQty)
     * - 合价 = 用量 × 单价
     * - 子工料机(parentId != null)不参与汇总
     * - 单位为%的工料机不参与汇总（其单价字段为null）
     */
    private Map<String, java.math.BigDecimal> buildVariableValueMap(
            Long compileTreeId,
            List<com.yhy.module.core.controller.admin.workbench.vo.WbBoqDivisionRespVO> divisionTree,
            Set<Long> baseNumberRangeIds) {
        Map<String, java.math.BigDecimal> result = new HashMap<>();
        boolean hasRange = baseNumberRangeIds != null && !baseNumberRangeIds.isEmpty();

        // 如果有基数范围，先收集范围内的所有节点ID（包括子节点）
        Set<Long> nodesInRange = new HashSet<>();
        if (hasRange) {
            collectNodesInRange(divisionTree, baseNumberRangeIds, nodesInRange, false);
        }

        // 收集范围内的定额节点及其工程量
        Map<Long, java.math.BigDecimal> quotaNodeQtyMap = new HashMap<>();
        collectQuotaNodeQtyMap(divisionTree, quotaNodeQtyMap, hasRange, nodesInRange, false);

        // 收集范围内的统一取费节点
        List<com.yhy.module.core.controller.admin.workbench.vo.WbBoqDivisionRespVO> unifiedFeeNodes = new ArrayList<>();
        collectUnifiedFeeNodes(divisionTree, unifiedFeeNodes, hasRange, nodesInRange, false);

        log.info("汇总值计算 - 定额节点数量: {}, 统一取费节点数量: {}", quotaNodeQtyMap.size(), unifiedFeeNodes.size());

        // 按分类汇总各价格字段
        // categoryId: 1=人工, 2=主材, 3=材料, 4=机械, 5=设备
        Map<Long, Map<String, java.math.BigDecimal>> categoryPriceMap = new HashMap<>();

        // 处理普通定额节点的工料机
        if (!quotaNodeQtyMap.isEmpty()) {
            // 从工料机消耗表批量查询原始数据（DO层面，不走简陋的convert）
            List<com.yhy.module.core.dal.dataobject.workbench.WbBoqResourceDO> allResources =
                wbBoqResourceMapper.selectListByDivisionIds(new ArrayList<>(quotaNodeQtyMap.keySet()));

            log.info("汇总值计算 - 工料机数量(含子): {}", allResources != null ? allResources.size() : 0);

            if (allResources != null) {
                for (com.yhy.module.core.dal.dataobject.workbench.WbBoqResourceDO resource : allResources) {
                    // 跳过子工料机（复合工料机只取父值）
                    if (resource.getParentId() != null) continue;

                    Long categoryId = resource.getCategoryId();
                    if (categoryId == null) continue;

                    // 获取消耗量（优先调整消耗量）
                    java.math.BigDecimal consumeQty = resource.getAdjustConsumeQty() != null
                        ? resource.getAdjustConsumeQty() : resource.getConsumeQty();
                    if (consumeQty == null) consumeQty = java.math.BigDecimal.ZERO;

                    // 获取定额工程量
                    java.math.BigDecimal quotaQty = quotaNodeQtyMap.get(resource.getDivisionId());
                    if (quotaQty == null) quotaQty = java.math.BigDecimal.ONE;

                    // 用量 = 定额工程量 × 消耗量（单位为%的工料机：用量=消耗量）
                    java.math.BigDecimal usageQty;
                    if ("%".equals(resource.getUnit())) {
                        usageQty = consumeQty;
                    } else {
                        usageQty = quotaQty.multiply(consumeQty);
                    }

                    Map<String, java.math.BigDecimal> priceMap = categoryPriceMap.computeIfAbsent(categoryId, k -> new HashMap<>());

                    // 合价 = 用量 × 单价
                    addPriceToMap(priceMap, "tax_excl_base_price", resource.getTaxExclBasePrice(), usageQty);
                    addPriceToMap(priceMap, "tax_incl_base_price", resource.getTaxInclBasePrice(), usageQty);
                    addPriceToMap(priceMap, "tax_excl_compile_price", resource.getTaxExclCompilePrice(), usageQty);
                    addPriceToMap(priceMap, "tax_incl_compile_price", resource.getTaxInclCompilePrice(), usageQty);
                }
            }
        }

        // 将统一取费节点的子目工料机也汇总到分类价格映射中
        if (!unifiedFeeNodes.isEmpty()) {
            aggregateUnifiedFeeResources(unifiedFeeNodes, compileTreeId, categoryPriceMap);
        }

        if (categoryPriceMap.isEmpty()) {
            return result;
        }

        log.info("汇总值计算 - 分类价格映射: {}", categoryPriceMap);

        // 从工料机总类表查询所有分类，构建变量代号到值的映射
        // 变量代号来源：yhy_resource_category 表的四个代码字段
        // - tax_excl_base_code: 除税基价代码（如 DRGF）
        // - tax_incl_base_code: 含税基价代码（如 HDRGF）
        // - tax_excl_compile_code: 除税编制价代码（如 RGF）
        // - tax_incl_compile_code: 含税编制价代码（如 HRGF）
        List<ResourceCategoryDO> categories = resourceCategoryMapper.selectList();
        
        for (ResourceCategoryDO category : categories) {
            Map<String, java.math.BigDecimal> priceMap = categoryPriceMap.get(category.getId());
            if (priceMap == null) continue;
            
            // 除税基价代码 -> tax_excl_base_price
            if (cn.hutool.core.util.StrUtil.isNotBlank(category.getTaxExclBaseCode())) {
                java.math.BigDecimal value = priceMap.get("tax_excl_base_price");
                if (value != null) {
                    result.put(category.getTaxExclBaseCode(), value);
                }
            }
            // 含税基价代码 -> tax_incl_base_price
            if (cn.hutool.core.util.StrUtil.isNotBlank(category.getTaxInclBaseCode())) {
                java.math.BigDecimal value = priceMap.get("tax_incl_base_price");
                if (value != null) {
                    result.put(category.getTaxInclBaseCode(), value);
                }
            }
            // 除税编制价代码 -> tax_excl_compile_price
            if (cn.hutool.core.util.StrUtil.isNotBlank(category.getTaxExclCompileCode())) {
                java.math.BigDecimal value = priceMap.get("tax_excl_compile_price");
                if (value != null) {
                    result.put(category.getTaxExclCompileCode(), value);
                }
            }
            // 含税编制价代码 -> tax_incl_compile_price
            if (cn.hutool.core.util.StrUtil.isNotBlank(category.getTaxInclCompileCode())) {
                java.math.BigDecimal value = priceMap.get("tax_incl_compile_price");
                if (value != null) {
                    result.put(category.getTaxInclCompileCode(), value);
                }
            }
        }
        
        // 收集分部分项的费用代号（costCode）及其对应的合价（amount）
        collectCostCodeValues(divisionTree, result);
        
        log.info("汇总值计算 - 变量值映射: {}", result);

        return result;
    }

    /**
     * 收集分部分项的费用代号及其对应的合价值
     * 遍历分部分项树，将每个节点的 costCode -> amount 添加到变量映射中
     */
    private void collectCostCodeValues(
            List<com.yhy.module.core.controller.admin.workbench.vo.WbBoqDivisionRespVO> nodes,
            Map<String, java.math.BigDecimal> result) {
        if (nodes == null) return;
        
        for (com.yhy.module.core.controller.admin.workbench.vo.WbBoqDivisionRespVO node : nodes) {
            // 如果节点有费用代号且有合价，则添加到映射
            String costCode = node.getCostCode();
            java.math.BigDecimal amount = node.getAmount();
            if (cn.hutool.core.util.StrUtil.isNotBlank(costCode) && amount != null) {
                // 费用代号可能重复，使用累加方式
                result.merge(costCode, amount, java.math.BigDecimal::add);
            }
            
            // 递归处理子节点
            if (node.getChildren() != null && !node.getChildren().isEmpty()) {
                collectCostCodeValues(node.getChildren(), result);
            }
        }
    }

    /**
     * 累加价格到映射
     */
    private void addPriceToMap(Map<String, java.math.BigDecimal> priceMap, String priceField, 
            java.math.BigDecimal unitPrice, java.math.BigDecimal qty) {
        if (unitPrice == null) return;
        java.math.BigDecimal amount = unitPrice.multiply(qty);
        priceMap.merge(priceField, amount, java.math.BigDecimal::add);
    }

    /**
     * 收集范围内的定额节点ID及其工程量（quotaQty）
     * 替代原 collectQuotaNodeIds，同时收集 id → qty 映射
     */
    private void collectQuotaNodeQtyMap(
            List<com.yhy.module.core.controller.admin.workbench.vo.WbBoqDivisionRespVO> nodes,
            Map<Long, java.math.BigDecimal> quotaNodeQtyMap,
            boolean hasRange,
            Set<Long> nodesInRange,
            boolean parentInRange) {
        if (nodes == null) return;

        for (com.yhy.module.core.controller.admin.workbench.vo.WbBoqDivisionRespVO node : nodes) {
            boolean inRange = !hasRange || parentInRange || nodesInRange.contains(node.getId());

            // 统一取费节点：递归处理其子节点（不收集自身，由 collectUnifiedFeeNodes 单独处理）
            if ("unified_fee".equals(node.getNodeType())) {
                if (node.getChildren() != null && !node.getChildren().isEmpty()) {
                    collectQuotaNodeQtyMap(node.getChildren(), quotaNodeQtyMap, hasRange, nodesInRange, inRange);
                }
                continue;
            }

            // 收集定额节点及其工程量
            if ("quota".equals(node.getNodeType()) && inRange) {
                java.math.BigDecimal qty = node.getQty() != null ? node.getQty() : java.math.BigDecimal.ONE;
                quotaNodeQtyMap.put(node.getId(), qty);
            }

            // 递归处理子节点
            if (node.getChildren() != null && !node.getChildren().isEmpty()) {
                collectQuotaNodeQtyMap(node.getChildren(), quotaNodeQtyMap, hasRange, nodesInRange, inRange);
            }
        }
    }

    /**
     * 收集范围内的统一取费节点（unified_fee）
     * 用于基数设置汇总值计算时，将统一取费涉及的子目工料机也纳入分类汇总
     */
    private void collectUnifiedFeeNodes(
            List<com.yhy.module.core.controller.admin.workbench.vo.WbBoqDivisionRespVO> nodes,
            List<com.yhy.module.core.controller.admin.workbench.vo.WbBoqDivisionRespVO> unifiedFeeNodes,
            boolean hasRange,
            Set<Long> nodesInRange,
            boolean parentInRange) {
        if (nodes == null) return;

        for (com.yhy.module.core.controller.admin.workbench.vo.WbBoqDivisionRespVO node : nodes) {
            boolean inRange = !hasRange || parentInRange || nodesInRange.contains(node.getId());

            if ("unified_fee".equals(node.getNodeType()) && inRange) {
                unifiedFeeNodes.add(node);
            }

            // 递归处理子节点
            if (node.getChildren() != null && !node.getChildren().isEmpty()) {
                collectUnifiedFeeNodes(node.getChildren(), unifiedFeeNodes, hasRange, nodesInRange, inRange);
            }
        }
    }

    /**
     * 将统一取费节点的子目工料机汇总到分类价格映射中
     * 
     * 统一取费节点没有自己的 yhy_wb_boq_resource 数据，需要溯源到后台统一取费设置的子定额及其子目工料机：
     * unified_fee 节点 → attributes.sourceUnifiedFeeSettingId → 快照 WbUnifiedFeeSettingDO
     * → 子费用(child) → WbUnifiedFeeResourceDO → ResourceItemDO（获取价格和分类）
     */
    private void aggregateUnifiedFeeResources(
            List<com.yhy.module.core.controller.admin.workbench.vo.WbBoqDivisionRespVO> unifiedFeeNodes,
            Long compileTreeId,
            Map<Long, Map<String, java.math.BigDecimal>> categoryPriceMap) {
        if (unifiedFeeNodes == null || unifiedFeeNodes.isEmpty()) return;

        for (com.yhy.module.core.controller.admin.workbench.vo.WbBoqDivisionRespVO ufNode : unifiedFeeNodes) {
            // 通过 QuotaPriceCalculatorService 获取统一取费节点的分类合价
            // 该方法内部会：获取母定额列表 → 获取母定额分类合价 → 用calcBase公式计算%工料机合价 → 按categoryId汇总
            Map<Long, com.yhy.module.core.controller.admin.workbench.vo.price.CategoryPriceSum> ufCategorySums =
                quotaPriceCalculatorService.getUnifiedFeeCategorySums(ufNode.getId());

            if (ufCategorySums == null || ufCategorySums.isEmpty()) {
                log.info("[基数汇总-统一取费] 统一取费节点无分类合价, nodeId={}", ufNode.getId());
                continue;
            }

            log.info("[基数汇总-统一取费] nodeId={}, 分类合价={}", ufNode.getId(), ufCategorySums);

            // 将统一取费的分类合价合并到总的 categoryPriceMap
            for (Map.Entry<Long, com.yhy.module.core.controller.admin.workbench.vo.price.CategoryPriceSum> entry : ufCategorySums.entrySet()) {
                Long categoryId = entry.getKey();
                com.yhy.module.core.controller.admin.workbench.vo.price.CategoryPriceSum sum = entry.getValue();

                Map<String, java.math.BigDecimal> priceMap = categoryPriceMap.computeIfAbsent(categoryId, k -> new HashMap<>());
                priceMap.merge("tax_excl_base_price", sum.getTaxExclBaseSum() != null ? sum.getTaxExclBaseSum() : java.math.BigDecimal.ZERO, java.math.BigDecimal::add);
                priceMap.merge("tax_incl_base_price", sum.getTaxInclBaseSum() != null ? sum.getTaxInclBaseSum() : java.math.BigDecimal.ZERO, java.math.BigDecimal::add);
                priceMap.merge("tax_excl_compile_price", sum.getTaxExclCompileSum() != null ? sum.getTaxExclCompileSum() : java.math.BigDecimal.ZERO, java.math.BigDecimal::add);
                priceMap.merge("tax_incl_compile_price", sum.getTaxInclCompileSum() != null ? sum.getTaxInclCompileSum() : java.math.BigDecimal.ZERO, java.math.BigDecimal::add);
            }
        }
    }

    /**
     * 收集基数范围内的所有节点ID（包括选中节点的所有子节点）
     */
    private void collectNodesInRange(
            List<com.yhy.module.core.controller.admin.workbench.vo.WbBoqDivisionRespVO> nodes,
            Set<Long> selectedIds,
            Set<Long> result,
            boolean parentSelected) {
        if (nodes == null) return;
        
        for (com.yhy.module.core.controller.admin.workbench.vo.WbBoqDivisionRespVO node : nodes) {
            boolean isSelected = selectedIds.contains(node.getId()) || parentSelected;
            if (isSelected) {
                result.add(node.getId());
            }
            // 递归处理子节点
            if (node.getChildren() != null && !node.getChildren().isEmpty()) {
                collectNodesInRange(node.getChildren(), selectedIds, result, isSelected);
            }
        }
    }

    /**
     * 根据计算基数公式计算汇总值
     */
    private java.math.BigDecimal calculateSummaryValue(
            Map<String, Object> calcBase,
            Map<String, java.math.BigDecimal> variableValueMap) {
        if (calcBase == null) {
            return null;
        }
        
        // 获取公式字符串
        Object formulaObj = calcBase.get("formula");
        if (formulaObj == null) {
            return null;
        }
        String formula = formulaObj.toString().trim();
        if (StrUtil.isBlank(formula)) {
            return null;
        }
        
        try {
            // 替换公式中的变量为对应的值
            String expression = formula;
            for (Map.Entry<String, java.math.BigDecimal> entry : variableValueMap.entrySet()) {
                String code = entry.getKey();
                java.math.BigDecimal value = entry.getValue();
                expression = expression.replaceAll("\\b" + code + "\\b", value.toPlainString());
            }
            
            // 将未知变量替换为0
            expression = expression.replaceAll("[A-Za-z][A-Za-z0-9_]*", "0");
            
            log.debug("汇总值计算 - 公式: {}, 表达式: {}", formula, expression);
            
            // 使用 JavaScript 引擎计算表达式
            javax.script.ScriptEngineManager manager = new javax.script.ScriptEngineManager();
            javax.script.ScriptEngine engine = manager.getEngineByName("JavaScript");
            if (engine == null) {
                engine = manager.getEngineByName("nashorn");
            }
            if (engine == null) {
                log.warn("无法获取脚本引擎，无法计算公式: {}", formula);
                return null;
            }
            
            Object result = engine.eval(expression);
            if (result instanceof Number) {
                return new java.math.BigDecimal(result.toString()).setScale(2, java.math.RoundingMode.HALF_UP);
            }
            return null;
        } catch (Exception e) {
            log.warn("计算汇总值失败，公式: {}, 错误: {}", formula, e.getMessage());
            return null;
        }
    }

    @Override
    public List<QuotaVariableSettingRespVO> getVariableSettingListByCompileTree(
            Long compileTreeId, 
            List<Long> baseNumberRangeIds) {
        
        // 1. 按单位工程信息中的定额数据库（创建单位工程时选定的 quotaCatalogItemId = 定额专业）查询变量设置列表
        Set<Long> specialtyIds = getQuotaSpecialtyIdsFromUnitInfo(compileTreeId);
        
        if (specialtyIds.isEmpty()) {
            log.info("[getVariableSettingListByCompileTree] 单位工程 {} 未配置定额数据库，无法加载变量设置", compileTreeId);
            return new ArrayList<>();
        }
        
        log.info("[getVariableSettingListByCompileTree] 单位工程 {} 定额数据库(定额专业): {}", compileTreeId, specialtyIds);
        
        // 2. 获取分部分项树数据（只查询一次，供所有专业共用）
        List<com.yhy.module.core.controller.admin.workbench.vo.WbBoqDivisionRespVO> divisionTree = 
            wbBoqDivisionService.getTreeWithPrice(compileTreeId);
        
        // 3. 构建变量值映射（只计算一次）
        Map<String, java.math.BigDecimal> variableValueMap = buildVariableValueMap(
            compileTreeId,
            divisionTree, 
            baseNumberRangeIds != null ? new HashSet<>(baseNumberRangeIds) : null
        );
        
        log.info("[getVariableSettingListByCompileTree] 变量值映射: {}", variableValueMap);
        
        // 4. 加载该定额专业下的变量设置（按 id+category 去重，因为同一个定额取费可能出现在多个类别中）
        List<QuotaVariableSettingRespVO> result = new ArrayList<>();
        Set<String> addedKeys = new HashSet<>();
        
        for (Long specialtyId : specialtyIds) {
            List<QuotaVariableSettingRespVO> settings = getVariableSettingListAllWithFeeItems(specialtyId);
            for (QuotaVariableSettingRespVO vo : settings) {
                // 使用 id + category 作为去重键
                String key = vo.getId() + "_" + (vo.getCategory() != null ? vo.getCategory() : "");
                if (vo.getId() != null && !addedKeys.contains(key)) {
                    addedKeys.add(key);
                    // 计算汇总值
                    java.math.BigDecimal summaryValue = calculateSummaryValue(vo.getCalcBase(), variableValueMap);
                    vo.setSummaryValue(summaryValue);
                    result.add(vo);
                }
            }
        }
        
        // 5. 按 category 和 sortOrder 排序
        result.sort((a, b) -> {
            int categoryCompare = (a.getCategory() != null ? a.getCategory() : "").compareTo(b.getCategory() != null ? b.getCategory() : "");
            if (categoryCompare != 0) {
                return categoryCompare;
            }
            int sortA = a.getSortOrder() != null ? a.getSortOrder() : 0;
            int sortB = b.getSortOrder() != null ? b.getSortOrder() : 0;
            return Integer.compare(sortA, sortB);
        });
        
        return result;
    }

    /**
     * 基数设置弹窗表格：变量设置列表的查询锚点。
     * 使用单位工程 {@link com.yhy.module.core.dal.dataobject.workbench.WbUnitInfoDO#getQuotaCatalogItemId()}（创建单位工程时选定的定额数据库/定额专业），
     * 不再根据分部分项树中已套定额反推专业。
     */
    private Set<Long> getQuotaSpecialtyIdsFromUnitInfo(Long compileTreeId) {
        Set<Long> specialtyIds = new HashSet<>();
        com.yhy.module.core.dal.dataobject.workbench.WbUnitInfoDO unitInfo =
                wbUnitInfoMapper.selectByCompileTreeId(compileTreeId);
        if (unitInfo == null || unitInfo.getQuotaCatalogItemId() == null) {
            log.info("[getQuotaSpecialtyIdsFromUnitInfo] 单位工程 {} 无单位信息或未配置定额数据库(quotaCatalogItemId)", compileTreeId);
            return specialtyIds;
        }
        specialtyIds.add(unitInfo.getQuotaCatalogItemId());
        log.info("[getQuotaSpecialtyIdsFromUnitInfo] 单位工程 {} 定额数据库(定额专业)ID={}", compileTreeId, unitInfo.getQuotaCatalogItemId());
        return specialtyIds;
    }

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

    /**
     * 验证定额专业节点（变量设置现在绑定到 specialty 节点）
     */
    private void validateSpecialtyNode(Long catalogItemId) {
        QuotaCatalogItemDO catalogItem = quotaCatalogItemMapper.selectById(catalogItemId);
        if (catalogItem == null) {
            throw exception(QUOTA_CATALOG_ITEM_NOT_EXISTS);
        }
        if (!"specialty".equals(catalogItem.getNodeType())) {
            throw exception(QUOTA_VARIABLE_SETTING_INVALID_NODE_TYPE);
        }
    }

    /**
     * 验证类别
     */
    private void validateCategory(String category) {
        if (!VALID_CATEGORIES.contains(category)) {
            throw exception(QUOTA_VARIABLE_SETTING_INVALID_CATEGORY);
        }
    }

    /**
     * 验证代号格式：不允许纯数字，必须包含字母
     */
    private void validateCodeFormat(String code) {
        // 纯数字检查：如果全是数字则不允许
        if (code.matches("^\\d+$")) {
            throw exception(QUOTA_VARIABLE_SETTING_CODE_FORMAT_INVALID);
        }
        // 格式检查：必须以字母开头，只能包含字母、数字、下划线
        if (!code.matches("^[A-Za-z][A-Za-z0-9_]*$")) {
            throw exception(QUOTA_VARIABLE_SETTING_CODE_FORMAT_INVALID);
        }
    }

    /**
     * 验证代号唯一性
     */
    private void validateCodeUnique(Long id, Long catalogItemId, String category, String code) {
        QuotaVariableSettingDO existing = quotaVariableSettingMapper.selectByCode(catalogItemId, category, code);
        if (existing != null && !existing.getId().equals(id)) {
            throw exception(QUOTA_VARIABLE_SETTING_CODE_DUPLICATE);
        }
    }

    /**
     * 验证费用代号在同一定额专业节点下跨四个标签页的唯一性
     * 四个标签页（分部分项/措施项目/其他项目/单位汇总）不允许出现相同的费用代号
     */
    private void validateCodeUniqueAcrossCategories(Long id, Long catalogItemId, String currentCategory, String code) {
        QuotaVariableSettingDO existing = quotaVariableSettingMapper.selectByCodeAcrossCategories(catalogItemId, code);
        if (existing != null && !existing.getId().equals(id)) {
            // 同类别内的重复由 validateCodeUnique 处理，这里只处理跨类别的情况
            if (!existing.getCategory().equals(currentCategory)) {
                String categoryLabel = getCategoryLabel(existing.getCategory());
                throw exception(QUOTA_VARIABLE_SETTING_CODE_DUPLICATE_ACROSS_TABS, code, categoryLabel);
            }
        }
    }

    /**
     * 获取类别的中文标签
     */
    private String getCategoryLabel(String category) {
        switch (category) {
            case "division": return "分部分项";
            case "measure": return "措施项目";
            case "other": return "其他项目";
            case "unit_summary": return "单位汇总";
            default: return category;
        }
    }
}
