"""Chunked coverage analysis. Processes a slice of sources per invocation.
Saves and resumes progress via a pickle file. Run repeatedly until done."""
import sys; sys.path.insert(0, '/home/claude/build')
import networkx as nx
import pickle
import time
import os
from collections import defaultdict
from level_cycles import all_level_resolutions
from triangulation_gen import enumerate_all_triangulations

STATE_FILE = '/tmp/n11_state.pkl'
N = 11
CHUNK_SECONDS = 200  # how long this invocation should work

def degree_seq(G):
    return tuple(sorted((d for _, d in G.degree()), reverse=True))

def min_degree(G):
    return min(d for _, d in G.degree())

# Load or initialize state
if os.path.exists(STATE_FILE):
    with open(STATE_FILE, 'rb') as f:
        state = pickle.load(f)
    print(f"Resumed. Done: {state['next_idx']}/{state['total']}")
else:
    t0 = time.time()
    print("Initializing...")
    reps = enumerate_all_triangulations(N)
    print(f"  {len(reps)} iso-classes in {time.time()-t0:.1f}s")
    deg_b = defaultdict(list); hash_b = defaultdict(list)
    for i, G in enumerate(reps):
        ds = degree_seq(G)
        deg_b[ds].append(i)
        hash_b[(ds, nx.weisfeiler_lehman_graph_hash(G))].append((i, G))
    state = {
        'reps': reps,
        'deg_b': dict(deg_b),
        'hash_b': dict(hash_b),
        'produced_by': {},
        'next_idx': 0,
        'total': len(reps),
    }
    with open(STATE_FILE, 'wb') as f:
        pickle.dump(state, f)
    print(f"Init done in {time.time()-t0:.1f}s")

reps = state['reps']
deg_b = state['deg_b']
hash_b = state['hash_b']
produced_by = state['produced_by']

def fast_iso(G):
    ds = degree_seq(G)
    if ds not in deg_b: return -1
    h = nx.weisfeiler_lehman_graph_hash(G)
    for idx, rep in hash_b.get((ds, h), []):
        if nx.is_isomorphic(G, rep): return idx
    return -1

start = time.time()
i = state['next_idx']
processed = 0
while i < state['total'] and time.time() - start < CHUNK_SECONDS:
    G = reps[i]
    s = set()
    for Gp, _, _, _ in all_level_resolutions(G):
        c = fast_iso(Gp)
        if c >= 0: s.add(c)
    produced_by[i] = s
    i += 1
    processed += 1

state['next_idx'] = i
state['produced_by'] = produced_by
with open(STATE_FILE, 'wb') as f:
    pickle.dump(state, f)

elapsed = time.time() - start
print(f"Processed {processed} sources in {elapsed:.0f}s. "
      f"Now at {i}/{state['total']}.")

if i >= state['total']:
    print("\n=== ALL DONE ===")
    all_prod = set().union(*produced_by.values())
    uncov = set(range(state['total'])) - all_prod
    md4_idx = set(j for j, G in enumerate(reps) if min_degree(G) >= 4)
    md4_reached = set().union(*(produced_by[k] & md4_idx
                                for k in range(state['total'])))
    md4_uncov = md4_idx - md4_reached
    print(f"General: {state['total'] - len(uncov)}/{state['total']} reached")
    if uncov:
        for j in sorted(uncov)[:20]:
            print(f"  uncovered T{j}: degree {degree_seq(reps[j])}")
    print(f"md4: {len(md4_idx) - len(md4_uncov)}/{len(md4_idx)} reached")
    if md4_uncov:
        for j in sorted(md4_uncov):
            print(f"  uncovered md4 T{j}: degree {degree_seq(reps[j])}")
