#!/usr/bin/env bash
# Orchestrate a live Claude Code adapter capture session (#30).
#
# Sets up an isolated scratch repo, installs Lifeloop's CCD-compat
# Claude host assets into it pointing at the capture wrapper, and
# prints the operator commands required to drive Claude through every
# lifecycle path #30 covers. After the operator finishes the
# in-harness session, they re-run this script with `--sanitize` to
# fold the raw captures into committable fixtures via
# `scripts/sanitize-live-evidence.py`.
#
# This script is the **rig**, not the captured evidence: it is the
# deterministic recapture procedure documented in
# `docs/playbooks/capture-live-adapter-evidence.md`.
#
# Usage:
#   scripts/regenerate-live-claude-evidence.sh setup     # set up scratch repo + print operator instructions
#   scripts/regenerate-live-claude-evidence.sh sanitize  # fold raw captures into fixtures
#   scripts/regenerate-live-claude-evidence.sh teardown  # clean up scratch dirs (does not touch fixtures)
#
# Required env (set in the operator's shell):
#   LIFELOOP_LIVE_REAL_CCD       absolute path to the real CCD binary
#   LIFELOOP_LIVE_SCRATCH_DIR    absolute scratch dir (default: $TMPDIR/lifeloop-live-claude)
#
# Optional env:
#   LIFELOOP_LIVE_CAPTURE_DIR   raw capture dir
#                               (default: $LIFELOOP_LIVE_SCRATCH_DIR/captures)

set -euo pipefail

SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd -- "$SCRIPT_DIR/.." && pwd)"

scratch="${LIFELOOP_LIVE_SCRATCH_DIR:-${TMPDIR:-/tmp}/lifeloop-live-claude}"
capture_dir="${LIFELOOP_LIVE_CAPTURE_DIR:-$scratch/captures}"
fixture_root="$REPO_ROOT/tests/fixtures/live"
scratch_marker="$scratch/.lifeloop-live-claude-scratch"

require_real_ccd() {
    if [[ -z "${LIFELOOP_LIVE_REAL_CCD:-}" ]]; then
        echo "error: LIFELOOP_LIVE_REAL_CCD is unset; export it to your installed CCD binary" >&2
        exit 64
    fi
    if [[ ! -x "$LIFELOOP_LIVE_REAL_CCD" ]]; then
        echo "error: LIFELOOP_LIVE_REAL_CCD is not executable: $LIFELOOP_LIVE_REAL_CCD" >&2
        exit 64
    fi
}

require_safe_scratch() {
    if [[ -z "$scratch" || "$scratch" != /* ]]; then
        echo "error: LIFELOOP_LIVE_SCRATCH_DIR must be an absolute non-empty path: $scratch" >&2
        exit 64
    fi
    if [[ "$scratch" == "/" || "$scratch" == "${HOME:-}" || "$scratch" == "$REPO_ROOT" || "$scratch" == "$REPO_ROOT/"* ]]; then
        echo "error: refusing unsafe scratch dir: $scratch" >&2
        exit 64
    fi
}

reset_scratch() {
    require_safe_scratch
    if [[ -e "$scratch" && ! -f "$scratch_marker" ]]; then
        echo "error: refusing to remove unmarked scratch dir: $scratch" >&2
        exit 65
    fi
    rm -rf "$scratch"
    mkdir -p "$scratch" "$capture_dir"
    touch "$scratch_marker"
}

remove_scratch() {
    require_safe_scratch
    if [[ ! -f "$scratch_marker" ]]; then
        echo "error: refusing to remove unmarked scratch dir: $scratch" >&2
        exit 65
    fi
    rm -rf "$scratch"
}

cmd_setup() {
    require_real_ccd
    reset_scratch

    # Build the lifeloop CLI so `asset preview` is callable below.
    (cd "$REPO_ROOT" && cargo build --quiet --bin lifeloop)
    local lifeloop_bin="$REPO_ROOT/target/debug/lifeloop"

    # Render the CCD-compat Claude settings.json and write it into the
    # scratch repo. The capture wrapper goes in by setting CCD_BIN at
    # operator-session time — the rendered settings.json refers to
    # `${CCD_BIN:-ccd}` already, so the wrapper transparently
    # intercepts hook stdin without rewriting any settings.
    git init --quiet "$scratch"
    cd "$scratch"

    "$lifeloop_bin" asset preview --host claude --mode native_hook \
        | python3 -c '
import json, os, pathlib, sys
assets = json.load(sys.stdin)
for asset in assets:
    target = pathlib.Path(asset["relative_path"])
    target.parent.mkdir(parents=True, exist_ok=True)
    target.write_text(asset["contents"])
    if asset.get("mode") is not None:
        os.chmod(target, asset["mode"])
'

    cat <<EOF
==============================================================================
Live Claude Code capture session — operator instructions

Scratch repo:    $scratch
Raw capture dir: $capture_dir
Real CCD binary: $LIFELOOP_LIVE_REAL_CCD

Step 1. Open a NEW shell with these envs:

    export LIFELOOP_LIVE_CAPTURE_DIR=$capture_dir
    export LIFELOOP_LIVE_REAL_CCD=$LIFELOOP_LIVE_REAL_CCD
    export CCD_BIN=$SCRIPT_DIR/capture-live-hook.sh

Step 2. cd into the scratch repo and start a Claude Code session:

    cd $scratch
    claude

Step 3. Drive Claude through every lifecycle path #30 covers. The
recommended minimal walk:

  - SessionStart    – simply launching Claude in step 2 fires this.
  - UserPromptSubmit – send any short prompt, e.g. "echo hello".
  - PreCompact      – /compact in the Claude UI (if available in this
                       Claude version).
  - Stop            – let Claude reach end-of-turn (return to prompt).
  - SessionEnd      – /exit or close the session cleanly.

Step 4. When the session is done, sanitize:

    $0 sanitize

Sanitized fixtures will land under:

    $fixture_root/claude/<event>/<n>.json

Then \`make verify\` to re-run the live-evidence comparison tests,
and commit the fixtures.
==============================================================================
EOF
}

cmd_sanitize() {
    if [[ ! -d "$capture_dir" ]]; then
        echo "error: capture dir not found: $capture_dir" >&2
        echo "Run \`$0 setup\` first and complete the operator session." >&2
        exit 65
    fi
    python3 "$SCRIPT_DIR/sanitize-live-evidence.py" \
        --harness claude \
        --capture-dir "$capture_dir" \
        --output-dir "$fixture_root"
}

cmd_teardown() {
    remove_scratch
    echo "removed scratch dir: $scratch (fixtures under $fixture_root were NOT touched)"
}

case "${1:-}" in
    setup) cmd_setup ;;
    sanitize) cmd_sanitize ;;
    teardown) cmd_teardown ;;
    "" | -h | --help)
        cat <<EOF
$(basename "$0") {setup | sanitize | teardown}
See script header comment for full operator procedure.
EOF
        ;;
    *) echo "unknown subcommand: $1" >&2; exit 64 ;;
esac
