import os
import json
from memory_profiler import profile
from typing import Dict, List, Any, Optional, Tuple
from equipment_calculation.software_types import (
    get_software_type,
    SoftwareCategory,
    EngineeringType,
    ALL_SOFTWARE_TYPES,
)
from equipment_calculation.software_calculators import get_calculator


# 软件类别名称映射字典，将各种变体映射到标准类别
CATEGORY_MAPPING = {
    # 主网及其变体
    "主网": "主网",
    "主网工程": "主网",
    "主网项目": "主网",
    # 配网及其变体
    "配网": "配网",
    "配网造价": "配网",
    "配网清单": "配网",
    # 技改及其变体
    "技改": "技改",
    "技改工程": "技改",
    "技改项目": "技改",
    "技改造价": "技改",
    "技改清单": "技改",
}


# 项目类型名称映射字典，将各种变体映射到标准类型（预算/清单）
PROJECT_TYPE_MAPPING = {
    # 预算类变体
    "概预算工程": "预算",
}


####应该是加在json后没释放
def parse_json_content(json_file_path: str) -> Tuple[Optional[str], Optional[str], Optional[str]]:
    """
    从JSON文件内容中解析：
    - 软件类别: 来自 basicData["软件类别"]（若无则尝试 basicData["软件名称"] 作为兜底）
    - 项目类型: 来自 basicData["项目类型"]（期望为 "预算" 或 "清单"）
    - 工程类型: 来自 projectData.projectInfo["工程类型"]

    :param json_file_path: JSON文件路径
    :return: (category, project_type, engineering_type) 元组，解析失败对应位置返回 None
    """
    try:
        with open(json_file_path, "r", encoding="utf-8") as f:
            data = json.load(f)

        # 提取 basicData
        basic_data = data.get("basicData", {}) if isinstance(data, dict) else {}
        # 软件类别（优先 软件类别，其次 软件名称）
        category = basic_data.get("软件名称") or basic_data.get("软件名称")
        engineering_type = basic_data.get("项目类型")
        # 规范化项目类型为 预算/清单
        if engineering_type:
            mapped_pt = PROJECT_TYPE_MAPPING.get(engineering_type)
            if mapped_pt:
                engineering_type = mapped_pt
            else:
                print(f"警告: basicData.项目类型 '{engineering_type}' 不是有效值，将使用默认值 '清单'")
                engineering_type = "清单"

        # 规范化软件类别
        if category:
            if category in CATEGORY_MAPPING:
                category = CATEGORY_MAPPING[category]
            else:
                print(f"警告: basicData中的软件名称/名称 '{category}' 不是有效值，将使用默认值 '主网'")
                category = "主网"

        # 提取工程类型：projectData.projectInfo.工程类型
        project_info = data.get("projectData", {}).get("projectInfo", {}) if isinstance(data, dict) else {}
        project_type = project_info.get("工程类型")

        # 打印解析结果（便于调试）
        print(f"解析完成: 软件类别={category}, 项目类型={engineering_type}, 工程类型={project_type}")

        return category, engineering_type, project_type

    except Exception as e:
        print(f"解析JSON文件内容时出错: {str(e)}")
        return None, None, None


def process_json_file(json_file_path: str, output_dir: str, calculate_type: str, project_name: str = None) -> bool:
    """
    处理单个JSON文件

    Args:
        json_file_path: JSON文件路径
        output_dir: 输出目录
        calculate_type: 计算类型（all: 全部, quantity: 工程量取费, resource: 人材机合价）
        project_name: 项目名称，如果不指定则处理所有项目

    Returns:
        bool: 处理是否成功
    """
    try:
        # 从JSON文件内容中解析软件类别、项目类型(预算/清单) 和 工程类型(如 变电/线路/配网)
        # parse_json_content 返回顺序为: (category, project_type, engineering_type)
        category, project_type, engineering_type = parse_json_content(json_file_path)

        # 如果解析失败，使用默认值
        if category is None:
            category = "主网"
            print(f"无法从文件中解析软件类别，使用默认值: {category}")

        # 项目类型(预算/清单) 兜底
        if project_type is None:
            project_type = "预算"
            print(f"无法从文件中解析项目类型(预算/清单)，使用默认值: {project_type}")

        # 可选：记录工程类型
        if engineering_type:
            print(f"工程类型: {engineering_type}")

        # 获取软件类型
        # 这里的第二个参数应为项目类型(预算/清单)
        software_type = get_software_type(category, project_type)
        print(f"使用软件类型: {software_type.name}")

        # 获取计算器
        calculator = get_calculator(software_type)
        if not calculator:
            print(f"错误: 未找到软件类型 {software_type.name} 的计算器")
            return False

        # 获取文件名（不含扩展名），并替换空格和特殊字符
        base_filename = os.path.basename(json_file_path).replace(".json", "")
        # 创建安全的目录名（替换空格和特殊字符）
        safe_dirname = base_filename.replace(" ", "_").replace("\\", "_").replace("/", "_")

        # 设置自定义输出目录
        custom_output_dir = os.path.join(output_dir, safe_dirname)

        # 确保输出目录存在
        try:
            os.makedirs(custom_output_dir, exist_ok=True)
            print(f"创建输出目录: {custom_output_dir}")
        except Exception as e:
            print(f"创建输出目录失败: {str(e)}")
            # 尝试使用更安全的目录名
            safe_dirname = f"project_{hash(base_filename) % 10000}"
            custom_output_dir = os.path.join(output_dir, safe_dirname)
            os.makedirs(custom_output_dir, exist_ok=True)
            print(f"使用备用输出目录: {custom_output_dir}")

        # 检查计算器是否有set_output_dir方法，如果有则直接设置输出目录
        if hasattr(calculator, "set_output_dir"):
            calculator.set_output_dir(custom_output_dir)
            print(f"已设置计算器输出目录: {custom_output_dir}")

            # 根据计算类型执行计算
            if calculate_type in ["all", "quantity"]:
                print("开始计算工程量取费表...")
                calculator.calculate_quantity_fee_tables(
                    json_file_path=json_file_path,
                    project_name=project_name,
                    # 这里的 project_type 传递给计算器用于BCL筛选，应为 工程类型，例如"变电/线路/配网"
                    project_type=engineering_type,
                )

            if calculate_type in ["all", "resource"]:
                print("开始计算人材机合价...")
                calculator.calculate_resource_fee_tables(json_file_path=json_file_path, project_name=project_name)
        else:
            # 创建一个新的计算器，复制原始计算器的属性
            class CustomOutputCalculator:
                def __init__(self, original_calculator, custom_dir):
                    self.original_calculator = original_calculator
                    self.custom_dir = custom_dir

                def get_output_dir(self):
                    return self.custom_dir

                def __getattr__(self, name):
                    return getattr(self.original_calculator, name)

            # 创建自定义输出目录的计算器
            custom_calculator = CustomOutputCalculator(calculator, custom_output_dir)

            # 根据计算类型执行计算
            if calculate_type in ["all", "quantity"]:
                print("开始计算工程量取费表...")
                custom_calculator.calculate_quantity_fee_tables(
                    json_file_path=json_file_path,
                    project_name=project_name,
                    # 这里的 project_type 传递给计算器用于BCL筛选，应为 工程类型，例如"变电/线路/配网"
                    project_type=engineering_type,
                )

            if calculate_type in ["all", "resource"]:
                print("开始计算人材机合价...")
                custom_calculator.calculate_resource_fee_tables(
                    json_file_path=json_file_path, project_name=project_name
                )

        print(f"文件 {json_file_path} 处理完成")
        return True

    except Exception as e:
        print(f"处理文件 {json_file_path} 时出错: {str(e)}")
        import traceback

        traceback.print_exc()
        return False


def process_directory(input_dir: str, output_dir: str, calculate_type: str = "quantity") -> None:
    """
    批量处理目录中的JSON文件

    Args:
        input_dir: 输入目录路径
        output_dir: 输出目录路径
        calculate_type: 计算类型（all: 全部, quantity: 工程量取费, resource: 人材机合价）
    """
    # 确保输出目录存在
    os.makedirs(output_dir, exist_ok=True)

    # 获取目录中的所有JSON文件
    json_files = [f for f in os.listdir(input_dir) if f.lower().endswith(".json")]

    if not json_files:
        print(f"警告: 在目录 {input_dir} 中未找到JSON文件")
        return

    print(f"找到 {len(json_files)} 个JSON文件，开始处理...")

    # 处理每个JSON文件
    success_count = 0
    for i, json_file in enumerate(json_files, 1):
        json_file_path = os.path.join(input_dir, json_file)
        print(f"处理文件 {i}/{len(json_files)}: {json_file}")

        if process_json_file(json_file_path, output_dir, calculate_type):
            success_count += 1

    print(f"处理完成，成功: {success_count}/{len(json_files)}")


def bcl_calculate(input_dir: str, output_dir: str, calculate_type: str = "quantity") -> None:
    """
    主函数，处理指定目录中的所有JSON文件

    Args:
        input_dir: 输入目录路径
        output_dir: 输出目录路径
        calculate_type: 计算类型（all: 全部, quantity: 工程量取费, resource: 人材机合价）
    """
    print(f"开始处理目录: {input_dir}")
    print(f"输出目录: {output_dir}")
    print(f"计算类型: {calculate_type}")

    process_directory(input_dir, output_dir, calculate_type)

    print("所有文件处理完成")
