import json
import os
import sys
from typing import Dict, List, Any, Optional, Tuple, Set
from equipment_calculation.expressioncalculator import ExpressionCalculator
from equipment_calculation.bcl_utils import (
    ZjQuantityBCLContext,
    ZjProjectBCLContext,
    ZjBillBCLContext,
    init_bcl_calculator,
    BCLVariant,
    calculator,
    create_project_contexts,
    BCLDataSourceItem,
    BCLDataSourceContext,
    create_list_from_node,
    create_node_from_type,
)
from equipment_calculation.item_acquisition import (
    get_cost_table_children,
    get_quantity_nodes,
    get_bill_cost_table,
    find_cost_table,
    get_bill_node_by_id,
    load_project_data,
)

# 缓存已计算过的费用
calculated_fees = {}

# # 添加一个全局变量和一个缓存字典来存储清单数量
# _BILL_QUANTITY = 1.0
# bill_quantity_cache = {}


# 主网处理地形系数
def process_DXdata(json_data):
    """
    处理 projectData 中的线路特征段数据，计算每条 bpBillZhXsTable 记录的加权值，
    并返回一个字典列表，每个字典包含 '特征段' 和对应的 ItemName: 计算结果。

    :param json_data: 解析后的 JSON 数据（字典格式）
    :return: 字典列表，例如 [{"特征段": "1", "工地运输...": "21.8"}, ...]
    """
    result_list = []

    # 遍历每个线路特征段
    for segment in json_data.get("projectData", {}).get("线路特征段", []):
        seg_name = segment.get("Name", "")
        # 提取特征段中的数字，例如 "特征段1" -> "1"
        seg_number = "".join(filter(str.isdigit, seg_name))
        if not seg_number:
            seg_number = "0"  # 默认值

        # 获取 bpBillZhXsTable 列表
        table_list = segment.get("bpBillZhXsTable", [])
        for item in table_list:
            try:
                # 提取字段并转换为浮点数
                KnapScale = float(item.get("KnapScale", 0))
                Knap = float(item.get("Knap", 0))
                HillScale = float(item.get("HillScale", 0))
                Hill = float(item.get("Hill", 0))
                EdelweissScale = float(item.get("EdelweissScale", 0))
                edelweiss = float(item.get("edelweiss", 0))
                MountainScale = float(item.get("MountainScale", 0))
                Mountain = float(item.get("Mountain", 0))
                SloughScale = float(item.get("SloughScale", 0))
                Slough = float(item.get("Slough", 0))
                RiverScale = float(item.get("RiverScale", 0))
                River = float(item.get("River", 0))
                DesertScale = float(item.get("DesertScale", 0))
                Desert = float(item.get("Desert", 0))

                # 执行计算
                total = (
                    (KnapScale * Knap)
                    + (HillScale * Hill)
                    + (EdelweissScale * edelweiss)
                    + (MountainScale * Mountain)
                    + (SloughScale * Slough)
                    + (RiverScale * River)
                    + (DesertScale * Desert)
                ) * 0.01

                # 保留一位小数，格式化为字符串
                calculated_value = f"{total:.1f}"

                # 获取 ItemName
                item_name = item.get("ItemName", "未知项目")

                # 构建结果字典
                item_dict = {"特征段": seg_number, item_name: calculated_value}

                # 添加到结果列表
                result_list.append(item_dict)

            except (ValueError, TypeError) as e:
                print(f"数据转换错误，跳过该项: {e}")
                continue

    return result_list


# 在create_list_from_node函数后添加一个包装函数
def create_list_from_node_with_bill_quantity(node, quantity=None):
    """创建清单对象并设置数量，不使用全局变量"""
    from equipment_calculation.bcl_utils import create_list_from_node

    bill_obj = create_list_from_node(node)

    # 设置清单数量
    if quantity is not None:
        bill_quantity = quantity
    elif "数量" in node:
        try:
            bill_quantity = float(node["数量"])
        except (ValueError, TypeError):
            bill_quantity = 1.0
    else:
        bill_quantity = 1.0

    # 设置到对象属性
    if hasattr(bill_obj, "quantity"):
        bill_obj.quantity = bill_quantity
    else:
        # 如果对象没有quantity属性，尝试设置到其他可能的属性
        try:
            setattr(bill_obj, "quantity", bill_quantity)
        except:
            pass
    print(f"设置清单对象数量: {bill_quantity}")

    return bill_obj


# 递归查找取费表中包含"取费基数"的节点
def find_fee_base_nodes(node: Dict[str, Any], result: List[Dict[str, Any]] = None) -> List[Dict[str, Any]]:
    """
    递归查找取费表中包含"取费基数"的节点或有子节点的费用项

    Args:
        node: 取费表节点
        result: 结果列表

    Returns:
        List[Dict[str, Any]]: 包含"取费基数"的节点或有子节点的费用项列表
    """
    if result is None:
        result = []

    if isinstance(node, dict):
        # 检查当前节点是否包含"取费基数"字段或有子节点
        has_children = "children" in node and isinstance(node["children"], list) and node["children"]
        has_code = "代码" in node

        if "取费基数" in node or (has_children and has_code):
            result.append(node)

        # 递归检查子节点
        for key, value in node.items():
            if key == "children" and isinstance(value, list):
                for child in value:
                    find_fee_base_nodes(child, result)
            elif isinstance(value, (dict, list)):
                find_fee_base_nodes(value, result)

    elif isinstance(node, list):
        for item in node:
            find_fee_base_nodes(item, result)

    return result


# 查找取费表中的费用项
def find_fee_item_by_code(cost_table: Dict[str, Any], code: str) -> Optional[Dict[str, Any]]:
    """
    在取费表中查找指定代码的费用项

    Args:
        cost_table: 取费表
        code: 费用代码

    Returns:
        Optional[Dict[str, Any]]: 找到的费用项，如果未找到则返回None
    """

    def search_in_node(node):
        if isinstance(node, dict):
            if node.get("代码") == code:
                return node

            for key, value in node.items():
                if isinstance(value, (dict, list)):
                    result = search_in_node(value)
                    if result:
                        return result

        elif isinstance(node, list):
            for item in node:
                result = search_in_node(item)
                if result:
                    return result

        return None

    return search_in_node(cost_table)


# 计算表外变量
def calculate_external_variable(var_name: str, context: ZjQuantityBCLContext, calculation_strategy=None) -> float:
    """
    使用BCL计算器计算表外变量

    Args:
        var_name: 变量名
        context: BCL上下文
        calculation_strategy: 计算策略对象

    Returns:
        float: 计算结果
    """
    try:
        # 如果有计算策略，使用计算策略的方法
        if calculation_strategy:
            print(f"使用计算策略 {calculation_strategy.__class__.__name__} 计算表外变量: {var_name}")
            # 直接调用计算策略的方法，而不是递归调用自己
            return calculation_strategy.calculate_external_variable(var_name, context)

        # 否则使用默认实现
        print(f"使用默认方法计算表外变量: {var_name}")
        result = calculator.calculate(var_name, context)

        # 检查结果类型，如果是 BCLVariant，尝试转换为浮点数
        if hasattr(result, "__class__") and result.__class__.__name__ == "BCLVariant":
            # 根据错误信息，BCLVariant 格式似乎是 Variant(BCLVariantType.FLOAT, 0.0)
            # 尝试访问 value 属性或使用适当的方法获取值
            try:
                # 尝试不同的方法获取值
                if hasattr(result, "value"):
                    return float(result.value)
                elif hasattr(result, "get_value"):
                    return float(result.get_value())
                elif hasattr(result, "__getitem__"):
                    # 如果 BCLVariant 支持索引访问，尝试获取第二个元素
                    return float(result[1])
                else:
                    # 尝试直接转换为字符串，然后解析值
                    str_result = str(result)
                    # 假设格式是 Variant(BCLVariantType.FLOAT, 0.0)
                    # 尝试提取括号中的第二个值
                    import re

                    match = re.search(r"Variant\([^,]+,\s*([^)]+)\)", str_result)
                    if match:
                        return float(match.group(1))

                    # 如果上述方法都失败，尝试直接转换
                    return float(result)
            except (ValueError, TypeError, AttributeError, IndexError) as e:
                print(f"无法将 BCLVariant 类型的结果转换为浮点数: {result}, 错误: {e}")
                # 打印更多调试信息
                print(f"BCLVariant 类型: {type(result)}")
                print(f"BCLVariant 属性: {dir(result)}")
                return 0.0

        # 如果不是 BCLVariant，尝试直接转换为浮点数
        return float(result) if result is not None else 0.0
    except Exception as e:
        print(f"计算表外变量 '{var_name}' 时出错: {e}")
        return 0.0


# 计算表内费用
def calculate_internal_fee(
    fee_code: str,
    cost_table: Dict[str, Any],
    project_node: Dict[str, Any],
    context: ZjQuantityBCLContext,
    in_calculation: set = None,
    calculation_strategy=None,
) -> float:
    """
    计算表内费用

    Args:
        fee_code: 费用代码
        cost_table: 取费表
        project_node: 项目划分节点
        context: BCL上下文
        in_calculation: 正在计算中的费用代码集合，用于检测循环依赖
        calculation_strategy: 计算策略对象

    Returns:
        float: 计算结果
    """
    # 初始化正在计算的集合
    if in_calculation is None:
        in_calculation = set()

    # 检查是否存在循环依赖
    if fee_code in in_calculation:
        print(f"检测到循环依赖: {fee_code}")
        return 0.0

    # 将当前费用代码添加到正在计算的集合中
    in_calculation.add(fee_code)

    # 检查缓存中是否已有计算结果
    cache_key = f"{fee_code}_{project_node.get('id', '')}"
    if cache_key in calculated_fees:
        # 从正在计算的集合中移除当前费用代码
        in_calculation.remove(fee_code)
        return calculated_fees[cache_key]

    # 查找费用项
    fee_item = find_fee_item_by_code(cost_table, fee_code)
    if not fee_item:
        print(f"未找到代码为 '{fee_code}' 的费用项")
        # 从正在计算的集合中移除当前费用代码
        in_calculation.remove(fee_code)
        return 0.0

    # 获取取费基数和费率
    fee_base = fee_item.get("取费基数")
    fee_rate = fee_item.get("费率(%)") or fee_item.get("费率")

    # 尝试转换费率为浮点数
    try:
        if fee_rate is None or (isinstance(fee_rate, str) and fee_rate.strip() == ""):
            rate = 1.0
        else:
            rate = float(fee_rate) / 100.0
    except (ValueError, TypeError):
        print(f"无法将费率 '{fee_rate}' 转换为浮点数，使用默认值1.0")
        rate = 1.0

    # 如果有取费基数，直接计算
    if fee_base:
        base_value = calculate_fee_base(
            fee_base, cost_table, project_node, context, in_calculation, calculation_strategy
        )
        # 确保 base_value 不为 None
        base_value = base_value if base_value is not None else 0.0
        result = base_value * rate
    else:
        # 如果没有取费基数，检查是否有子节点
        children = fee_item.get("children", [])
        if children:
            # 计算所有子节点的费用之和
            result = 0.0
            for child in children:
                child_code = child.get("代码", "")
                if child_code:
                    child_result = calculate_internal_fee(
                        child_code, cost_table, project_node, context, in_calculation, calculation_strategy
                    )
                    # 确保 child_result 不为 None
                    child_result = child_result if child_result is not None else 0.0
                    result += child_result
        else:
            print(f"费用项 '{fee_code}' 没有取费基数且没有子节点")
            result = 0.0

    # 缓存计算结果
    calculated_fees[cache_key] = result

    # 从正在计算的集合中移除当前费用代码
    in_calculation.remove(fee_code)

    return result


# 计算取费基数
def calculate_fee_base(
    fee_base: str,
    cost_table: Dict[str, Any],
    project_node: Dict[str, Any],
    context: ZjQuantityBCLContext,
    in_calculation: set = None,
    calculation_strategy=None,
) -> float:
    """
    计算取费基数

    Args:
        fee_base: 取费基数表达式
        cost_table: 取费表
        project_node: 项目划分节点
        context: BCL上下文
        in_calculation: 正在计算中的费用代码集合，用于检测循环依赖
        calculation_strategy: 计算策略对象

    Returns:
        float: 计算结果
    """
    # 初始化正在计算的集合
    if in_calculation is None:
        in_calculation = set()

    # 检查是否是直接引用另一个费用项的代码
    if fee_base in calculated_fees:
        return calculated_fees[fee_base]

    # 创建表达式计算器
    expr_calculator = ExpressionCalculator()
    parse_success = expr_calculator.parse_expression(fee_base)
    if not parse_success:
        print(f"测试表达式解析失败: {expr_calculator.get_last_error()}")
        return None

    # 获取表达式中的变量
    variables = expr_calculator.variables
    variable_values = {}

    # 处理每个变量
    for var in variables:
        # 检查是否为表内费用代码
        if var in calculated_fees:
            # 使用缓存的计算结果
            variable_values[var] = calculated_fees[var]
        elif find_fee_item_by_code(cost_table, var):
            # 计算表内费用
            value = calculate_internal_fee(var, cost_table, project_node, context, in_calculation, calculation_strategy)
            # 确保值不为 None
            variable_values[var] = value if value is not None else 0.0
        else:
            # 处理表外变量
            value = calculate_external_variable(var, context, calculation_strategy)
            # 确保值不为 None
            variable_values[var] = value if value is not None else 0.0

    # 计算最终结果
    try:
        result, value = expr_calculator.evaluate(variable_values)
        # 确保结果不为 None
        return value if result else 0.0
    except Exception as e:
        print(f"计算表达式 '{fee_base}' 时出错: {e}")
        return 0.0


# 计算项目节点的所有费用
def calculate_all_fees(
    project_node: Dict[str, Any],
    cost_table: Dict[str, Any],
    json_file_path: str = None,
    engineering_type: str = None,
    calculation_strategy=None,
) -> Dict[str, float]:
    """
    计算项目节点的所有费用，确保清单数量正确传递
    """
    results = {}

    # 1. 工程信息上下文
    project_context = create_project_contexts(json_file_path=json_file_path)

    with open(json_file_path, "r", encoding="utf-8") as file:
        data = json.load(file)

    processed_data = process_DXdata(data)

    DXITEM = [BCLDataSourceItem(item) for item in processed_data]

    # 2. 构建数据源链式上下文
    if engineering_type == "清单工程":
        # 获取清单节点
        bill_id = project_node.get("bill_id")
        bill_name = project_node.get("bill_name")
        bill_node_data = project_node.get("bill_node")
        if not bill_node_data:
            bill_node_data = {
                "id": bill_id,
                "GUID": bill_id,
                "清单名称": bill_name,
                "取费表名称": project_node.get("取费表名称"),
                "清单全码": project_node.get("清单全码", ""),
                "编码": project_node.get("清单编码", ""),
                "单位": project_node.get("单位", ""),
            }

        # 使用包装函数创建清单对象
        bill_obj = create_list_from_node_with_bill_quantity(bill_node_data)

        # 创建工程量对象
        project_obj = create_node_from_type(project_node)

        dxitem_context = BCLDataSourceContext(DXITEM, project_context)
        dxitem_context.variables["@特征段地形系数"] = BCLVariant(DXITEM)

        # 递归数据源链式上下文
        billItem = BCLDataSourceItem(bill_obj)

        # 将清单数量直接设置到billItem对象上
        if hasattr(bill_obj, "quantity"):
            billItem.bill_quantity = bill_obj.quantity
            print(f"设置清单数据源项数量: {billItem.bill_quantity}")

        QuantityItem = BCLDataSourceItem(project_obj, billItem)
        bill_context = BCLDataSourceContext([billItem], dxitem_context)
        context = BCLDataSourceContext([QuantityItem], bill_context)

        # 如果计算策略存在，设置清单数量
        if calculation_strategy and hasattr(calculation_strategy, "set_bill_quantity"):
            if hasattr(bill_obj, "quantity"):
                calculation_strategy.set_bill_quantity(bill_obj.quantity)
                print(f"设置计算策略的清单数量: {bill_obj.quantity}")
    else:
        # 非清单工程 - 工程信息 -> 工程量节点
        project_obj = create_node_from_type(project_node)

        dxitem_context = BCLDataSourceContext(DXITEM, project_context)
        dxitem_context.variables["@特征段地形系数"] = BCLVariant(DXITEM)

        QuantityItem = BCLDataSourceItem(project_obj)
        context = BCLDataSourceContext([QuantityItem], dxitem_context)

    # 查找包含取费基数的节点
    fee_base_nodes = find_fee_base_nodes(cost_table)

    # 计算每个费用项
    for node in fee_base_nodes:
        fee_name = node.get("费用名称", node.get("name", "未知费用"))
        fee_code = node.get("代码", "")
        fee_base = node.get("取费基数", "")
        fee_rate = node.get("费率(%)") or node.get("费率")

        # 尝试转换费率为浮点数
        try:
            if fee_rate is None or (isinstance(fee_rate, str) and fee_rate.strip() == ""):
                rate = 1.0
            else:
                rate = float(fee_rate) / 100.0
        except (ValueError, TypeError):
            print(f"无法将费率 '{fee_rate}' 转换为浮点数，使用默认值1.0")
            rate = 1.0

        if fee_base:
            # 计算取费基数
            base_value = calculate_fee_base(fee_base, cost_table, project_node, context, None, calculation_strategy)
            # 应用费率
            result = base_value * rate
            results[fee_name] = result

            # 缓存计算结果
            if fee_code:
                calculated_fees[fee_code] = result
        elif fee_code:
            # 如果没有取费基数但有代码，尝试计算
            result = calculate_internal_fee(fee_code, cost_table, project_node, context, None, calculation_strategy)
            results[fee_name] = result

    return results


# 计算工程量取费表
##原始代码
# def calculate_quantity_fees(
#     json_file_path: str, project_name: str, adjustment_type: str = None, engineering_type: str = None
# ) -> str:
#     """
#     计算工程量取费表

#     Args:
#         json_file_path: JSON文件路径
#         project_name: 项目名称
#         adjustment_type: 调差类型
#         engineering_type: 工程类型
#     Returns:
#         str: 输出文件路径
#     """
#     # 设置输出目录和文件名
#     output_dir = "计算结果"
#     os.makedirs(output_dir, exist_ok=True)
#     output_file = os.path.join(
#         output_dir, f"{project_name}_{adjustment_type}_{engineering_type}_calculation_results.json"
#     )

#     # 初始化BCL计算器
#     if not init_bcl_calculator():
#         return None

#     # 根据工程类型获取取费表和项目节点
#     if engineering_type == "预算工程":
#         # 预算工程 - 获取项目划分节点的取费表
#         cost_table_children = get_cost_table_children(json_file_path, project_name)
#         project_children = get_quantity_nodes(json_file_path, project_name, adjustment_type, engineering_type)

#         if not cost_table_children or not project_children:
#             print(f"未找到项目 '{project_name}' 的取费表或项目划分节点")
#             return None

#     elif engineering_type == "清单工程":
#         # 清单工程 - 获取清单节点的取费表
#         project_children = get_quantity_nodes(json_file_path, project_name, adjustment_type, engineering_type)
#         if not project_children:
#             print(f"未找到项目 '{project_name}' 的项目划分节点")
#             return None

#         # 为每个工程量节点找到对应的清单节点取费表
#         cost_tables = {}

#         for project_node in project_children:
#             node_name = project_node.get("项目名称", project_node.get("name", "未知节点"))

#             # 获取清单节点信息
#             bill_id = project_node.get("bill_id")
#             fee_table_name = project_node.get("取费表名称")

#             if not bill_id:
#                 print(f"工程量节点 '{node_name}' 缺少清单节点ID")
#                 continue

#             if not fee_table_name:
#                 print(f"工程量节点 '{node_name}' 没有取费表名称")
#                 continue

#             # 使用清单节点的取费表名称直接查找取费表
#             if bill_id not in cost_tables:
#                 print(f"查找清单节点 '{project_node.get('bill_name', '未命名清单')}' 的取费表: {fee_table_name}")

#                 # 查找取费表
#                 with open(json_file_path, "r", encoding="utf-8") as f:
#                     data = json.load(f)

#                 project_data = data.get("projectData", {})
#                 cost_setting = project_data.get("costSetting", {})

#                 # 查找取费表
#                 cost_table = find_cost_table(cost_setting, fee_table_name)

#                 if not cost_table:
#                     print(f"未找到取费表 '{fee_table_name}'")
#                     continue

#                 cost_table_children = cost_table.get("children", None)
#                 if not cost_table_children:
#                     print(f"取费表 '{fee_table_name}' 没有子节点")
#                     continue

#                 cost_tables[bill_id] = cost_table_children
#                 print(f"成功获取清单节点 '{project_node.get('bill_name', '未命名清单')}' 的取费表")
#     else:
#         print(f"不支持的工程类型: {engineering_type}")
#         return None

#     # 初始化缓存
#     calculated_fees.clear()

#     # 计算每个项目节点的费用
#     all_results = {}

#     for project_node in project_children:
#         # 为每个节点清空缓存，确保计算独立
#         calculated_fees.clear()

#         node_name = project_node.get("项目名称", project_node.get("name", "未知节点"))

#         # 根据工程类型获取对应的取费表
#         if engineering_type == "预算工程":
#             # 预算工程 - 使用项目划分节点的取费表
#             node_results = calculate_all_fees(
#                 project_node, {"children": cost_table_children}, json_file_path, engineering_type
#             )
#         else:  # 清单工程
#             # 清单工程 - 使用清单节点的取费表
#             parent_id = project_node.get("parent_id")
#             if parent_id and parent_id in cost_tables:
#                 node_results = calculate_all_fees(
#                     project_node, {"children": cost_tables[parent_id]}, json_file_path, engineering_type
#                 )
#             else:
#                 print(f"无法获取工程量节点 '{node_name}' 的取费表")
#                 continue

#         all_results[node_name] = node_results

#         # 输出计算结果
#         print(f"费用计算结果:")
#         for fee_name, fee_value in node_results.items():
#             print(f"  {fee_name}: {fee_value}")

#     # 保存计算结果到JSON文件
#     with open(output_file, "w", encoding="utf-8") as f:
#         json.dump(all_results, f, ensure_ascii=False, indent=2)

#     print(f"\n计算结果已保存到 {output_file}")

#     return output_file


def calculate_quantity_fees(
    json_file_path: str,
    project_name: str,
    engineering_type: str = None,
    project_guid: str = None,
    calculation_strategy=None,
) -> str:
    """
    计算工程量取费表，不使用全局变量，并按清单节点组织结果
    """
    # 如果没有提供计算策略，使用默认策略
    if calculation_strategy is None:
        from equipment_calculation.calculation_strategy import DefaultCalculationStrategy

        calculation_strategy = DefaultCalculationStrategy()

    # 设置输出目录和文件名
    # 检查calculation_strategy是否有get_output_dir方法
    if hasattr(calculation_strategy, "get_output_dir"):
        output_dir = calculation_strategy.get_output_dir()
    else:
        # 兼容旧代码，使用默认目录
        output_dir = "计算结果"

    os.makedirs(output_dir, exist_ok=True)

    # 获取项目划分节点的GUID
    _, _, _, _, target_node = load_project_data(json_file_path, project_name, project_guid)
    if target_node:
        node_guid = target_node.get("GUID") or target_node.get("guid", "")
        # 如果传入了GUID但与节点的GUID不一致，优先使用传入的GUID
        project_guid = project_guid if project_guid else node_guid

    # 将文件名中的非法字符替换为下划线
    safe_project_name = (
        project_name.replace("/", "_")
        .replace("\\", "_")
        .replace(":", "_")
        .replace("*", "_")
        .replace("?", "_")
        .replace('"', "_")
        .replace("<", "_")
        .replace(">", "_")
        .replace("|", "_")
    )

    # 将GUID中的非法字符替换为下划线
    safe_project_guid = ""
    if project_guid:
        safe_project_guid = (
            project_guid.replace("/", "_")
            .replace("\\", "_")
            .replace(":", "_")
            .replace("*", "_")
            .replace("?", "_")
            .replace('"', "_")
            .replace("<", "_")
            .replace(">", "_")
            .replace("|", "_")
            .replace("{", "")
            .replace("}", "")
        )
        # 添加下划线作为分隔符
        safe_project_guid = f"_{safe_project_guid}"

    output_file = os.path.join(
        output_dir,
        f"{safe_project_name}{safe_project_guid}_{engineering_type}_calculation_results.json",
    )

    # 创建一个字典来存储清单数量
    bill_quantities = {}
    # 创建一个字典来存储清单信息
    bill_info = {}

    # 根据工程类型获取取费表和项目节点
    if engineering_type == "预算工程":
        # 预算工程 - 获取项目划分节点的取费表，传递project_guid参数
        cost_table_children = get_cost_table_children(json_file_path, project_name, project_guid)
        project_children = get_quantity_nodes(json_file_path, project_name, engineering_type, project_guid)

        if not cost_table_children or not project_children:
            print(f"未找到项目 '{project_name}' (GUID: {project_guid}) 的取费表或项目划分节点")
            return None

    elif engineering_type == "清单工程":
        # 清单工程 - 获取清单节点的取费表，传递project_guid参数
        project_children = get_quantity_nodes(json_file_path, project_name, engineering_type, project_guid)
        if not project_children:
            print(f"未找到项目 '{project_name}' (GUID: {project_guid}) 的项目划分节点")
            return None

        # 为每个工程量节点找到对应的清单节点取费表
        cost_tables = {}

        for project_node in project_children:
            node_name = project_node.get("项目名称", project_node.get("name", "未知节点"))

            # 获取清单节点信息
            bill_id = project_node.get("bill_id")
            bill_name = project_node.get("bill_name", "未命名清单")
            fee_table_name = project_node.get("取费表名称")

            if not bill_id:
                print(f"工程量节点 '{node_name}' 缺少清单节点ID")
                continue

            if not fee_table_name:
                print(f"工程量节点 '{node_name}' 没有取费表名称")
                continue

            # 保存清单信息
            if bill_id not in bill_info:
                bill_info[bill_id] = {
                    "name": bill_name,
                    "fee_table_name": fee_table_name,
                    "guid": bill_id.replace("{", "").replace("}", "") if bill_id else "",
                }

            # 使用清单节点的取费表名称直接查找取费表
            if bill_id not in cost_tables:
                print(f"查找清单节点 '{bill_name}' 的取费表: {fee_table_name}")

                # 查找取费表
                with open(json_file_path, "r", encoding="utf-8") as f:
                    data = json.load(f)

                project_data = data.get("projectData", {})
                cost_setting = project_data.get("costSetting", {})

                # 查找取费表
                cost_table = find_cost_table(cost_setting, fee_table_name)

                if not cost_table:
                    print(f"未找到取费表 '{fee_table_name}'")
                    continue

                cost_table_children = cost_table.get("children", None)
                if not cost_table_children:
                    print(f"取费表 '{fee_table_name}' 没有子节点")
                    continue

                cost_tables[bill_id] = cost_table_children
                print(f"成功获取清单节点 '{bill_name}' 的取费表")

        # 在读取JSON文件后，遍历项目中的清单节点，获取并保存清单数量
        with open(json_file_path, "r", encoding="utf-8") as f:
            data = json.load(f)

        project_data = data.get("projectData", {})
        bill_data = project_data.get("bill", {})

        # 递归函数获取所有清单节点
        def get_bill_nodes(node):
            if isinstance(node, dict):
                # 检查是否是清单节点
                is_bill = (
                    node.get("类型") == "8" or node.get("类型") == "清单" or "清单名称" in node or "清单编码" in node
                )

                if is_bill and ("id" in node or "GUID" in node):
                    # 强制使用GUID而不是ID
                    bill_id = node.get("GUID") or node.get("guid") or node.get("id")
                    quantity = 1.0  # 默认值
                    if "数量" in node:
                        try:
                            quantity = float(node["数量"])
                        except (TypeError, ValueError):
                            pass

                    # 保存到字典
                    bill_quantities[bill_id] = quantity

                    # 补充清单信息
                    if bill_id not in bill_info:
                        bill_info[bill_id] = {
                            "name": node.get("清单名称", "未命名清单"),
                            "fee_table_name": node.get("取费表名称", ""),
                            "guid": bill_id.replace("{", "").replace("}", "") if bill_id else "",
                        }

                    print(f"保存清单 {bill_id} 的数量: {quantity}")

                # 递归处理子节点
                if "children" in node and isinstance(node["children"], list):
                    for child in node["children"]:
                        get_bill_nodes(child)

        # 获取和设置清单数量
        get_bill_nodes(bill_data)

    else:
        print(f"不支持的工程类型: {engineering_type}")
        return None

    # 在获取 project_children 后添加预处理代码
    if project_children and calculation_strategy and hasattr(calculation_strategy, "preprocess_quantity_fee_node"):
        print(f"对项目节点进行预处理...")

        # 递归处理所有节点
        def process_node(node):
            calculation_strategy.preprocess_quantity_fee_node(node)
            if "children" in node and isinstance(node["children"], list):
                for child in node["children"]:
                    process_node(child)

        # 处理每个项目节点
        for node in project_children:
            process_node(node)

    # 初始化缓存
    calculated_fees.clear()  # 使用全局缓存，而不是计算策略中的缓存

    # 创建一个新的结果结构，按清单节点组织
    structured_results = {}

    # 辅助函数：处理ID，移除花括号并保留完整ID
    def process_id(id_str):
        if not id_str:
            return ""
        # 移除花括号
        return id_str.replace("{", "").replace("}", "")

    # 计算每个项目节点的费用
    for project_node in project_children:
        # 为每个节点清空缓存，确保计算独立
        calculated_fees.clear()

        node_name = project_node.get("项目名称", project_node.get("name", "未知节点"))
        # 强制使用GUID而不是ID
        node_id = project_node.get("GUID") or project_node.get("guid") or project_node.get("id", "")

        # 处理节点ID
        processed_node_id = process_id(node_id)

        # 创建唯一的节点键
        node_key = node_name
        if processed_node_id:
            node_key = f"{node_name}_{processed_node_id}"

        # 添加清单数量信息到项目节点
        if engineering_type == "清单工程":
            # 强制使用GUID而不是ID
            bill_id = project_node.get("bill_guid") or project_node.get("bill_id")
            if bill_id in bill_quantities:
                project_node["bill_quantity"] = bill_quantities[bill_id]
                print(f"为工程量节点 '{node_name}' 设置清单数量: {bill_quantities[bill_id]}")

                # 如果计算策略支持，设置清单数量
                if calculation_strategy and hasattr(calculation_strategy, "set_bill_quantity"):
                    calculation_strategy.set_bill_quantity(bill_quantities[bill_id])

        # 根据工程类型获取对应的取费表
        if engineering_type == "预算工程":
            # 预算工程 - 使用项目划分节点的取费表
            node_results = calculation_strategy.calculate_all_fees(
                project_node, {"children": cost_table_children}, json_file_path, engineering_type
            )

            # 直接使用节点键作为结果键
            structured_results[node_key] = node_results

        else:  # 清单工程
            # 清单工程 - 使用清单节点的取费表
            # 强制使用GUID而不是ID
            bill_id = project_node.get("bill_guid") or project_node.get("bill_id")
            if bill_id and bill_id in cost_tables:
                node_results = calculation_strategy.calculate_all_fees(
                    project_node, {"children": cost_tables[bill_id]}, json_file_path, engineering_type
                )

                # 获取清单信息
                bill_name = bill_info.get(bill_id, {}).get("name", "未命名清单")

                # 处理清单ID - 强制使用GUID
                processed_bill_id = process_id(bill_id)

                # 创建清单的唯一键 - 强制使用GUID
                bill_key = bill_name
                if processed_bill_id:
                    # 获取清单的guid而不是id
                    bill_guid = bill_info.get(bill_id, {}).get("guid", processed_bill_id)
                    if not bill_guid:
                        bill_guid = processed_bill_id
                    bill_key = f"{bill_name}_{bill_guid}"

                # 如果清单节点不存在于结果中，创建它
                if bill_key not in structured_results:
                    structured_results[bill_key] = {}

                # 将工程量节点的计算结果添加到对应的清单节点下
                structured_results[bill_key][node_key] = node_results
            else:
                print(f"无法获取工程量节点 '{node_name}' 的取费表")
                continue

        # 输出计算结果
        print(f"费用计算结果:")
        for fee_name, fee_value in node_results.items():
            print(f"  {fee_name}: {fee_value}")

    # 保存计算结果到JSON文件
    with open(output_file, "w", encoding="utf-8") as f:
        json.dump(structured_results, f, ensure_ascii=False, indent=2)

    print(f"\n计算结果已保存到 {output_file}")

    # 如果有计算策略，应用后处理规则
    if calculation_strategy:
        calculation_strategy.post_process_quantity_fees(output_file, project_name)

    return output_file
