#!/usr/bin/env bash
# ── tmux-state.sh — Session State Persistence Manifest ───────────────────
# Snapshots all tmux pane state to ~/.timmy/tmux-state.json
# Run every supervisor cycle. Cold-start reads this manifest to resume.
# ──────────────────────────────────────────────────────────────────────────

set -euo pipefail

MANIFEST="${HOME}/.timmy/tmux-state.json"
mkdir -p "$(dirname "$MANIFEST")"

python3 << 'PYEOF'
import json, subprocess, os, time, re, sys
from datetime import datetime, timezone
from pathlib import Path

MANIFEST = os.path.expanduser("~/.timmy/tmux-state.json")

def run(cmd):
    """Run command, return stdout or empty string."""
    try:
        r = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=5)
        return r.stdout.strip()
    except Exception:
        return ""

def get_sessions():
    """Get all tmux sessions with metadata."""
    raw = run("tmux list-sessions -F '#{session_name}|#{session_windows}|#{session_created}|#{session_attached}|#{session_group}|#{session_id}'")
    sessions = []
    for line in raw.splitlines():
        if not line.strip():
            continue
        parts = line.split("|")
        if len(parts) < 6:
            continue
        sessions.append({
            "name": parts[0],
            "windows": int(parts[1]),
            "created_epoch": int(parts[2]),
            "created": datetime.fromtimestamp(int(parts[2]), tz=timezone.utc).isoformat(),
            "attached": parts[3] == "1",
            "group": parts[4],
            "id": parts[5],
        })
    return sessions

def get_panes():
    """Get all tmux panes with full metadata."""
    fmt = '#{session_name}|#{window_index}|#{pane_index}|#{pane_pid}|#{pane_title}|#{pane_width}x#{pane_height}|#{pane_active}|#{pane_current_command}|#{pane_start_command}|#{pane_tty}|#{pane_id}|#{window_name}|#{session_id}'
    raw = run(f"tmux list-panes -a -F '{fmt}'")
    panes = []
    for line in raw.splitlines():
        if not line.strip():
            continue
        parts = line.split("|")
        if len(parts) < 13:
            continue
        session, win, pane, pid, title, size, active, cmd, start_cmd, tty, pane_id, win_name, sess_id = parts[:13]
        w, h = size.split("x") if "x" in size else ("0", "0")
        panes.append({
            "session": session,
            "window_index": int(win),
            "window_name": win_name,
            "pane_index": int(pane),
            "pane_id": pane_id,
            "pid": int(pid) if pid.isdigit() else 0,
            "title": title,
            "width": int(w),
            "height": int(h),
            "active": active == "1",
            "command": cmd,
            "start_command": start_cmd,
            "tty": tty,
            "session_id": sess_id,
        })
    return panes

def extract_hermes_state(pane):
    """Try to extract hermes session info from a pane."""
    info = {
        "is_hermes": False,
        "profile": None,
        "model": None,
        "provider": None,
        "session_id": None,
        "task": None,
    }
    title = pane.get("title", "")
    cmd = pane.get("command", "")
    start = pane.get("start_command", "")

    # Detect hermes processes
    is_hermes = any(k in (title + " " + cmd + " " + start).lower()
                    for k in ["hermes", "timmy", "mimo", "claude", "gpt"])
    if not is_hermes and cmd not in ("python3", "python3.11", "bash", "zsh", "fish"):
        return info

    # Try reading pane content for model/provider clues
    pane_content = run(f"tmux capture-pane -t '{pane['session']}:{pane['window_index']}.{pane['pane_index']}' -p -S -20 2>/dev/null")

    # Extract model from pane content patterns
    model_patterns = [
        r"(?:mimo-v2-pro|claude-[\w.-]+|gpt-[\w.-]+|gemini-[\w.-]+|qwen[\w:.-]*)",
    ]
    for pat in model_patterns:
        m = re.search(pat, pane_content, re.IGNORECASE)
        if m:
            info["model"] = m.group(0)
            info["is_hermes"] = True
            break

    # Provider inference from model
    model = (info["model"] or "").lower()
    if "mimo" in model:
        info["provider"] = "nous"
    elif "claude" in model:
        info["provider"] = "anthropic"
    elif "gpt" in model:
        info["provider"] = "openai"
    elif "gemini" in model:
        info["provider"] = "google"
    elif "qwen" in model:
        info["provider"] = "custom"

    # Profile from session name
    session = pane["session"].lower()
    if "burn" in session:
        info["profile"] = "burn"
    elif session in ("dev", "0"):
        info["profile"] = "default"
    else:
        info["profile"] = session

    # Try to extract session ID (hermes uses UUIDs)
    uuid_match = re.findall(r'[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}', pane_content)
    if uuid_match:
        info["session_id"] = uuid_match[-1]  # most recent
        info["is_hermes"] = True

    # Last prompt — grab the last user-like line
    lines = pane_content.splitlines()
    for line in reversed(lines):
        stripped = line.strip()
        if stripped and not stripped.startswith(("─", "│", "╭", "╰", "▸", "●", "○")) and len(stripped) > 10:
            info["task"] = stripped[:200]
            break

    return info

def get_context_percent(pane):
    """Estimate context usage from pane content heuristics."""
    content = run(f"tmux capture-pane -t '{pane['session']}:{pane['window_index']}.{pane['pane_index']}' -p -S -5 2>/dev/null")
    # Look for context indicators like "ctx 45%" or "[░░░░░░░░░░]"
    ctx_match = re.search(r'ctx\s*(\d+)%', content)
    if ctx_match:
        return int(ctx_match.group(1))
    bar_match = re.search(r'\[(░+█*█*░*)\]', content)
    if bar_match:
        bar = bar_match.group(1)
        filled = bar.count('█')
        total = len(bar)
        if total > 0:
            return int((filled / total) * 100)
    return None

def build_manifest():
    """Build the full tmux state manifest."""
    now = datetime.now(timezone.utc)
    sessions = get_sessions()
    panes = get_panes()

    pane_manifests = []
    for p in panes:
        hermes = extract_hermes_state(p)
        ctx = get_context_percent(p)

        entry = {
            "address": f"{p['session']}:{p['window_index']}.{p['pane_index']}",
            "pane_id": p["pane_id"],
            "pid": p["pid"],
            "size": f"{p['width']}x{p['height']}",
            "active": p["active"],
            "command": p["command"],
            "title": p["title"],
            "profile": hermes["profile"],
            "model": hermes["model"],
            "provider": hermes["provider"],
            "session_id": hermes["session_id"],
            "task": hermes["task"],
            "context_pct": ctx,
            "is_hermes": hermes["is_hermes"],
        }
        pane_manifests.append(entry)

    # Active pane summary
    active_panes = [p for p in pane_manifests if p["active"]]
    primary = active_panes[0] if active_panes else {}

    manifest = {
        "version": 1,
        "timestamp": now.isoformat(),
        "timestamp_epoch": int(now.timestamp()),
        "hostname": os.uname().nodename,
        "sessions": sessions,
        "panes": pane_manifests,
        "summary": {
            "total_sessions": len(sessions),
            "total_panes": len(pane_manifests),
            "hermes_panes": sum(1 for p in pane_manifests if p["is_hermes"]),
            "active_pane": primary.get("address"),
            "active_model": primary.get("model"),
            "active_provider": primary.get("provider"),
        },
    }

    return manifest

# --- Main ---
manifest = build_manifest()

# Write manifest
with open(MANIFEST, "w") as f:
    json.dump(manifest, f, indent=2)

# Also write to ~/.hermes/tmux-state.json for compatibility
hermes_manifest = os.path.expanduser("~/.hermes/tmux-state.json")
os.makedirs(os.path.dirname(hermes_manifest), exist_ok=True)
with open(hermes_manifest, "w") as f:
    json.dump(manifest, f, indent=2)

print(f"[tmux-state] {manifest['summary']['total_panes']} panes, "
      f"{manifest['summary']['hermes_panes']} hermes, "
      f"active={manifest['summary']['active_pane']} "
      f"@ {manifest['summary']['active_model']}")
print(f"[tmux-state] written to {MANIFEST}")
PYEOF
