"""
	PDF 用户上传接口（FastAPI）
- 处理文件名和其对应关系
"""

from typing import Dict, Tuple
import os
import re
from typing import Dict
from fastapi import FastAPI, HTTPException
import logging
import json
from modelsAPI.model_api import OpenaiAPI
from openai import OpenAI
from typing import Dict, List, Any
from config import TREE_JSON_PATH

app = FastAPI(title="PDF Upload Service")
logging.basicConfig(level=logging.INFO, format="%(asctime)s | %(levelname)s | %(name)s | %(message)s")
logger = logging.getLogger("filename_neo4j_process")


# =========================
# 图方法
# =========================


def node_to_json(node):
    props = dict(node)
    # ks_raw = props.get("knowledge_source")
    # knowledge_source = json.loads(ks_raw) if ks_raw else []

    ks_raw = props.get("knowledge_source")
    knowledge_source = []

    if ks_raw and isinstance(ks_raw, str):
        try:
            ks_list = json.loads(ks_raw)
            if isinstance(ks_list, list):
                for ks in ks_list:
                    if isinstance(ks, dict):
                        ks.pop("info", None)
                        knowledge_source.append(ks)
        except (json.JSONDecodeError, TypeError):
            # 解析失败或类型错误时，保持 knowledge_source 为空列表
            pass

    name = props.get("名称")
    props.pop("embedding", None)
    props.pop("knowledge_source", None)
    props.pop("name", None)  # 现在安全地移除
    props.pop("fulltext", None)
    if "last_updated" in props:
        props["最后更新时间"] = props.pop("last_updated")

    if "created_at" in props:
        props["创建时间"] = props.pop("created_at")
    return {
        "id": node.element_id,
        "name": name,  # 使用提前保存的值
        "labels": list(node.labels),
        "knowledge_source": knowledge_source,
        "properties": props,
    }


def rel_to_json(rel):
    props = dict(rel)
    props.pop("embedding", None)
    props.pop("fact_timeline", None)
    return {"id": rel.element_id, "label": rel.type, "source": rel.start_node.element_id, "target": rel.end_node.element_id, "properties": props}


def is_simple_path(path):
    """
	    判断是否为无环路径（simple path）
    """
    seen = set()
    for n in path.nodes:
        nid = n.element_id
        if nid in seen:
            return False
        seen.add(nid)
    return True

def build_tree(nodes: List[Dict], links: List[Dict]) -> List[Dict]:
    """
    按照固定层级构建树：舰船(根) -> 舰艇 -> 系统 -> 子系统
    不再依赖 links 的连线，直接根据 labels 归类，彻底避免环状数据导致的死循环。
    默认只展开第一艘舰艇的系统/子系统，其它舰艇 children 置空（前端可点击按需加载）。
    """

    # 1. 辅助函数：判断标签是否包含关键字
    def has_label(node, keyword):
        labels = node.get("labels", [])
        if isinstance(labels, list):
            return any(keyword in lbl for lbl in labels)
        return False

    # 2. 辅助函数：构建标准的树节点格式
    def make_node(node_data):
        return {
            "id": node_data["id"],
            "name": node_data.get("name", ""),
            "labels": node_data.get("labels", []),

            "properties": node_data.get("properties", {}),
            "color": node_data.get("color"),
            "size": node_data.get("size"),
            "children": []
        }

    # 3. 将节点按层级分类存储到字典中，方便快速查找
    fleet_roots = {} # 第零层：舰船/舰号（父根节点）
    ships = {}      # 第一层：舰艇
    systems = {}    # 第二层：系统（不含子系统）
    sub_systems = {} # 第三层：子系统

    for node in nodes:
        # 注意：判断逻辑要互斥，防止"子系统"被误判为"系统"
        if has_label(node, "舰船") or has_label(node, "舰号"):
            fleet_roots[node["id"]] = make_node(node)
        elif has_label(node, "舰艇"):
            ships[node["id"]] = make_node(node)
        elif has_label(node, "子系统"):
            sub_systems[node["id"]] = make_node(node)
        elif has_label(node, "系统"):
            systems[node["id"]] = make_node(node)
        else:
            pass

    # 4. 组装系统 -> 子系统、舰艇 -> 系统 关系（先把完整子树挂好）
    for link in links:
        if link.get("label") != "包含":
            continue

        src_id = link["source"]
        tgt_id = link["target"]

        if src_id in ships and tgt_id in systems:
            ships[src_id]["children"].append(systems[tgt_id])
        elif src_id in systems and tgt_id in sub_systems:
            systems[src_id]["children"].append(sub_systems[tgt_id])
        elif src_id in ships and tgt_id in sub_systems:
            ships[src_id]["children"].append(sub_systems[tgt_id])

    # 5. 所有舰艇 children 全部保留，仅用 collapsed 标记默认折叠状态。
    #    前端读取 collapsed=False 的舰艇默认展开，其它折叠；
    #    用户点击折叠的舰艇时，前端切换 collapsed 即可展示子树。
    # 只保留名称包含 "122" 的舰艇（隐藏 JZ贱 等其它分支）
    #sorted_ships = sorted(
    #    (s for s in ships.values() if "122" in s.get("name", "")),
    #    key=lambda s: s.get("name", ""),
    #)
    sorted_ships = sorted(ships.values(), key=lambda s: s.get("name", ""))
    for idx, ship in enumerate(sorted_ships):
        ship["collapsed"] = (idx != 0)

    # 6. 挂载到舰船根节点；如果数据里没有舰船根，就创建一个虚拟根
    if fleet_roots:
        # 取第一个舰船根节点作为父（一般场景下只有一个）
        root = next(iter(fleet_roots.values()))
        root["children"] = sorted_ships
        return [root]
    else:
        virtual_root = {
            "id": "__fleet_root__",
            "name": "舰号",
            "labels": ["舰船"],
            "properties": {},
            "color": None,
            "size": None,
            "children": sorted_ships,
        }
        return [virtual_root]
def build_tree1(nodes: List[Dict], links: List[Dict]) -> List[Dict]:
    """
    从 nodes + links 构建树结构。
	    - 根节点：label 包含 '舰艇' 的节点
	    - 父子关系：links 中 label == '包含' 的边 (source -> target)
    - 支持多棵树（多个舰艇根节点）
    """

    # 1. 建立 id -> node 的映射
    node_map: Dict[str, Dict] = {n["id"]: n for n in nodes}

    # 2. 建立 父id -> [子id列表] 的邻接表（只处理"包含"关系）
    children_map: Dict[str, List[str]] = {n["id"]: [] for n in nodes}
    parent_map: Dict[str, str] = {}  # 子id -> 父id，用于识别根节点

    for link in links:
        if link.get("label") == "包含":
            src = link["source"]
            tgt = link["target"]
            if src in children_map:
                children_map[src].append(tgt)
            if tgt not in parent_map:
                parent_map[tgt] = src

    # 3. 递归构建子树
    def build_subtree(node_id: str, ancestors: set = None) -> Dict[str, Any]:
        if ancestors is None:
            ancestors = set()
        if node_id in ancestors:
            return None
        ancestors.add(node_id)
        node = node_map[node_id]
        subtree = {"id": node_id, "name": node.get("name", ""), "labels": node.get("labels", []), "properties": node.get("properties", {}), "color": node.get("color"), "size": node.get("size"), "children": []}
        current_ancestors = ancestors | {node_id}
        for child_id in children_map.get(node_id, []):
            child = build_subtree(child_id, current_ancestors)
            if child is not None:
                subtree["children"].append(child)
        return subtree

    # def build_subtree(node_id: str) -> Dict[str, Any]:
    #     node = node_map[node_id]
    #     subtree = {
    #         "id": node_id,
    #         "name": node.get("name", ""),
    #         "labels": node.get("labels", []),
    #         "properties": node.get("properties", {}),
    #         "color": node.get("color"),
    #         "size": node.get("size"),
    #         "children": []
    #     }
    #     for child_id in children_map.get(node_id, []):
    #         subtree["children"].append(build_subtree(child_id))
    #     return subtree

	    # 4. 找出所有根节点：label 包含 '舰艇'，且没有父节点
    roots = [n["id"] for n in nodes if "舰艇" in n.get("labels", []) and n["id"] not in parent_map]

    # 5. 为每棵树构建结构，并打上树编号
    forest = []
    for i, root_id in enumerate(roots):
        tree = build_subtree(root_id)
        tree["tree_index"] = i  # 区分不同树
        forest.append(tree)

    return forest


def _filter_searchable(graph: dict) -> dict:
    """剥掉节点的 Searchable 标签；若剥完后 labels 为空则整体移除该节点及其关联边"""
    for n in graph["nodes"]:
        n["labels"] = [l for l in n.get("labels", []) if l != "Searchable"]
    valid_ids = {n["id"] for n in graph["nodes"] if n["labels"]}
    return {
        "nodes": [n for n in graph["nodes"] if n["id"] in valid_ids],
        "links": [
            l for l in graph["links"]
            if l["source"] in valid_ids and l["target"] in valid_ids
        ],
    }


def build_graph_from_paths(paths):
    """
    核心公共方法
    从 Neo4j 路径结果构建 graphData
    """
    node_map: Dict[str, dict] = {}
    link_map: Dict[str, dict] = {}

    def add_edge(rel):
        s = rel.start_node
        t = rel.end_node

        if s.element_id not in node_map:
            node_map[s.element_id] = node_to_json(s)

        if t.element_id not in node_map:
            node_map[t.element_id] = node_to_json(t)

        if rel.element_id not in link_map:
            link_map[rel.element_id] = rel_to_json(rel)

    for record in paths:
        p = record["p"]

        if not is_simple_path(p):
            continue

        for r in p.relationships:
            add_edge(r)

    return _filter_searchable({"nodes": list(node_map.values()), "links": list(link_map.values())})


# def fetch_graph_sample(driver,limit: int = 200):
#     """提取采样逻辑为可复用函数"""
#     with driver.session() as session:
#         result = session.run("""
#             MATCH (n)
#             WITH n
#             ORDER BY elementId(n) ASC
#             LIMIT $limit
#             MATCH (n)-[rel]->(m)
#             RETURN n, rel, m
#         """, limit=limit)

#         node_map, link_map = {}, {}
#         for rec in result:
#             n, r, m = rec["n"], rec["rel"], rec["m"]
#             node_map[n.element_id] = node_to_json(n)
#             node_map[m.element_id] = node_to_json(m)
#             link_map[r.element_id] = rel_to_json(r)

#         return {
#             "nodes": list(node_map.values()),
#             "links": list(link_map.values())
#         }

# def fetch_graph_sample(driver, limit: int = 200):
#     """
#     获取采样数据：
	#     1. 选取前 $limit 个种子节点（Level 0 或 1，取决于业务定义，这里定义种子为 Level 0）。
#     2. 对每个节点向下探索最多 3 跳。
#     3. 每个节点对象中增加 level 字段，表示其距离种子节点的深度。
#     """
#     with driver.session() as session:
#         # Cypher 逻辑：
#         # - 选出种子节点 n
#         # - 匹配 1-3 跳路径
	#         # - 返回种子节点 n，路径 path，以及该路径的长度（即层级）
#         result = session.run("""
#             MATCH (n)
#             WITH n ORDER BY elementId(n) ASC LIMIT $limit
#             OPTIONAL MATCH path = (n)-[*1..3]-(m)
#             UNWIND nodes(path) AS x
#             WITH n, path, m, COUNT(DISTINCT x) AS nodeCount
#             WHERE path IS NULL OR nodeCount = LENGTH(path) + 1
#             RETURN n, path, LENGTH(path) AS depth
#         """, limit=limit)

#         node_map, link_map = {}, {}

#         for rec in result:
#             seed_node = rec["n"]
#             path = rec["path"]
#             depth = rec["depth"] or 0 # 如果没有路径，深度为 0

#             # 1. 处理种子节点 (Level 0)
#             if seed_node.element_id not in node_map:
#                 node_data = node_to_json(seed_node)
#                 node_data["level"] = 0  # 种子节点设为 0 层
#                 node_map[seed_node.element_id] = node_data

#             # 2. 如果存在路径，解析路径中的所有节点和关系
#             if path:
#                 # 路径中的节点处理
#                 # path.nodes 包含了从起始到终点的所有节点
#                 for i, node in enumerate(path.nodes):
#                     if node.element_id not in node_map:
#                         node_data = node_to_json(node)
#                         # 层级即为该节点在当前路径中的索引
#                         node_data["level"] = i
#                         node_map[node.element_id] = node_data
#                     else:
#                         # 如果节点已存在，保留最小的 level (即最靠近种子的距离)
#                         node_map[node.element_id]["level"] = min(node_map[node.element_id].get("level", 3), i)

#                 # 路径中的关系处理
#                 for rel in path.relationships:
#                     if rel.element_id not in link_map:
#                         link_map[rel.element_id] = rel_to_json(rel)

#         return {
#             "nodes": list(node_map.values()),
#             "links": list(link_map.values())
#         }


# def fetch_graph_sample(driver, limit=200, max_depth=3):
#     with driver.session() as session:
#         result = session.run("""
#             MATCH (c)
#             WITH c ORDER BY elementId(c) ASC LIMIT $limit

#             MATCH path = (c)-[*0..3]-(n)
#             WITH n, MIN(length(path)) AS dist
#             OPTIONAL MATCH (n)-[r]-(m)

#             RETURN n, dist, r, m
#         """, limit=limit)

#         node_map, link_map = {}, {}

#         for rec in result:
#             n = rec["n"]
#             dist = rec["dist"]
#             r = rec["r"]
#             m = rec["m"]

#             # ---- 节点 ----
#             nid = n.element_id
#             if nid not in node_map:
#                 node_data = node_to_json(n)
#                 node_data["level"] = min(dist, 3) if dist is not None else 0
#                 node_map[nid] = node_data

#             # ---- 边 ----
#             if r and m:
#                 mid = m.element_id
#                 if mid not in node_map:
#                     node_map[mid] = node_to_json(m)

#                 link_map.setdefault(r.element_id, rel_to_json(r))

#         return {
#             "nodes": list(node_map.values()),
#             "links": list(link_map.values())
#         }

def fetch_graph_sample(driver):
    """
    查询所有舰艇及其关联的系统和子系统
    路径结构：(舰艇) -> (系统) -> (子系统)
    """
    
    with driver.session() as session:
        # =========================
        # 统一查询：舰艇 -> 系统 -> 子系统
        # =========================
        # 逻辑说明：
        # 1. MATCH (ship:舰艇)：选中所有舰艇
        # 2. -[*1..3]->(target)：查找深度为 1 到 3 的路径
	        #    - 深度 1：舰艇 -> 系统
	        #    - 深度 2：舰艇 -> 系统 -> 子系统
        # 3. WHERE target:系统 OR target:子系统：确保终点是我们关心的节点类型
        #    (防止查询出其他无关的深层节点)
        
        query = """
            MATCH path = (ship:舰艇)-[*1..3]->(target)
            WHERE target:系统 OR target:子系统
            RETURN path
        """
        
        result = session.run(query)
        
        # =========================
        # 解析 paths
        # =========================
        all_nodes = {}
        all_links = {}

        for record in result:
            path = record["path"]
            
            # 解析节点
            for node in path.nodes:
                # 兼容 ID 获取
                node_key = node.element_id if hasattr(node, 'element_id') else node.id
                if node_key not in all_nodes:
                    all_nodes[node_key] = node_to_json(node)

            # 解析关系
            for rel in path.relationships:
                rel_key = rel.element_id if hasattr(rel, 'element_id') else rel.id
                if rel_key not in all_links:
                    all_links[rel_key] = rel_to_json(rel)

        return _filter_searchable({
            "nodes": list(all_nodes.values()),
            "links": list(all_links.values())
        })
def fetch_graph_sample1(driver):
    """
    查找路径：
    1. 优先：舰艇 -> 子系统
	    2. fallback：舰艇 -> 系统

    注意：根据数据库实际结构，使用 Label (:舰艇, :子系统) 进行匹配，
    而不是 WHERE category = '...'
    """

    # 定义采样数量
    SAMPLE_LIMIT = 400

    with driver.session() as session:
        # =========================
        # 第一阶段：舰艇 -> 子系统
        # =========================
        # 修改点：直接使用 (:舰艇) 和 (:子系统) 标签匹配，去掉 WHERE 子句
        result = session.run(
            """
            MATCH (ship:舰艇)
            WITH ship LIMIT $limit

            MATCH path = (ship)-[*1..3]->(sub:子系统)
            RETURN path
        """,
            limit=SAMPLE_LIMIT,
        )

        paths = [record["path"] for record in result]

        # =========================
	        # fallback：舰艇 -> 系统
        # =========================
        if not paths:
            # 修改点：直接使用 (:舰艇) 和 (:系统) 标签匹配
            result = session.run(
                """
                MATCH (ship:舰艇)
                WITH ship LIMIT $limit

                MATCH path = (ship)-[*1..2]->(sys:系统)
                RETURN path
            """,
                limit=SAMPLE_LIMIT,
            )

            paths = [record["path"] for record in result]

        # =========================
        # 解析 paths
        # =========================
        all_nodes = {}
        all_links = {}

        for path in paths:
            # 节点
            for node in path.nodes:
                # 兼容 Neo4j 5.x (element_id) 和 4.x (id)
                node_key = node.element_id if hasattr(node, "element_id") else node.id
                if node_key not in all_nodes:
                    all_nodes[node_key] = node_to_json(node)

            # 关系
            for rel in path.relationships:
                rel_key = rel.element_id if hasattr(rel, "element_id") else rel.id
                if rel_key not in all_links:
                    all_links[rel_key] = rel_to_json(rel)

        return {"nodes": list(all_nodes.values()), "links": list(all_links.values())}


# =============================
# 文件名处理方法
# =============================

# BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# TREE_JSON_PATH = os.path.join(BASE_DIR, "tree_data.json")


def _load_tree_data() -> Dict:
    # 注意：实际运行时请确保 result.json 存在，此处仅为代码结构展示
    if not os.path.exists(TREE_JSON_PATH):
        return {}
    with open(TREE_JSON_PATH, "r", encoding="utf-8") as f:
        return json.load(f)


def _resolve_ship_root(data: Dict, ship_code: str) -> Dict:
    raw = data.get(ship_code)
    if not isinstance(raw, dict):
        return {}

	    # 结构 1：直接是节点 {"code","name","children"}
    if {"code", "name", "children"}.issubset(raw.keys()):
        return raw

	    # 结构 2：包装层 {"101": {...root node...}}
    if ship_code in raw and isinstance(raw[ship_code], dict):
        node = raw[ship_code]
        if {"code", "name", "children"}.issubset(node.keys()):
            return node

    # 兜底：取第一个 node-like 值
    for value in raw.values():
        if isinstance(value, dict) and {"code", "name", "children"}.issubset(value.keys()):
            return value

    return {}


def _longest_common_substring_len(a: str, b: str) -> int:
    if not a or not b:
        return 0
    # 优化：确保 a 是较短的字符串以节省空间（可选），原逻辑保持不动也没问题
    dp = [0] * (len(b) + 1)
    best = 0
    for i in range(1, len(a) + 1):
        prev = 0
        for j in range(1, len(b) + 1):
            temp = dp[j]
            if a[i - 1] == b[j - 1]:
                dp[j] = prev + 1
                if dp[j] > best:
                    best = dp[j]
            else:
                dp[j] = 0
            prev = temp
    return best


def _match_level4_tool(filename_text: str, device_node: Dict) -> Tuple[str, str]:
    """
    匹配 Level 4 零件。
    规则：如果文件名与 Level 3 (设备名) 的最大共同字符串长度 >= 与任何 Level 4 (零件名) 的长度，
         则只匹配到 Level 3，返回空。
         只有当 Level 4 的匹配度严格高于 Level 3 时，才返回 Level 4 信息。
    """
    children = device_node.get("children") or {}

    # 1. 计算文件名与当前设备 (Level 3) 名称的匹配度
    device_name = str(device_node.get("name") or "")
    level3_score = _longest_common_substring_len(filename_text, device_name)

    best_name = ""
    best_code = ""
    best_score = 0  # 记录 Level 4 中的最佳得分

    # 2. 遍历 Level 4 子节点
    for child in children.values():
        tool_name = str(child.get("name") or "")
        tool_code = str(child.get("code") or "")

        score = _longest_common_substring_len(filename_text, tool_name)

        # 关键修改：只有当 Level 4 的得分严格大于 Level 3 的得分时，才视为有效匹配
        # 并且要比当前找到的其他 Level 4 更好
        if score > level3_score and score > best_score:
            best_score = score
            best_name = tool_name
            best_code = tool_code

    # 如果没有找到比 Level 3 匹配度更高的 Level 4，则返回空
    if best_score <= level3_score:
        return "", ""

    return best_name, best_code


def process_filename(filename: str) -> Dict[str, str]:
    name, _ = os.path.splitext(os.path.basename(filename))
    parts = re.split(r"[_-]", name)

    xian_number = ""
    level_1_system_name = ""
    level_2_system_name = ""
    device_name = ""
    tool_name = ""
    tool_code = ""

    if not parts:
        return {}

    xian_number_code = parts[0].strip()
    level1_code = ""
    level2_code = ""
    device_code = ""

    if len(parts) > 1:
        part = parts[1].strip()
        # 防止索引越界
        if len(part) < 2:
            return {}

        level1_code = part[:2]
        level2_code = part[2:4] if len(part) > 2 else ""
        device_code = part[4:] if len(part) > 4 else ""
	        # 若 parts[2] 存在，则用 parts[2] 作为 device_code（支持 122-06A0014-B01001_发动机 格式）
        if len(parts) > 2:
            device_code_alt = parts[2].strip()
            if device_code_alt:
                device_code = device_code_alt

        data = _load_tree_data()
        root = _resolve_ship_root(data, xian_number_code)
        if not root:
            return {}

        xian_number = root.get("name", "")

        # 一级
        s1 = root.get("children", {}).get(level1_code)
        if s1:
            level_1_system_name = s1.get("name", "")

            # 二级
            s2 = s1.get("children", {}).get(level2_code)
            if s2:
                level_2_system_name = s2.get("name", "")

                # 三级设备
                device = s2.get("children", {}).get(device_code)
                if device:
                    device_name = device.get("name", "")
                    # 若存在 level4，匹配与文件名最大相同字符串的零件
                    # 内部已包含逻辑：如果设备名匹配度更高，则不返回零件
                    tool_name, tool_code = _match_level4_tool(name, device)

    return {
        "Xian_Number": xian_number,
        "Xian_Number_code": xian_number_code,
        "Level_1_System_Name": level_1_system_name,
        "level1_code": level1_code,
        "Level_2_System_Name": level_2_system_name,
        "level2_code": level2_code,
        "Device_Name": device_name,
        "device_code": device_code,
        "tool_name": tool_name,
        "tool_code": tool_code,
    }


def get_entity(filename):
    result = process_filename(filename)
    entities = []
    relationships = []
    low_level = ""

    if result:
        xian_number = result.get("Xian_Number")
        xian_number_code = result.get("Xian_Number_code")
        level_1_system_name = result.get("Level_1_System_Name")
        level1_code = result.get("level1_code")
        level_2_system_name = result.get("Level_2_System_Name")
        level2_code = result.get("level2_code")
        device_name = result.get("Device_Name")
        device_code = result.get("device_code")
        tool_name = result.get("tool_name")
        tool_code = result.get("tool_code")

        if xian_number:
            entities.append(
                {
                    "type": "舰艇",
                    "properties": {
                        "名称": xian_number,
                        "舷号": xian_number_code,
                    },
                }
            )
            low_level = xian_number

        if level_1_system_name:
            entities.append(
                {
                    "type": "系统",
                    "properties": {
                        "名称": level_1_system_name,
                        "系统编码": level1_code,
                    },
                }
            )
            low_level = level_1_system_name

        if xian_number and level_1_system_name:
            relationships.append(
                {
                    "type": "包含",
                    "from_entity": xian_number,
                    "to_entity": level_1_system_name,
                }
            )

        if level_2_system_name:
            entities.append(
                {
                    "type": "子系统",
                    "properties": {
                        "名称": level_2_system_name,
                        "子系统编码": level2_code,
                    },
                }
            )
            low_level = level_2_system_name

        if level_1_system_name and level_2_system_name:
            relationships.append(
                {
                    "type": "包含",
                    "from_entity": level_1_system_name,
                    "to_entity": level_2_system_name,
                }
            )

        if device_name:
            entities.append(
                {
                    "type": "设备",
                    "properties": {
                        "名称": device_name,
                        "设备编码": device_code,
                    },
                }
            )
            low_level = device_name

        if level_2_system_name and device_name:
            relationships.append(
                {
                    "type": "包含",
                    "from_entity": level_2_system_name,
                    "to_entity": device_name,
                }
            )

        if tool_name:
            entities.append(
                {
                    "type": "零件",
                    "properties": {
                        "名称": tool_name,
                        "零件编码": tool_code,
                    },
                }
            )
            low_level = tool_name

        if device_name and tool_name:
            relationships.append(
                {
                    "type": "包含",
                    "from_entity": device_name,
                    "to_entity": tool_name,
                }
            )

    final_result = {
        "entities": entities,
        "relationships": relationships,
        "low_level": low_level,
    }

    return final_result


# 示例测试逻辑（非必须，仅供验证）
if __name__ == "__main__":
    # 阀控蓄电池脉冲式快速充电装置
     test_list = [
         "163-06A0015-B01001_发动机-维修手册.pdf",
      
     ]
     for i in test_list:
         result = get_entity(i)
         lower_entity = result.get("low_level")
         print(result)
         print(lower_entity)
    #from neo4j import GraphDatabase

    #NEO4J_URI = "bolt://192.168.0.111:7687"
    #NEO4J_USER = os.getenv("NEO4J_USER", "neo4j")
    #NEO4J_PASSWORD = os.getenv("NEO4J_PASSWORD","zdht123@")  # 不设默认值，强制要求提供
    #NEO4J_DATABASE = os.getenv("NEO4J_DATABASE", "neo4j")
    #driver = GraphDatabase.driver(
     #   NEO4J_URI,
      #  auth=(NEO4J_USER, NEO4J_PASSWORD),
      #  database=NEO4J_DATABASE
    #)
    #res = fetch_graph_sample(driver)
    #print(res)