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

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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import com.yhy.module.core.controller.admin.quota.vo.QuotaFeeItemSaveReqVO;
import com.yhy.module.core.controller.admin.quota.vo.QuotaFeeItemWithRateRespVO;
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.QuotaRateItemDO;
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.QuotaRateItemMapper;
import com.yhy.module.core.service.quota.QuotaFeeItemService;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import com.yhy.module.core.controller.admin.resource.vo.ResourceCategoryFullRespVO;
import com.yhy.module.core.service.quota.QuotaCatalogItemService;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
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 QuotaFeeItemServiceImpl implements QuotaFeeItemService {

    @Resource
    private QuotaFeeItemMapper quotaFeeItemMapper;

    @Resource
    private QuotaCatalogItemMapper quotaCatalogItemMapper;

    @Resource
    private QuotaRateItemMapper quotaRateItemMapper;

    @Resource
    private QuotaCatalogItemService quotaCatalogItemService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createFeeItem(QuotaFeeItemSaveReqVO createReqVO) {
        // 1. 验证模式节点
        validateRateModeNode(createReqVO.getCatalogItemId());

        // 2. 验证费率项ID（如果提供）
        if (createReqVO.getRateItemId() != null) {
            validateRateItem(createReqVO.getCatalogItemId(), createReqVO.getRateItemId());
        }

        // 3. 验证计算基数（创建时不是系统行）
        if (createReqVO.getCalcBase() != null) {
            validateCalcBaseInternal(createReqVO.getCalcBase(), false, createReqVO.getCatalogItemId());
        }

        // 4. 验证自定义序号唯一性
        if (StrUtil.isNotBlank(createReqVO.getCustomCode())) {
            validateCustomCodeUnique(null, createReqVO.getCatalogItemId(), createReqVO.getCustomCode());
        }

        // 5. 验证代号唯一性
        if (StrUtil.isNotBlank(createReqVO.getCode())) {
            validateCodeUnique(null, createReqVO.getCatalogItemId(), createReqVO.getCode());
        }

        // 6. 设置排序值
        if (createReqVO.getSortOrder() == null) {
            Integer maxSortOrder = quotaFeeItemMapper.selectMaxSortOrder(createReqVO.getCatalogItemId());
            createReqVO.setSortOrder(maxSortOrder + 1);
        }

        // 7. 插入数据
        QuotaFeeItemDO feeItem = BeanUtils.toBean(createReqVO, QuotaFeeItemDO.class);
        quotaFeeItemMapper.insert(feeItem);

        return feeItem.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateFeeItem(QuotaFeeItemSaveReqVO updateReqVO) {
        // 1. 验证存在
        QuotaFeeItemDO existingItem = quotaFeeItemMapper.selectById(updateReqVO.getId());
        if (existingItem == null) {
            throw exception(QUOTA_FEE_ITEM_NOT_EXISTS);
        }

        // 2. 系统行保护：禁止修改代号
        if (existingItem.isSystemRow() && updateReqVO.getCode() != null 
                && !existingItem.getCode().equals(updateReqVO.getCode())) {
            throw exception(QUOTA_FEE_ITEM_SYSTEM_ROW_CANNOT_MODIFY_CODE);
        }

        // 3. 验证模式节点
        validateRateModeNode(updateReqVO.getCatalogItemId());

        // 4. 验证费率项ID（如果提供）
        if (updateReqVO.getRateItemId() != null) {
            validateRateItem(updateReqVO.getCatalogItemId(), updateReqVO.getRateItemId());
        }

        // 5. 验证计算基数
        // - 综合单价(ZHDJ)：跳过变量验证，公式使用其他取费项代号作为变量
        // - 普通取费项：验证公式中的变量是否有效（取费项代号或类别价格代码）
        if (updateReqVO.getCalcBase() != null) {
            String systemCode = existingItem.getSystemCode();
            boolean isZhdj = QuotaFeeItemDO.SYSTEM_CODE_ZHDJ.equals(systemCode);
            validateCalcBaseInternal(updateReqVO.getCalcBase(), isZhdj, updateReqVO.getCatalogItemId());
        }

        // 6. 验证自定义序号唯一性
        if (StrUtil.isNotBlank(updateReqVO.getCustomCode())) {
            validateCustomCodeUnique(updateReqVO.getId(), updateReqVO.getCatalogItemId(), updateReqVO.getCustomCode());
        }

        // 7. 验证代号唯一性（非系统行才验证）
        if (!existingItem.isSystemRow() && StrUtil.isNotBlank(updateReqVO.getCode())) {
            validateCodeUnique(updateReqVO.getId(), updateReqVO.getCatalogItemId(), updateReqVO.getCode());
        }

        // 8. 更新数据（系统行不更新 systemCode 和 sortOrder）
        QuotaFeeItemDO updateObj = BeanUtils.toBean(updateReqVO, QuotaFeeItemDO.class);
        if (existingItem.isSystemRow()) {
            updateObj.setSystemCode(null); // 不更新 systemCode
            updateObj.setSortOrder(null);  // 不更新 sortOrder
        }
        quotaFeeItemMapper.updateById(updateObj);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteFeeItem(Long id) {
        // 1. 验证存在
        QuotaFeeItemDO feeItem = quotaFeeItemMapper.selectById(id);
        if (feeItem == null) {
            throw exception(QUOTA_FEE_ITEM_NOT_EXISTS);
        }

        // 2. 系统行保护：禁止删除
        if (feeItem.isSystemRow()) {
            throw exception(QUOTA_FEE_ITEM_SYSTEM_ROW_CANNOT_DELETE);
        }

        // 3. 删除
        quotaFeeItemMapper.deleteById(id);
    }

    @Override
    public QuotaFeeItemDO getFeeItem(Long id) {
        return quotaFeeItemMapper.selectById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<QuotaFeeItemDO> getFeeItemList(Long catalogItemId) {
        List<QuotaFeeItemDO> list = quotaFeeItemMapper.selectListByCatalogItemId(catalogItemId);
        
        // 懒加载：确保综合单价行存在
        ensureSystemRowExists(catalogItemId, list);
        
        return list;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<QuotaFeeItemWithRateRespVO> getFeeItemWithRateList(Long catalogItemId) {
        // 1. 验证模式节点
        validateRateModeNode(catalogItemId);

        // 2. 查询所有费率项节点（只获取目录节点，不获取子项）
        List<QuotaRateItemDO> allRateItems = quotaRateItemMapper.selectListByCatalogItemId(catalogItemId);
        // 过滤只保留目录节点
        List<QuotaRateItemDO> rateItems = allRateItems.stream()
                .filter(QuotaRateItemDO::isDirectory)
                .collect(Collectors.toList());

        // 3. 查询取费项列表
        List<QuotaFeeItemDO> feeItems = quotaFeeItemMapper.selectListByCatalogItemId(catalogItemId);

        // 4. 使用公共方法构建结果
        return buildFeeItemWithRateList(rateItems, feeItems, catalogItemId);
    }

    @Override
    public List<QuotaFeeItemWithRateRespVO> buildFeeItemWithRateList(
            List<? extends QuotaRateItemDO> rateItems,
            List<? extends QuotaFeeItemDO> feeItems,
            Long catalogItemId) {
        // 1. 构建取费项Map（使用 rate_item_id）
        Map<Long, ? extends QuotaFeeItemDO> feeItemByRateItemIdMap = feeItems.stream()
                .filter(item -> item.getRateItemId() != null)
                .collect(Collectors.toMap(
                        QuotaFeeItemDO::getRateItemId,
                        item -> item,
                        (existing, replacement) -> existing
                ));

        // 2. 转换为 VO 并合并取费项数据
        List<QuotaFeeItemWithRateRespVO> allVos = new ArrayList<>();
        for (QuotaRateItemDO rateItem : rateItems) {
            QuotaFeeItemWithRateRespVO vo = new QuotaFeeItemWithRateRespVO();

            // 设置费率项信息
            vo.setRateItemId(rateItem.getId());
            vo.setRateItemName(rateItem.getName());
            vo.setRateItemCustomCode(rateItem.getCustomCode());
            vo.setRateCode(rateItem.getRateCode());
            vo.setParentId(rateItem.getParentId());
            vo.setNodeType(rateItem.getNodeType());
            vo.setCatalogItemId(catalogItemId);
            // 费率代号始终从费率项获取（无论是否有取费项）
            vo.setRatePercentage(rateItem.getRateCode());
            // 费率实际值从费率项的默认值获取
            vo.setRateValue(rateItem.getDefaultValue());

            // 查找对应的取费项（使用 rate_item_id）
            QuotaFeeItemDO feeItem = feeItemByRateItemIdMap.get(rateItem.getId());

            if (feeItem != null) {
                // 设置取费项信息
                vo.setFeeItemId(feeItem.getId());
                vo.setFeeItemName(feeItem.getName());
                vo.setFeeItemCustomCode(feeItem.getCustomCode());
                vo.setCalcBase(feeItem.getCalcBase());
                vo.setCode(feeItem.getCode());
                vo.setFeeCategory(feeItem.getFeeCategory());
                vo.setBaseDescription(feeItem.getBaseDescription());
                vo.setSortOrder(feeItem.getSortOrder());
                vo.setHidden(feeItem.getHidden());
                vo.setVariable(feeItem.getVariable());
                vo.setCreateTime(feeItem.getCreateTime());
                vo.setUpdateTime(feeItem.getUpdateTime());
                vo.setHasFeeItem(true);
            } else {
                vo.setHasFeeItem(false);
            }

            allVos.add(vo);
        }

        // 3. 构建树形结构
        List<QuotaFeeItemWithRateRespVO> result = buildTree(allVos);

        // 4. 追加综合单价系统行（始终排在最后）
        appendSystemRowFromList(catalogItemId, feeItems, result);

        return result;
    }

    /**
     * 追加系统行到结果列表末尾（只有综合单价）
     */
    private void appendSystemRow(Long catalogItemId, List<QuotaFeeItemDO> feeItems, List<QuotaFeeItemWithRateRespVO> result) {
        // 处理综合单价系统行
        QuotaFeeItemDO zhdjItem = feeItems.stream()
                .filter(item -> QuotaFeeItemDO.SYSTEM_CODE_ZHDJ.equals(item.getSystemCode()))
                .findFirst()
                .orElse(null);

        if (zhdjItem == null) {
            zhdjItem = new QuotaFeeItemDO();
            zhdjItem.setCatalogItemId(catalogItemId);
            zhdjItem.setName("综合单价");
            zhdjItem.setCode(QuotaFeeItemDO.SYSTEM_CODE_ZHDJ);
            zhdjItem.setSystemCode(QuotaFeeItemDO.SYSTEM_CODE_ZHDJ);
            zhdjItem.setSortOrder(QuotaFeeItemDO.SYSTEM_ROW_SORT_ORDER);
            zhdjItem.setHidden(false);
            zhdjItem.setVariable(false);
            quotaFeeItemMapper.insert(zhdjItem);
            log.info("[appendSystemRow] 自动创建综合单价系统行, catalogItemId={}, id={}", catalogItemId, zhdjItem.getId());
        }

        // 转换综合单价为 VO
        QuotaFeeItemWithRateRespVO zhdjVo = convertSystemRowToVO(zhdjItem, catalogItemId);
        result.add(zhdjVo);
    }

    /**
     * 追加系统行到结果列表末尾（供buildFeeItemWithRateList使用，自动创建缺失的系统行）
     */
    private void appendSystemRowFromList(Long catalogItemId, List<? extends QuotaFeeItemDO> feeItems, List<QuotaFeeItemWithRateRespVO> result) {
        // 处理综合单价系统行
        QuotaFeeItemDO zhdjItem = feeItems.stream()
                .filter(item -> QuotaFeeItemDO.SYSTEM_CODE_ZHDJ.equals(item.getSystemCode()))
                .findFirst()
                .orElse(null);

        if (zhdjItem == null) {
            // 自动创建综合单价系统行
            zhdjItem = new QuotaFeeItemDO();
            zhdjItem.setCatalogItemId(catalogItemId);
            zhdjItem.setName("综合单价");
            zhdjItem.setCode(QuotaFeeItemDO.SYSTEM_CODE_ZHDJ);
            zhdjItem.setSystemCode(QuotaFeeItemDO.SYSTEM_CODE_ZHDJ);
            zhdjItem.setSortOrder(QuotaFeeItemDO.SYSTEM_ROW_SORT_ORDER);
            zhdjItem.setHidden(false);
            zhdjItem.setVariable(false);
            quotaFeeItemMapper.insert(zhdjItem);
            log.info("[appendSystemRowFromList] 自动创建综合单价系统行, catalogItemId={}, id={}", catalogItemId, zhdjItem.getId());
        }
        QuotaFeeItemWithRateRespVO zhdjVo = convertSystemRowToVO(zhdjItem, catalogItemId);
        result.add(zhdjVo);
    }

    /**
     * 将系统行 DO 转换为 VO
     */
    private QuotaFeeItemWithRateRespVO convertSystemRowToVO(QuotaFeeItemDO item, Long catalogItemId) {
        QuotaFeeItemWithRateRespVO vo = new QuotaFeeItemWithRateRespVO();
        vo.setFeeItemId(item.getId());
        vo.setFeeItemName(item.getName());
        vo.setFeeItemCustomCode(item.getCustomCode());
        vo.setCalcBase(item.getCalcBase());
        vo.setCode(item.getCode());
        vo.setFeeCategory(item.getFeeCategory());
        vo.setBaseDescription(item.getBaseDescription());
        vo.setSortOrder(item.getSortOrder());
        vo.setHidden(item.getHidden());
        vo.setVariable(item.getVariable());
        vo.setSystemCode(item.getSystemCode());
        vo.setCatalogItemId(catalogItemId);
        vo.setHasFeeItem(true);
        vo.setCreateTime(item.getCreateTime());
        vo.setUpdateTime(item.getUpdateTime());
        // 系统行没有关联费率项
        vo.setRateItemId(null);
        vo.setRateItemName(null);
        vo.setNodeType("system"); // 标识为系统行
        return vo;
    }

    /**
     * 构建树形结构
     */
    private List<QuotaFeeItemWithRateRespVO> buildTree(List<QuotaFeeItemWithRateRespVO> allVos) {
        // 1. 构建 ID -> VO 的映射
        Map<Long, QuotaFeeItemWithRateRespVO> voMap = allVos.stream()
                .collect(Collectors.toMap(QuotaFeeItemWithRateRespVO::getRateItemId, vo -> vo));

        // 2. 构建树形结构
        List<QuotaFeeItemWithRateRespVO> rootNodes = new ArrayList<>();
        for (QuotaFeeItemWithRateRespVO vo : allVos) {
            if (vo.getParentId() == null) {
                // 根节点
                rootNodes.add(vo);
            } else {
                // 子节点，添加到父节点的 children 中
                QuotaFeeItemWithRateRespVO parent = voMap.get(vo.getParentId());
                if (parent != null) {
                    if (parent.getChildren() == null) {
                        parent.setChildren(new ArrayList<>());
                    }
                    parent.getChildren().add(vo);
                }
            }
        }

        // 3. 对每个节点的子节点按 sortOrder 排序（如果有取费项）
        sortTreeNodes(rootNodes);

        return rootNodes;
    }

    /**
     * 递归排序树节点
     */
    private void sortTreeNodes(List<QuotaFeeItemWithRateRespVO> nodes) {
        if (CollUtil.isEmpty(nodes)) {
            return;
        }

        // 不需要排序，保持费率项的原有顺序（已经按 sortOrder 排序）
        // 费率项在查询时已经按 sortOrder 排序，这里不再重新排序

        // 递归排序子节点
        for (QuotaFeeItemWithRateRespVO node : nodes) {
            if (CollUtil.isNotEmpty(node.getChildren())) {
                sortTreeNodes(node.getChildren());
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void swapSort(Long nodeId1, Long nodeId2) {
        // 1. 查询两个节点（使用行锁）
        QuotaFeeItemDO node1 = quotaFeeItemMapper.selectById(nodeId1);
        QuotaFeeItemDO node2 = quotaFeeItemMapper.selectById(nodeId2);

        if (node1 == null || node2 == null) {
            throw exception(QUOTA_FEE_ITEM_NOT_EXISTS);
        }

        // 2. 系统行保护：禁止参与排序交换
        if (node1.isSystemRow() || node2.isSystemRow()) {
            throw exception(QUOTA_FEE_ITEM_SYSTEM_ROW_CANNOT_SWAP);
        }

        // 3. 验证是否在同一模式下
        if (!node1.getCatalogItemId().equals(node2.getCatalogItemId())) {
            throw exception(QUOTA_FEE_ITEM_NOT_SAME_CATALOG);
        }

        // 4. 交换排序值
        Integer tempSort = node1.getSortOrder();
        node1.setSortOrder(node2.getSortOrder());
        node2.setSortOrder(tempSort);

        // 5. 更新数据库
        quotaFeeItemMapper.updateById(node1);
        quotaFeeItemMapper.updateById(node2);
    }

    @Override
    public void validateFeeItemExists(Long id) {
        if (quotaFeeItemMapper.selectById(id) == null) {
            throw exception(QUOTA_FEE_ITEM_NOT_EXISTS);
        }
    }

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

    /**
     * 验证模式节点
     */
    private void validateRateModeNode(Long catalogItemId) {
        QuotaCatalogItemDO catalogItem = quotaCatalogItemMapper.selectById(catalogItemId);
        if (catalogItem == null) {
            throw exception(QUOTA_CATALOG_ITEM_NOT_EXISTS);
        }

        // 验证 node_type 必须是 rate_mode
        Map<String, Object> attributes = catalogItem.getAttributes();
        if (attributes == null || !"rate_mode".equals(attributes.get("node_type"))) {
            throw exception(QUOTA_FEE_ITEM_CATALOG_NOT_RATE_MODE);
        }
    }

    /**
     * 验证费率项
     */
    private void validateRateItem(Long catalogItemId, Long rateItemId) {
        QuotaRateItemDO rateItem = quotaRateItemMapper.selectById(rateItemId);
        if (rateItem == null) {
            throw exception(QUOTA_FEE_ITEM_NOT_EXISTS);
        }

        // 验证费率项必须属于同一模式节点
        if (!rateItem.getCatalogItemId().equals(catalogItemId)) {
            throw exception(QUOTA_FEE_ITEM_NOT_SAME_CATALOG);
        }

        // 验证费率项必须是目录节点（只有一级和二级目录可以关联取费项）
        if (!rateItem.isDirectory()) {
            throw exception(QUOTA_FEE_ITEM_RATE_ITEM_NOT_DIRECTORY);
        }
    }

    /**
     * 验证计算基数（公开方法，供其他服务调用）
     * @param calcBase 计算基数对象
     */
    @Override
    public void validateCalcBase(Map<String, Object> calcBase) {
        validateCalcBaseInternal(calcBase, false, null);
    }

    /**
     * 验证计算基数
     * @param calcBase 计算基数对象
     * @param isZhdj 是否为综合单价行，跳过变量验证
     * @param catalogItemId 模式节点ID，用于获取有效的取费项代号和类别价格代码
     */
    private void validateCalcBaseInternal(Map<String, Object> calcBase, boolean isZhdj, Long catalogItemId) {
        // 1. 验证必填字段
        String formula = (String) calcBase.get("formula");
        if (StrUtil.isBlank(formula)) {
            throw exception(QUOTA_FEE_CALC_BASE_FORMULA_EMPTY);
        }

        // 综合单价(ZHDJ)：只验证公式不为空，跳过变量验证
        // 因为综合单价的公式使用其他取费项的代号作为变量，不需要 categoryId/priceField 定义
        if (isZhdj) {
            return;
        }

        // 普通取费项：验证公式中的变量是否有效（取费项代号或类别价格代码）
        if (catalogItemId != null) {
            validateFeeItemFormulaVariables(formula, catalogItemId);
            return;
        }

        // 兼容旧逻辑：验证 variables 中的 categoryId 和 priceField
        @SuppressWarnings("unchecked")
        Map<String, Object> variables = (Map<String, Object>) calcBase.get("variables");
        if (MapUtil.isEmpty(variables)) {
            throw exception(QUOTA_FEE_CALC_BASE_VARIABLES_EMPTY);
        }

        // 2. 验证价格代码必须是固定的4个值
        Set<String> allowedCodes = CollUtil.newHashSet(
                QuotaFeeItemDO.PRICE_CODE_TAX_EXCL_BASE,
                QuotaFeeItemDO.PRICE_CODE_TAX_INCL_BASE,
                QuotaFeeItemDO.PRICE_CODE_TAX_EXCL_COMPILE,
                QuotaFeeItemDO.PRICE_CODE_TAX_INCL_COMPILE
        );

        // 遍历 variables，验证每个变量的格式和价格字段类型
        for (Map.Entry<String, Object> entry : variables.entrySet()) {
            String varName = entry.getKey();
            Object varValue = entry.getValue();
            
            // 验证值必须是 Map 类型
            if (!(varValue instanceof Map)) {
                throw exception(QUOTA_FEE_CALC_BASE_INVALID_PRICE_CODE, "变量 " + varName + " 的值格式错误");
            }
            
            @SuppressWarnings("unchecked")
            Map<String, Object> varInfo = (Map<String, Object>) varValue;
            
            // 验证必须包含 categoryId 和 priceField
            if (!varInfo.containsKey("categoryId") || !varInfo.containsKey("priceField")) {
                throw exception(QUOTA_FEE_CALC_BASE_INVALID_PRICE_CODE, 
                    "变量 " + varName + " 缺少 categoryId 或 priceField 字段");
            }
            
            // 验证 priceField 必须是允许的价格代码
            String priceField = (String) varInfo.get("priceField");
            if (!allowedCodes.contains(priceField)) {
                throw exception(QUOTA_FEE_CALC_BASE_INVALID_PRICE_CODE, priceField);
            }
        }

        // 3. 验证公式语法
        validateFormulaSyntax(formula, variables.keySet());
    }

    /**
     * 验证普通取费项公式中的变量是否有效（可引用取费项代号和类别价格代码）
     * @param formula 公式字符串
     * @param catalogItemId 模式节点ID
     */
    private void validateFeeItemFormulaVariables(String formula, Long catalogItemId) {
        // 1. 提取公式中的所有变量（去除运算符和数字）
        String cleanFormula = formula.replaceAll("[+\\-*/()\\s\\d.]+", " ");
        String[] potentialCodes = cleanFormula.trim().split("\\s+");
        
        if (potentialCodes.length == 0 || (potentialCodes.length == 1 && potentialCodes[0].isEmpty())) {
            return; // 没有变量，只有数字和运算符
        }
        
        // 2. 获取所有有效的变量代码
        Set<String> validCodes = new HashSet<>();
        
        // 2.1 获取取费项代号（排除当前行和综合单价）
        List<QuotaFeeItemDO> feeItems = quotaFeeItemMapper.selectList(
            new LambdaQueryWrapperX<QuotaFeeItemDO>()
                .eq(QuotaFeeItemDO::getCatalogItemId, catalogItemId)
        );
        for (QuotaFeeItemDO item : feeItems) {
            if (StrUtil.isNotBlank(item.getCode()) 
                && !QuotaFeeItemDO.SYSTEM_CODE_ZHDJ.equals(item.getSystemCode())) {
                validCodes.add(item.getCode());
            }
        }
        
        // 2.2 获取类别价格代码（从机类字典获取）
        List<ResourceCategoryFullRespVO> categories = quotaCatalogItemService.getCategoriesByCatalogItem(catalogItemId);
        for (ResourceCategoryFullRespVO category : categories) {
            // 添加4个价格代码
            if (StrUtil.isNotBlank(category.getTaxExclBaseCode())) {
                validCodes.add(category.getTaxExclBaseCode());
            }
            if (StrUtil.isNotBlank(category.getTaxInclBaseCode())) {
                validCodes.add(category.getTaxInclBaseCode());
            }
            if (StrUtil.isNotBlank(category.getTaxExclCompileCode())) {
                validCodes.add(category.getTaxExclCompileCode());
            }
            if (StrUtil.isNotBlank(category.getTaxInclCompileCode())) {
                validCodes.add(category.getTaxInclCompileCode());
            }
        }
        
        // 3. 检查公式中的变量是否都有效
        List<String> invalidCodes = new ArrayList<>();
        for (String code : potentialCodes) {
            if (StrUtil.isNotBlank(code) && !validCodes.contains(code)) {
                invalidCodes.add(code);
            }
        }
        
        if (!invalidCodes.isEmpty()) {
            throw exception(QUOTA_FEE_CALC_BASE_INVALID_PRICE_CODE, 
                "公式中包含无效的变量: " + String.join(", ", invalidCodes));
        }
    }

    /**
     * 验证公式语法
     */
    private void validateFormulaSyntax(String formula, Set<String> variableNames) {
        List<String> sortedVarNames = new ArrayList<>(variableNames);
        sortedVarNames.sort((a, b) -> Integer.compare(b.length(), a.length()));

        int index = 0;
        int parenDepth = 0;
        // Simple tokenizer/validator: enforce operator/operand order and parenthesis balance.
        boolean expectOperand = true;
        boolean hasToken = false;

        while (index < formula.length()) {
            char c = formula.charAt(index);
            if (Character.isWhitespace(c)) {
                index++;
                continue;
            }

            if (c == '(') {
                if (!expectOperand) {
                    throw exception(QUOTA_FEE_CALC_BASE_FORMULA_INVALID_CHAR, "缺少运算符（'(' 前）");
                }
                parenDepth++;
                index++;
                expectOperand = true;
                hasToken = true;
                continue;
            }

            if (c == ')') {
                if (expectOperand) {
                    throw exception(QUOTA_FEE_CALC_BASE_FORMULA_INVALID_CHAR, "缺少操作数（')' 前）");
                }
                parenDepth--;
                if (parenDepth < 0) {
                    throw exception(QUOTA_FEE_CALC_BASE_FORMULA_BRACKET_MISMATCH);
                }
                index++;
                expectOperand = false;
                hasToken = true;
                continue;
            }

            if (isOperator(c)) {
                if (expectOperand) {
                    if (c == '-') {
                        // Unary minus is allowed when an operand is expected.
                        index++;
                        hasToken = true;
                        continue;
                    }
                    throw exception(QUOTA_FEE_CALC_BASE_FORMULA_INVALID_CHAR, "运算符位置不合法: " + c);
                }
                expectOperand = true;
                index++;
                hasToken = true;
                continue;
            }

            if (isNumberStart(c)) {
                if (!expectOperand) {
                    throw exception(QUOTA_FEE_CALC_BASE_FORMULA_INVALID_CHAR, "数字前缺少运算符");
                }
                // Consume a numeric literal (integer or decimal).
                index = consumeNumber(formula, index);
                expectOperand = false;
                hasToken = true;
                continue;
            }

            String matchedVar = matchVariable(formula, index, sortedVarNames);
            if (matchedVar != null) {
                if (!expectOperand) {
                    throw exception(QUOTA_FEE_CALC_BASE_FORMULA_INVALID_CHAR, "变量前缺少运算符");
                }
                index += matchedVar.length();
                expectOperand = false;
                hasToken = true;
                continue;
            }

            throw exception(QUOTA_FEE_CALC_BASE_FORMULA_INVALID_CHAR, String.valueOf(c));
        }

        if (parenDepth != 0) {
            throw exception(QUOTA_FEE_CALC_BASE_FORMULA_BRACKET_MISMATCH);
        }
        if (!hasToken || expectOperand) {
            throw exception(QUOTA_FEE_CALC_BASE_FORMULA_INVALID_CHAR, "公式语法不正确");
        }
    }

    private boolean isOperator(char c) {
        return c == '+' || c == '-' || c == '*' || c == '/';
    }

    private boolean isNumberStart(char c) {
        return Character.isDigit(c) || c == '.';
    }

    private int consumeNumber(String formula, int start) {
        int index = start;
        boolean hasDigit = false;
        boolean hasDot = false;

        while (index < formula.length()) {
            char c = formula.charAt(index);
            if (Character.isDigit(c)) {
                hasDigit = true;
                index++;
                continue;
            }
            if (c == '.' && !hasDot) {
                hasDot = true;
                index++;
                continue;
            }
            break;
        }

        if (!hasDigit) {
            throw exception(QUOTA_FEE_CALC_BASE_FORMULA_INVALID_CHAR, "数字格式不正确");
        }

        return index;
    }

    private String matchVariable(String formula, int index, List<String> sortedVarNames) {
        for (String varName : sortedVarNames) {
            if (varName.isEmpty()) {
                continue;
            }
            if (formula.startsWith(varName, index)) {
                return varName;
            }
        }
        return null;
    }

    /**
     * 验证自定义序号唯一性
     */
    private void validateCustomCodeUnique(Long id, Long catalogItemId, String customCode) {
        QuotaFeeItemDO existingItem = quotaFeeItemMapper.selectByCustomCode(catalogItemId, customCode);
        if (existingItem != null && !existingItem.getId().equals(id)) {
            throw exception(QUOTA_FEE_ITEM_CUSTOM_CODE_DUPLICATE, customCode);
        }
    }

    /**
     * 验证代号唯一性
     */
    private void validateCodeUnique(Long id, Long catalogItemId, String code) {
        QuotaFeeItemDO existingItem = quotaFeeItemMapper.selectByCode(catalogItemId, code);
        if (existingItem != null && !existingItem.getId().equals(id)) {
            throw exception(QUOTA_FEE_ITEM_CODE_DUPLICATE, code);
        }
    }

    /**
     * 确保系统行存在（懒加载）
     * 如果综合单价行不存在，则自动创建
     */
    private void ensureSystemRowExists(Long catalogItemId, List<QuotaFeeItemDO> list) {
        // 检查是否已存在 ZHDJ 系统行
        boolean hasZhdj = list.stream()
                .anyMatch(item -> QuotaFeeItemDO.SYSTEM_CODE_ZHDJ.equals(item.getSystemCode()));
        
        if (!hasZhdj) {
            // 创建综合单价系统行
            QuotaFeeItemDO zhdjRow = new QuotaFeeItemDO();
            zhdjRow.setCatalogItemId(catalogItemId);
            zhdjRow.setName("综合单价");
            zhdjRow.setCode(QuotaFeeItemDO.SYSTEM_CODE_ZHDJ);
            zhdjRow.setSystemCode(QuotaFeeItemDO.SYSTEM_CODE_ZHDJ);
            zhdjRow.setSortOrder(QuotaFeeItemDO.SYSTEM_ROW_SORT_ORDER);
            zhdjRow.setHidden(false);
            zhdjRow.setVariable(false);
            
            quotaFeeItemMapper.insert(zhdjRow);
            list.add(zhdjRow);
            
            log.info("[ensureSystemRowExists] 自动创建综合单价系统行, catalogItemId={}, id={}", 
                    catalogItemId, zhdjRow.getId());
        }
    }
}

