#!/usr/bin/env python3
"""One-off recovery: mirror internal/tui/backfill.go for the current dev.
DRY_RUN=1 prints candidates without writing. Otherwise atomic merge+rename."""
import json, os, subprocess, sys, secrets, datetime
from pathlib import Path

CURRENT_USER = os.environ.get("BF_USER", "tom")
DRY_RUN = os.environ.get("DRY_RUN", "1") == "1"

HOME = Path.home()
CLAUDE_PROJECTS = HOME / ".claude" / "projects"
SESSIONS = HOME / ".config" / "claude-code" / "sessions.json"
PROJECTS_DIR = Path("/opt/projects")
PREFIX = "-opt-projects--"
MARKER = "-worktrees-"


def worktree_branches(project_dir: Path) -> dict:
    try:
        out = subprocess.check_output(
            ["git", "-C", str(project_dir), "worktree", "list", "--porcelain"],
            text=True, stderr=subprocess.DEVNULL)
    except Exception:
        return {}
    res, cur = {}, None
    for line in out.split("\n"):
        if line.startswith("worktree "):
            cur = os.path.basename(line[len("worktree "):])
        elif line.startswith("branch "):
            branch = line[len("branch "):].removeprefix("refs/heads/")
            if cur and branch:
                res[cur] = branch
        elif line == "":
            cur = None
    return res


def newest_jsonl(d: Path):
    newest = None
    for p in d.rglob("*.jsonl"):
        try:
            mt = p.stat().st_mtime
        except OSError:
            continue
        if newest is None or mt > newest:
            newest = mt
    return newest


def main():
    file = {}
    if SESSIONS.exists():
        file = json.loads(SESSIONS.read_text())
    convs = file.get("conversations", [])

    def key(c):
        return (c.get("project", ""), c.get("branch", ""), c.get("user", ""))
    existing = {key(c) for c in convs}  # archived included

    branch_cache = {}
    candidates = []
    for entry in sorted(os.listdir(CLAUDE_PROJECTS)):
        d = CLAUDE_PROJECTS / entry
        if not d.is_dir() or not entry.startswith(PREFIX):
            continue
        rest = entry[len(PREFIX):]
        i = rest.find(MARKER)
        if i <= 0:
            continue
        project, slug = rest[:i], rest[i + len(MARKER):]
        if not project or not slug:
            continue
        project_dir = PROJECTS_DIR / project
        if not project_dir.is_dir():
            continue
        if project not in branch_cache:
            branch_cache[project] = worktree_branches(project_dir)
        branch = branch_cache[project].get(slug)
        if not branch:
            continue  # worktree gone → ambiguous → skip
        owner = branch.split("/", 1)[0] if "/" in branch else ""
        if owner != CURRENT_USER:
            continue
        if (project, branch, CURRENT_USER) in existing:
            continue
        mt = newest_jsonl(d)
        if mt is None:
            continue
        dt = datetime.datetime.fromtimestamp(mt, datetime.timezone.utc)
        stamp = dt.strftime("%Y-%m-%dT%H:%M:%SZ")
        cid = dt.strftime("%Y%m%d-%H%M%S") + "-" + secrets.token_hex(4)
        rec = {
            "id": cid, "title": branch, "user": CURRENT_USER,
            "project": project, "branch": branch, "cli": "claude",
            "tmux_session": f"{project}-{slug}",
            "created_at": stamp, "updated_at": stamp, "last_action": "backfill",
        }
        existing.add((project, branch, CURRENT_USER))
        candidates.append(rec)

    print(f"[{'DRY-RUN' if DRY_RUN else 'WRITE'}] user={CURRENT_USER} candidates={len(candidates)}")
    for c in candidates:
        print(f"  + {c['project']} / {c['branch']}  (updated {c['updated_at']})  id={c['id']}")
    if DRY_RUN or not candidates:
        return

    # Atomic read-merge-write against freshest on-disk state.
    fresh = json.loads(SESSIONS.read_text()) if SESSIONS.exists() else {}
    fresh_convs = fresh.get("conversations", [])
    fresh_keys = {(c.get("project",""), c.get("branch",""), c.get("user","")) for c in fresh_convs}
    added = 0
    for c in candidates:
        if (c["project"], c["branch"], c["user"]) in fresh_keys:
            continue
        fresh_convs.append(c); added += 1
    fresh["conversations"] = fresh_convs
    tmp = SESSIONS.with_suffix(".json.tmp-backfill")
    tmp.write_text(json.dumps(fresh, ensure_ascii=False, indent=2))
    os.replace(tmp, SESSIONS)
    print(f"[WRITE] added={added} → {SESSIONS}")


if __name__ == "__main__":
    main()
