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

import cn.hutool.core.collection.CollUtil;
import com.yhy.module.core.controller.admin.quota.vo.QuotaFeeItemWithRateRespVO;
import com.yhy.module.core.controller.admin.quota.vo.QuotaResourceRespVO;
import com.yhy.module.core.controller.admin.workbench.vo.price.CategoryPriceSum;
import com.yhy.module.core.controller.admin.workbench.vo.price.ResourcePriceDetail;
import com.yhy.module.core.dal.dataobject.workbench.WbBoqDivisionDO;
import com.yhy.module.core.dal.dataobject.workbench.WbCompileTreeDO;
import com.yhy.module.core.dal.dataobject.workbench.WbProjectTreeDO;
import com.yhy.module.core.dal.dataobject.workbench.WbUnitFeeSettingDO;
import com.yhy.module.core.dal.dataobject.workbench.WbUnitInfoDO;
import com.yhy.module.core.dal.dataobject.workbench.WbUnitRateSettingDO;
import com.yhy.module.core.dal.mysql.quota.QuotaRateItemMapper;
import com.yhy.module.core.dal.mysql.workbench.WbBoqDivisionMapper;
import com.yhy.module.core.dal.mysql.workbench.WbCompileTreeMapper;
import com.yhy.module.core.dal.mysql.workbench.WbProjectTreeMapper;
import com.yhy.module.core.dal.mysql.workbench.WbUnitFeeSettingMapper;
import com.yhy.module.core.dal.mysql.workbench.WbUnitInfoMapper;
import com.yhy.module.core.dal.mysql.workbench.WbUnitRateSettingMapper;
import com.yhy.module.core.service.quota.QuotaFeeItemService;
import com.yhy.module.core.service.quota.QuotaItemService;
import com.yhy.module.core.service.quota.QuotaResourceService;
import com.yhy.module.core.service.workbench.QuotaPriceCalculatorService;
import com.yhy.module.core.service.workbench.WbBoqResourceService;
import com.yhy.module.core.service.workbench.WbUnitFeeSettingService;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 单位工程取费项设定 Service 实现
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class WbUnitFeeSettingServiceImpl implements WbUnitFeeSettingService {

    private final WbUnitFeeSettingMapper feeSettingMapper;
    private final WbUnitInfoMapper unitInfoMapper;
    private final WbBoqDivisionMapper divisionMapper;
    private final QuotaFeeItemService quotaFeeItemService;
    private final QuotaItemService quotaItemService;
    private final QuotaResourceService quotaResourceService;
    private final QuotaPriceCalculatorService quotaPriceCalculatorService;
    private final QuotaRateItemMapper quotaRateItemMapper;
    private final WbUnitRateSettingMapper unitRateSettingMapper;
    private final WbCompileTreeMapper compileTreeMapper;
    private final WbBoqResourceService wbBoqResourceService;
    private final WbProjectTreeMapper projectTreeMapper;
    private final com.yhy.module.core.service.workbench.WbSnapshotReadService wbSnapshotReadService;
    private final com.yhy.module.core.dal.mysql.workbench.WbRateItemMapper wbRateItemMapper;

    @Override
    public List<QuotaFeeItemWithRateRespVO> getMergedFeeList(Long unitId) {
        // 1. 获取单位工程信息
        WbUnitInfoDO unitInfo = unitInfoMapper.selectById(unitId);
        if (unitInfo == null || unitInfo.getRateModeId() == null) {
            log.warn("单位工程不存在或未设置费率模式, unitId={}", unitId);
            return new ArrayList<>();
        }

        // 2. 获取取费项列表 - 只从快照表读取，不回退到后台标准库
        List<QuotaFeeItemWithRateRespVO> standardList = wbSnapshotReadService.getFeeItemWithRateTree(unitInfo.getCompileTreeId(), unitInfo.getRateModeId());
        if (standardList == null) {
            log.warn("[getMergedFeeList] 快照不存在, compileTreeId={}", unitInfo.getCompileTreeId());
            standardList = new ArrayList<>();
        } else {
            log.debug("[getMergedFeeList] 从快照表读取取费项, compileTreeId={}, count={}", 
                    unitInfo.getCompileTreeId(), standardList.size());
        }
        if (CollUtil.isEmpty(standardList)) {
            return new ArrayList<>();
        }

        // 3. 获取覆写值
        List<WbUnitFeeSettingDO> overrides = feeSettingMapper.selectByUnitId(unitId);
        Map<Long, WbUnitFeeSettingDO> overrideMap = overrides.stream()
                .collect(Collectors.toMap(WbUnitFeeSettingDO::getSourceFeeItemId, Function.identity(), (a, b) -> a));

        // 4. 扁平化并合并
        List<QuotaFeeItemWithRateRespVO> result = new ArrayList<>();
        flattenAndMerge(standardList, overrideMap, null, result);

        return result;
    }

    @Override
    public List<QuotaFeeItemWithRateRespVO> getMergedFeeListByDivisionId(Long divisionId) {
        return getMergedFeeListByDivisionIdInternal(divisionId, null);
    }

    @Override
    public List<QuotaFeeItemWithRateRespVO> getMergedFeeListByDivisionId(Long divisionId,
            Map<Long, CategoryPriceSum> categorySums) {
        return getMergedFeeListByDivisionIdInternal(divisionId, categorySums);
    }

    /**
     * 内部实现：获取取费项列表并计算子单价
     * @param divisionId 分部分项节点ID（定额节点）
     * @param preComputedCategorySums 预计算的分类汇总（如果非null则跳过重新加载工料机）
     */
    private List<QuotaFeeItemWithRateRespVO> getMergedFeeListByDivisionIdInternal(Long divisionId,
            Map<Long, CategoryPriceSum> preComputedCategorySums) {
        // 1. 获取分部分项节点
        WbBoqDivisionDO division = divisionMapper.selectById(divisionId);
        if (division == null) {
            log.warn("分部分项节点不存在, divisionId={}", divisionId);
            return new ArrayList<>();
        }

        // 2. 只有定额类型的节点才有取费数据
        if (!"quota".equals(division.getNodeType())) {
            log.info("非定额节点，不返回取费数据, divisionId={}, nodeType={}", divisionId, division.getNodeType());
            return new ArrayList<>();
        }

        // 3. 获取费率模式ID：优先使用单位工程的用户选择，fallback到标准库树推导
        if (division.getSourceQuotaItemId() == null) {
            log.warn("定额节点未关联定额基价, divisionId={}", divisionId);
            return new ArrayList<>();
        }
        
        Long rateModeId = null;
        // 3.1 获取定额所属的专业ID
        Long quotaSpecialtyId = quotaItemService.getSpecialtyIdByQuotaItem(division.getSourceQuotaItemId());
        
        // 3.2 提前查询单位工程信息（后续多个 fallback 步骤复用，避免重复查询）
        WbUnitInfoDO unitInfo = division.getCompileTreeId() != null
                ? unitInfoMapper.selectByCompileTreeId(division.getCompileTreeId()) : null;
        
        // 3.3 优先从单位工程的费率模式绑定中查找（单位工程级费率及取费）
        if (quotaSpecialtyId != null && unitInfo != null) {
            // 从单位工程的 rateModeBindings 中查找对应定额专业的费率模式
            rateModeId = unitInfo.getRateModeIdByQuotaSpecialty(quotaSpecialtyId);
            if (rateModeId != null) {
                log.info("从单位工程费率绑定获取定额专业对应的费率模式, divisionId={}, quotaSpecialtyId={}, rateModeId={}", 
                        divisionId, quotaSpecialtyId, rateModeId);
            }
        }
        
        // 3.4 fallback：从项目的费率模式绑定中查找（兼容旧数据）
        if (rateModeId == null && quotaSpecialtyId != null && division.getCompileTreeId() != null) {
            WbCompileTreeDO compileTree = compileTreeMapper.selectById(division.getCompileTreeId());
            if (compileTree != null && compileTree.getProjectId() != null) {
                WbProjectTreeDO project = projectTreeMapper.selectById(compileTree.getProjectId());
                if (project != null) {
                    // 从项目的 rateModeBindings 中查找对应定额专业的费率模式
                    rateModeId = project.getRateModeIdByQuotaSpecialty(quotaSpecialtyId);
                    if (rateModeId != null) {
                        log.info("从项目费率绑定获取定额专业对应的费率模式（fallback）, divisionId={}, quotaSpecialtyId={}, rateModeId={}", 
                                divisionId, quotaSpecialtyId, rateModeId);
                    }
                }
            }
        }
        
        // 3.5 fallback：从单位工程主费率模式获取（仅当定额专业与单位工程专业一致时）
        if (rateModeId == null && unitInfo != null && unitInfo.getRateModeId() != null) {
            // 检查单位工程的定额专业是否与定额所属专业一致
            if (quotaSpecialtyId == null || quotaSpecialtyId.equals(unitInfo.getQuotaCatalogItemId())) {
                rateModeId = unitInfo.getRateModeId();
                log.info("从单位工程主费率模式获取, divisionId={}, compileTreeId={}, rateModeId={}", 
                        divisionId, division.getCompileTreeId(), rateModeId);
            }
        }
        
        // 3.6 fallback：通过定额基价在标准库树中查找默认费率模式
        if (rateModeId == null) {
            rateModeId = quotaItemService.getRateModeIdByQuotaItem(division.getSourceQuotaItemId());
            log.info("通过定额基价查找默认费率模式, divisionId={}, sourceQuotaItemId={}, rateModeId={}", 
                    divisionId, division.getSourceQuotaItemId(), rateModeId);
        }

        if (rateModeId == null) {
            log.warn("未找到费率模式节点, divisionId={}, sourceQuotaItemId={}", 
                    divisionId, division.getSourceQuotaItemId());
            return new ArrayList<>();
        }
        
        // 4. 获取取费项列表 - 只从快照表读取，不回退到后台标准库
        List<QuotaFeeItemWithRateRespVO> standardList = wbSnapshotReadService.getFeeItemWithRateTree(division.getCompileTreeId(), rateModeId);
        if (standardList == null) {
            log.warn("[getMergedFeeListByDivisionId] 快照不存在, compileTreeId={}", division.getCompileTreeId());
            standardList = new ArrayList<>();
        } else {
            log.debug("[getMergedFeeListByDivisionId] 从快照表读取取费项, compileTreeId={}, count={}", 
                    division.getCompileTreeId(), standardList.size());
        }
        if (CollUtil.isEmpty(standardList)) {
            return new ArrayList<>();
        }

        // 6. 获取覆写值（基于分部分项节点）
        List<WbUnitFeeSettingDO> overrides = feeSettingMapper.selectByDivisionId(divisionId);
        
        // 6.1 有 sourceFeeItemId 的覆写值（用于合并标准库取费项）
        Map<Long, WbUnitFeeSettingDO> overrideMap = overrides.stream()
                .filter(o -> o.getSourceFeeItemId() != null)
                .collect(java.util.stream.Collectors.toMap(
                        WbUnitFeeSettingDO::getSourceFeeItemId, 
                        java.util.function.Function.identity(), 
                        (a, b) -> a));
        
        // 6.2 有 sourceRateItemId 的覆写值（用于合并标准库费率项）
        Map<Long, WbUnitFeeSettingDO> rateOverrideMap = overrides.stream()
                .filter(o -> o.getSourceFeeItemId() == null && o.getSourceRateItemId() != null)
                .collect(java.util.stream.Collectors.toMap(
                        WbUnitFeeSettingDO::getSourceRateItemId, 
                        java.util.function.Function.identity(), 
                        (a, b) -> a));
        
        // 6.3 没有 sourceFeeItemId 也没有 sourceRateItemId 的才是真正的自定义行
        List<WbUnitFeeSettingDO> customRows = overrides.stream()
                .filter(o -> o.getSourceFeeItemId() == null && o.getSourceRateItemId() == null && !Boolean.TRUE.equals(o.getUserDeleted()))
                .collect(java.util.stream.Collectors.toList());

        // 7. 扁平化并合并
        List<QuotaFeeItemWithRateRespVO> result = new ArrayList<>();
        flattenAndMerge(standardList, overrideMap, rateOverrideMap, result);
        
        // 7.1 添加自定义行（自定义行不设置 feeItemId，让前端知道这是自定义行）
        for (WbUnitFeeSettingDO customRow : customRows) {
            QuotaFeeItemWithRateRespVO vo = new QuotaFeeItemWithRateRespVO();
            // 自定义行不设置 feeItemId，前端保存时也不传 feeItemId
            vo.setFeeItemName(customRow.getName());
            vo.setCode(customRow.getCode());
            vo.setRatePercentage(customRow.getRatePercentage());
            vo.setFeeCategory(customRow.getFeeCategory());
            vo.setBaseDescription(customRow.getBaseDescription());
            vo.setSortOrder(customRow.getSortOrder());
            vo.setCalcBase(customRow.getCalcBase());
            vo.setHidden(customRow.getHidden());
            vo.setVariable(customRow.getVariable());
            result.add(vo);
        }
        
        // 7.2 按 sortOrder 排序
        result.sort((a, b) -> {
            Integer sortA = a.getSortOrder() != null ? a.getSortOrder() : Integer.MAX_VALUE;
            Integer sortB = b.getSortOrder() != null ? b.getSortOrder() : Integer.MAX_VALUE;
            return sortA.compareTo(sortB);
        });

        // 8. 获取项目ID（通过 compileTreeId 查询）
        Long projectId = null;
        if (division.getCompileTreeId() != null) {
            WbCompileTreeDO compileTree = compileTreeMapper.selectById(division.getCompileTreeId());
            if (compileTree != null) {
                projectId = compileTree.getProjectId();
            }
        }
        
        // 9. 计算子单价
        if (preComputedCategorySums != null) {
            // 使用调用方预计算的分类汇总（避免重复加载工料机数据）
            calculateSubPricesWithCategorySums(result, preComputedCategorySums, projectId);
        } else {
            // 需要自行加载工料机数据计算分类汇总
            calculateSubPrices(result, divisionId, division.getSourceQuotaItemId(), projectId);
        }

        return result;
    }

    /**
     * 计算取费项的子单价（需要自行加载工料机数据）
     * @param feeItems 取费项列表
     * @param divisionId 分部分项节点ID
     * @param quotaItemId 定额基价ID
     * @param projectId 项目ID（用于查询项目级别的费率设置）
     */
    private void calculateSubPrices(List<QuotaFeeItemWithRateRespVO> feeItems, Long divisionId, Long quotaItemId, Long projectId) {
        if (CollUtil.isEmpty(feeItems)) {
            return;
        }

        // 1. 优先从工作台获取工料机数据（包含用户的调整消耗量）
        List<com.yhy.module.core.controller.admin.workbench.vo.WbBoqResourceRespVO> wbResources = 
                wbBoqResourceService.getListByDivisionId(divisionId);
        
        List<ResourcePriceDetail> resourceDetails;
        if (CollUtil.isNotEmpty(wbResources)) {
            // 使用工作台数据（含adjustConsumeQty）
            resourceDetails = wbResources.stream()
                    .map(this::convertWbResourceToDetail)
                    .collect(Collectors.toList());
        } else {
            // 【已禁用fallback】费用切割复制后与后台无关，工作台没有数据则直接返回
            return;
        }
        
        // 2. 计算分类汇总
        Map<Long, CategoryPriceSum> categorySums = quotaPriceCalculatorService.calculateCategorySums(resourceDetails);

        // 3. 使用分类汇总计算子单价
        calculateSubPricesWithCategorySums(feeItems, categorySums, projectId);
    }

    /**
     * 使用预计算的分类汇总计算取费项子单价（避免重复加载工料机数据）
     * @param feeItems 取费项列表
     * @param categorySums 分类汇总
     * @param projectId 项目ID（用于查询项目级别的费率设置）
     */
    private void calculateSubPricesWithCategorySums(List<QuotaFeeItemWithRateRespVO> feeItems, 
            Map<Long, CategoryPriceSum> categorySums, Long projectId) {
        if (CollUtil.isEmpty(feeItems)) {
            return;
        }

        // 1. 构建取费项代号到子单价的映射（用于综合单价计算）
        Map<String, BigDecimal> feeItemSubPriceMap = new java.util.HashMap<>();

        // 2. 先计算普通取费项的子单价（非综合单价）
        for (QuotaFeeItemWithRateRespVO feeItem : feeItems) {
            if (!"ZHDJ".equals(feeItem.getSystemCode())) {
                calculateSingleSubPrice(feeItem, categorySums, projectId);
                // 记录代号和子单价的映射
                if (feeItem.getCode() != null && feeItem.getSubPrice() != null) {
                    feeItemSubPriceMap.put(feeItem.getCode(), feeItem.getSubPrice());
                }
            }
        }

        // 3. 再计算综合单价（引用其他取费项的子单价）
        for (QuotaFeeItemWithRateRespVO feeItem : feeItems) {
            if ("ZHDJ".equals(feeItem.getSystemCode())) {
                calculateZhdjSubPrice(feeItem, feeItemSubPriceMap, projectId);
            }
        }
    }

    /**
     * 转换工作台工料机VO为ResourcePriceDetail
     * 直接复用 VO 中已计算好的合价（含%工料机公式计算值和复合工料机汇总值）
     */
    private ResourcePriceDetail convertWbResourceToDetail(com.yhy.module.core.controller.admin.workbench.vo.WbBoqResourceRespVO resource) {
        return ResourcePriceDetail.builder()
                .resourceId(resource.getId())
                .resourceCode(resource.getCode())
                .resourceName(resource.getName())
                .resourceType(resource.getResourceType())
                .categoryId(resource.getCategoryId())
                .taxExclBaseTotalSum(resource.getTaxExclBaseTotalSum())
                .taxInclBaseTotalSum(resource.getTaxInclBaseTotalSum())
                .taxExclCompileTotalSum(resource.getTaxExclCompileTotalSum())
                .taxInclCompileTotalSum(resource.getTaxInclCompileTotalSum())
                .isPercentUnit("%".equals(resource.getUnit()))
                .calcBase(resource.getCalcBase())
                .build();
    }

    /**
     * 转换QuotaResourceRespVO为ResourcePriceDetail
     */
    private ResourcePriceDetail convertToResourcePriceDetail(QuotaResourceRespVO resource) {
        return ResourcePriceDetail.builder()
                .resourceId(resource.getId())
                .resourceCode(resource.getResourceCode())
                .resourceName(resource.getResourceName())
                .resourceType(resource.getResourceType())
                .categoryId(resource.getResourceCategoryId())
                .taxExclBaseTotalSum(resource.getTaxExclBaseTotalSum())
                .taxInclBaseTotalSum(resource.getTaxInclBaseTotalSum())
                .taxExclCompileTotalSum(resource.getTaxExclCompileTotalSum())
                .taxInclCompileTotalSum(resource.getTaxInclCompileTotalSum())
                .isPercentUnit("%".equals(resource.getResourceUnit()))
                .build();
    }

    /**
     * 计算单个取费项的子单价
     * @param feeItem 取费项
     * @param categorySums 分类汇总
     * @param projectId 项目ID（用于查询项目级别的费率设置）
     */
    private void calculateSingleSubPrice(QuotaFeeItemWithRateRespVO feeItem, Map<Long, CategoryPriceSum> categorySums, Long projectId) {
        // 1. 计算基数值
        BigDecimal calcBaseValue = BigDecimal.ZERO;
        if (feeItem.getCalcBase() != null) {
            calcBaseValue = quotaPriceCalculatorService.evaluateCalcBaseFormula(feeItem.getCalcBase(), categorySums);
        }
        feeItem.setCalcBaseValue(calcBaseValue);

        // 2. 获取费率百分比（优先从项目级别的费率设置中获取）
        BigDecimal ratePercent = getRatePercentValue(feeItem, projectId);
        feeItem.setRateValue(ratePercent);

        // 3. 计算子单价：子单价 = 计算基数值 × (费率% / 100)
        BigDecimal subPrice = calcBaseValue
                .multiply(ratePercent)
                .divide(new BigDecimal("100"), 6, RoundingMode.HALF_UP);
        feeItem.setSubPrice(subPrice);
    }

    /**
     * 获取费率百分比的实际数值
     * 优先级：
     * 1. 如果 ratePercentage 是数字，直接使用
     * 2. 优先从项目级别的费率设置（WbUnitRateSettingDO）中获取
     * 3. 否则从关联的费率项的 settings.fieldValues 中获取第一个字段值
     * 
     * @param feeItem 取费项
     * @param projectId 项目ID（用于查询项目级别的费率设置）
     */
    private BigDecimal getRatePercentValue(QuotaFeeItemWithRateRespVO feeItem, Long projectId) {
        String ratePercentage = feeItem.getRatePercentage();
        
        // 1. 尝试直接解析为数字
        if (ratePercentage != null && !ratePercentage.isEmpty()) {
            try {
                return new BigDecimal(ratePercentage);
            } catch (NumberFormatException e) {
                // 不是数字，继续尝试从费率项获取
                log.debug("[取费项计算] 费率代号非数字: {}，尝试从费率设置获取实际值", ratePercentage);
            }
        }

        Long rateItemId = feeItem.getRateItemId();
        if (rateItemId == null) {
            log.debug("[取费项计算] 未关联费率项, feeItemId={}", feeItem.getFeeItemId());
            return BigDecimal.ZERO;
        }

        // 2. 优先从项目级别的费率设置中获取（用户选择的子节点值）
        if (projectId != null) {
            WbUnitRateSettingDO rateSetting = unitRateSettingMapper.selectByUnitIdAndRateItemId(projectId, rateItemId);
            if (rateSetting != null && rateSetting.getFieldValues() != null && !rateSetting.getFieldValues().isEmpty()) {
                // 获取第一个字段的值
                Object firstValue = rateSetting.getFieldValues().get(1);
                if (firstValue == null) {
                    firstValue = rateSetting.getFieldValues().values().iterator().next();
                }
                if (firstValue != null) {
                    try {
                        BigDecimal value = new BigDecimal(firstValue.toString());
                        log.debug("[取费项计算] 从项目费率设置获取费率值, projectId={}, rateItemId={}, rateCode={}, value={}", 
                                projectId, rateItemId, ratePercentage, value);
                        return value;
                    } catch (NumberFormatException e) {
                        log.warn("[取费项计算] 项目费率设置值无法转换为数字, projectId={}, rateItemId={}, value={}", 
                                projectId, rateItemId, firstValue);
                    }
                }
            }
        }

        // 3. 从关联的费率项的 settings.fieldValues 中获取（只从快照表读取）
        Map<String, Object> settings = null;
        
        // 只从快照表读取，不回退到后台标准库
        com.yhy.module.core.dal.dataobject.workbench.WbRateItemDO wbRateItem = wbRateItemMapper.selectById(rateItemId);
        if (wbRateItem != null) {
            settings = wbRateItem.getSettings();
            log.debug("[取费项计算] 从快照表读取费率项, rateItemId={}", rateItemId);
        } else {
            log.warn("[取费项计算] 快照表中费率项不存在, rateItemId={}", rateItemId);
            return BigDecimal.ZERO;
        }
        if (settings == null) {
            log.debug("[取费项计算] 费率项无settings, rateItemId={}", rateItemId);
            return BigDecimal.ZERO;
        }

        @SuppressWarnings("unchecked")
        Map<String, Object> fieldValues = (Map<String, Object>) settings.get("fieldValues");
        if (fieldValues == null || fieldValues.isEmpty()) {
            log.debug("[取费项计算] 费率项无fieldValues, rateItemId={}", rateItemId);
            return BigDecimal.ZERO;
        }

        // 获取第一个字段值（通常是字段1）
        Object firstValue = fieldValues.get("1");
        if (firstValue == null) {
            // 尝试获取任意第一个值
            firstValue = fieldValues.values().iterator().next();
        }

        if (firstValue != null) {
            try {
                BigDecimal value = new BigDecimal(firstValue.toString());
                log.debug("[取费项计算] 从费率项获取费率值, rateItemId={}, rateCode={}, value={}", 
                        rateItemId, ratePercentage, value);
                return value;
            } catch (NumberFormatException e) {
                log.warn("[取费项计算] 字段值无法转换为数字, rateItemId={}, value={}", rateItemId, firstValue);
            }
        }

        return BigDecimal.ZERO;
    }

    /**
     * 计算综合单价的子单价（引用其他取费项的子单价）
     * @param feeItem 取费项
     * @param feeItemSubPriceMap 取费项代号到子单价的映射
     * @param projectId 项目ID（用于查询项目级别的费率设置）
     */
    private void calculateZhdjSubPrice(QuotaFeeItemWithRateRespVO feeItem, Map<String, BigDecimal> feeItemSubPriceMap, Long projectId) {
        BigDecimal calcBaseValue = BigDecimal.ZERO;
        
        if (feeItem.getCalcBase() != null) {
            @SuppressWarnings("unchecked")
            Map<String, Object> calcBase = feeItem.getCalcBase();
            String formula = (String) calcBase.get("formula");
            
            if (formula != null && !formula.isEmpty()) {
                // 解析公式，替换变量为对应取费项的子单价
                calcBaseValue = evaluateZhdjFormula(formula, feeItemSubPriceMap);
            }
        }
        feeItem.setCalcBaseValue(calcBaseValue);

        // 获取费率百分比（综合单价默认100%）
        BigDecimal ratePercent = new BigDecimal("100"); // 默认100%
        String ratePercentage = feeItem.getRatePercentage();
        if (ratePercentage != null && !ratePercentage.isEmpty()) {
            try {
                ratePercent = new BigDecimal(ratePercentage);
            } catch (NumberFormatException e) {
                log.debug("[综合单价计算] 费率代号非数字，使用默认值100%: {}", ratePercentage);
                ratePercent = new BigDecimal("100");
            }
        }
        feeItem.setRateValue(ratePercent);

        // 计算子单价：subPrice = calcBaseValue * ratePercent / 100
        BigDecimal subPrice = calcBaseValue
                .multiply(ratePercent)
                .divide(new BigDecimal("100"), 6, RoundingMode.HALF_UP);
        feeItem.setSubPrice(subPrice);
    }

    /**
     * 解析综合单价公式（变量为其他取费项的代号）
     */
    private BigDecimal evaluateZhdjFormula(String formula, Map<String, BigDecimal> feeItemSubPriceMap) {
        if (formula == null || formula.isEmpty()) {
            return BigDecimal.ZERO;
        }

        try {
            // 替换变量为对应的子单价值
            String expression = formula;
            for (Map.Entry<String, BigDecimal> entry : feeItemSubPriceMap.entrySet()) {
                String varName = entry.getKey();
                BigDecimal value = entry.getValue();
                // 使用正则替换，确保只替换完整的变量名
                expression = expression.replaceAll("\\b" + java.util.regex.Pattern.quote(varName) + "\\b", 
                        value.toPlainString());
            }

            // 使用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("[综合单价计算] 无法获取JavaScript引擎");
                return BigDecimal.ZERO;
            }

            Object result = engine.eval(expression);
            if (result instanceof Number) {
                return new BigDecimal(result.toString()).setScale(6, RoundingMode.HALF_UP);
            }
            return BigDecimal.ZERO;
        } catch (Exception e) {
            log.error("[综合单价计算] 公式计算异常: formula={}, error={}", formula, e.getMessage());
            return BigDecimal.ZERO;
        }
    }

    /**
     * 扁平化树形数据并合并覆写值
     * @param items 标准库取费项/费率项列表
     * @param overrideMap 取费项覆写映射（key: feeItemId）
     * @param rateOverrideMap 费率项覆写映射（key: rateItemId）
     * @param result 结果列表
     */
    private void flattenAndMerge(List<QuotaFeeItemWithRateRespVO> items, 
                                  Map<Long, WbUnitFeeSettingDO> overrideMap,
                                  Map<Long, WbUnitFeeSettingDO> rateOverrideMap,
                                  List<QuotaFeeItemWithRateRespVO> result) {
        for (QuotaFeeItemWithRateRespVO item : items) {
            // 检查是否被用户删除
            WbUnitFeeSettingDO override = null;
            if (item.getFeeItemId() != null) {
                override = overrideMap.get(item.getFeeItemId());
            } else if (item.getRateItemId() != null && rateOverrideMap != null) {
                // 只有 rateItemId 没有 feeItemId 的费率项，从 rateOverrideMap 获取覆写
                override = rateOverrideMap.get(item.getRateItemId());
            }
            
            if (override != null && Boolean.TRUE.equals(override.getUserDeleted())) {
                // 用户已删除，跳过
                continue;
            }

            // 创建新的 VO 并合并覆写值
            QuotaFeeItemWithRateRespVO merged = new QuotaFeeItemWithRateRespVO();
            // 复制标准库数据
            merged.setRateItemId(item.getRateItemId());
            merged.setRateItemName(item.getRateItemName());
            merged.setRateItemCustomCode(item.getRateItemCustomCode());
            merged.setRateCode(item.getRateCode());
            merged.setParentId(item.getParentId());
            merged.setNodeType(item.getNodeType());
            merged.setFeeItemId(item.getFeeItemId());
            merged.setFeeItemName(item.getFeeItemName());
            merged.setFeeItemCustomCode(item.getFeeItemCustomCode());
            merged.setCalcBase(item.getCalcBase());
            merged.setRatePercentage(item.getRatePercentage());
            merged.setCode(item.getCode());
            merged.setFeeCategory(item.getFeeCategory());
            merged.setBaseDescription(item.getBaseDescription());
            merged.setSortOrder(item.getSortOrder());
            merged.setHidden(item.getHidden());
            merged.setVariable(item.getVariable());
            merged.setSystemCode(item.getSystemCode());
            merged.setCatalogItemId(item.getCatalogItemId());
            merged.setHasFeeItem(item.getHasFeeItem());

            // 应用覆写值
            if (override != null) {
                if (override.getName() != null) {
                    merged.setFeeItemName(override.getName());
                }
                if (override.getRatePercentage() != null) {
                    merged.setRatePercentage(override.getRatePercentage());
                }
                if (override.getCode() != null) {
                    merged.setCode(override.getCode());
                }
                if (override.getFeeCategory() != null) {
                    merged.setFeeCategory(override.getFeeCategory());
                }
                if (override.getBaseDescription() != null) {
                    merged.setBaseDescription(override.getBaseDescription());
                }
                if (override.getSortOrder() != null) {
                    merged.setSortOrder(override.getSortOrder());
                }
                if (override.getHidden() != null) {
                    merged.setHidden(override.getHidden());
                }
                if (override.getVariable() != null) {
                    merged.setVariable(override.getVariable());
                }
                if (override.getCalcBase() != null) {
                    merged.setCalcBase(override.getCalcBase());
                }
            }

            result.add(merged);

            // 递归处理子节点
            if (CollUtil.isNotEmpty(item.getChildren())) {
                flattenAndMerge(item.getChildren(), overrideMap, rateOverrideMap, result);
            }
        }
    }

    @Override
    public void saveOverride(Long divisionId, Long feeItemId, String name, String ratePercentage,
                             String code, String feeCategory, String baseDescription) {
        WbUnitFeeSettingDO existing = feeSettingMapper.selectByDivisionIdAndFeeItemId(divisionId, feeItemId);
        
        if (existing != null) {
            existing.setName(name);
            existing.setRatePercentage(ratePercentage);
            existing.setCode(code);
            existing.setFeeCategory(feeCategory);
            existing.setBaseDescription(baseDescription);
            feeSettingMapper.updateById(existing);
        } else {
            WbUnitFeeSettingDO setting = new WbUnitFeeSettingDO();
            setting.setDivisionId(divisionId);
            setting.setSourceFeeItemId(feeItemId);
            setting.setName(name);
            setting.setRatePercentage(ratePercentage);
            setting.setCode(code);
            setting.setFeeCategory(feeCategory);
            setting.setBaseDescription(baseDescription);
            // 设置基线值
            setting.setBaseName(name);
            setting.setBaseRatePercentage(ratePercentage);
            setting.setBaseCode(code);
            setting.setBaseFeeCategory(feeCategory);
            setting.setBaseBaseDescription(baseDescription);
            feeSettingMapper.insert(setting);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchSaveOverrides(Long divisionId, List<FeeOverrideDTO> overrides) {
        if (CollUtil.isEmpty(overrides)) {
            return;
        }

        // 0. 校验所有 calcBase 中的公式合法性
        for (FeeOverrideDTO dto : overrides) {
            if (dto.getCalcBase() != null) {
                String formula = (String) dto.getCalcBase().get("formula");
                if (formula != null && !formula.isEmpty()) {
                    String error = validateFormula(formula);
                    if (error != null) {
                        throw cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception(
                            com.yhy.module.core.enums.ErrorCodeConstants.FORMULA_INVALID, 
                            dto.getName(), error);
                    }
                }
            }
        }

        // 1. 先删除该 divisionId 下所有的覆写记录（全量替换策略）
        feeSettingMapper.deleteByDivisionId(divisionId);

        // 2. 重新插入所有记录
        for (FeeOverrideDTO dto : overrides) {
            WbUnitFeeSettingDO setting = new WbUnitFeeSettingDO();
            setting.setDivisionId(divisionId);
            setting.setSourceFeeItemId(dto.getFeeItemId());
            setting.setSourceRateItemId(dto.getRateItemId());
            setting.setSortOrder(dto.getSortOrder());
            setting.setName(dto.getName());
            setting.setRatePercentage(dto.getRatePercentage());
            setting.setCode(dto.getCode());
            setting.setFeeCategory(dto.getFeeCategory());
            setting.setBaseDescription(dto.getBaseDescription());
            setting.setHidden(dto.getHidden());
            setting.setVariable(dto.getVariable());
            setting.setUserDeleted(dto.getUserDeleted());
            setting.setCalcBase(dto.getCalcBase());
            // 设置基线值
            setting.setBaseName(dto.getName());
            setting.setBaseRatePercentage(dto.getRatePercentage());
            setting.setBaseCode(dto.getCode());
            setting.setBaseFeeCategory(dto.getFeeCategory());
            setting.setBaseBaseDescription(dto.getBaseDescription());
            setting.setBaseCalcBase(dto.getCalcBase());
            feeSettingMapper.insert(setting);
        }
    }

    @Override
    public void markAsDeleted(Long divisionId, Long feeItemId) {
        // 1. 先尝试通过 sourceFeeItemId 查找（标准库取费项的覆写）
        WbUnitFeeSettingDO existing = feeSettingMapper.selectByDivisionIdAndFeeItemId(divisionId, feeItemId);
        
        if (existing != null) {
            existing.setUserDeleted(true);
            feeSettingMapper.updateById(existing);
            return;
        }
        
        // 2. 尝试通过 id 查找（自定义行，feeItemId 实际上是覆写记录的 id）
        WbUnitFeeSettingDO customRow = feeSettingMapper.selectById(feeItemId);
        if (customRow != null && divisionId.equals(customRow.getDivisionId()) && customRow.getSourceFeeItemId() == null) {
            // 自定义行直接物理删除
            feeSettingMapper.deleteById(feeItemId);
            return;
        }
        
        // 3. 都没找到，创建新的删除标记记录
        WbUnitFeeSettingDO setting = new WbUnitFeeSettingDO();
        setting.setDivisionId(divisionId);
        setting.setSourceFeeItemId(feeItemId);
        setting.setUserDeleted(true);
        feeSettingMapper.insert(setting);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void initializeSettings(Long divisionId) {
        log.info("初始化定额节点取费项设定, divisionId={}", divisionId);
        
        // 获取分部分项节点
        WbBoqDivisionDO division = divisionMapper.selectById(divisionId);
        if (division == null || !"quota".equals(division.getNodeType())) {
            log.warn("分部分项节点不存在或非定额节点, divisionId={}", divisionId);
            return;
        }
        
        // 获取费率模式ID：优先使用单位工程的用户选择，fallback到标准库树推导
        Long rateModeId = null;
        if (division.getCompileTreeId() != null) {
            WbUnitInfoDO unitInfo = unitInfoMapper.selectByCompileTreeId(division.getCompileTreeId());
            if (unitInfo != null && unitInfo.getRateModeId() != null) {
                rateModeId = unitInfo.getRateModeId();
            }
        }
        if (rateModeId == null) {
            rateModeId = quotaItemService.getRateModeIdByQuotaItem(division.getSourceQuotaItemId());
        }
        if (rateModeId == null) {
            log.warn("未找到费率模式节点, divisionId={}, sourceQuotaItemId={}", 
                    divisionId, division.getSourceQuotaItemId());
            return;
        }
        
        // 获取取费项 - 只从快照表读取，不回退到后台标准库
        List<QuotaFeeItemWithRateRespVO> feeItems = wbSnapshotReadService.getFeeItemWithRateTree(division.getCompileTreeId(), rateModeId);
        if (CollUtil.isEmpty(feeItems)) {
            log.warn("[initializeSettings] 快照取费项为空, compileTreeId={}, rateModeId={}", division.getCompileTreeId(), rateModeId);
            return;
        }

        // 扁平化并创建快照
        List<WbUnitFeeSettingDO> settings = new ArrayList<>();
        flattenAndCreateSnapshot(feeItems, divisionId, settings, new int[]{1});

        // 批量插入
        for (WbUnitFeeSettingDO setting : settings) {
            feeSettingMapper.insert(setting);
        }
    }

    /**
     * 扁平化树形数据并创建快照
     */
    private void flattenAndCreateSnapshot(List<QuotaFeeItemWithRateRespVO> items, Long divisionId,
                                          List<WbUnitFeeSettingDO> result, int[] sortOrder) {
        for (QuotaFeeItemWithRateRespVO item : items) {
            WbUnitFeeSettingDO setting = new WbUnitFeeSettingDO();
            setting.setDivisionId(divisionId);
            setting.setSourceFeeItemId(item.getFeeItemId());
            setting.setSourceRateItemId(item.getRateItemId());
            setting.setSortOrder(sortOrder[0]++);
            
            // 当前值
            String name = item.getRateItemName() != null ? item.getRateItemName() : item.getFeeItemName();
            setting.setName(name);
            setting.setRatePercentage(item.getRatePercentage());
            setting.setCode(item.getCode());
            setting.setFeeCategory(item.getFeeCategory());
            setting.setBaseDescription(item.getBaseDescription());
            setting.setHidden(item.getHidden());
            setting.setVariable(item.getVariable());
            setting.setSystemRow("ZHDJ".equals(item.getCode()) || "SJ".equals(item.getCode()) || "system".equals(item.getNodeType()));
            
            // 基线值（快照）
            setting.setBaseName(name);
            setting.setBaseRatePercentage(item.getRatePercentage());
            setting.setBaseCode(item.getCode());
            setting.setBaseFeeCategory(item.getFeeCategory());
            setting.setBaseBaseDescription(item.getBaseDescription());

            result.add(setting);

            // 递归处理子节点
            if (CollUtil.isNotEmpty(item.getChildren())) {
                flattenAndCreateSnapshot(item.getChildren(), divisionId, result, sortOrder);
            }
        }
    }

    /**
     * 校验公式合法性
     * @param formula 公式字符串
     * @return 错误信息，null 表示校验通过
     */
    private String validateFormula(String formula) {
        // 允许公式为空
        if (formula == null || formula.trim().isEmpty()) {
            return null; // 空公式视为合法
        }
        
        // 检查括号是否匹配
        int bracketCount = 0;
        for (char c : formula.toCharArray()) {
            if (c == '(') bracketCount++;
            if (c == ')') bracketCount--;
            if (bracketCount < 0) {
                return "括号不匹配";
            }
        }
        if (bracketCount != 0) {
            return "括号不匹配";
        }
        
        // 检查是否有连续的运算符（去除空格后）
        String noSpaces = formula.replaceAll("\\s+", "");
        if (noSpaces.matches(".*[+\\-*/]{2,}.*")) {
            return "存在连续的运算符";
        }
        
        // 检查是否以 * 或 / 开头，或以运算符结尾
        String trimmed = formula.trim();
        if (trimmed.matches("^[*/].*") || trimmed.matches(".*[+\\-*/]$")) {
            return "公式格式不正确";
        }
        
        // 检查是否包含非法字符（只允许字母、数字、下划线、运算符、括号、空格、小数点、中文）
        if (!noSpaces.matches("^[a-zA-Z0-9_+\\-*/().\\u4e00-\\u9fa5]+$")) {
            return "包含非法字符";
        }
        
        return null; // 校验通过
    }
}
