#!/usr/bin/env bash
# Golden fixture harness for memory plugin hooks
# Usage: ./harness.sh <hook-name> <input-json-file> [--scenario-dir <dir>]
# Builds sandbox, runs hook, emits normalized stdout+sideeffects for diffing
#
# Normalization rules (applied in this order to avoid corruption):
#   1. ISO 8601 timestamps (\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z) → __TIMESTAMP__
#   2. Date-only (\d{4}-\d{2}-\d{2}) → __DATE__
#   3. Sandbox temp path ($SANDBOX) → __SANDBOX__
#   4. cwd=<path> log values → cwd=__CWD__ (varies by machine/invocation dir)
#   5. Real home /home/jared → __REAL_HOME__
#
# SAFETY: git shim blocks any git call targeting /home/jared/.memsearch
set -euo pipefail

# Set HOOKS_DIR to override, e.g. for Python port replay:
#   HOOKS_DIR=/path/to/python-hooks ./harness.sh ...
HOOKS_DIR="${HOOKS_DIR:-$(cd "$(dirname "$0")/../hooks" && pwd -P)}"

usage() {
  echo "Usage: $0 <hook-name> <input-json-file> [--scenario-dir <dir>]" >&2
  exit 1
}

[[ $# -lt 2 ]] && usage

HOOK_NAME="$1"
INPUT_JSON_FILE="$2"
SCENARIO_DIR=""

shift 2
while [[ $# -gt 0 ]]; do
  case "$1" in
    --scenario-dir) SCENARIO_DIR="$2"; shift 2 ;;
    *) echo "Unknown arg: $1" >&2; usage ;;
  esac
done

# Validate hook name
HOOK_SCRIPT="$HOOKS_DIR/${HOOK_NAME}.sh"
if [[ ! -f "$HOOK_SCRIPT" ]]; then
  echo "Hook script not found: $HOOK_SCRIPT" >&2
  exit 1
fi

# ---------------------------------------------------------------------------
# 1. Create sandbox
# ---------------------------------------------------------------------------
SANDBOX=$(mktemp -d)
# Canonicalize to avoid symlink leaks (e.g. /tmp → /private/tmp on macOS)
SANDBOX=$(cd "$SANDBOX" && pwd -P)

cleanup() {
  rm -rf "$SANDBOX"
}
trap cleanup EXIT

# ---------------------------------------------------------------------------
# 2. Populate sandbox structure
# ---------------------------------------------------------------------------
mkdir -p "$SANDBOX/.claude/plugins/os-vault"
cat > "$SANDBOX/.claude/plugins/os-vault/config.yaml" <<YAML
vault_path: ${SANDBOX}/vault
graphify_output_dir: ${SANDBOX}/vault/graphify-out
ollama_model: qwen25-coder-7b-16k
stale_threshold_days: 7
memsearch_dir: /home/jared/.memsearch
YAML

mkdir -p "$SANDBOX/vault/journal"
mkdir -p "$SANDBOX/.cache/graphify"
mkdir -p "$SANDBOX/bin"

# ---------------------------------------------------------------------------
# 3. Graphify shim
# ---------------------------------------------------------------------------
export GRAPHIFY_SHIM_LOG="$SANDBOX/graphify-shim.log"
cat > "$SANDBOX/bin/graphify" <<BASH
#!/usr/bin/env bash
echo "[fake-graphify] args: \$@" >> "${SANDBOX}/graphify-shim.log"
exit 0
BASH
chmod +x "$SANDBOX/bin/graphify"

# ---------------------------------------------------------------------------
# 4. Git shim — SAFETY CRITICAL
# Absolute log path baked in at write-time (not env-var-only) so it works
# inside session-end's subshell even if env is stripped.
# Emits NOTHING to stdout/stderr — only writes to log file.
# Blocks any git call with /home/jared/.memsearch as an argument.
# ---------------------------------------------------------------------------
export SANDBOX_GIT_SHIM_LOG="$SANDBOX/git-shim.log"
cat > "$SANDBOX/bin/git" <<BASH
#!/usr/bin/env bash
# Safety shim: block any git operation targeting the real memsearch repo
for arg in "\$@"; do
  if [ "\$arg" = "/home/jared/.memsearch" ]; then
    echo "[git-shim] BLOCKED: git \$@ (memsearch path detected)" >> "${SANDBOX}/git-shim.log"
    exit 0
  fi
done
exec /usr/bin/git "\$@"
BASH
chmod +x "$SANDBOX/bin/git"

# ---------------------------------------------------------------------------
# 5. Expand __SANDBOX__ in input JSON, write to temp file
# ---------------------------------------------------------------------------
INPUT_EXPANDED="$SANDBOX/input-expanded.json"
sed "s|__SANDBOX__|${SANDBOX}|g" "$INPUT_JSON_FILE" > "$INPUT_EXPANDED"

# ---------------------------------------------------------------------------
# 6. Export environment for hook run
# ---------------------------------------------------------------------------
export PATH="$SANDBOX/bin:$PATH"
export HOME="$SANDBOX"

# ---------------------------------------------------------------------------
# 7. Normalization function
# ---------------------------------------------------------------------------
normalize() {
  local sandbox_path="$1"
  sed \
    -e "s|[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}T[0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}Z|__TIMESTAMP__|g" \
    -e "s|[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}|__DATE__|g" \
    -e "s|${sandbox_path}|__SANDBOX__|g" \
    -e "s|cwd=[^ ]*|cwd=__CWD__|g" \
    -e "s|/home/jared|__REAL_HOME__|g"
}

# ---------------------------------------------------------------------------
# 8. Record log file offset before run (for delta capture)
# ---------------------------------------------------------------------------
LOG_OFFSET=0
if [[ -f /tmp/memory-hook.log ]]; then
  LOG_OFFSET=$(wc -c < /tmp/memory-hook.log)
fi

# ---------------------------------------------------------------------------
# 9. Run hook
# ---------------------------------------------------------------------------
HOOK_STDOUT=""
HOOK_EXIT=0

HOOK_STDOUT=$(bash "$HOOK_SCRIPT" < "$INPUT_EXPANDED" 2>/dev/null) || HOOK_EXIT=$?

# For session-start-stale: poll for graphify-shim.log up to 5 seconds
if [[ "$HOOK_NAME" == "session-start" ]]; then
  for i in $(seq 1 10); do
    if [[ -f "$SANDBOX/graphify-shim.log" ]]; then
      break
    fi
    sleep 0.5
  done
fi

# ---------------------------------------------------------------------------
# 10. Output normalized stdout and exit code
# ---------------------------------------------------------------------------
echo "$HOOK_STDOUT" | normalize "$SANDBOX"
echo "EXIT_CODE: $HOOK_EXIT"

# ---------------------------------------------------------------------------
# 11. If scenario-dir given, write artifacts there
# ---------------------------------------------------------------------------
if [[ -n "$SCENARIO_DIR" ]]; then
  mkdir -p "$SCENARIO_DIR/sideeffects"

  echo "$HOOK_STDOUT" | normalize "$SANDBOX" > "$SCENARIO_DIR/stdout.txt"
  echo "$HOOK_EXIT" > "$SCENARIO_DIR/exit_code.txt"
  cp "$INPUT_JSON_FILE" "$SCENARIO_DIR/input.json"

  # Memory hook log delta
  if [[ -f /tmp/memory-hook.log ]]; then
    dd if=/tmp/memory-hook.log bs=1 skip="$LOG_OFFSET" 2>/dev/null \
      | normalize "$SANDBOX" \
      > "$SCENARIO_DIR/sideeffects/memory-hook-log-delta.txt"
  else
    echo "(no log entries)" > "$SCENARIO_DIR/sideeffects/memory-hook-log-delta.txt"
  fi

  # Graphify shim log
  if [[ -f "$SANDBOX/graphify-shim.log" ]]; then
    normalize "$SANDBOX" < "$SANDBOX/graphify-shim.log" \
      > "$SCENARIO_DIR/sideeffects/graphify-shim-args.txt"
  fi

  # Git shim log
  if [[ -f "$SANDBOX/git-shim.log" ]]; then
    normalize "$SANDBOX" < "$SANDBOX/git-shim.log" \
      > "$SCENARIO_DIR/sideeffects/git-shim-log.txt"
  fi

  # ---------------------------------------------------------------------------
  # Hook-specific side effects
  # ---------------------------------------------------------------------------
  SESSION_ID=$(python3 -c "import json; d=json.load(open('$INPUT_EXPANDED')); print(d.get('session_id','unknown'))" 2>/dev/null || echo "unknown")

  if [[ "$HOOK_NAME" == "session-end" ]]; then
    # Journal file (find by glob)
    local journal_file
    journal_file=$(ls "$SANDBOX/vault/journal/"*.md 2>/dev/null | head -1)
    if [[ -n "$journal_file" && -f "$journal_file" ]]; then
      normalize "$SANDBOX" < "$journal_file" > "$SCENARIO_DIR/sideeffects/journal.txt"
    else
      echo "(journal file not created)" > "$SCENARIO_DIR/sideeffects/journal.txt"
    fi
    # Touch file deleted?
    if [[ ! -f "/tmp/claude-vault-touched-${SESSION_ID}" ]]; then
      echo "deleted" > "$SCENARIO_DIR/sideeffects/touch-file-deleted.txt"
    else
      echo "NOT deleted (unexpected)" > "$SCENARIO_DIR/sideeffects/touch-file-deleted.txt"
    fi
  fi

  if [[ "$HOOK_NAME" == "post-tool-use-write" ]]; then
    # Touch file content
    if [[ -f "/tmp/claude-vault-touched-${SESSION_ID}" ]]; then
      normalize "$SANDBOX" < "/tmp/claude-vault-touched-${SESSION_ID}" \
        > "$SCENARIO_DIR/sideeffects/touch-file.txt"
    else
      echo "(not created)" > "$SCENARIO_DIR/sideeffects/touch-file.txt"
    fi
    # Post-tool-use log
    if [[ -f "$SANDBOX/.cache/graphify/post-tool-use.log" ]]; then
      normalize "$SANDBOX" < "$SANDBOX/.cache/graphify/post-tool-use.log" \
        > "$SCENARIO_DIR/sideeffects/post-tool-use-log.txt"
    else
      echo "(not created)" > "$SCENARIO_DIR/sideeffects/post-tool-use-log.txt"
    fi
    # Stamp deleted?
    if [[ ! -f "$SANDBOX/.cache/graphify/vault-rebuild.stamp" ]]; then
      echo "yes" > "$SCENARIO_DIR/sideeffects/stamp-deleted.txt"
    else
      echo "no" > "$SCENARIO_DIR/sideeffects/stamp-deleted.txt"
    fi
  fi

  if [[ "$HOOK_NAME" == "session-context" ]]; then
    # Flag file created?
    if [[ -f "/tmp/memory-context-injected-${SESSION_ID}" ]]; then
      echo "yes" > "$SCENARIO_DIR/sideeffects/flag-file-created.txt"
    else
      echo "no" > "$SCENARIO_DIR/sideeffects/flag-file-created.txt"
    fi
  fi
fi
