#!/usr/bin/env python3
"""Find exact collapse depth for each strand count via DFS.

Uses depth-first search with backtracking to find the maximum depth
reachable before the Coprimality Guard kills all paths.
"""

import sys, math, time, json
sys.path.insert(0, '/home/allaun/SilverSight/scripts')
from full_chiral_dag import *

A0 = [0, 1, 2, 3, 4, 5]
S = 5

def find_collapse_depth(n_strands):
    pairs = chiral_pairs(n_strands=n_strands, band_gap=0, base_prime_offset=10)
    root = DAGNode(pairs, A0, S, depth=0)
    visited = {root.modulus_hash(): root}

    max_depth = 0
    nodes_created = 1
    coprimality_prunes = 0
    energy_prunes = 0
    dedup = 0

    # DFS stack: (node, strand_idx, dir_idx)
    # We try crossings in a systematic order: strand 0..n-1, both directions
    stack = [(root, 0, 0)]
    path = [root]

    start = time.time()
    report_at = 0

    while stack:
        node, s, d_idx = stack[-1]

        # If we exhausted all crossings at this node, backtrack
        if s >= n_strands:
            stack.pop()
            path.pop()
            continue

        direction = 'over' if d_idx == 0 else 'under'

        # Move to next crossing at this node
        if d_idx == 1:
            stack[-1] = (node, s + 1, 0)
        else:
            stack[-1] = (node, s, d_idx + 1)

        result = coupled_crossing(node.pairs, s, direction, A0, S)
        if result is None:
            coprimality_prunes += 1
            continue

        new_pairs, new_energy = result
        child = DAGNode(pairs=new_pairs, A0=A0, S=S, depth=node.depth + 1)

        if child.energy > node.energy + 1e-9:
            energy_prunes += 1
            continue

        h = child.modulus_hash()
        if h in visited:
            existing = visited[h]
            if existing.energy <= child.energy:
                dedup += 1
                continue

        visited[h] = child
        nodes_created += 1
        path.append(child)
        stack.append((child, 0, 0))

        if child.depth > max_depth:
            max_depth = child.depth

        # Report
        if nodes_created > report_at:
            report_at = nodes_created + 50000
            elapsed = time.time() - start
            print(
                f"  [{elapsed:.0f}s] n_strands={n_strands} "
                f"depth={max_depth} "
                f"nodes={nodes_created} "
                f"copr={coprimality_prunes} "
                f"en={energy_prunes} "
                f"dedup={dedup}"
            )
            sys.stdout.flush()

        if max_depth > 500:
            return {
                'n_strands': n_strands,
                'collapse_depth': max_depth,
                'collapsed': False,
                'reason': 'depth_limit',
                'nodes_created': nodes_created,
                'coprimality_prunes': coprimality_prunes,
                'energy_prunes': energy_prunes,
                'dedup': dedup,
                'runtime_s': round(time.time() - start, 2),
                'max_modulus': max(m for n in visited.values() for m in n.moduli),
            }

    # DFS exhausted — we've found the deepest reachable state
    return {
        'n_strands': n_strands,
        'collapse_depth': max_depth,
        'collapsed': True,
        'reason': 'dfs_exhausted',
        'nodes_created': nodes_created,
        'coprimality_prunes': coprimality_prunes,
        'energy_prunes': energy_prunes,
        'dedup': dedup,
        'runtime_s': round(time.time() - start, 2),
        'max_modulus': max(m for n in visited.values() for m in n.moduli) if visited else 0,
    }

results = []
for n_strands in [2, 3, 4, 5, 6, 7, 8]:
    print(f"\n{'=' * 60}")
    print(f"Strands: {n_strands}")
    print(f"{'=' * 60}")
    r = find_collapse_depth(n_strands)
    results.append(r)
    status = "COLLAPSED" if r['collapsed'] else "NO COLLAPSE"
    print(f"\n  → {status} at depth {r['collapse_depth']}")
    print(f"    nodes={r['nodes_created']}, runtime={r['runtime_s']}s")
    print(f"    copr={r['coprimality_prunes']}, en={r['energy_prunes']}")
    print(f"    max_mod={r['max_modulus']}")

print(f"\n{'=' * 60}")
print("FINAL RESULTS")
print(f"{'=' * 60}")
for r in results:
    print(f"  {r['n_strands']} strands: "
          f"{'COLLAPSED' if r['collapsed'] else 'LIMIT'}"
          f" @ depth {r['collapse_depth']} "
          f"({r['nodes_created']} nodes, {r['runtime_s']}s)")
