#!/usr/bin/env python3
"""Resolve a pid to its Claude Code session transcript, title, and cwd.

Single source of truth for session identification, used by both
agent-reaper.py and reaper-notifier.py.
"""
import json
import os
import sys

PROJECTS_DIR = os.path.expanduser("~/.claude/projects")
TITLE_TRUNCATE = 60


def find_transcript_path(pid):
    fd_dir = f"/proc/{pid}/fd"
    try:
        entries = os.listdir(fd_dir)
    except OSError:
        return None
    candidates = []
    for entry in entries:
        try:
            target = os.readlink(os.path.join(fd_dir, entry))
        except OSError:
            continue
        if not target.endswith(".jsonl"):
            continue
        if not target.startswith(PROJECTS_DIR + os.sep):
            continue
        try:
            mtime = os.stat(target).st_mtime
        except OSError:
            continue
        candidates.append((mtime, target))
    if not candidates:
        return None
    candidates.sort()
    return candidates[-1][1]


def find_cwd(pid):
    try:
        return os.readlink(f"/proc/{pid}/cwd")
    except OSError:
        return None


def _extract_text(content):
    if isinstance(content, str):
        return content
    if isinstance(content, list):
        for block in content:
            if isinstance(block, dict) and block.get("type") == "text":
                text = block.get("text")
                if text:
                    return text
    return None


def title_from_transcript(transcript_path):
    """Newest `summary` entry wins; else first non-meta user message text."""
    last_summary = None
    first_user_text = None
    try:
        with open(transcript_path, "r", errors="replace") as f:
            for line in f:
                line = line.strip()
                if not line:
                    continue
                try:
                    obj = json.loads(line)
                except (json.JSONDecodeError, ValueError):
                    continue
                if obj.get("type") == "summary":
                    summary = obj.get("summary")
                    if summary:
                        last_summary = summary
                elif first_user_text is None and obj.get("type") == "user" and not obj.get("isMeta"):
                    text = _extract_text(obj.get("message", {}).get("content"))
                    if text:
                        first_user_text = text
    except OSError:
        return None
    if last_summary:
        return last_summary
    if first_user_text:
        text = " ".join(first_user_text.split())
        if len(text) > TITLE_TRUNCATE:
            return text[:TITLE_TRUNCATE] + "..."
        return text
    return None


def resolve_session(pid):
    transcript_path = find_transcript_path(pid)
    cwd = find_cwd(pid)
    title = None
    if transcript_path:
        title = title_from_transcript(transcript_path)
    if not title:
        title = os.path.basename(cwd) if cwd else f"pid {pid}"
    return {"transcript_path": transcript_path, "title": title, "cwd": cwd}


def self_test():
    import shutil
    import tempfile

    tmpdir = tempfile.mkdtemp(prefix="resolve-session-selftest-")
    try:
        transcript_path = os.path.join(tmpdir, "fake-session.jsonl")
        with open(transcript_path, "w") as f:
            f.write(json.dumps({"type": "user", "isMeta": True,
                                 "message": {"role": "user", "content": "<local-command-caveat>ignore</local-command-caveat>"}}) + "\n")
            f.write(json.dumps({"type": "user",
                                 "message": {"role": "user", "content": "please fix the flaky checkout test suite before merging"}}) + "\n")
            f.write(json.dumps({"type": "summary", "summary": "Fix flaky checkout tests"}) + "\n")

        title = title_from_transcript(transcript_path)
        assert title == "Fix flaky checkout tests", f"summary should win, got {title!r}"

        with open(transcript_path, "w") as f:
            f.write(json.dumps({"type": "user",
                                 "message": {"role": "user", "content": "x" * 100}}) + "\n")
        title = title_from_transcript(transcript_path)
        assert title.endswith("...") and len(title) == TITLE_TRUNCATE + 3, f"truncation wrong: {title!r}"

        empty_path = os.path.join(tmpdir, "empty.jsonl")
        open(empty_path, "w").close()
        assert title_from_transcript(empty_path) is None, "empty transcript should yield no title"

        print("resolve-session self-test OK", file=sys.stderr)
        return True
    finally:
        shutil.rmtree(tmpdir, ignore_errors=True)


if __name__ == "__main__":
    if "--self-test" in sys.argv:
        sys.exit(0 if self_test() else 1)
    if len(sys.argv) > 1:
        print(json.dumps(resolve_session(sys.argv[1])))
    else:
        print("usage: resolve-session.py <pid> | --self-test", file=sys.stderr)
        sys.exit(2)
