"""Take one failure case from the cross-level conjecture test and search
the full subset space of cross-level edges to find an iso(G)-recovering
subset, OR confirm no such subset exists."""
import sys, os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import networkx as nx
from itertools import combinations
from triangulation_gen import enumerate_all_triangulations
from level_cycles import compute_levels, level_sources
from simple_level_resolution_coverage import (
    simple_level_resolutions, is_triangulation,
)
from test_cross_level_iso_inverse import flip_edge, apply_flips


def find_first_failure(n):
    """Return the first (G, S, T) triple at n where no subset of size ≤ 6 of
    inherited cross-level edges recovers iso(G)."""
    isos = enumerate_all_triangulations(n)
    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 > 4:
                    break
                if not is_triangulation(Gp, n):
                    continue
                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
                # Find cross-level inherited edges
                levels = compute_levels(G, source_set)
                algorithm_added = Gp_edges - G_edges
                cross_in_T = {e for e in Gp_edges
                               if levels.get(tuple(e)[0], -1)
                               != levels.get(tuple(e)[1], -1)}
                cross_inherited = cross_in_T - algorithm_added
                # Try all subsets up to size 6
                found = False
                for k in range(1, min(6, len(cross_inherited)) + 1):
                    if found:
                        break
                    for subset in combinations(cross_inherited, k):
                        T_flipped, _ = apply_flips(
                            Gp, [tuple(e) for e in subset])
                        if nx.is_isomorphic(T_flipped, G):
                            found = True
                            break
                if not found:
                    return src_idx, kind, lab, source_set, G, Gp, levels, \
                           algorithm_added, cross_in_T, cross_inherited
    return None


def exhaustive_search(G, Gp, cross_inherited, max_k=None):
    """Search all subsets of cross_inherited for one whose flip yields iso(G).
    Returns the subset if found, else None."""
    edges = list(cross_inherited)
    if max_k is None:
        max_k = len(edges)
    for k in range(1, max_k + 1):
        for subset in combinations(edges, k):
            T_flipped, _ = apply_flips(Gp, [tuple(e) for e in subset])
            if nx.is_isomorphic(T_flipped, G):
                return subset
    return None


def main():
    print("Finding first failure at n=7...")
    result = find_first_failure(7)
    if result is None:
        print("No failures found at n=7 (within max_k=6)")
        return
    src_idx, kind, lab, source_set, G, Gp, levels, algorithm_added, \
        cross_in_T, cross_inherited = result
    print(f"\n  src={src_idx} source=({kind}, {lab})")
    print(f"  source set: {sorted(source_set)}")
    print(f"  G edges: {sorted(G.edges())}")
    print(f"  Gp (= T) edges: {sorted(Gp.edges())}")
    print(f"  G \\ T (removed): {sorted([tuple(e) for e in (set(frozenset(x) for x in G.edges()) - set(frozenset(y) for y in Gp.edges()))])}")
    print(f"  T \\ G (added by alg): {sorted([tuple(e) for e in algorithm_added])}")
    print(f"  G-levels: {dict(sorted(levels.items()))}")
    print(f"  cross-level edges in T: {sorted([tuple(e) for e in cross_in_T])}")
    print(f"  cross-level inherited (not added by algorithm): "
          f"{sorted([tuple(e) for e in cross_inherited])}")
    print(f"  num inherited cross-level: {len(cross_inherited)}")

    # Exhaustive search
    print(f"\n  Exhaustively searching all subsets of cross_inherited "
          f"({2**len(cross_inherited)} subsets total)...")
    subset = exhaustive_search(G, Gp, cross_inherited)
    if subset is None:
        print(f"  NO subset of cross-level inherited edges recovers iso(G)")
    else:
        print(f"  Found subset of size {len(subset)}: "
              f"{sorted([tuple(e) for e in subset])}")
        # Verify
        T_flipped, _ = apply_flips(Gp, [tuple(e) for e in subset])
        print(f"    flipped graph edges: {sorted(T_flipped.edges())}")
        print(f"    iso to G: {nx.is_isomorphic(T_flipped, G)}")

    # Also try the FULL set of cross-level edges (including algorithm-added)
    print(f"\n  Also trying full cross-level set (including algorithm-added)...")
    subset2 = exhaustive_search(G, Gp, cross_in_T)
    if subset2 is None:
        print(f"  Even using all cross-level edges, no recovery found")
    else:
        print(f"  Found subset of size {len(subset2)} from full cross set: "
              f"{sorted([tuple(e) for e in subset2])}")


if __name__ == "__main__":
    main()
