#!/usr/bin/env python3
import collections
import json
from datetime import datetime
from pathlib import Path

primary = Path(
    r"EtherGold/dev/sessions/2026-07-20_2014-UTC_23hop-synth-busy-pos4-mrt/4/data/events.jsonl.20260720T201407Z"
)
tail = Path(
    r"EtherGold/dev/sessions/2026-07-20_2014-UTC_23hop-synth-busy-pos4-mrt/4/data/events.jsonl"
)


def parse_utc(s):
    if not s:
        return None
    return datetime.fromisoformat(s.replace("Z", "+00:00"))


bucket = collections.defaultdict(
    lambda: {
        "n": 0,
        "qw": [],
        "stale": 0,
        "defer_abandon": 0,
        "reject": 0,
        "eth_done": 0,
        "synth_done": 0,
        "mrt_x1": 0,
        "local_37bc_eth": 0,
    }
)

local_tid = "37BC"
local_msgs = collections.Counter()
local_ev = collections.Counter()
reject_reasons = collections.Counter()
defer_reasons = collections.Counter()
superseded = 0
x1_harvest = collections.Counter()
grace_absorb = 0
no_x1_paths = []
max_qw_events = []
session_meta = None
learned_tid = None

for path in (primary, tail):
    if not path.exists():
        continue
    with path.open(encoding="utf-8", errors="replace") as fh:
        for line in fh:
            line = line.strip()
            if not line:
                continue
            try:
                o = json.loads(line)
            except Exception:
                continue
            utc = o.get("utc")
            t = parse_utc(utc)
            if not t:
                continue
            key = t.strftime("%H:%M")[:-1] + "0"
            b = bucket[key]
            ev = o.get("ev", "")
            tid = o.get("tid")
            qw = o.get("queue_wait_us")
            if qw is not None:
                try:
                    qwf = float(qw)
                    b["qw"].append(qwf)
                    if qwf > 5_000_000:
                        max_qw_events.append((utc, ev, tid, qwf, o.get("msg")))
                except Exception:
                    pass
            if ev == "eth_to_uart_done":
                b["eth_done"] += 1
                if tid == local_tid:
                    b["local_37bc_eth"] += 1
            if ev == "trunk_round_stale_abandon":
                b["stale"] += 1
            if ev == "transaction_defer_abandon":
                b["defer_abandon"] += 1
                defer_reasons[o.get("reason", "?")] += 1
            if ev == "post_x9_handoff_capture_reject":
                b["reject"] += 1
                reject_reasons[
                    o.get("reason")
                    or o.get("why")
                    or o.get("detail")
                    or "?"
                ] += 1
            if ev == "status_0d_synth_done":
                b["synth_done"] += 1
            if ev == "mrt_x1_after_x9":
                b["mrt_x1"] += 1
            if tid == local_tid:
                local_ev[ev] += 1
                if o.get("msg"):
                    local_msgs[str(o["msg"])] += 1
            if ev == "local_round_superseded":
                superseded += 1
            if ev == "x1_harvest_path":
                x1_harvest[
                    o.get("path") or o.get("reason") or o.get("src") or "ok"
                ] += 1
            if "no_x1" in ev or "await_local_x1" in ev:
                no_x1_paths.append((utc, ev, tid))
            if ev == "post_x9_inject_grace_absorb":
                grace_absorb += 1
            if ev == "session_start":
                session_meta = o
            if ev == "local_slave_tid_learned":
                learned_tid = o.get("tid") or o.get("local_tid")

print("session", session_meta)
print("learned_tid", learned_tid)
print(
    "=== 10-min load (queue med/p90, stale, defer_abandon, reject, eth_done, synth_done, mrt_x1) ==="
)
for k in sorted(bucket):
    b = bucket[k]
    qw = sorted(b["qw"])
    if qw:
        med = qw[len(qw) // 2]
        p90 = qw[int(len(qw) * 0.9)]
    else:
        med = p90 = 0
    print(
        f"{k} eth={b['eth_done']:5d} loc={b['local_37bc_eth']:4d} "
        f"stale={b['stale']:4d} defAb={b['defer_abandon']:4d} rej={b['reject']:3d} "
        f"synth={b['synth_done']:3d} mrt_x1={b['mrt_x1']:3d} "
        f"qw_med={med/1e3:.0f}ms p90={p90/1e3:.0f}ms"
    )

print()
print("local 37BC top ev:", local_ev.most_common(20))
print("local 37BC msgs:", local_msgs.most_common(15))
print("superseded", superseded, "grace_absorb", grace_absorb)
print("reject_reasons", reject_reasons.most_common(10))
print("defer_abandon reasons", defer_reasons.most_common(10))
print("x1_harvest", x1_harvest.most_common(10))
print("no_x1 events", len(no_x1_paths), no_x1_paths[:5])
print("queue>5s count", len(max_qw_events))
print("worst queue samples:")
for s in sorted(max_qw_events, key=lambda x: -x[3])[:12]:
    print(f"  {s[0]} {s[1]} tid={s[2]} msg={s[4]} qw={s[3]/1e6:.1f}s")

# Local harvest success rate proxy
synth = local_ev.get("status_0d_synth_done", 0)
mrt = local_ev.get("mrt_x1_after_x9", 0)
rej = sum(1 for e, c in local_ev.items() if "reject" in e for _ in range(c))
print("local synth_done", synth, "local mrt_x1_after_x9", mrt)
