#!/usr/bin/env bash
# Recover ~/.mugi/chats_db.json from a known-good backup snapshot.
#
# Designed for the "all my chats turned into Untitled" scenario where two or more
# `mugi_backend` processes raced the same chats_db.json file and the live state on
# disk is now corrupt. The script:
#
#   1. Stops every running mugi backend AND the Mugi.app host so the file is idle.
#   2. Picks the newest backup snapshot whose `conversation_meta` has more than
#      `MIN_REAL_TITLES` non-Untitled entries (env-tunable, default 50).
#   3. Stages the chosen snapshot as the new chats_db.json (atomic rename).
#   4. Leaves the previously-corrupt file beside it as
#      ~/.mugi/chats_db.json.corrupt-<UTC-ts> for forensics.
#
# After the script finishes, relaunch Mugi normally. The new single-instance lock
# in mugi_backend prevents the race that caused the corruption from happening
# again.
#
# Usage:
#   bash scripts/recover_chats_db.sh                # auto-pick newest good backup
#   BACKUP_NAME=chats_db.json.20260506T171817546285Z bash scripts/recover_chats_db.sh
#   MIN_REAL_TITLES=10 bash scripts/recover_chats_db.sh

set -euo pipefail

MUGI_DIR="${MUGI_DIR:-$HOME/.mugi}"
CHATS_DB="$MUGI_DIR/chats_db.json"
BACKUP_DIR="$MUGI_DIR/backups/chats_db"
MIN_REAL_TITLES="${MIN_REAL_TITLES:-50}"

if [[ ! -d "$BACKUP_DIR" ]]; then
  echo "[recover] no backup dir at $BACKUP_DIR — nothing to restore" >&2
  exit 1
fi

# 1) Stop the app + every backend instance so nobody clobbers the restore.
echo "[recover] stopping Mugi.app + every backend instance..."
osascript -e 'tell application "Mugi" to quit' >/dev/null 2>&1 || true
sleep 1
# Send SIGTERM first so the drain hook can flush; SIGKILL only if they linger.
pkill -TERM -f 'mugi_backend' 2>/dev/null || true
pkill -TERM -f 'Mugi.app/Contents/MacOS/Mugi' 2>/dev/null || true
sleep 3
pkill -KILL -f 'mugi_backend' 2>/dev/null || true
pkill -KILL -f 'Mugi.app/Contents/MacOS/Mugi' 2>/dev/null || true
sleep 1
remaining=$(pgrep -fl 'mugi_backend|Mugi.app/Contents/MacOS/Mugi' || true)
if [[ -n "$remaining" ]]; then
  echo "[recover] WARNING: still running after kill:" >&2
  echo "$remaining" >&2
fi

# 2) Pick the snapshot.
PYTHON="${PYTHON:-${HOME}/.mugi/venv/bin/python}"
if [[ ! -x "$PYTHON" ]]; then
  PYTHON="$(command -v python3)"
fi

if [[ -n "${BACKUP_NAME:-}" ]]; then
  CHOSEN="$BACKUP_DIR/$BACKUP_NAME"
  if [[ ! -f "$CHOSEN" ]]; then
    echo "[recover] BACKUP_NAME=$BACKUP_NAME not found in $BACKUP_DIR" >&2
    exit 1
  fi
else
  CHOSEN="$("$PYTHON" - "$BACKUP_DIR" "$MIN_REAL_TITLES" <<'PY'
import json, os, sys
backup_dir, threshold = sys.argv[1], int(sys.argv[2])
candidates = []
for name in os.listdir(backup_dir):
    if not name.startswith("chats_db.json."): continue
    p = os.path.join(backup_dir, name)
    try:
        with open(p) as f:
            d = json.load(f)
    except Exception:
        continue
    meta = d.get("conversation_meta") or {}
    if not isinstance(meta, dict):
        continue
    real = sum(
        1 for tid, m in meta.items()
        if isinstance(m, dict)
        and (m.get("title") or "").strip() not in ("", "Untitled")
    )
    if real >= threshold:
        candidates.append((os.path.getmtime(p), real, p))
if not candidates:
    sys.exit("no backup met the title-quality threshold")
candidates.sort()
print(candidates[-1][2])
PY
)"
fi

echo "[recover] chosen snapshot: $CHOSEN"

# 3) Stage current file as .corrupt for forensics, then drop the snapshot in place.
ts="$(date -u +%Y%m%dT%H%M%SZ)"
if [[ -f "$CHATS_DB" ]]; then
  mv -f "$CHATS_DB" "$CHATS_DB.corrupt-$ts"
  echo "[recover] preserved current file at $CHATS_DB.corrupt-$ts"
fi

cp "$CHOSEN" "$CHATS_DB.tmp"
mv -f "$CHATS_DB.tmp" "$CHATS_DB"
chmod 600 "$CHATS_DB"

echo "[recover] restored $CHATS_DB from $(basename "$CHOSEN")"
"$PYTHON" - "$CHATS_DB" <<'PY'
import json, sys
with open(sys.argv[1]) as f:
    d = json.load(f)
meta = d.get("conversation_meta") or {}
real = sum(
    1 for tid, m in meta.items()
    if isinstance(m, dict)
    and (m.get("title") or "").strip() not in ("", "Untitled")
)
print(f"[recover]   conversation_meta entries: {len(meta)}")
print(f"[recover]   real (non-Untitled) titles: {real}")
print(f"[recover]   tabs[0].thread_ids count:   "
      f"{len((d.get('tabs') or [{}])[0].get('thread_ids') or [])}")
PY

echo "[recover] done. Relaunch Mugi when ready."
