#!/usr/bin/env python3
"""Drift-watch tick — emit ONE KIT-DRIFT line only when the drift signature changes.

Signature spans every mirror AND every derived instance in drift.json (not just entabeni),
so a new instance (terminus) is watched the same as the first one (entabeni). The signature
deliberately omits the rolling `changed_24h` count (it drifts with the clock, not with real
state) — that count is still shown in the emitted line, just not used to decide "changed".

Loop usage (from the drift-watch monitor):
    while true; do python3 _drift-scan.py >/dev/null 2>&1; python3 _drift-emit.py; sleep 900; done
First tick establishes the baseline silently; only subsequent real changes emit.
"""
import json
import os
import subprocess

HERE = os.path.dirname(os.path.abspath(__file__))
drift = json.load(open(os.path.join(HERE, "drift.json")))
mirrors = drift.get("mirrors", [])
instances = drift.get("instances", [])
summary = drift.get("summary", {})

# An unreachable instance reports scar_count=None (Mac B asleep) — which is NOT a real drift, just a
# transient we can't see. Carry the LAST-KNOWN count forward so None never enters the signature; only a
# genuine scar-count change (e.g. 25 -> 26) emits. This kills the sleep/wake flap (L17 noise-exclusion).
lastknown_file = os.path.join(HERE, ".drift-instance-lastknown.json")
lastknown = json.load(open(lastknown_file)) if os.path.exists(lastknown_file) else {}
inst_sig = []
for i in instances:
    count = i.get("scar_count")
    if count is not None:
        lastknown[i["name"]] = count  # reachable — record the ground truth
    resolved = count if count is not None else lastknown.get(i["name"], "unknown")
    inst_sig.append(f"{i['name']}:{resolved}")
json.dump(lastknown, open(lastknown_file, "w"))

sig_parts = [f"{m['name']}:{m['status']}:{m['drift_count']}:{','.join(m['local_scars'])}" for m in mirrors]
sig_parts += inst_sig  # reachability + transient-None both excluded — only real state change is drift
sig_parts.append(f"pq:{drift.get('promotion_queue')}")
signature = "|".join(sig_parts)

sig_file = os.path.join(HERE, ".drift-watch-sig")
previous = open(sig_file).read().strip() if os.path.exists(sig_file) else ""

if previous and signature != previous:
    locals_ = [f"{m['name']}:{','.join(m['local_scars'])}" for m in mirrors if m["local_scars"]]
    behind = [m["name"] for m in mirrors if m["status"] == "BEHIND"]
    inst_line = " · ".join(
        f"{i['name'].replace('think-like-', '')} {i.get('scar_prefix')}={i.get('scar_count')}"
        + ("" if i.get("reachable") else "(UNREACHABLE)")
        + (f" chg24h={len(i.get('changed_24h', []))}" if i.get("changed_24h") else "")
        for i in instances
    )
    line = (f"KIT-DRIFT change @ {drift.get('scanned')}: clean={summary.get('clean')} "
            f"behind={summary.get('behind')} local-scars={summary.get('local_scars')} "
            f"pq={drift.get('promotion_queue')}")
    if locals_:
        line += f" | LOCAL: {'; '.join(locals_)}"
    if behind:
        line += f" | BEHIND: {','.join(behind)}"
    if inst_line:
        line += f" | instances: {inst_line}"
    print(line, flush=True)
    subprocess.run(["python3", os.path.join(HERE, "_build.py")], capture_output=True)

open(sig_file, "w").write(signature)
