#!/usr/bin/env python3
"""W-1 execution driver. Mutation arms with proven reverts.

Anchors are built from the FILE IN HAND and translated to ITS eol before the
count: `git checkout --` re-materializes a file with git's eol conversion, so an
anchor written LF can count 0 in a CRLF working file and the arm would mutate
blind (hertz, 2026-09-11). Every revert is proven by oid AND byte shape,
because `git hash-object` and `git diff --quiet` both NORMALIZE line endings and
would report an identical blob over bytes that just changed.
"""
import subprocess, sys, pathlib, json, time

LANE = pathlib.Path(r"C:/Users/decid/Documents/projects/spt-core/.worktrees/304-w2-bootstrap-tcp")
TARGET = "crates/spt-daemon/src/firewall.rs"
OUT = pathlib.Path(r"C:/Users/decid/AppData/Local/Temp/claude/C--Users-decid-Documents-projects-spt-core/d4e86801-8514-4527-94e1-84f30b0d84d9/scratchpad")


def run(args, **kw):
    """Direct capture. No pipeline, so the status is the command's own."""
    p = subprocess.run(args, cwd=LANE, capture_output=True, text=True, **kw)
    return p.returncode, p.stdout, p.stderr


def shape(path):
    raw = (LANE / path).read_bytes()
    crlf = raw.count(b"\r\n")
    bare_lf = raw.count(b"\n") - crlf
    rc, out, _ = run(["git", "hash-object", path])
    rc2, head, _ = run(["git", "rev-parse", f"HEAD:{path}"])
    return {
        "bytes": len(raw),
        "crlf": crlf,
        "bare_lf": bare_lf,
        "work_blob": out.strip(),
        "head_blob": head.strip(),
        "clean": out.strip() == head.strip(),
    }


def mutate(path, anchor_lf, replacement_lf):
    """Translate anchor to the file's ACTUAL eol, require EXACTLY one match."""
    raw = (LANE / path).read_bytes()
    eol = b"\r\n" if raw.count(b"\r\n") > 0 else b"\n"
    anchor = anchor_lf.encode().replace(b"\n", eol)
    repl = replacement_lf.encode().replace(b"\n", eol)
    n = raw.count(anchor)
    eol_name = "CRLF" if eol == b"\r\n" else "LF"
    if n != 1:
        return False, f"ANCHOR COUNT {n} (need exactly 1) with eol={eol_name}"
    (LANE / path).write_bytes(raw.replace(anchor, repl, 1))
    return True, "mutated"


def revert(path):
    run(["git", "checkout", "--", path])
    return shape(path)


def nextest(label, extra):
    args = ["cargo", "nextest", "run", "-p", "spt-daemon", "--lib", "--no-fail-fast"] + extra
    t0 = time.time()
    rc, out, err = run(args)
    body = out + err
    (OUT / f"w1-{label}.raw").write_text(body, encoding="utf-8", errors="replace")
    return {"label": label, "exit": rc, "secs": round(time.time() - t0, 1), "body": body}


def summary_line(body):
    for line in body.splitlines():
        if "tests run:" in line or "test run:" in line:
            return line.strip()
    return "NO SUMMARY LINE"
