#!/usr/bin/env python3
"""st-review — the review digest walk (docs/19; auto-runs each morning, runnable any time): each pending
Steward result as a Claude-Code-style interactive question, where the decision is a ROUTING choice (who
handles the conclusion).

Plain terminal, zero deps, zero spend until a route is chosen. Decisions → tools/review/decisions.jsonl.
"""
import json, os, subprocess, sys, datetime
from pathlib import Path

HERE = Path(__file__).resolve().parent
ROOT = HERE.parent
sys.path.insert(0, str(ROOT / "tools" / "review"))
import queue as Q  # noqa

B = "\033[1m"; D = "\033[2m"; G = "\033[32m"; Y = "\033[33m"; C = "\033[36m"; R = "\033[31m"; X = "\033[0m"
# Design (Henry, 2026-06-10): the HUMAN is the decision point — always (if the AI decided, the Steward
# could have done it autonomously; the digest exists FOR the approval). The AI is the EXECUTOR
# of the decision, needed only when the decision implies work. Review time = solving it NOW → interactive
# sessions only (no background/economy fire-and-forget here). Model choice = ir's own picker: `ir "<prompt>"`
# with no model opens the real ir-choose frontend (native Anthropic included) and seeds the session.


def record(item, route, detail=""):
    rec = {"ts": datetime.datetime.now().isoformat(timespec="seconds"), "item_id": item["item_id"],
           "title": item["title"][:120], "route": route, "detail": detail}
    with open(Q.DECISIONS, "a") as f:
        f.write(json.dumps(rec) + "\n")
    print(f"{D}→ recorded: {route} {detail}{X}\n")


def apply_branch(item):
    d = item["artifacts"].get("dir", "")
    diff = item["artifacts"].get("diff", "")
    if not (d and Path(diff).exists() and item.get("repo")):
        print(f"{R}no diff/repo for this item{X}"); return False
    stamp = Path(d).name; idx = Path(diff).stem.split("-")[-1]
    branch = f"steward/{stamp}-job{idx}"
    wt = f"/tmp/st-review-apply-{stamp}-{idx}"
    repo = item["repo"]
    subprocess.run(["git", "-C", repo, "worktree", "remove", "--force", wt], capture_output=True)
    r = subprocess.run(["git", "-C", repo, "worktree", "add", "-b", branch, wt, "HEAD"], capture_output=True, text=True)
    if r.returncode != 0:
        print(f"{R}branch/worktree failed: {r.stderr.strip()[:120]}{X}"); return False
    try:
        r = subprocess.run(["git", "-C", wt, "apply", "--3way", diff], capture_output=True, text=True)
        if r.returncode != 0:
            print(f"{R}apply failed: {r.stderr.strip()[:140]}{X}"); return False
        subprocess.run(["git", "-C", wt, "add", "-A"], capture_output=True)
        subprocess.run(["git", "-C", wt, "commit", "-m", f"steward: {item['title'][:90]} (via st-review)"], capture_output=True)
        print(f"{G}✓ branch {branch} in {repo}{X}\n  review: git -C {repo} log {branch} -p")
        return branch
    finally:
        subprocess.run(["git", "-C", repo, "worktree", "remove", "--force", wt], capture_output=True)


def case_file(item, directive=""):
    p = Path(f"/tmp/st-case-{item['item_id'].replace('/', '-')}.md")
    j = ""
    jp = item["artifacts"].get("judge", "")
    if jp and Path(jp).exists():
        j = Path(jp).read_text(errors="replace")
    p.write_text(f"""# The Steward case: {item['title']}

Repo: {item['repo']}
Kind: {item['kind']} · judge recommendation: {item['recommendation']} (confidence {item['confidence']})

## Claim
{item['claim']}

## Why it's held / the load-bearing unknown
{item['why']}

## What would confirm
{item['what_would_confirm']}

## Full judge case (JSON)
{j}

## Artifacts
- diff: {item['artifacts'].get('diff', '-')}
- run dir: {item['artifacts'].get('dir', '-')}

## Directive
{directive or 'Review this case, decide the right resolution, and implement or report back.'}
""")
    return p


def decide_and_execute(item, preset=""):
    """The human DECIDES; an AI session EXECUTES it. Launches `ir "$(case+decision)"` foreground:
    ir's own picker (incl. native Anthropic) → session whose FIRST PROMPT = case + the decision."""
    if preset:
        decision = f"Run the confirm check and report PASS/FAIL with evidence: {preset}"
        print(f"  {D}decision = {decision[:120]}{X}")
    else:
        print(f"  {B}Your decision{X} {D}(what should be done — you choose; the AI executes){X}")
        decision = input("  decision> ").strip()
    if not decision:
        print(f"{D}(no decision — back){X}")
        return None
    cf = case_file(item, directive=(
        f"THE USER'S DECISION: {decision}\n\n"
        "Execute this decision. Work in a branch/worktree, never commit to the default branch, and "
        "report what you did with evidence. If the decision needs verification first (see 'What would "
        "confirm'), do that check before implementing and show the result."))
    repo = item["repo"] if item.get("repo") and Path(item["repo"]).is_dir() else os.getcwd()
    print(f"{G}case+decision → {cf}{X}")
    go = input(f"  launch the session now (ir picker, interactive)? [Y/n] ").strip().lower()
    if go in ("", "y", "yes"):
        prompt = cf.read_text()
        print(f"{D}— launching ir from {repo}; pick the model; the session opens with your decision —{X}")
        subprocess.call(["ir", prompt], cwd=repo)
        print(f"{D}— session ended; resuming review —{X}")
    else:
        print(f'  later:  {B}cd {repo} && ir "$(cat {cf})"{X}')
    return decision[:100]


def main():
    q = Q.build()
    if not q:
        print(f"{D}Nothing pending — the Steward will propose as it finds.{X}"); return
    print(f"{B}🌅 The Steward review — {len(q)} pending{X}\n{D}routing: every conclusion goes to the cheapest adequate executor{X}\n")
    for n, item in enumerate(q, 1):
        kc = {"fix-merge": G, "fix-review": G, "hold": Y, "analysis": C, "thread-proposed": D}.get(item["kind"], "")
        print(f"{B}[{n}/{len(q)}] {item['title'][:100]}{X}")
        print(f"  {kc}{item['kind']}{X} · judge: {B}{(item['recommendation'] or '?').upper()}{X}"
              + (f" (conf {item['confidence']})" if item["confidence"] is not None else "")
              + f" · {D}{Path(item['repo']).name if item['repo'] else '?'}{X}")
        if item["why"]:
            print(f"  {Y}⚠ {item['why'][:140]}{X}")
        if item["what_would_confirm"]:
            print(f"  {D}confirm: {item['what_would_confirm'][:140]}{X}")
        # ONE question: the DECISION itself (kind-aware). Whether an AI is needed FOLLOWS from the
        # decision (Henry: approve-a-fix and pick-option-B are both decisions; one is mechanical, one
        # implies work). Mechanical decisions execute directly; work-implying ones go to ir (picker +
        # decision-seeded interactive session).
        has_diff = bool(item["artifacts"].get("diff") and Path(item["artifacts"]["diff"]).exists())
        if item["kind"] in ("fix-merge", "fix-review"):
            if item.get("pr_url"):
                opts = [(f"merge on GitHub → {item['pr_url']}", "open-pr"),
                        ("modify/extend it (your instruction → AI session)", "ai-custom"),
                        ("reject & dismiss (close PR yourself)", "dismiss")]
            else:
                opts = [("approve this fix → review branch", "branch"),
                        ("modify/extend it (your instruction → AI session)", "ai-custom"),
                        ("reject & dismiss", "dismiss")]
        elif item["kind"] in ("hold", "analysis"):
            opts = []
            if item["what_would_confirm"]:
                opts.append((f"run the confirm check first → AI session", "ai-confirm"))
            opts += [("your decision (free text → AI session executes it)", "ai-custom"),
                     ("dismiss (not worth pursuing)", "dismiss")]
        else:  # thread-proposed etc.
            opts = [("investigate/resolve now → AI session", "ai-custom"),
                    ("dismiss", "dismiss")]
        print(f"  {B}Decision:{X}")
        for i, (lbl, _) in enumerate(opts):
            print(f"   [{i+1}] {lbl}")
        print(f"   {D}[{'d] diff · ' if has_diff else ''}s] skip · [q] quit{X}")
        while True:
            c = input("  > ").strip().lower()
            if c == "q":
                return
            if c == "s":
                print(f"{D}skipped{X}\n")
                break
            if c == "d" and has_diff:
                txt = Path(item["artifacts"]["diff"]).read_text(errors="replace")
                print(f"{D}{txt[:4000]}{X}" + (f"\n{D}[…{len(txt)-4000} more bytes]{X}" if len(txt) > 4000 else ""))
                continue
            if not c.isdigit() or not (1 <= int(c) <= len(opts)):
                print(f"{D}pick a decision (1-{len(opts)}), {'d, ' if has_diff else ''}s or q{X}")
                continue
            label, act = opts[int(c) - 1]
            if act == "open-pr":
                url=item["pr_url"]
                subprocess.run(["xdg-open", url], capture_output=True)  # best-effort; URL printed anyway
                print(f"{G}→ {url}{X}  (merge there — that's the approval)")
                record(item, "opened-pr", url)
                break
            if act == "branch":
                br = apply_branch(item)
                if br:
                    record(item, "approved", br)
                break
            if act == "dismiss":
                tid = item.get("thread_id")
                if tid:
                    for store in (Path.home() / ".local/share/the-steward/repos").iterdir():
                        db = store / "ledger.db"
                        if db.exists():
                            r = subprocess.run([sys.executable, str(ROOT / "tools/ledger/ledger.py"), "--db", str(db),
                                                "dismiss", "--id", tid, "--rationale", "dismissed via st-review"],
                                               capture_output=True, text=True)
                            if '"ok": true' in r.stdout:
                                print(f"{G}✓ thread {tid} dismissed{X}")
                                break
                record(item, "dismissed")
                break
            if act in ("ai-confirm", "ai-custom"):
                preset = item["what_would_confirm"] if act == "ai-confirm" else ""
                det = decide_and_execute(item, preset)
                if det:
                    record(item, "decided", det)
                    break
                continue


if __name__ == "__main__":
    main()
