"""Visualize the Phase 2 failure case: n=10, tri[13], face source (1,2,4),
L_1. Highlights the interior odd triangle (0,5,6) that the algorithm misses,
and labels each edge by its (apex, apex) level pair."""
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
from depth_monovariant_check import (
    faces_of_subgraph, canonical_face, edges_of_face,
    apex_levels_for_edge, identify_outer_face,
)
from viz_cycling import layout_outerplanar


def main():
    tris = enumerate_all_triangulations(10)
    G = tris[13]
    ip, emb = nx.check_planarity(G)
    source = {1, 2, 4}
    levels = compute_levels(G, source)
    by_level = defaultdict(list)
    for v, lv in levels.items():
        by_level[lv].append(v)
    nodes_k = by_level[1]
    k = 1

    faces = faces_of_subgraph(G, emb, nodes_k)
    outer = identify_outer_face(faces, G, emb, levels, k)
    outer_face = next(f for f in faces if canonical_face(f) == outer)
    outer_edges = set(frozenset(list(e)) for e in edges_of_face(outer_face))

    pos = layout_outerplanar(nodes_k, outer_face, G)
    fig, ax = plt.subplots(1, 1, figsize=(10, 10))

    # Shade odd faces
    for f in faces:
        cf = canonical_face(f)
        if cf == outer:
            continue
        if len(f) % 2 != 1:
            continue
        incident = any(frozenset([a, b]) in outer_edges
                       for (a, b) in edges_of_face(f))
        if incident:
            color = '#ffd45a'  # outer-incident odd (Phase 2 targets)
            label = "Phase 2 flips here"
            alpha = 0.65
        else:
            color = '#ff5555'  # interior odd (algorithm misses)
            label = "MISSED"
            alpha = 0.7
        poly = plt.Polygon([pos[v] for v in f], color=color, alpha=alpha,
                            zorder=1)
        ax.add_patch(poly)
        cx, cy = np.mean([pos[v] for v in f], axis=0)
        ax.text(cx, cy, f"{tuple(f)}\nlen={len(f)} (odd)\n{label}",
                fontsize=9, ha='center', va='center', zorder=3,
                fontweight='bold')

    # Edges with apex-pair labels
    Lk = G.subgraph(nodes_k)
    for u, v in Lk.edges():
        ax.plot([pos[u][0], pos[v][0]], [pos[u][1], pos[v][1]],
                color='black', linewidth=1.6, zorder=2)
        result = apex_levels_for_edge(G, emb, u, v, levels)
        if result is None:
            continue
        (la, lb), (w, x) = result
        mid = (pos[u] + pos[v]) / 2
        # Apex pair relative to k
        pair = tuple(sorted([la - k, lb - k]))
        if pair == (0, 0):
            apex_color = '#cc0000'   # intra-level (k,k)
            apex_label = '(k,k)'
        elif pair == (-1, 0) or pair == (-1, 1):
            apex_color = '#006400'   # cross-parity / free
            apex_label = f'(k{la-k:+d},k{lb-k:+d})'
        else:
            apex_color = '#0044aa'
            apex_label = f'(k{la-k:+d},k{lb-k:+d})'
        ax.text(mid[0], mid[1], apex_label, fontsize=7,
                ha='center', va='center', color=apex_color,
                bbox=dict(facecolor='white', edgecolor='none', alpha=0.85,
                           pad=1.0), zorder=4)

    for v in nodes_k:
        ax.plot(pos[v][0], pos[v][1], 'o', color='black', markersize=14,
                zorder=4)
        ax.text(pos[v][0] * 1.13, pos[v][1] * 1.13, str(v), fontsize=16,
                ha='center', va='center', color='blue', zorder=5,
                fontweight='bold')

    # Highlight the cross-level edge of (0,5,6) that would resolve it.
    # Find the (k,k+1) or (k,k-1) or (k-1,k+1) edge of triangle (0,5,6).
    interior_face = (0, 5, 6)
    for (u, v) in edges_of_face(interior_face):
        if not Lk.has_edge(u, v):
            continue
        result = apex_levels_for_edge(G, emb, u, v, levels)
        if result is None:
            continue
        (la, lb), _ = result
        pair = tuple(sorted([la - k, lb - k]))
        if pair != (0, 0):
            ax.plot([pos[u][0], pos[v][0]], [pos[u][1], pos[v][1]],
                    color='green', linewidth=4.0, zorder=2.5, linestyle=':')
            break

    ax.set_aspect('equal')
    ax.axis('off')
    ax.set_xlim(-1.3, 1.3)
    ax.set_ylim(-1.4, 1.3)
    title = (
        "Phase 2 gap: n=10, tri[13], source face (1,2,4), $L_1$\n"
        "Yellow = outer-incident odd faces (Phase 2 flips one edge each).\n"
        "Red = interior odd face (0,5,6) — Phase 2 misses, Phase 1 ignores "
        "(not tricky-everywhere — its (0,6) edge has apex (k,k+1)).\n"
        "Green dotted: the cross-level edge (0,6) whose flip would resolve "
        "(0,5,6) but is unreached by either phase."
    )
    fig.suptitle(title, fontsize=12)
    plt.tight_layout(rect=[0, 0, 1, 0.90])
    out = os.path.join(os.path.dirname(os.path.abspath(__file__)),
                        '..', 'phase2_gap_visualization.png')
    plt.savefig(out, dpi=130, bbox_inches='tight')
    print(f"saved: {out}")


if __name__ == "__main__":
    main()
