from abc import ABC, abstractmethod
from memory_profiler import profile
from typing import Dict, List, Any, Optional, Set, Tuple


class CalculationStrategy(ABC):
    """
    计算策略接口

    定义了计算过程中可以自定义的各种方法。
    每种软件类型可以提供自己的实现，以修改计算过程中的任何规则。
    """

    @abstractmethod
    def get_output_dir(self) -> str:
        """
        获取输出目录路径

        Returns:
            str: 输出目录路径
        """
        pass

    @abstractmethod
    def calculate_fee_base(
        self,
        fee_base: str,
        cost_table: Dict[str, Any],
        project_node: Dict[str, Any],
        context: Any,
        in_calculation: Optional[Set[str]] = None,
    ) -> float:
        """
        计算取费基数

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

        Returns:
            float: 计算结果
        """
        pass

    @abstractmethod
    def calculate_internal_fee(
        self,
        fee_code: str,
        cost_table: Dict[str, Any],
        project_node: Dict[str, Any],
        context: Any,
        in_calculation: Optional[Set[str]] = None,
    ) -> float:
        """
        计算表内费用

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

        Returns:
            float: 计算结果
        """
        pass

    @abstractmethod
    def calculate_external_variable(self, var_name: str, context: Any) -> float:
        """
        计算表外变量

        Args:
            var_name: 变量名
            context: 计算上下文

        Returns:
            float: 计算结果
        """
        pass

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

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

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

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

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

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

    @abstractmethod
    def calculate_all_fees(
        self,
        project_node: Dict[str, Any],
        cost_table: Dict[str, Any],
        json_file_path: Optional[str] = None,
        engineering_type: Optional[str] = None,
        json_data: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, float]:
        """
        计算所有费用

        Args:
            project_node: 项目节点
            cost_table: 取费表
            json_file_path: JSON文件路径
            engineering_type: 工程类型

        Returns:
            Dict[str, float]: 计算结果，键为费用名称，值为计算结果
        """
        pass

    @abstractmethod
    def calculate_rcj_count(
        self,
        rcj_nodes: List[Tuple[Dict[str, Any], str]],
        project_children: List[Dict[str, Any]],
        json_file_path: Optional[str] = None,
        json_data: Optional[Dict[str, Any]] = None,
    ) -> List[Dict[str, Any]]:
        """
        计算人材机节点的数量，考虑父级消耗量

        Args:
            rcj_nodes: 人材机节点列表，每个元素是(节点, 父级ID)元组
            project_children: 项目划分级别下的所有工程量节点
            json_file_path: JSON文件路径，用于获取工程信息

        Returns:
            List[Dict[str, Any]]: 计算数量后的人材机节点列表
        """
        pass

    @abstractmethod
    def cat_rcj_count(self, rcj_nodes: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """
        汇总人材机节点的数量

        Args:
            rcj_nodes: 人材机节点列表

        Returns:
            List[Dict[str, Any]]: 汇总后的人材机节点列表
        """
        pass

    @abstractmethod
    def calc_rcj_fee(self, rcj_nodes: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """
        计算人材机节点的费用

        Args:
            rcj_nodes: 人材机节点列表

        Returns:
            List[Dict[str, Any]]: 计算费用后的人材机节点列表
        """
        pass

    @abstractmethod
    def format_rcj_output(self, rcj_nodes: List[Dict[str, Any]], node_type: str) -> List[Dict[str, Any]]:
        """
        格式化人材机节点输出

        Args:
            rcj_nodes: 人材机节点列表
            node_type: 节点类型

        Returns:
            List[Dict[str, Any]]: 格式化后的人材机节点列表
        """
        pass

    @abstractmethod
    def calculate_rcj_fees(
        self,
        json_file_path: str,
        project_name: str,
        engineering_type: str,
        project_guid: str = None,
    ) -> Dict[str, Any]:
        """
        计算人材机合价

        Args:
            json_file_path: JSON文件路径
            project_name: 项目名称
            engineering_type: 工程类型
            project_guid: 项目GUID，用于区分同名项目

        Returns:
            Dict[str, Any]: 计算结果
        """
        pass

    @abstractmethod
    def post_process_quantity_fees(self, output_file: str, project_name: str) -> None:
        """
        对工程量取费表进行后处理

        Args:
            output_file: 输出文件路径
            project_name: 项目名称
        """
        pass

    @abstractmethod
    def preprocess_project_nodes(self, project_nodes: List[Dict[str, Any]]) -> None:
        """
        对工程量节点进行预处理

        Args:
            project_nodes: 工程量节点列表
        """
        pass


class DefaultCalculationStrategy(CalculationStrategy):
    """
    默认计算策略实现

    实现了所有计算方法的默认行为，与原有代码逻辑相同。
    其他计算策略可以继承此类，只重写需要修改的方法。
    """

    def __init__(self):
        # 导入必要的模块
        from equipment_calculation.expressioncalculator import ExpressionCalculator
        from equipment_calculation.bcl_utils import calculator
        from equipment_calculation.item_acquisition import get_quantity_nodes, get_classified_resource_nodes

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

        # 默认输出目录
        self.output_dir = "计算结果"

    def get_output_dir(self) -> str:
        """获取输出目录路径"""
        return self.output_dir

    def set_output_dir(self, output_dir: str) -> None:
        """设置输出目录路径"""
        self.output_dir = output_dir

    def calculate_fee_base(
        self,
        fee_base: str,
        cost_table: Dict[str, Any],
        project_node: Dict[str, Any],
        context: Any,
        in_calculation: Optional[Set[str]] = None,
    ) -> float:
        """计算取费基数"""
        from equipment_calculation.quantity_fee_calculator import calculate_fee_base as original_calculate_fee_base

        # 传递自身作为计算策略
        return original_calculate_fee_base(fee_base, cost_table, project_node, context, in_calculation, self)

    def calculate_internal_fee(
        self,
        fee_code: str,
        cost_table: Dict[str, Any],
        project_node: Dict[str, Any],
        context: Any,
        in_calculation: Optional[Set[str]] = None,
    ) -> float:
        """计算表内费用"""
        from equipment_calculation.quantity_fee_calculator import (
            calculate_internal_fee as original_calculate_internal_fee,
        )

        # 传递自身作为计算策略
        return original_calculate_internal_fee(fee_code, cost_table, project_node, context, in_calculation, self)

    def calculate_external_variable(self, var_name: str, context: Any) -> float:
        """计算表外变量"""
        # 直接使用 calculator.calculate，而不是调用 quantity_fee_calculator.py 中的函数
        from equipment_calculation.bcl_utils import calculator

        result = calculator.calculate(var_name, context)
        return float(result) if result is not None else 0.0

    def find_fee_item_by_code(self, cost_table: Dict[str, Any], code: str) -> Optional[Dict[str, Any]]:
        """在取费表中查找指定代码的费用项"""
        from equipment_calculation.quantity_fee_calculator import (
            find_fee_item_by_code as original_find_fee_item_by_code,
        )

        return original_find_fee_item_by_code(cost_table, code)

    def find_fee_base_nodes(self, node: Dict[str, Any], result: Optional[List] = None) -> List[Dict[str, Any]]:
        """递归查找取费表中包含"取费基数"的节点或有子节点的费用项"""
        from equipment_calculation.quantity_fee_calculator import find_fee_base_nodes as original_find_fee_base_nodes

        return original_find_fee_base_nodes(node, result)

    def calculate_all_fees(
        self,
        project_node: Dict[str, Any],
        cost_table: Dict[str, Any],
        json_file_path: Optional[str] = None,
        engineering_type: Optional[str] = None,
        json_data: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, float]:
        """计算所有费用"""
        from equipment_calculation.quantity_fee_calculator import calculate_all_fees as original_calculate_all_fees

        # 传递自身作为计算策略
        return original_calculate_all_fees(
            project_node,
            cost_table,
            json_file_path,
            engineering_type,
            self,
            json_data=json_data,
        )

    def calculate_rcj_count(
        self,
        rcj_nodes: List[Tuple[Dict[str, Any], str]],
        project_children: List[Dict[str, Any]],
        json_file_path: Optional[str] = None,
    ) -> List[Dict[str, Any]]:
        """计算人材机节点的数量"""
        from equipment_calculation.resource_fee_calculator import calc_rcj_count as original_calc_rcj_count

        return original_calc_rcj_count(rcj_nodes, project_children, json_file_path, json_data=json_data)

    def cat_rcj_count(self, rcj_nodes: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """汇总人材机节点的数量"""
        from equipment_calculation.resource_fee_calculator import cat_rcj_count as original_cat_rcj_count

        return original_cat_rcj_count(rcj_nodes)

    def calc_rcj_fee(self, rcj_nodes: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """计算人材机节点的费用"""
        from equipment_calculation.resource_fee_calculator import calc_rcj_fee as original_calc_rcj_fee

        return original_calc_rcj_fee(rcj_nodes)

    def format_rcj_output(self, rcj_nodes: List[Dict[str, Any]], node_type: str) -> List[Dict[str, Any]]:
        """格式化人材机节点输出"""
        from equipment_calculation.resource_fee_calculator import format_rcj_output as original_format_rcj_output

        return original_format_rcj_output(rcj_nodes, node_type)

    # 修复 calculate_rcj_fees 未定义的错误
    def calculate_rcj_fees(
        self,
        json_file_path,
        project_name,
        project_guid=None,
        json_data: Optional[Dict[str, Any]] = None,
        engineering_type: Optional[str] = None,
    ):
        """
        计算项目划分节点下所有人材机节点的合价

        Args:
            json_file_path: JSON文件路径
            project_name: 项目名称
            project_guid: 项目GUID，用于区分同名项目

        Returns:
            Dict[str, Any]: 计算结果
        """
        # 导入原始函数
        from equipment_calculation.resource_fee_calculator import calculate_rcj_fees as original_calculate_rcj_fees

        # 调用原始函数
        return original_calculate_rcj_fees(json_file_path, project_name, project_guid, json_data=json_data)

    def post_process_quantity_fees(self, output_file: str, project_name: str) -> None:
        """对工程量取费表进行后处理"""
        # 默认实现不做任何操作
        pass

    def preprocess_project_nodes(self, project_nodes: List[Dict[str, Any]]) -> None:
        """
        默认实现不做任何预处理
        """
        pass
