"""Find any (G, S) at n=12 whose algorithm output is iso to the icosahedron,
record the symmetric difference, and test the cross-level conjecture."""
import sys, os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import networkx as nx
from triangulation_gen_fast import enumerate_all_triangulations_fast
from level_cycles import compute_levels, level_sources
from simple_level_resolution_coverage import (
    simple_level_resolutions, is_triangulation,
)


def is_md5(T):
    return min(T.degree(v) for v in T.nodes()) >= 5


def main():
    isos = enumerate_all_triangulations_fast(12)
    md5_idx = [i for i, T in enumerate(isos) if is_md5(T)]
    print(f"md5 iso-classes at n=12: {md5_idx}")
    if len(md5_idx) != 1:
        print(f"unexpected: not exactly 1 md5 class")
    icosa = isos[md5_idx[0]]
    print(f"icosahedron edges: {sorted(icosa.edges())[:8]}...")

    # For each (G, S), check if algorithm output is iso to icosa.
    found = []
    for src_idx, G in enumerate(isos):
        ip, emb = nx.check_planarity(G)
        if not ip:
            continue
        for kind, lab, source_set in level_sources(G, emb):
            count = 0
            for Gp in simple_level_resolutions(G, source_set):
                count += 1
                if count > 30:
                    break
                if not is_triangulation(Gp, 12):
                    continue
                if not nx.is_isomorphic(Gp, icosa):
                    continue
                # Require bipartite parity subgraphs (real simple level res)
                levels = compute_levels(G, source_set)
                even = [v for v in Gp.nodes() if levels.get(v, 0) % 2 == 0]
                odd = [v for v in Gp.nodes() if levels.get(v, 0) % 2 == 1]
                if not (nx.is_bipartite(Gp.subgraph(even)) and
                        nx.is_bipartite(Gp.subgraph(odd))):
                    continue
                # Skip degenerate cases where algorithm did nothing
                G_edges = set(frozenset(e) for e in G.edges())
                Gp_edges = set(frozenset(e) for e in Gp.edges())
                if G_edges == Gp_edges:
                    continue
                added = Gp_edges - G_edges
                removed = G_edges - Gp_edges
                def lvl_diff(e):
                    u, v = tuple(e)
                    return abs(levels.get(u, -1) - levels.get(v, -1))
                added_diffs = [lvl_diff(e) for e in added]
                removed_diffs = [lvl_diff(e) for e in removed]
                found.append({
                    'src_idx': src_idx,
                    'source': (kind, lab),
                    'source_set': sorted(source_set),
                    'added_count': len(added),
                    'removed_count': len(removed),
                    'added_diffs': sorted(added_diffs),
                    'removed_diffs': sorted(removed_diffs),
                    'added_all_cross': all(d >= 1 for d in added_diffs),
                    'removed_all_same': all(d == 0 for d in removed_diffs),
                })
                if len(found) >= 5:
                    break
            if len(found) >= 5:
                break
        if len(found) >= 5:
            break
    print(f"\nfound {len(found)} preimages of icosahedron")
    for ex in found:
        print(f"\n  src_iso={ex['src_idx']}, source={ex['source']}")
        print(f"    source set: {ex['source_set']}")
        print(f"    added={ex['added_count']} (level diffs {ex['added_diffs']}), "
              f"removed={ex['removed_count']} (level diffs {ex['removed_diffs']})")
        print(f"    added all cross-level: {ex['added_all_cross']}; "
              f"removed all same-level: {ex['removed_all_same']}")


if __name__ == "__main__":
    main()
