"""Test Conjecture 5.26 (the strengthening, clauses 1–4) on
chord-apex+Kempe colourings of reduced duals for n in [21, 22].

For each (G, v, i, φ) chord-apex+Kempe colouring, enumerate every
witness (F, e_1, e_2) satisfying clauses 1–3, then check whether
at least one such witness also satisfies clause 4.  The conjecture
states: SOME witness for each colouring passes clause 4.

A "passing colouring" = ∃ witness with clauses 1–4 all satisfied.
A "failing colouring" = ALL its witnesses fail clause 4.

The known result for n ≤ 20 is 142,812 / 142,812 (pass) via the
existing check_conj_final_scaled.py.  This script extends to
n = 21 and n = 22, with incremental JSONL output and a
10-minute PROGRESS heartbeat.

Run with: sage experiments/test_conj_5_26_n_21_22.py [n_min] [n_max]
"""
import json
import os
import sys
import time
from pathlib import Path

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,
    find_all_36_witnesses,
    check_clause_4,
)
from check_heawood_on_kempe import dual_of


RESULTS_FILE = Path(HERE) / '..' / 'test_conj_5_26_n_21_22_results.jsonl'


def test_one(D):
    """For dual D, iterate reductions + chord-apex+Kempe colourings.
    Returns counters and any failing examples."""
    D.is_planar(set_embedding=True)
    n_col_with_witness = 0
    n_pass = 0
    n_fail = 0
    failures = []   # only failing colourings get recorded
    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:
                witnesses = find_all_36_witnesses(H, edges, list(col), named)
                if not witnesses:
                    continue
                n_col_with_witness += 1
                ok = False
                for w in witnesses:
                    try:
                        if check_clause_4(H, edges, list(col), named, w):
                            ok = True
                            break
                    except Exception:
                        # Defensive: treat exception as "this witness fails"
                        # and keep trying others.
                        pass
                if ok:
                    n_pass += 1
                else:
                    n_fail += 1
                    failures.append({
                        'i_red': i_red,
                        'n_witnesses': len(witnesses),
                    })
    return n_col_with_witness, n_pass, n_fail, failures


def write_result(record):
    with open(RESULTS_FILE, 'a') as f:
        f.write(json.dumps(record) + '\n')
        f.flush()


def main(n_min=21, n_max=22, progress_every_seconds=600):
    started_at = time.time()
    print(f"Testing Conjecture 5.26 (clauses 1–4) on n in [{n_min}, {n_max}]")
    print(f"Writing incremental results to {RESULTS_FILE}")
    print(f"PROGRESS lines every {progress_every_seconds}s")
    sys.stdout.flush()
    if RESULTS_FILE.exists():
        bak = RESULTS_FILE.with_suffix(RESULTS_FILE.suffix + '.bak')
        RESULTS_FILE.rename(bak)
        print(f"Previous results moved to {bak}")
    RESULTS_FILE.touch()

    grand_col = 0
    grand_pass = 0
    grand_fail = 0
    last_progress = time.time()

    for n in range(n_min, n_max + 1):
        n_start = time.time()
        print(f"\n=== n_G = {n} ===")
        sys.stdout.flush()
        try:
            triangulations_iter = graphs.triangulations(n, minimum_degree=5)
        except Exception as ex:
            print(f"n={n}: cannot enumerate ({ex})")
            continue
        n_col_n = 0
        n_pass_n = 0
        n_fail_n = 0
        tri_idx = 0
        for G in triangulations_iter:
            G.is_planar(set_embedding=True)
            D = dual_of(G)
            tri_start = time.time()
            nc, np_, nf, fails = test_one(D)
            tri_elapsed = time.time() - tri_start
            n_col_n += nc
            n_pass_n += np_
            n_fail_n += nf
            record = {
                'n_G': n, 'tri_idx': tri_idx,
                'n_col_with_witness': nc,
                'n_pass': np_,
                'n_fail': nf,
                'tri_elapsed_s': round(tri_elapsed, 2),
                'wall_elapsed_s': round(time.time() - started_at, 2),
                'failures': fails,
            }
            write_result(record)
            now = time.time()
            if now - last_progress >= progress_every_seconds:
                wall_min = (now - started_at) / 60
                print(f"PROGRESS: wall {wall_min:.1f} min; "
                      f"n={n} tri {tri_idx+1} done ({tri_elapsed:.1f}s); "
                      f"running totals @n={n}: col={n_col_n}, "
                      f"pass={n_pass_n}, fail={n_fail_n}")
                sys.stdout.flush()
                last_progress = now
            tri_idx += 1

        n_elapsed = time.time() - n_start
        summary = {
            'type': 'n_summary',
            'n_G': n, 'n_tri': tri_idx,
            'n_col_with_witness': n_col_n,
            'n_pass': n_pass_n,
            'n_fail': n_fail_n,
            'n_elapsed_s': round(n_elapsed, 2),
            'wall_elapsed_s': round(time.time() - started_at, 2),
        }
        write_result(summary)
        print(f"\nn={n}: {tri_idx} triangulations, {n_col_n} colourings "
              f"with witnesses [{n_elapsed:.0f}s]")
        print(f"  pass clause 4: {n_pass_n}")
        print(f"  fail clause 4: {n_fail_n}")
        sys.stdout.flush()

        grand_col += n_col_n
        grand_pass += n_pass_n
        grand_fail += n_fail_n

    total_elapsed = time.time() - started_at
    final = {
        'type': 'grand_summary',
        'n_min': n_min, 'n_max': n_max,
        'grand_col_with_witness': grand_col,
        'grand_pass': grand_pass,
        'grand_fail': grand_fail,
        'wall_elapsed_s': round(total_elapsed, 2),
    }
    write_result(final)
    print("\n" + "=" * 70)
    print(f"DONE: n in [{n_min}, {n_max}], {grand_col} colourings with "
          f"clause-1-3 witness in {total_elapsed/60:.1f} min")
    print(f"  pass: {grand_pass}")
    print(f"  fail: {grand_fail}")
    if grand_fail == 0 and grand_col > 0:
        print(f"✓ Conjecture 5.26 EMPIRICALLY EXTENDED to n_G ≤ {n_max}")


if __name__ == '__main__':
    n_min = int(sys.argv[1]) if len(sys.argv) > 1 else 21
    n_max = int(sys.argv[2]) if len(sys.argv) > 2 else 22
    main(n_min=n_min, n_max=n_max)
