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

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;
import static com.yhy.module.core.enums.ErrorCodeConstants.WB_COMPILE_TREE_NOT_EXISTS;
import static com.yhy.module.core.enums.ErrorCodeConstants.WB_UNIT_INFO_CODE_EXISTS;
import static com.yhy.module.core.enums.ErrorCodeConstants.WB_UNIT_INFO_NODE_NOT_UNIT;
import static com.yhy.module.core.enums.ErrorCodeConstants.WB_UNIT_INFO_NOT_EXISTS;

import com.yhy.module.core.controller.admin.workbench.vo.WbUnitInfoRespVO;
import com.yhy.module.core.controller.admin.workbench.vo.WbUnitInfoSaveReqVO;
import com.yhy.module.core.controller.admin.workbench.vo.WbUsedQuotaSpecialtyRespVO;
import com.yhy.module.core.convert.workbench.WbUnitInfoConvert;
import com.yhy.module.core.dal.dataobject.boq.BoqCatalogItemDO;
import com.yhy.module.core.dal.dataobject.quota.QuotaCatalogItemDO;
import com.yhy.module.core.dal.dataobject.quota.QuotaCatalogTreeDO;
import com.yhy.module.core.dal.dataobject.quota.QuotaItemDO;
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.WbUnitInfoDO;
import com.yhy.module.core.dal.mysql.boq.BoqCatalogItemMapper;
import com.yhy.module.core.dal.mysql.quota.QuotaCatalogItemMapper;
import com.yhy.module.core.dal.mysql.quota.QuotaCatalogTreeMapper;
import com.yhy.module.core.dal.mysql.quota.QuotaItemMapper;
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.WbUnitInfoMapper;
import com.yhy.module.core.service.workbench.WbUnitInfoService;
import java.util.ArrayList;
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 实现类
 *
 * @author yhy
 */
@Service
@Validated
@Slf4j
public class WbUnitInfoServiceImpl implements WbUnitInfoService {

    @Resource
    private WbUnitInfoMapper wbUnitInfoMapper;

    @Resource
    private WbCompileTreeMapper wbCompileTreeMapper;

    @Resource
    private BoqCatalogItemMapper boqCatalogItemMapper;

    @Resource
    private QuotaCatalogItemMapper quotaCatalogItemMapper;

    @Resource
    private WbBoqDivisionMapper wbBoqDivisionMapper;

    @Resource
    private QuotaItemMapper quotaItemMapper;

    @Resource
    private QuotaCatalogTreeMapper quotaCatalogTreeMapper;

    @Resource
    private WbProjectTreeMapper wbProjectTreeMapper;

    @Resource
    private com.yhy.module.core.service.workbench.WbSnapshotService wbSnapshotService;

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

    @Override
    public WbUnitInfoRespVO getByCompileTreeId(Long compileTreeId) {
        // 1. 校验节点存在且是单位工程节点
        validateUnitNode(compileTreeId);

        // 2. 查询单位工程信息
        WbUnitInfoDO unitInfo = wbUnitInfoMapper.selectByCompileTreeId(compileTreeId);
        if (unitInfo == null) {
            return null;
        }

        // 3. 转换并填充关联名称
        WbUnitInfoRespVO respVO = WbUnitInfoConvert.INSTANCE.convert(unitInfo);
        fillRelatedNames(respVO, unitInfo);

        return respVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long save(WbUnitInfoSaveReqVO saveReqVO) {
        // 1. 校验节点存在且是单位工程节点
        WbCompileTreeDO node = validateUnitNode(saveReqVO.getCompileTreeId());

        // 2. 校验工程编号唯一性
        validateUnitCodeUnique(node.getProjectId(), saveReqVO.getUnitCode(), saveReqVO.getId());

        // 3. 校验并处理费率模式绑定（项目级锁定规则）
        handleRateModeBinding(node.getProjectId(), saveReqVO);

        // 4. 查询是否已存在
        WbUnitInfoDO existInfo = wbUnitInfoMapper.selectByCompileTreeId(saveReqVO.getCompileTreeId());

        if (existInfo != null) {
            // 更新
            WbUnitInfoDO updateObj = WbUnitInfoConvert.INSTANCE.convert(saveReqVO);
            updateObj.setId(existInfo.getId());
            wbUnitInfoMapper.updateById(updateObj);

            // 如果定额专业或费率模式变化，更新快照
            boolean quotaChanged = !java.util.Objects.equals(existInfo.getQuotaCatalogItemId(), saveReqVO.getQuotaCatalogItemId());
            boolean rateModeChanged = !java.util.Objects.equals(existInfo.getRateModeId(), saveReqVO.getRateModeId());
            if ((quotaChanged || rateModeChanged) && saveReqVO.getQuotaCatalogItemId() != null && saveReqVO.getRateModeId() != null) {
                log.info("[save] 定额专业或费率模式变化，更新快照, compileTreeId={}, quotaCatalogItemId={}, rateModeId={}",
                        saveReqVO.getCompileTreeId(), saveReqVO.getQuotaCatalogItemId(), saveReqVO.getRateModeId());
                wbSnapshotService.deleteSnapshot(saveReqVO.getCompileTreeId());
                wbSnapshotService.createSnapshot(saveReqVO.getCompileTreeId(), saveReqVO.getQuotaCatalogItemId(), saveReqVO.getRateModeId());
            }

            return existInfo.getId();
        } else {
            // 新增
            WbUnitInfoDO unitInfo = WbUnitInfoConvert.INSTANCE.convert(saveReqVO);
            wbUnitInfoMapper.insert(unitInfo);

            // 创建快照
            if (saveReqVO.getQuotaCatalogItemId() != null && saveReqVO.getRateModeId() != null) {
                log.info("[save] 新建单位工程，创建快照, compileTreeId={}, quotaCatalogItemId={}, rateModeId={}",
                        saveReqVO.getCompileTreeId(), saveReqVO.getQuotaCatalogItemId(), saveReqVO.getRateModeId());
                wbSnapshotService.createSnapshot(saveReqVO.getCompileTreeId(), saveReqVO.getQuotaCatalogItemId(), saveReqVO.getRateModeId());
            }

            // 导入分部分项模板（如果指定了专业类别 specialtyType）
            if (cn.hutool.core.util.StrUtil.isNotBlank(saveReqVO.getSpecialtyType())) {
                try {
                    Long catalogItemId = Long.parseLong(saveReqVO.getSpecialtyType());
                    log.info("[save] 新建单位工程，导入分部分项模板, compileTreeId={}, catalogItemId={}",
                            saveReqVO.getCompileTreeId(), catalogItemId);
                    wbBoqDivisionService.importTemplate(saveReqVO.getCompileTreeId(), catalogItemId);
                } catch (NumberFormatException e) {
                    log.warn("[save] specialtyType 不是有效的节点ID，跳过模板导入: {}", saveReqVO.getSpecialtyType());
                }
            }

            return unitInfo.getId();
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteByCompileTreeId(Long compileTreeId) {
        // 删除快照数据
        wbSnapshotService.deleteSnapshot(compileTreeId);
        // 删除单位工程信息
        wbUnitInfoMapper.deleteByCompileTreeId(compileTreeId);
    }

    @Override
    public void validateUnitCodeUnique(Long projectId, String unitCode, Long selfId) {
        // 查询项目下所有单位工程节点
        List<WbCompileTreeDO> allNodes = wbCompileTreeMapper.selectListByProjectId(projectId);

        // 过滤出单位工程节点
        for (WbCompileTreeDO node : allNodes) {
            if (!WbCompileTreeDO.NODE_TYPE_UNIT.equals(node.getNodeType())) {
                continue;
            }

            // 查询该节点的单位工程信息
            WbUnitInfoDO unitInfo = wbUnitInfoMapper.selectByCompileTreeId(node.getId());
            if (unitInfo == null) {
                continue;
            }

            // 排除自身
            if (selfId != null && selfId.equals(unitInfo.getId())) {
                continue;
            }

            // 检查编号是否重复
            if (unitCode.equals(unitInfo.getUnitCode())) {
                throw exception(WB_UNIT_INFO_CODE_EXISTS);
            }
        }
    }

    /**
     * 校验节点存在且是单位工程节点
     */
    private WbCompileTreeDO validateUnitNode(Long compileTreeId) {
        WbCompileTreeDO node = wbCompileTreeMapper.selectById(compileTreeId);
        if (node == null) {
            throw exception(WB_COMPILE_TREE_NOT_EXISTS);
        }
        if (!WbCompileTreeDO.NODE_TYPE_UNIT.equals(node.getNodeType())) {
            throw exception(WB_UNIT_INFO_NODE_NOT_UNIT);
        }
        return node;
    }

    /**
     * 处理费率模式绑定（项目级锁定规则）
     * 规则：同一定额专业下，项目级只允许选择一个费率模式
     * - 如果项目已锁定该定额专业的费率模式，则强制使用已锁定的值
     * - 如果项目已绑定但未锁定，则校验是否一致
     * - 如果项目未绑定，则创建绑定关系并锁定
     */
    private void handleRateModeBinding(Long projectId, WbUnitInfoSaveReqVO saveReqVO) {
        Long quotaCatalogItemId = saveReqVO.getQuotaCatalogItemId();
        Long rateModeId = saveReqVO.getRateModeId();

        // 如果没有选择定额专业或费率模式，跳过
        if (quotaCatalogItemId == null || rateModeId == null) {
            return;
        }

        // 获取项目节点
        WbProjectTreeDO project = wbProjectTreeMapper.selectById(projectId);
        if (project == null) {
            log.warn("[handleRateModeBinding] 项目不存在, projectId={}", projectId);
            return;
        }

        // 检查是否已锁定
        boolean isLocked = project.isRateModeLocked(quotaCatalogItemId);
        Long boundRateModeId = project.getRateModeIdByQuotaSpecialty(quotaCatalogItemId);

        if (isLocked && boundRateModeId != null) {
            // 已锁定：强制使用已锁定的值
            if (!boundRateModeId.equals(rateModeId)) {
                log.info("[handleRateModeBinding] 定额专业已锁定，强制使用已锁定的费率模式, projectId={}, quotaCatalogItemId={}, lockedRateModeId={}, requestedRateModeId={}",
                        projectId, quotaCatalogItemId, boundRateModeId, rateModeId);
                // 强制覆盖请求中的值
                saveReqVO.setRateModeId(boundRateModeId);
            }
        } else if (boundRateModeId != null) {
            // 已绑定但未锁定：校验是否一致
            if (!boundRateModeId.equals(rateModeId)) {
                QuotaCatalogItemDO boundRateMode = quotaCatalogItemMapper.selectById(boundRateModeId);
                String boundRateModeName = boundRateMode != null ? boundRateMode.getName() : String.valueOf(boundRateModeId);
                throw exception0(400, "该定额专业已绑定费率模式【" + boundRateModeName + "】，请在项目级【费率及取费】页面切换");
            }
        } else {
            // 未绑定：创建绑定关系并锁定（首个单位工程）
            QuotaCatalogItemDO quotaSpecialty = quotaCatalogItemMapper.selectById(quotaCatalogItemId);
            String quotaSpecialtyName = quotaSpecialty != null ? quotaSpecialty.getName() : "";

            QuotaCatalogItemDO rateMode = quotaCatalogItemMapper.selectById(rateModeId);
            String rateModeName = rateMode != null ? rateMode.getName() : "";

            // 绑定并锁定费率模式
            project.bindAndLockRateMode(quotaCatalogItemId, rateModeId, rateModeName, quotaSpecialtyName, saveReqVO.getId());
            wbProjectTreeMapper.updateById(project);

            log.info("[handleRateModeBinding] 项目绑定并锁定费率模式, projectId={}, quotaCatalogItemId={}, rateModeId={}",
                    projectId, quotaCatalogItemId, rateModeId);
        }
    }

    /**
     * 填充关联名称
     */
    private void fillRelatedNames(WbUnitInfoRespVO respVO, WbUnitInfoDO unitInfo) {
        // 清单数据库名称
        if (unitInfo.getBoqCatalogItemId() != null) {
            BoqCatalogItemDO boqCatalogItem = boqCatalogItemMapper.selectById(unitInfo.getBoqCatalogItemId());
            if (boqCatalogItem != null) {
                respVO.setBoqCatalogItemName(boqCatalogItem.getName());
            }
        }

        // 定额数据库名称
        if (unitInfo.getQuotaCatalogItemId() != null) {
            QuotaCatalogItemDO quotaCatalogItem = quotaCatalogItemMapper.selectById(unitInfo.getQuotaCatalogItemId());
            if (quotaCatalogItem != null) {
                respVO.setQuotaCatalogItemName(quotaCatalogItem.getName());
            }
        }

        // 执行费率文件名称
        if (unitInfo.getRateModeId() != null) {
            QuotaCatalogItemDO rateMode = quotaCatalogItemMapper.selectById(unitInfo.getRateModeId());
            if (rateMode != null) {
                respVO.setRateModeName(rateMode.getName());
            }
        }
    }

    // ==================== 单位工程级费率及取费相关方法 ====================

    @Resource
    private com.yhy.module.core.dal.mysql.workbench.WbUnitFeeSettingMapper wbUnitFeeSettingMapper;

    @Resource
    private com.yhy.module.core.dal.mysql.workbench.WbUnifiedFeeConfigMapper wbUnifiedFeeConfigMapper;

    @Resource
    private com.yhy.module.core.service.quota.QuotaItemService quotaItemService;

    @Override
    public List<Map<String, Object>> getUnitRateModeBindings(Long compileTreeId) {
        // 1. 校验单位工程存在
        WbCompileTreeDO node = validateUnitNode(compileTreeId);

        // 2. 获取单位工程信息
        WbUnitInfoDO unitInfo = wbUnitInfoMapper.selectByCompileTreeId(compileTreeId);
        if (unitInfo == null) {
            return new ArrayList<>();
        }

        // 3. 获取费率模式绑定
        Map<String, Map<String, Object>> bindings = unitInfo.getRateModeBindings();

        // 4. 获取用户上次选择的定额专业ID
        Long lastSelectedQuotaSpecialty = unitInfo.getLastSelectedQuotaSpecialty();
        String lastSelectedStr = lastSelectedQuotaSpecialty != null ? String.valueOf(lastSelectedQuotaSpecialty) : null;

        // 5. 转换为列表格式，确保所有ID为字符串类型避免前端大整数精度丢失
        List<Map<String, Object>> result = new ArrayList<>();
        for (Map.Entry<String, Map<String, Object>> entry : bindings.entrySet()) {
            Map<String, Object> item = new HashMap<>(entry.getValue());
            item.put("quotaCatalogItemId", entry.getKey()); // 保持字符串类型
            // 确保 rateModeId 为字符串类型
            if (item.get("rateModeId") != null) {
                item.put("rateModeId", String.valueOf(item.get("rateModeId")));
            }
            // 标记是否为上次选择的定额专业
            item.put("isLastSelected", entry.getKey().equals(lastSelectedStr));
            result.add(item);
        }

        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void switchUnitRateMode(Long compileTreeId, Long quotaCatalogItemId, Long rateModeId) {
        // 1. 校验单位工程存在
        WbCompileTreeDO node = validateUnitNode(compileTreeId);

        // 2. 获取单位工程信息
        WbUnitInfoDO unitInfo = wbUnitInfoMapper.selectByCompileTreeId(compileTreeId);
        if (unitInfo == null) {
            throw exception(WB_UNIT_INFO_NOT_EXISTS);
        }

        // 3. 校验费率模式存在且属于该定额专业
        QuotaCatalogItemDO rateMode = quotaCatalogItemMapper.selectById(rateModeId);
        if (rateMode == null) {
            throw exception0(400, "费率模式不存在");
        }
        if (!"rate_mode".equals(rateMode.getNodeType())) {
            throw exception0(400, "所选节点不是费率模式节点");
        }
        if (!quotaCatalogItemId.equals(rateMode.getParentId())) {
            throw exception0(400, "费率模式不属于该定额专业");
        }

        // 4. 获取定额专业名称
        QuotaCatalogItemDO quotaSpecialty = quotaCatalogItemMapper.selectById(quotaCatalogItemId);
        String quotaSpecialtyName = quotaSpecialty != null ? quotaSpecialty.getName() : "";

        // 5. 更新单位工程的费率模式绑定
        unitInfo.bindRateMode(quotaCatalogItemId, rateModeId, rateMode.getName(), quotaSpecialtyName);

        // 6. 如果是主定额专业，同步更新 rate_mode_id 字段
        if (quotaCatalogItemId.equals(unitInfo.getQuotaCatalogItemId())) {
            unitInfo.setRateModeId(rateModeId);
            log.info("[switchUnitRateMode] 同步更新单位工程主费率模式, compileTreeId={}, rateModeId={}",
                    compileTreeId, rateModeId);
        }

        wbUnitInfoMapper.updateById(unitInfo);

        // 7. 获取该定额专业下所有旧的费率模式ID（用于清除统一取费）
        List<Long> oldRateModeIds = quotaCatalogItemMapper.selectChildIds(quotaCatalogItemId);
        // 从旧费率模式列表中移除新的费率模式
        oldRateModeIds.remove(rateModeId);

        // 8. 清除该单位工程下相关定额节点的单价构成覆写数据
        // 批量获取所有定额节点的 sourceQuotaItemId，然后批量查询对应的定额专业ID
        int clearedFeeSettingCount = 0;
        List<WbBoqDivisionDO> quotaNodes = wbBoqDivisionMapper.selectQuotaNodesByCompileTreeId(compileTreeId);
        
        // 收集所有有效的 sourceQuotaItemId
        Set<Long> sourceQuotaItemIds = new HashSet<>();
        for (WbBoqDivisionDO quotaNode : quotaNodes) {
            if (quotaNode.getSourceQuotaItemId() != null) {
                sourceQuotaItemIds.add(quotaNode.getSourceQuotaItemId());
            }
        }
        
        // 批量查询定额基价 → 子目树 → 定额专业，建立 sourceQuotaItemId → specialtyId 映射
        Map<Long, Long> quotaItemToSpecialtyMap = new HashMap<>();
        if (!sourceQuotaItemIds.isEmpty()) {
            List<QuotaItemDO> quotaItems = quotaItemMapper.selectList(QuotaItemDO::getId, sourceQuotaItemIds);
            Set<Long> catalogTreeIds = new HashSet<>();
            Map<Long, Long> quotaItemToCatalogTree = new HashMap<>();
            for (QuotaItemDO item : quotaItems) {
                if (item.getCatalogItemId() != null) {
                    catalogTreeIds.add(item.getCatalogItemId());
                    quotaItemToCatalogTree.put(item.getId(), item.getCatalogItemId());
                }
            }
            if (!catalogTreeIds.isEmpty()) {
                List<QuotaCatalogTreeDO> catalogTrees = quotaCatalogTreeMapper.selectList(QuotaCatalogTreeDO::getId, catalogTreeIds);
                Map<Long, Long> catalogTreeToSpecialty = new HashMap<>();
                for (QuotaCatalogTreeDO tree : catalogTrees) {
                    if (tree.getCatalogItemId() != null) {
                        catalogTreeToSpecialty.put(tree.getId(), tree.getCatalogItemId());
                    }
                }
                // 组装最终映射：sourceQuotaItemId → specialtyId
                for (Map.Entry<Long, Long> entry : quotaItemToCatalogTree.entrySet()) {
                    Long specialtyId = catalogTreeToSpecialty.get(entry.getValue());
                    if (specialtyId != null) {
                        quotaItemToSpecialtyMap.put(entry.getKey(), specialtyId);
                    }
                }
            }
        }
        
        // 根据映射清除属于当前定额专业的定额节点的单价构成
        for (WbBoqDivisionDO quotaNode : quotaNodes) {
            if (quotaNode.getSourceQuotaItemId() != null) {
                Long quotaSpecialtyId = quotaItemToSpecialtyMap.get(quotaNode.getSourceQuotaItemId());
                if (quotaCatalogItemId.equals(quotaSpecialtyId)) {
                    int deleted = wbUnitFeeSettingMapper.deleteByDivisionId(quotaNode.getId());
                    clearedFeeSettingCount += deleted;
                }
            }
        }

        // 9. 更新快照（切换费率模式）
        wbSnapshotService.switchRateModeSnapshot(compileTreeId, rateModeId);
        log.info("[switchUnitRateMode] 更新单位工程快照, compileTreeId={}, rateModeId={}",
                compileTreeId, rateModeId);

        // 10. 清除该定额专业下旧费率模式的统一取费配置和节点
        int totalDeletedUnifiedFeeConfig = 0;
        int totalDeletedUnifiedFeeNodes = 0;
        for (Long oldRateModeId : oldRateModeIds) {
            // 删除统一取费配置记录
            int deletedConfig = wbUnifiedFeeConfigMapper.deleteByRateModeId(compileTreeId, oldRateModeId);
            totalDeletedUnifiedFeeConfig += deletedConfig;
            // 删除统一取费节点
            int deletedNodes = wbBoqDivisionMapper.deleteUnifiedFeeNodesByRateModeId(compileTreeId, oldRateModeId);
            totalDeletedUnifiedFeeNodes += deletedNodes;
        }

        if (totalDeletedUnifiedFeeConfig > 0 || totalDeletedUnifiedFeeNodes > 0) {
            log.info("[switchUnitRateMode] 清除统一取费, compileTreeId={}, quotaCatalogItemId={}, newRateModeId={}, deletedConfig={}, deletedNodes={}",
                    compileTreeId, quotaCatalogItemId, rateModeId, totalDeletedUnifiedFeeConfig, totalDeletedUnifiedFeeNodes);
        }

        log.info("[switchUnitRateMode] 单位工程切换费率模式, compileTreeId={}, quotaCatalogItemId={}, rateModeId={}, clearedFeeSettings={}",
                compileTreeId, quotaCatalogItemId, rateModeId, clearedFeeSettingCount);
    }

    @Override
    public Map<String, Object> getUnitRateConfig(Long compileTreeId, Long quotaCatalogItemId) {
        // 1. 校验单位工程存在
        validateUnitNode(compileTreeId);

        // 2. 获取单位工程信息
        WbUnitInfoDO unitInfo = wbUnitInfoMapper.selectByCompileTreeId(compileTreeId);
        if (unitInfo == null) {
            return new HashMap<>();
        }

        // 3. 获取绑定信息
        Map<String, Object> binding = unitInfo.getRateModeBinding(quotaCatalogItemId);
        if (binding == null) {
            return new HashMap<>();
        }

        // 4. 返回完整的费率配置
        Map<String, Object> result = new HashMap<>(binding);
        result.put("quotaCatalogItemId", String.valueOf(quotaCatalogItemId));

        // 确保 rateModeId 为字符串类型，避免前端大整数精度丢失
        if (result.get("rateModeId") != null) {
            result.put("rateModeId", String.valueOf(result.get("rateModeId")));
        }

        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveUnitRateConfig(Long compileTreeId, Long quotaCatalogItemId, Long rateModeId,
                                   Map<String, Object> rateSettings, Map<String, Object> feeSettings) {
        // 1. 校验单位工程存在
        validateUnitNode(compileTreeId);

        // 2. 获取单位工程信息
        WbUnitInfoDO unitInfo = wbUnitInfoMapper.selectByCompileTreeId(compileTreeId);
        if (unitInfo == null) {
            throw exception(WB_UNIT_INFO_NOT_EXISTS);
        }

        // 3. 如果传入了rateModeId，更新费率模式绑定
        if (rateModeId != null) {
            Long currentRateModeId = unitInfo.getRateModeIdByQuotaSpecialty(quotaCatalogItemId);
            if (currentRateModeId == null || !currentRateModeId.equals(rateModeId)) {
                // 费率模式发生变化，需要切换
                switchUnitRateMode(compileTreeId, quotaCatalogItemId, rateModeId);
                // 重新获取unitInfo对象
                unitInfo = wbUnitInfoMapper.selectByCompileTreeId(compileTreeId);
            } else {
                // 费率模式未变化，但仍需更新bindTime
                QuotaCatalogItemDO rateMode = quotaCatalogItemMapper.selectById(rateModeId);
                QuotaCatalogItemDO quotaSpecialty = quotaCatalogItemMapper.selectById(quotaCatalogItemId);
                String rateModeName = rateMode != null ? rateMode.getName() : "";
                String quotaSpecialtyName = quotaSpecialty != null ? quotaSpecialty.getName() : "";
                unitInfo.bindRateMode(quotaCatalogItemId, rateModeId, rateModeName, quotaSpecialtyName);
            }
        }

        // 4. 保存费率覆写值
        if (rateSettings != null) {
            unitInfo.saveRateSettings(quotaCatalogItemId, rateSettings);
        }

        // 5. 保存取费覆写值
        if (feeSettings != null) {
            unitInfo.saveFeeSettings(quotaCatalogItemId, feeSettings);
        }

        // 6. 记录用户上次选择的定额专业（用于页面刷新时回显）
        unitInfo.setLastSelectedQuotaSpecialty(quotaCatalogItemId);

        wbUnitInfoMapper.updateById(unitInfo);

        log.info("[saveUnitRateConfig] 保存单位工程费率配置, compileTreeId={}, quotaCatalogItemId={}, rateModeId={}",
                compileTreeId, quotaCatalogItemId, rateModeId);
    }

    @Override
    public List<WbUsedQuotaSpecialtyRespVO> getUsedQuotaSpecialtiesByUnit(Long compileTreeId) {
        // 1. 校验单位工程存在
        validateUnitNode(compileTreeId);

        // 2. 获取单位工程信息（用于获取已绑定的定额专业）
        WbUnitInfoDO unitInfo = wbUnitInfoMapper.selectByCompileTreeId(compileTreeId);
        
        // 3. 从绑定关系中获取已绑定的定额专业ID集合
        Set<Long> specialtyIds = new HashSet<>();
        if (unitInfo != null && unitInfo.getRateModeBindings() != null) {
            for (String key : unitInfo.getRateModeBindings().keySet()) {
                try {
                    specialtyIds.add(Long.parseLong(key));
                } catch (NumberFormatException e) {
                    log.warn("[getUsedQuotaSpecialtiesByUnit] 无效的定额专业ID格式: {}", key);
                }
            }
        }

        // 4. 查询该单位工程下的分部分项定额节点（有sourceQuotaItemId的），补充定额专业
        Set<Long> quotaItemIds = new HashSet<>();
        List<WbBoqDivisionDO> quotaNodes = wbBoqDivisionMapper.selectQuotaNodesByCompileTreeId(compileTreeId);
        for (WbBoqDivisionDO node : quotaNodes) {
            if (node.getSourceQuotaItemId() != null) {
                quotaItemIds.add(node.getSourceQuotaItemId());
            }
        }

        // 5. 批量查询定额基价，获取 catalogItemId（子目树节点ID）
        if (!quotaItemIds.isEmpty()) {
            List<QuotaItemDO> quotaItems = quotaItemMapper.selectList(QuotaItemDO::getId, quotaItemIds);
            Set<Long> catalogTreeIds = new HashSet<>();
            Map<Long, Long> quotaItemToCatalogTree = new HashMap<>(); // quotaItemId -> catalogTreeId
            for (QuotaItemDO item : quotaItems) {
                if (item.getCatalogItemId() != null) {
                    catalogTreeIds.add(item.getCatalogItemId());
                    quotaItemToCatalogTree.put(item.getId(), item.getCatalogItemId());
                }
            }

            // 6. 批量查询子目树节点，获取 catalogItemId（定额专业节点ID）
            if (!catalogTreeIds.isEmpty()) {
                List<QuotaCatalogTreeDO> catalogTrees = quotaCatalogTreeMapper.selectList(QuotaCatalogTreeDO::getId, catalogTreeIds);
                for (QuotaCatalogTreeDO tree : catalogTrees) {
                    if (tree.getCatalogItemId() != null) {
                        specialtyIds.add(tree.getCatalogItemId());
                    }
                }
            }
        }

        if (specialtyIds.isEmpty()) {
            return new ArrayList<>();
        }

        // 7. 批量查询定额专业节点
        List<QuotaCatalogItemDO> specialties = quotaCatalogItemMapper.selectList(QuotaCatalogItemDO::getId, specialtyIds);
        Map<Long, String> specialtyMap = new HashMap<>();
        for (QuotaCatalogItemDO specialty : specialties) {
            specialtyMap.put(specialty.getId(), specialty.getName());
        }

        // 8. 获取每个定额专业下的费率模式节点
        List<WbUsedQuotaSpecialtyRespVO> result = new ArrayList<>();
        for (Map.Entry<Long, String> entry : specialtyMap.entrySet()) {
            Long specialtyId = entry.getKey();
            String specialtyName = entry.getValue();

            // 查询该定额专业下的所有费率模式节点
            List<QuotaCatalogItemDO> rateModes = quotaCatalogItemMapper.selectByParentIdAndNodeType(specialtyId, "rate_mode");
            for (QuotaCatalogItemDO rateMode : rateModes) {
                result.add(WbUsedQuotaSpecialtyRespVO.builder()
                        .quotaCatalogItemId(specialtyId)
                        .quotaCatalogItemName(specialtyName)
                        .rateModeId(rateMode.getId())
                        .rateModeName(rateMode.getName())
                        .unitCount(0)
                        .compileTreeId(compileTreeId)
                        .build());
            }
        }

        return result;
    }

    @Override
    public List<WbUsedQuotaSpecialtyRespVO> getUsedQuotaSpecialtiesByProject(Long projectId) {
        // 查询项目下所有单位工程节点
        List<WbCompileTreeDO> allNodes = wbCompileTreeMapper.selectListByProjectId(projectId);
        List<WbCompileTreeDO> unitNodes = allNodes.stream()
                .filter(n -> "unit".equals(n.getNodeType()))
                .collect(java.util.stream.Collectors.toList());

        if (unitNodes.isEmpty()) {
            return new ArrayList<>();
        }

        // 遍历每个单位工程，收集定额专业（按 quotaCatalogItemId + rateModeId 去重）
        Map<String, WbUsedQuotaSpecialtyRespVO> deduplicatedMap = new java.util.LinkedHashMap<>();
        for (WbCompileTreeDO unitNode : unitNodes) {
            try {
                List<WbUsedQuotaSpecialtyRespVO> unitSpecialties = getUsedQuotaSpecialtiesByUnit(unitNode.getId());
                for (WbUsedQuotaSpecialtyRespVO vo : unitSpecialties) {
                    String key = vo.getQuotaCatalogItemId() + "_" + vo.getRateModeId();
                    deduplicatedMap.putIfAbsent(key, vo);
                }
            } catch (Exception e) {
                log.warn("[getUsedQuotaSpecialtiesByProject] 查询单位工程定额专业失败，compileTreeId={}", unitNode.getId(), e);
            }
        }

        return new ArrayList<>(deduplicatedMap.values());
    }
}
