import bpy
import re


def clean_name(name):
    if re.match(r"(.*)\.\d{3}$", name):
        return name[:-4]
    return name


def is_node_groups_duplicate(node_groups):
    node_group_types = sorted([n.type for n in node_groups[0].nodes])
    return all(
        sorted([n.type for n in ng.nodes]) == node_group_types for ng in node_groups[1:]
    )


def remap_node_group_duplicates(nodes=None, force=False):
    if nodes is None:
        nodes = list(bpy.data.node_groups)

    nodes = [n for n in nodes if not n.library]

    failed = []
    merged = []

    # Group by name
    groups = {}
    for node in nodes:
        groups.setdefault(clean_name(node.name), []).append(node)

    for node in bpy.data.node_groups:
        name = clean_name(node.name)
        if name in groups and node not in groups[name]:
            groups[name].append(node)

    print("\nMerge Duplicate NodeGroup...")

    for node_groups in groups.values():
        if len(node_groups) == 1:
            continue

        if not force:
            node_groups.sort(key=lambda x: x.name, reverse=True)

        print(node_groups)

        for node_group in node_groups[1:]:
            is_duplicate = is_node_groups_duplicate((node_group, node_groups[0]))

            if not is_duplicate and not force:
                failed.append((node_group.name, node_groups[0].name))
                print(
                    f"Cannot merge Nodegroup {node_group.name} with {node_groups[0].name} they are different"
                )
                continue

            merged.append((node_group.name, node_groups[0].name))
            print(f"Merge Nodegroup {node_group.name} into {node_groups[0].name}")

            node_group.user_remap(node_groups[0])
            bpy.data.node_groups.remove(node_group)
            node_groups.remove(node_group)

    # Rename groups if it has no duplicate left
    for node_groups in groups.values():
        if len(node_groups) == 1 and not node_groups[0].library:
            node_groups[0].name = clean_name(node_groups[0].name)

    return merged, failed