from neo4j import GraphDatabase
import json
import hashlib
import json
from collections import defaultdict
def merge_records(record_list):
    grouped = defaultdict(list)
    
    for record in record_list:
        key = (record['type'], record['properties']['名称'])
        grouped[key].append(record)
    
    merged_list = []
    
    for key, group in grouped.items():
        if len(group) == 1:
            merged_list.append(group[0])
        else:
            base = group[0].copy()
            props_merged = base['properties'].copy()
            ks_set = set()

            for rec in group:
                try:
                    ks_value = rec['properties']['knowledge_source']
                    # 🔧 修复:处理不同类型的 knowledge_source
                    if isinstance(ks_value, list):
                        ks_list = ks_value
                    elif isinstance(ks_value, str):
                        ks_list = json.loads(ks_value)
                    else:
                        ks_list = []
                        
                    for item in ks_list:
                        ks_set.add(json.dumps(item, sort_keys=True, ensure_ascii=False))
                except (json.JSONDecodeError, KeyError, TypeError):
                    pass

                for k, v in rec['properties'].items():
                    if k != 'knowledge_source':
                        if props_merged.get(k) in ("", "香", None) and v not in ("", "香", None):
                            props_merged[k] = v
                        elif k not in props_merged:
                            props_merged[k] = v

            merged_ks = [json.loads(s) for s in ks_set]
            props_merged['knowledge_source'] = json.dumps(merged_ks, ensure_ascii=False)

            base['properties'] = props_merged
            merged_list.append(base)
    
    return merged_list
# def merge_records(record_list):
#     # 使用 (type, 名称) 作为 key 的字典来分组
#     grouped = defaultdict(list)
    
#     for record in record_list:
#         key = (record['type'], record['properties']['名称'])
#         grouped[key].append(record)
    
#     merged_list = []
    
#     for key, group in grouped.items():
#         if len(group) == 1:
#             # 没有重复，直接加入
#             merged_list.append(group[0])
#         else:
#             # 合并多个记录
#             base = group[0].copy()
#             props_merged = base['properties'].copy()
#             ks_set = set()

#             # 合并所有 knowledge_source
#             for rec in group:
#                 # 解析 knowledge_source（字符串形式的 JSON 列表）
#                 try:
#                     ks_list = json.loads(rec['properties']['knowledge_source'])
#                     for item in ks_list:
#                         # 转为字符串以便去重（或用 tuple/frozenset 如果结构固定）
#                         ks_set.add(json.dumps(item, sort_keys=True))
#                 except (json.JSONDecodeError, KeyError):
#                     pass  # 忽略无效或缺失的 knowledge_source

#                 # 合并 properties 中的其他字段（覆盖式合并，但只保留非空/非"香"等？这里按全合并）
#                 for k, v in rec['properties'].items():
#                     if k != 'knowledge_source':
#                         # 如果字段值不同且都不为空，可以选择保留非"香"、非空等策略
#                         # 这里简单策略：如果当前为空或为"香"，则用新值覆盖
#                         if props_merged.get(k) in ("", "香", None) and v not in ("", "香", None):
#                             props_merged[k] = v
#                         elif props_merged.get(k) in ("", "香", None) and v in ("", "香", None):
#                             # 都是无效值，保留原样
#                             pass
#                         elif k not in props_merged:
#                             props_merged[k] = v
#                         # 如果两者都有效且不同，也可以选择保留第一个或报冲突（这里暂不处理冲突）

#             # 重建 knowledge_source
#             merged_ks = [json.loads(s) for s in ks_set]
#             props_merged['knowledge_source'] = json.dumps(merged_ks, ensure_ascii=False)

#             base['properties'] = props_merged
#             merged_list.append(base)
    
#     return merged_list


def fetch_all_nodes_and_rels(tx):
    query = """
    MATCH (n)
    OPTIONAL MATCH (n)-[r]->()
    WITH 
        collect(DISTINCT {
            id: toString(id(n)),
            labels: labels(n),
            properties: properties(n)
        }) AS nodes,
        collect(DISTINCT r) AS rels
    UNWIND rels AS r
    WITH nodes, r
    WHERE r IS NOT NULL
    RETURN 
        nodes,
        collect({
            id: toString(id(r)),
            source: toString(id(startNode(r))),
            target: toString(id(endNode(r))),
            type: type(r),
            properties: properties(r)
        }) AS relationships
    """
    result = tx.run(query)
    record = result.single()
    if record:
        return record["nodes"], record["relationships"]
    else:
        return [], []


#从已有数据库导出已存在的关系
def reconstruct_relationships(entities, relationships):
    # 构建 id -> entity 的映射字典
    entity_map = {entity["id"]: entity for entity in entities}

    reconstructed_rela = []
    for rel in relationships:
        source_id = rel.get("source")
        target_id = rel.get("target")
        rel_type = rel.get("type")

        # 检查 source 和 target 是否存在
        source_entity = entity_map.get(source_id)
        target_entity = entity_map.get(target_id)

        if not source_entity or not target_entity:
            continue  # 跳过任一实体缺失的关系

        # 安全获取“名称”字段（注意：根据你的数据，“名称”在顶层，不在 properties 中！）
        from_name = source_entity["properties"].get("名称")
        to_name = target_entity["properties"].get("名称")

        # 如果任一名称缺失，也跳过
        if from_name is None or to_name is None:
            continue

        # 构建新关系
        reconstructed_rela.append({
            "type": rel_type,
            "from_entity": from_name,
            "to_entity": to_name
        })
    
    return reconstructed_rela

def reconstruct_entities(entities):
    # 要排除的字段（无论在顶层还是 properties 内部）
    excluded_keys = {"last_updated", "fulltext", "created_at"}
    
    reconstructed_rela = []
    for entity in entities:
        labels = entity.get("labels")
        if not isinstance(labels, list) or len(labels) == 0:
            continue
        
        entity_type = labels[0]
        
        # 合并顶层字段 + properties 内部字段
        combined_props = {}
        
        # 添加顶层字段（除了 id, labels, 和可能存在的 properties）
        for k, v in entity.items():
            if k in ("id", "labels", "properties"):
                continue
            combined_props[k] = v
        
        # 添加 properties 内部字段（如果存在）
        inner_props = entity.get("properties", {})
        if isinstance(inner_props, dict):
            for k, v in inner_props.items():
                combined_props[k] = v
        
        # 剔除不需要的字段
        filtered_props = {
            k: v for k, v in combined_props.items()
            if k not in excluded_keys
        }
        
        # 只有当 properties 非空时才保留（可选）
        if not filtered_props:
            continue
        
        reconstructed_rela.append({
            "type": entity_type,
            "properties": filtered_props
        })
    
    return reconstructed_rela
def deduplicate_relations(relations):
    seen = set()
    unique_relations = []
    for rel in relations:
        # 构造唯一键（必须是可哈希的）
        key = (rel["type"], rel["from_entity"], rel["to_entity"])
        if key not in seen:
            seen.add(key)
            unique_relations.append(rel)
    return unique_relations


def filter_node_relationship(URI,USERNAME,PASSWORD,merged_output_data):
    OUTPUT_FILE1 = "./huizong_node_rela.json"

    OUTPUT_FILE = "./neo4j_export_deduplicated.json"

    driver = GraphDatabase.driver(URI, auth=(USERNAME, PASSWORD))

    try:
        with driver.session() as session:
            print("正在从 Neo4j 获取所有节点和关系...")
            nodes, relationships = session.execute_read(fetch_all_nodes_and_rels)

            print(f"原始数据: {len(nodes)} 个实体, {len(relationships)} 条关系")
            final_entity = []
            final_relation = []
            all_entity_old = reconstruct_entities(nodes)
            all_relationships_old = reconstruct_relationships(nodes, relationships)
            all_entity_new = merged_output_data["entities"]
            all_relationships_new = merged_output_data["relationships"]
            final_entity.extend(all_entity_old)
            final_entity.extend(all_entity_new)
            final_relation.extend(all_relationships_old)
            final_relation.extend(all_relationships_new)
            print(f"汇总数据: {len(final_entity)} 个实体, {len(final_relation)} 条关系")
            
            filter_data1 = {
                "entities": final_entity,
                "relationships": final_relation
            }
            with open(OUTPUT_FILE1, 'w', encoding='utf-8') as f:
                json.dump(filter_data1, f, indent=2, ensure_ascii=False)
            filter_node = merge_records(final_entity)
            filter_relationships = deduplicate_relations(final_relation)
            print(f"过滤后数据: {len(filter_node)} 个实体, {len(filter_relationships)} 条关系")

            filter_data = {
                "entities": filter_node,
                "relationships": filter_relationships
            }

            with open(OUTPUT_FILE, 'w', encoding='utf-8') as f:
                json.dump(filter_data, f, indent=2, ensure_ascii=False)

            return filter_data
    except Exception as e:
        print(f"❌ 导出失败: {e}")
        
        import traceback
        traceback.print_exc()
        return merged_output_data
    finally:
        driver.close()

# if __name__ == "__main__":
#     URI = "bolt://192.168.0.46:57687"
#     USERNAME = "neo4j"
#     PASSWORD = "zdht123@"
#     # filter_node_relationship(URI=URI,USERNAME=USERNAME,PASSWORD=PASSWORD)

