"""For each chord-apex+Kempe colouring, walk K_b and record:

  - The Heawood-flip count along K_b (= # consecutive (v_k, v_{k+1})
    pairs with h_phi(v_k) != h_phi(v_{k+1})).
  - The Heawood-flip count is at least 4 empirically (n >= 14).

For colourings achieving the minimum flip count on K_b, dump the
sequence of (Heawood, edge-position-on-cycle) so we can see *where*
the flips fall. Are they at the merged edge?  At spike?  At specific
structural locations?  Or spread out?

Run with: sage experiments/check_min_flip_structure.py
"""
import os
import sys
import time

from sage.all import Graph
from sage.graphs.graph_generators import graphs

HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)

from check_conj_final_scaled import (
    apply_reduction,
    proper_3_edge_colorings,
    matches_chord_apex_kempe,
    trace_kempe_cycle,
    edge_idx,
)
from check_heawood_on_kempe import dual_of, heawood_numbers


def test_one(D):
    D.is_planar(set_embedding=True)
    n_col = 0
    # For each Kempe cycle (K_b, K_c), record (flip_count, length).
    flips_kb = {}    # flip_count -> count
    # For minimum-flip colourings, record the Heawood pattern + edge colours.
    min_patterns = []   # tuples (flip_count, length, pattern, colours)

    cur_min = None
    for face in D.faces():
        if len(face) != 5: continue
        for i_red in range(5):
            res = apply_reduction(D, face, i_red, 9999)
            if res is None: continue
            H = res['H']; named = res['named']
            H.is_planar(set_embedding=True)
            edges, colorings = proper_3_edge_colorings(H)
            cand = [c for c in colorings
                    if matches_chord_apex_kempe(edges, c, named)]
            for col in cand:
                n_col += 1
                try:
                    h = heawood_numbers(H, edges, col)
                except RuntimeError:
                    continue
                merged_idx = edge_idx(edges, named['merged'])
                a = col[merged_idx]
                bs = [c for c in range(3) if c != a]
                # Only check K_b for this script
                walk_b = trace_kempe_cycle(edges, col, merged_idx, (a, bs[0]))
                L = len(walk_b)
                h_seq = [h[walk_b[k][1]] for k in range(L)]
                # edge sequence: walk_b[k][0] is the edge index of the K_b
                # edge ENTERING walk_b[k][1].
                e_colors = [col[walk_b[k][0]] for k in range(L)]
                # Flip count: pairs (h_seq[k], h_seq[(k+1) % L]) that differ.
                flips = sum(1 for k in range(L) if h_seq[k] != h_seq[(k+1) % L])
                flips_kb[flips] = flips_kb.get(flips, 0) + 1
                if cur_min is None or flips < cur_min:
                    cur_min = flips
                    min_patterns = [(flips, L, tuple(h_seq), tuple(e_colors))]
                elif flips == cur_min and len(min_patterns) < 5:
                    min_patterns.append((flips, L, tuple(h_seq), tuple(e_colors)))
    return n_col, flips_kb, cur_min, min_patterns


def main(max_n=18, time_budget_per_n=1800):
    print(f"Min-flip Heawood pattern on K_b, n in [12, {max_n}]\n")
    overall_min = None
    overall_min_examples = []
    for n in range(12, max_n + 1):
        start = time.time()
        try:
            triangulations = list(graphs.triangulations(n, minimum_degree=5))
        except Exception as ex:
            print(f"n={n}: cannot enumerate ({ex})")
            continue
        n_col_n = 0
        flips_n = {}
        n_min = None
        min_examples = []
        for tri_idx, G in enumerate(triangulations):
            if time.time() - start > time_budget_per_n:
                print(f"  n={n}: timeout at tri {tri_idx}/{len(triangulations)}")
                break
            G.is_planar(set_embedding=True)
            D = dual_of(G)
            ni, fi, cm, exs = test_one(D)
            n_col_n += ni
            for k, v in fi.items(): flips_n[k] = flips_n.get(k, 0) + v
            if cm is not None:
                if n_min is None or cm < n_min:
                    n_min = cm
                    min_examples = list(exs)
                elif cm == n_min and len(min_examples) < 5:
                    min_examples.extend(exs[:5 - len(min_examples)])
        elapsed = time.time() - start
        print(f"n={n}: {n_col_n} col., flip_dist on K_b: "
              f"{sorted(flips_n.items())}, min flip: {n_min}  [{elapsed:.0f}s]")
        if min_examples:
            print(f"  Examples of min-flip K_b (flip count = {n_min}):")
            for ex in min_examples[:3]:
                flips_x, L, h_pat, e_cols = ex
                print(f"    L={L}, flips={flips_x}")
                print(f"      h sequence : {h_pat}")
                print(f"      colour seq : {e_cols}")
        sys.stdout.flush()
        if overall_min is None or (n_min is not None and n_min < overall_min):
            overall_min = n_min
            overall_min_examples = min_examples
    print()
    print(f"Overall minimum flip count on K_b across all tested: {overall_min}")


if __name__ == '__main__':
    main()
