"""WINDOW 3 at 71caedd4: the fixture pre-build, then producers 4 and 3.

Order matters: mock-session-fixture FIRST, because producer 4's previous
attempt measured nothing without it. Sequential, one attempt each, no retry.
A red does not stop the sequence - a cancelled later producer is a labelled
hole, and the window is the allocation.

Reported against predictions-window3.txt (14-19), filed before any build.
"""
import io, json, os, subprocess, sys, time
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import lane_producers as P
import lane_run as L

# Window 3 ran the three defaults at 71caedd4. Later windows pass their own
# label list and tag, so one driver serves them all rather than a new file per
# window: `python window3.py <tag> <label> [label ...]`.
TAG = sys.argv[1] if len(sys.argv) > 1 else "71caedd4"
REPORT = os.path.join(L.EVID, "window3-%s.json" % TAG)
FIXTURE_BIN = os.path.join(L.POOL, "debug", "mock-session.exe")
DEFAULT = ["mock-session-fixture", "attach-push-run", "webserve-e2e-run"]
SEQUENCE = sys.argv[2:] or DEFAULT


# THE START IS PART OF THE LAUNCH, not a message I remember to send around it.
# Window 3 opened at 10:16:42Z and closed 32 s later; my START never went out,
# and doyle - whose only view of the box IS the START - granted todlando a
# concurrent compile believing my window had not opened. The overlap went
# unmanaged. A 32 s window is exactly the one short enough to announce late and
# never notice, so the announcement now cannot lag the launch: no START file,
# no producers.
START_FILE = os.environ.get("HERTZ_START_FILE")
PEERS = ("doyle", "todlando")


def send_start():
    if not START_FILE or not os.path.isfile(START_FILE):
        raise SystemExit(
            "REFUSING TO RUN: set HERTZ_START_FILE to the START body. "
            "The sequencer's only view of the box is the START message."
        )
    body = io.open(START_FILE, "rb").read()
    results = {}
    for peer in PEERS:
        proc = subprocess.run(["spt", "send", peer], input=body,
                              capture_output=True)
        results[peer] = (proc.stdout or proc.stderr).decode(
            "utf-8", "replace").strip()
    # SENT and QUEUED are both success; anything else stops the window before a
    # producer runs, because an unannounced window is the failure being fixed.
    for peer, line in results.items():
        if not line.startswith(("SENT", "QUEUED")):
            raise SystemExit("START not delivered to %s: %s" % (peer, line))
    return results


def emit(state):
    json.dump(state, open(REPORT, "w"), indent=2)


def main():
    state = {"tip": TAG, "start_utc": time.strftime(
        "%Y-%m-%dT%H:%M:%SZ", time.gmtime()), "runs": {}}
    state["start_dispatch"] = send_start()
    emit(state)

    for label in SEQUENCE:
        obs = P.PRODUCERS[label]()
        row = {
            "exit": obs["producer_exit"], "stop_reason": obs["stop_reason"],
            "elapsed": round(obs["elapsed_seconds"], 1),
            "survivors": obs["survivors"],
            "pool_growth_bytes": obs["pool_growth_bytes"],
        }
        if label == "mock-session-fixture":
            # Prediction 14 is about the ARTIFACT, not the exit code: a build
            # that exits 0 without putting the binary where sibling_bin looks
            # would fail producer 4 exactly as before.
            row["fixture_present"] = os.path.isfile(FIXTURE_BIN)
            row["fixture_path"] = FIXTURE_BIN
        state["runs"][label] = row
        emit(state)

    state["end_utc"] = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
    state["post_census"] = L.builder_census()["by_owner"]
    emit(state)
    print(json.dumps(state, indent=2))


if __name__ == "__main__":
    main()
