"""Test the conjecture: if T = algorithm(G, S), then G and T differ only
in cross-level edges (with respect to G's BFS levels from S).

If true, then flipping the cross-level edges of T (back to their apex pairs)
recovers G. This would give a constructive inverse procedure.

For various (G, S) pairs, run the algorithm to get T, then check:
  (1) Are all edges in T \ G cross-level (endpoints at different G-levels)?
  (2) Are all edges in G \ T cross-level?
"""
import sys, os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import networkx as nx
from collections import defaultdict
from triangulation_gen import enumerate_all_triangulations
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 edges_summary(G, T, levels):
    """Compute edges only in G, only in T, and check cross-level status
    based on G-levels."""
    G_edges = set(frozenset(e) for e in G.edges())
    T_edges = set(frozenset(e) for e in T.edges())
    only_in_G = G_edges - T_edges
    only_in_T = T_edges - G_edges
    def is_cross_level(e):
        u, v = tuple(e)
        return levels.get(u, -1) != levels.get(v, -1)
    in_G_cross = sum(1 for e in only_in_G if is_cross_level(e))
    in_G_same = len(only_in_G) - in_G_cross
    in_T_cross = sum(1 for e in only_in_T if is_cross_level(e))
    in_T_same = len(only_in_T) - in_T_cross
    # Also categorize by level diff
    def level_diff(e):
        u, v = tuple(e)
        return abs(levels.get(u, -1) - levels.get(v, -1))
    G_diffs = sorted([level_diff(e) for e in only_in_G])
    T_diffs = sorted([level_diff(e) for e in only_in_T])
    return {
        'only_in_G': len(only_in_G),
        'only_in_T': len(only_in_T),
        'in_G_cross_level': in_G_cross,
        'in_G_same_level': in_G_same,
        'in_T_cross_level': in_T_cross,
        'in_T_same_level': in_T_same,
        'G_diffs': G_diffs,
        'T_diffs': T_diffs,
    }


def run_test(n, max_iso=20):
    if n >= 11:
        isos = enumerate_all_triangulations_fast(n)
    else:
        isos = enumerate_all_triangulations(n)
    print(f"\n=== n={n} ({len(isos)} iso-classes) ===")
    total = 0
    all_cross = 0
    some_same = 0
    examples_with_same = []
    for idx, G in enumerate(isos[:max_iso] if max_iso else isos):
        ip, emb = nx.check_planarity(G)
        if not ip:
            continue
        for kind, lab, source_set in level_sources(G, emb):
            levels = compute_levels(G, source_set)
            count = 0
            for Gp in simple_level_resolutions(G, source_set):
                count += 1
                if count > 8:
                    break
                if not is_triangulation(Gp, n):
                    continue
                summary = edges_summary(G, Gp, levels)
                total += 1
                if summary['in_G_same_level'] == 0 and \
                   summary['in_T_same_level'] == 0:
                    all_cross += 1
                else:
                    some_same += 1
                    if len(examples_with_same) < 3:
                        examples_with_same.append({
                            'iso': idx,
                            'source': (kind, lab),
                            **summary,
                        })
    print(f"  total (G, S, choice) triples tested: {total}")
    print(f"  all symdiff edges cross-level: {all_cross}")
    print(f"  some same-level: {some_same}")
    if examples_with_same:
        print(f"  examples with same-level edges (first {len(examples_with_same)}):")
        for ex in examples_with_same:
            print(f"    iso={ex['iso']} src={ex['source']}: "
                  f"|symdiff|={ex['only_in_G']}+{ex['only_in_T']}, "
                  f"same-level G={ex['in_G_same_level']}/T={ex['in_T_same_level']}, "
                  f"level diffs G={ex['G_diffs']}, T={ex['T_diffs']}")


if __name__ == "__main__":
    ns = [int(x) for x in sys.argv[1:]] if len(sys.argv) > 1 else [7, 8, 9]
    for n in ns:
        run_test(n, max_iso=30)
