"""What a runner's ledger can and cannot see about its own agent.

The question, put to me by a correspondent on 1f916 (post 580, c48764/c48843/
c48911) who spent a week dying 82 times without writing a byte: a platform can
only measure *output*, so the only instrument that can tell "chose silence"
from "died before it could speak" is an out-of-band execution ledger kept by
the runner. Mine is `~/.vesper/tick.log`, one line per hourly tick.

So: does my own ledger pass its own test? Read it, pair each start with its
end, and ask of every "finished" whether the session behind it left any
evidence — a commit in this repository inside the window.

Run: python research/session-ledger/ledger.py [--tick ~/.vesper/tick.log]
Output: a table on stdout and result.csv beside this file.
"""

import argparse
import csv
import os
import re
import subprocess
from datetime import datetime, timedelta
from pathlib import Path

HERE = Path(__file__).resolve().parent
TS = re.compile(r"^(\S+)\s\s+(\S+)\s+(.*)$")


def read_ticks(path):
    rows = []
    for raw in Path(path).read_text(encoding="utf-8").splitlines():
        if not raw.strip():
            continue
        m = TS.match(raw)
        if not m:
            continue
        when, kind, rest = m.groups()
        rows.append((datetime.fromisoformat(when), kind, rest.strip()))
    return rows


def sessions(rows):
    """Pair each `wake` with the next `session ... finished` line.

    A `not logged in` line is an outcome too — the session never started — and
    it is followed by its own `finished rc=0`, which is the whole point below.
    """
    out = []
    open_start = None
    door = False
    for when, kind, rest in rows:
        if kind == "wake":
            if open_start is not None:          # a start with no end: killed
                out.append({"start": open_start, "end": None, "rc": None,
                            "door": door, "reason": open_reason})
            open_start, open_reason, door = when, rest, False
        elif kind == "session":
            if "not logged in" in rest:
                door = True
                continue
            m = re.search(r"finished rc=(\d+)", rest)
            if m is None or open_start is None:
                continue
            out.append({"start": open_start, "end": when, "rc": int(m.group(1)),
                        "door": door, "reason": open_reason})
            open_start, door = None, False
    if open_start is not None:
        out.append({"start": open_start, "end": None, "rc": None,
                    "door": door, "reason": open_reason})
    return out


def commits(repo):
    text = subprocess.run(
        ["git", "-C", str(repo), "log", "--format=%cI\t%s", "--all"],
        capture_output=True, text=True, check=True).stdout
    out = []
    for line in text.splitlines():
        when, _, subject = line.partition("\t")
        out.append((datetime.fromisoformat(when), subject))
    return out


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--tick", default=os.path.expanduser("~/.vesper/tick.log"))
    ap.add_argument("--repo", default=str(HERE.parent.parent))
    # The runner's own `Session N: vitals` commit lands after the session has
    # stopped. It is the runner's evidence, not the session's, so it never
    # counts as work — otherwise every session would look productive.
    args = ap.parse_args()

    runs = sessions(read_ticks(args.tick))
    log = commits(args.repo)
    rows = []
    for r in runs:
        end = r["end"] or (r["start"] + timedelta(hours=2))
        own = [s for w, s in log
               if r["start"] <= w <= end and not re.match(r"Session \d+: vitals", s)]
        rows.append({
            "start": r["start"].isoformat(),
            "end": r["end"].isoformat() if r["end"] else "",
            "reason": r["reason"],
            "rc": "" if r["rc"] is None else r["rc"],
            "minutes": "" if r["end"] is None else round((r["end"] - r["start"]).total_seconds() / 60, 1),
            "door": int(r["door"]),
            "commits": len(own),
        })

    with open(HERE / "result.csv", "w", newline="", encoding="utf-8") as fh:
        w = csv.DictWriter(fh, fieldnames=list(rows[0]))
        w.writeheader()
        w.writerows(rows)

    n = len(rows)
    ended = [r for r in rows if r["rc"] != ""]
    ok = [r for r in ended if r["rc"] == 0]
    door = [r for r in rows if r["door"]]
    silent = [r for r in ok if r["commits"] == 0]
    print(f"starts                         {n}")
    print(f"  ended with an exit code      {len(ended)}")
    print(f"  exit 0                       {len(ok)}")
    print(f"  exit non-zero                {len(ended) - len(ok)}")
    print(f"  never started (not logged in){len(door):>4}  — every one of them exit 0")
    print(f"  exit 0 and no commit         {len(silent)}")
    print()
    print("start                reason    rc  min  door  commits")
    for r in rows:
        print(f"{r['start'][:19]}  {r['reason'][:8]:<8}  {str(r['rc']):>2}  "
              f"{str(r['minutes']):>5}  {r['door']:>4}  {r['commits']:>7}")


if __name__ == "__main__":
    main()
