"""Plot a missing iso-class (not reachable as a simple level resolution)
with its BFS levels from a face source highlighted, to show why it cannot
be a simple level resolution."""
import sys, os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import networkx as nx
import numpy as np
import matplotlib.pyplot as plt
from collections import defaultdict
from triangulation_gen import enumerate_all_triangulations
from level_cycles import compute_levels, level_sources, get_all_faces


def main():
    n, target_idx = 7, 2
    isos = enumerate_all_triangulations(n)
    T = isos[target_idx]
    print(f"iso[{target_idx}] at n={n}:")
    print(f"  edges: {sorted(T.edges())}")
    ds = sorted([T.degree(v) for v in T.nodes()], reverse=True)
    print(f"  degree seq: {ds}")

    ip, emb = nx.check_planarity(T)
    # Try every face source and check if T is self-resolution from that source
    faces = get_all_faces(emb)
    triangle_faces = [f for f in faces if len(f) == 3]
    print(f"  triangle faces: {triangle_faces}")

    # Pick a face source for visualization (e.g., the first one)
    source_face = triangle_faces[0]
    source_set = set(source_face)
    levels = compute_levels(T, source_set)
    print(f"  source face: {source_face}")
    print(f"  levels: {dict(sorted(levels.items()))}")

    # Compute parity subgraph status
    even_nodes = [v for v in T.nodes() if levels.get(v, 0) % 2 == 0]
    odd_nodes = [v for v in T.nodes() if levels.get(v, 0) % 2 == 1]
    even_sub = T.subgraph(even_nodes)
    odd_sub = T.subgraph(odd_nodes)
    print(f"  even-level vertices: {sorted(even_nodes)}")
    print(f"  odd-level vertices: {sorted(odd_nodes)}")
    print(f"  even subgraph bipartite: {nx.is_bipartite(even_sub)}")
    print(f"  odd subgraph bipartite: {nx.is_bipartite(odd_sub)}")

    pos = nx.planar_layout(T)
    # Rotate/scale so source face is on the outside.
    src_pts = np.array([pos[v] for v in source_face])
    centroid = np.mean(list(pos.values()), axis=0)
    for v in pos:
        pos[v] = (pos[v] - centroid) * 1.5

    fig, axes = plt.subplots(1, 3, figsize=(18, 6))

    # Color vertices by level
    level_colors = ['#3b82f6', '#f59e0b', '#10b981', '#ef4444', '#8b5cf6']

    def draw_subgraph_panel(ax, subset_nodes, title, hilite_edges=None):
        node_colors = [level_colors[levels[v] % len(level_colors)]
                       for v in T.nodes()]
        # Draw all edges in light gray
        nx.draw_networkx_edges(T, pos, ax=ax, edge_color='#cbd5e1', width=1.0,
                                alpha=0.6)
        # Draw subset-induced edges in dark
        sub = T.subgraph(subset_nodes)
        nx.draw_networkx_edges(sub, pos, ax=ax, edge_color='#1f2937',
                                width=2.5)
        # Highlight any odd cycles
        if hilite_edges:
            for (u, v) in hilite_edges:
                ax.plot([pos[u][0], pos[v][0]], [pos[u][1], pos[v][1]],
                        color='#ef4444', linewidth=4.0, zorder=2,
                        alpha=0.85)
        # Draw vertices
        node_alpha = [1.0 if v in subset_nodes else 0.25 for v in T.nodes()]
        for v in T.nodes():
            color = node_colors[list(T.nodes()).index(v)]
            a = 1.0 if v in subset_nodes else 0.25
            ax.scatter(pos[v][0], pos[v][1], c=color, s=550,
                       edgecolors='black', linewidths=1.2, zorder=3,
                       alpha=a)
            ax.text(pos[v][0], pos[v][1], f"{v}\nL{levels[v]}",
                    fontsize=10, ha='center', va='center',
                    fontweight='bold', zorder=4,
                    alpha=a)
        ax.set_title(title, fontsize=11)
        ax.set_aspect('equal')
        ax.axis('off')
        ax.set_xlim(-1.3, 1.3)
        ax.set_ylim(-1.3, 1.3)

    # Panel 1: full graph with source face highlighted
    src_pos = [pos[v] for v in source_face]
    poly = plt.Polygon(src_pos, color='#fef3c7', alpha=0.7, zorder=0.5)
    axes[0].add_patch(poly)
    draw_subgraph_panel(
        axes[0], set(T.nodes()),
        f"Full graph $T$ with face source $S = \\{{{','.join(map(str, sorted(source_face)))}\\}}$"
    )
    axes[0].set_title(
        f"Full graph $T$ with face source $S = \\{{{','.join(map(str, sorted(source_face)))}\\}}$\n"
        f"Yellow = source face (level 0)",
        fontsize=11
    )

    # Panel 2: even-parity subgraph
    even_edges = [(u, v) for u, v in even_sub.edges()]
    # Find odd cycle in even subgraph
    odd_cycle_even = None
    try:
        odd_cycle_even = nx.find_cycle(even_sub, orientation='ignore')
        # Filter only odd cycles
        if odd_cycle_even and len(odd_cycle_even) % 2 == 0:
            odd_cycle_even = None
    except nx.NetworkXNoCycle:
        odd_cycle_even = None
    hilite_e = [(u, v) for u, v, *_ in odd_cycle_even] if odd_cycle_even else None
    draw_subgraph_panel(
        axes[1], set(even_nodes),
        f"Even-parity subgraph $E_{{T,S}}(T)$\n"
        f"Vertices: $\\{{{','.join(map(str, sorted(even_nodes)))}\\}}$ "
        f"(L0 $\\cup$ L2 $\\cup\\ldots$)\n"
        f"Bipartite: {nx.is_bipartite(even_sub)} "
        f"{'(odd cycle highlighted)' if hilite_e else ''}",
        hilite_edges=hilite_e
    )

    # Panel 3: odd-parity subgraph
    odd_cycle_odd = None
    try:
        odd_cycle_odd = nx.find_cycle(odd_sub, orientation='ignore')
        if odd_cycle_odd and len(odd_cycle_odd) % 2 == 0:
            odd_cycle_odd = None
    except nx.NetworkXNoCycle:
        odd_cycle_odd = None
    hilite_o = [(u, v) for u, v, *_ in odd_cycle_odd] if odd_cycle_odd else None
    draw_subgraph_panel(
        axes[2], set(odd_nodes),
        f"Odd-parity subgraph $O_{{T,S}}(T)$\n"
        f"Vertices: $\\{{{','.join(map(str, sorted(odd_nodes)))}\\}}$ "
        f"(L1 $\\cup$ L3 $\\cup\\ldots$)\n"
        f"Bipartite: {nx.is_bipartite(odd_sub)} "
        f"{'(odd cycle highlighted)' if hilite_o else ''}",
        hilite_edges=hilite_o
    )

    plt.suptitle(
        f"$n = {n}$ iso[{target_idx}]: a triangulation that is NOT reached as a simple level resolution.\n"
        f"From any face source the source-face triangle creates an odd cycle in the even-parity subgraph; "
        f"the algorithm of \\S5 cannot remove it without destroying other structure.",
        fontsize=12, y=1.02
    )
    plt.tight_layout()
    out = os.path.join(os.path.dirname(os.path.abspath(__file__)),
                        '..', f'missing_iso_n{n}_idx{target_idx}.png')
    plt.savefig(out, dpi=130, bbox_inches='tight')
    print(f"saved: {out}")


if __name__ == "__main__":
    main()
