#!/usr/bin/env bash
# bash harness for logs-read.sh rc-spawn JSONL retrieval (Task 585).
#
# Covers the three in-scope deliverables:
#   1. `logs-read.sh <key>` (bare, no type) resolves the configDir Claude tree
#      and emits a merged session+subagent timeline. Key forms tested:
#      session_<bridge>, bare <bridge>, full <uuid>, <uuid> prefix.
#   2. A subagent `is_error:true` tool_result is flagged inline in the timeline
#      (agentType + failing tool + error text), and absent when the session is
#      clean.
#   3. `logs-read.sh --scan-subagent-errors` lists every subagents/agent-*.jsonl
#      carrying an is_error result, and only those.
#
# The fake configDir is pointed at via CLAUDE_CONFIG_DIR (the same override the
# session manager honours), so no real ~/.<brand> tree is touched. Each case
# builds an rc-spawn-shaped install (data/accounts/<id>/ with NO logs/ subdir)
# to prove the JSONL path does not depend on the dead per-account logs tree.
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LOGS_READ="$SCRIPT_DIR/logs-read.sh"
if [[ ! -x "$LOGS_READ" ]]; then
  echo "FATAL: $LOGS_READ not executable" >&2
  exit 1
fi

UUID_A="6031941e-0000-4000-8000-000000000001"
UUID_C="c0c0c0c0-0000-4000-8000-0000000000c0"
UUID_CLEAN="c1ea0000-0000-4000-8000-00000000c1ea"
BRIDGE="TESTBRIDGE01"

PASS=0
FAIL=0

# Build an rc-spawn-shaped install root + a fake CLAUDE tree. Echos two paths
# on stdout: "<logs_read_script> <claude_config_dir>".
setup_tree() {
  local root="$1"
  mkdir -p "$root/platform/scripts"
  mkdir -p "$root/data/accounts/acct-585"   # account dir exists, but NO logs/ — rc-spawn shape
  ln -sf "$LOGS_READ" "$root/platform/scripts/logs-read.sh"
  mkdir -p "$HOME/.$(basename "$root")/logs" # server.log home for best-effort missing-on-resolve
  local cfg="$root/cfg/.claude"
  mkdir -p "$cfg/sessions" "$cfg/projects/-fake-cwd"
  echo "$root/platform/scripts/logs-read.sh $cfg"
}

cleanup_tree() {
  local root="$1"
  rm -rf "$root"
  rm -rf "$HOME/.$(basename "$root")"
}

# Write a parent JSONL with a couple of turns.
write_parent() {
  local cfg="$1" uuid="$2"
  local f="$cfg/projects/-fake-cwd/${uuid}.jsonl"
  {
    printf '%s\n' '{"type":"user","timestamp":"2026-05-31T19:40:00.000Z","uuid":"p1","message":{"role":"user","content":"make me a pdf"}}'
    printf '%s\n' '{"type":"assistant","timestamp":"2026-05-31T19:40:02.000Z","uuid":"p2","message":{"role":"assistant","model":"claude-opus-4-8","content":[{"type":"text","text":"dispatching content-producer"}]}}'
  } > "$f"
}

# Write a FAILING subagent transcript (is_error tool_result) + its meta.
write_subagent_failed() {
  local cfg="$1" parent_uuid="$2" agent="$3" atype="$4"
  local d="$cfg/projects/-fake-cwd/${parent_uuid}/subagents"
  mkdir -p "$d"
  {
    printf '%s\n' '{"type":"user","timestamp":"2026-05-31T19:40:10.000Z","uuid":"s1","message":{"role":"user","content":"produce the pdf and deliver it"}}'
    printf '%s\n' '{"type":"assistant","timestamp":"2026-05-31T19:40:20.000Z","uuid":"s2","message":{"role":"assistant","content":[{"type":"tool_use","id":"toolu_DEAD","name":"Bash","input":{"command":"SendUserFile /tmp/out.pdf"}}]}}'
    printf '%s\n' '{"type":"user","timestamp":"2026-05-31T19:40:21.000Z","uuid":"s3","message":{"role":"user","content":[{"type":"tool_result","tool_use_id":"toolu_DEAD","is_error":true,"content":"/bin/bash: line 1: SendUserFile: command not found"}]},"toolUseResult":"Error: Exit code 127"}'
  } > "$d/${agent}.jsonl"
  printf '{"agentType":"%s","description":"render pdf"}\n' "$atype" > "$d/${agent}.meta.json"
}

# Write a parent JSONL that contains its OWN failing tool_result (NOT a
# subagent error — a parent Bash that exited non-zero). Used to prove the
# parent's own errors are not mislabelled as subagent errors.
write_parent_with_error() {
  local cfg="$1" uuid="$2"
  local f="$cfg/projects/-fake-cwd/${uuid}.jsonl"
  {
    printf '%s\n' '{"type":"user","timestamp":"2026-05-31T19:39:00.000Z","uuid":"e1","message":{"role":"user","content":"check the repo"}}'
    printf '%s\n' '{"type":"assistant","timestamp":"2026-05-31T19:39:02.000Z","uuid":"e2","message":{"role":"assistant","content":[{"type":"tool_use","id":"toolu_PARENT","name":"Bash","input":{"command":"grep nonesuch ."}}]}}'
    printf '%s\n' '{"type":"user","timestamp":"2026-05-31T19:39:03.000Z","uuid":"e3","message":{"role":"user","content":[{"type":"tool_result","tool_use_id":"toolu_PARENT","is_error":true,"content":"Exit code 1 PARENT_OWN_GREP_MISS"}]},"toolUseResult":"Error: Exit code 1"}'
  } > "$f"
}

# Write a CLEAN subagent transcript (no errors) + its meta.
write_subagent_clean() {
  local cfg="$1" parent_uuid="$2" agent="$3" atype="$4"
  local d="$cfg/projects/-fake-cwd/${parent_uuid}/subagents"
  mkdir -p "$d"
  {
    printf '%s\n' '{"type":"user","timestamp":"2026-05-31T19:41:10.000Z","uuid":"q1","message":{"role":"user","content":"review the diff"}}'
    printf '%s\n' '{"type":"assistant","timestamp":"2026-05-31T19:41:12.000Z","uuid":"q2","message":{"role":"assistant","content":[{"type":"tool_use","id":"toolu_OK","name":"Read","input":{"file_path":"/x"}}]}}'
    printf '%s\n' '{"type":"user","timestamp":"2026-05-31T19:41:13.000Z","uuid":"q3","message":{"role":"user","content":[{"type":"tool_result","tool_use_id":"toolu_OK","content":"ok"}]}}'
  } > "$d/${agent}.jsonl"
  printf '{"agentType":"%s","description":"review"}\n' "$atype" > "$d/${agent}.meta.json"
}

write_pidfile() {
  local cfg="$1" pid="$2" uuid="$3" bridge="$4"
  printf '{"pid":%s,"sessionId":"%s","bridgeSessionId":"session_%s","cwd":"/fake/cwd","startedAt":1780000000000}\n' \
    "$pid" "$uuid" "$bridge" > "$cfg/sessions/${pid}.json"
}

# meta sidecar carrying bridgeIds (persistent bridge->uuid map, survives PID-file deletion).
write_meta_bridgeids() {
  local cfg="$1" uuid="$2" bridge="$3"
  printf '{"senderId":"s1","role":"admin","channel":"browser","url":null,"startedAt":"2026-05-31T19:40:00.000Z","permissionMode":null,"effectivePermissionMode":null,"model":null,"hidden":false,"specialist":null,"scopeUnitToken":null,"bridgeIds":["%s"]}\n' \
    "$bridge" > "$cfg/projects/-fake-cwd/${uuid}.meta.json"
}

# Parent transcript whose BODY embeds a bridge key string (no pidfile, no
# bridgeIds) — exercises the content-grep last-resort resolver tier.
write_parent_with_key_in_body() {
  local cfg="$1" uuid="$2" keystr="$3"
  printf '%s\n' "{\"type\":\"user\",\"timestamp\":\"2026-05-31T19:38:00.000Z\",\"uuid\":\"b1\",\"message\":{\"role\":\"user\",\"content\":\"resumed from ${keystr} earlier\"}}" \
    > "$cfg/projects/-fake-cwd/${uuid}.jsonl"
}

run_case() {
  local name="$1"; shift
  echo ""
  echo "=== CASE: $name ==="
  if "$@"; then
    PASS=$((PASS + 1)); echo "PASS: $name"
  else
    FAIL=$((FAIL + 1)); echo "FAIL: $name"
  fi
}

# Capture stdout, stderr, rc of a logs-read.sh invocation under a fake config dir.
# Sets globals: OUT ERR RC.
invoke() {
  local script="$1" cfg="$2"; shift 2
  local errf; errf=$(mktemp)
  RC=0
  OUT=$(CLAUDE_CONFIG_DIR="$cfg" "$script" "$@" 2>"$errf") || RC=$?
  ERR=$(cat "$errf"); rm -f "$errf"
}

# --- Case A: bare key via PID-file bridge → timeline + flagged subagent error ---
case_bridge_timeline_flagged() {
  local root="/tmp/maxy-585-A-$$"; local out
  out=$(setup_tree "$root"); local script="${out% *}"; local cfg="${out#* }"
  write_parent "$cfg" "$UUID_A"
  write_subagent_failed "$cfg" "$UUID_A" "agent-aDEAD0000" "content-producer"
  write_pidfile "$cfg" 4242 "$UUID_A" "$BRIDGE"

  invoke "$script" "$cfg" "session_${BRIDGE}"
  cleanup_tree "$root"

  [[ $RC -eq 0 ]]                        || { echo "  expected exit 0, got $RC; err=$ERR"; return 1; }
  [[ "$OUT" == *"content-producer"* ]]   || { echo "  missing agentType content-producer"; return 1; }
  [[ "$OUT" == *"SendUserFile"* ]]       || { echo "  missing failing command SendUserFile"; return 1; }
  [[ "$OUT" == *"command not found"* ]]  || { echo "  missing error text"; return 1; }
  [[ "$OUT" == *"SUBAGENT ERROR"* ]]     || { echo "  missing SUBAGENT ERROR flag"; return 1; }
  [[ "$OUT" == *"6031941e"* ]]           || { echo "  missing resolved session uuid"; return 1; }
  return 0
}

# --- Case B: clean session → timeline, NO error flag ---
case_clean_no_flag() {
  local root="/tmp/maxy-585-B-$$"; local out
  out=$(setup_tree "$root"); local script="${out% *}"; local cfg="${out#* }"
  write_parent "$cfg" "$UUID_A"
  write_subagent_clean "$cfg" "$UUID_A" "agent-aCLEAN000" "code-reviewer"
  write_pidfile "$cfg" 4243 "$UUID_A" "$BRIDGE"

  invoke "$script" "$cfg" "session_${BRIDGE}"
  cleanup_tree "$root"

  [[ $RC -eq 0 ]]                       || { echo "  expected exit 0, got $RC; err=$ERR"; return 1; }
  [[ "$OUT" == *"code-reviewer"* ]]     || { echo "  missing clean subagent in timeline"; return 1; }
  [[ "$OUT" != *"SUBAGENT ERROR"* ]]    || { echo "  unexpected SUBAGENT ERROR flag on clean session"; return 1; }
  return 0
}

# --- Case C: resolve via bridgeIds sidecar when PID file is absent ---
case_resolve_via_bridgeids() {
  local root="/tmp/maxy-585-C-$$"; local out
  out=$(setup_tree "$root"); local script="${out% *}"; local cfg="${out#* }"
  write_parent "$cfg" "$UUID_C"
  write_subagent_failed "$cfg" "$UUID_C" "agent-aBEEF0000" "content-producer"
  write_meta_bridgeids "$cfg" "$UUID_C" "ENDEDBRIDGE9"   # no pidfile — session ended

  invoke "$script" "$cfg" "session_ENDEDBRIDGE9"
  cleanup_tree "$root"

  [[ $RC -eq 0 ]]              || { echo "  expected exit 0, got $RC; err=$ERR"; return 1; }
  [[ "$OUT" == *"c0c0c0c0"* ]] || { echo "  did not resolve UUID_C via bridgeIds"; return 1; }
  return 0
}

# --- Case D: resolve via UUID directly (full) ---
case_resolve_uuid_direct() {
  local root="/tmp/maxy-585-D-$$"; local out
  out=$(setup_tree "$root"); local script="${out% *}"; local cfg="${out#* }"
  write_parent "$cfg" "$UUID_A"
  write_subagent_clean "$cfg" "$UUID_A" "agent-aOK000000" "code-reviewer"

  invoke "$script" "$cfg" "$UUID_A"
  cleanup_tree "$root"

  [[ $RC -eq 0 ]]             || { echo "  expected exit 0, got $RC; err=$ERR"; return 1; }
  [[ "$OUT" == *"6031941e"* ]] || { echo "  did not return timeline for UUID_A"; return 1; }
  return 0
}

# --- Case E: --scan-subagent-errors lists failed subagent only ---
case_scan_lists_failed_only() {
  local root="/tmp/maxy-585-E-$$"; local out
  out=$(setup_tree "$root"); local script="${out% *}"; local cfg="${out#* }"
  write_parent "$cfg" "$UUID_A"
  write_subagent_failed "$cfg" "$UUID_A" "agent-aDEAD0000" "content-producer"
  write_parent "$cfg" "$UUID_CLEAN"
  write_subagent_clean "$cfg" "$UUID_CLEAN" "agent-aSAFE0000" "code-reviewer"

  invoke "$script" "$cfg" "--scan-subagent-errors"
  cleanup_tree "$root"

  [[ $RC -eq 0 ]]                          || { echo "  expected exit 0, got $RC; err=$ERR"; return 1; }
  [[ "$OUT" == *"agent-aDEAD0000"* ]]      || { echo "  failed subagent not listed"; return 1; }
  [[ "$OUT" == *"content-producer"* ]]     || { echo "  failed subagent agentType missing"; return 1; }
  [[ "$OUT" == *"command not found"* ]]    || { echo "  error text missing in scan"; return 1; }
  [[ "$OUT" != *"agent-aSAFE0000"* ]]      || { echo "  clean subagent wrongly listed"; return 1; }
  return 0
}

# --- Case G: parent's OWN tool error is not mislabelled as a subagent error ---
case_parent_error_not_subagent() {
  local root="/tmp/maxy-585-G-$$"; local out
  out=$(setup_tree "$root"); local script="${out% *}"; local cfg="${out#* }"
  write_parent_with_error "$cfg" "$UUID_A"
  write_subagent_clean "$cfg" "$UUID_A" "agent-aCLEAN111" "code-reviewer"
  write_pidfile "$cfg" 4244 "$UUID_A" "$BRIDGE"

  invoke "$script" "$cfg" "session_${BRIDGE}"
  cleanup_tree "$root"

  [[ $RC -eq 0 ]]                          || { echo "  expected exit 0, got $RC; err=$ERR"; return 1; }
  [[ "$OUT" == *"PARENT_OWN_GREP_MISS"* ]] || { echo "  parent tool error not shown in timeline"; return 1; }
  [[ "$OUT" != *"SUBAGENT ERROR"* ]]       || { echo "  parent error wrongly tagged SUBAGENT ERROR"; return 1; }
  [[ "$OUT" == *"subagent-errors: 0"* ]]   || { echo "  header miscounts parent error as subagent error"; return 1; }
  return 0
}

# --- Case I: content-grep resolves a unique body match (last-resort tier) ---
case_content_grep_single() {
  local root="/tmp/maxy-585-I-$$"; local out
  out=$(setup_tree "$root"); local script="${out% *}"; local cfg="${out#* }"
  write_parent_with_key_in_body "$cfg" "$UUID_A" "session_SOLOKEYxyz1"  # no pidfile, no bridgeIds

  invoke "$script" "$cfg" "session_SOLOKEYxyz1"
  cleanup_tree "$root"

  [[ $RC -eq 0 ]]              || { echo "  expected exit 0 (single content match), got $RC; err=$ERR"; return 1; }
  [[ "$OUT" == *"6031941e"* ]] || { echo "  content-grep did not resolve the unique match"; return 1; }
  return 0
}

# --- Case H: content-grep refuses when the key matches multiple transcripts ---
case_content_grep_ambiguous_refuses() {
  local root="/tmp/maxy-585-H-$$"; local out
  out=$(setup_tree "$root"); local script="${out% *}"; local cfg="${out#* }"
  write_parent_with_key_in_body "$cfg" "$UUID_A" "session_AMBIGKEYxyz"
  write_parent_with_key_in_body "$cfg" "$UUID_C" "session_AMBIGKEYxyz"  # same key in two bodies

  invoke "$script" "$cfg" "session_AMBIGKEYxyz"
  cleanup_tree "$root"

  # Must NOT silently pick one of the two — refuse (exit 1), like uuid-prefix ambiguity.
  [[ $RC -eq 1 ]] || { echo "  expected exit 1 (ambiguous content match), got $RC; out=$OUT"; return 1; }
  return 0
}

# --- Case F: unresolvable key → exit 1 (clean miss, not a crash) ---
case_unresolvable_miss() {
  local root="/tmp/maxy-585-F-$$"; local out
  out=$(setup_tree "$root"); local script="${out% *}"; local cfg="${out#* }"
  write_parent "$cfg" "$UUID_A"

  invoke "$script" "$cfg" "session_NOSUCHBRIDGE"
  cleanup_tree "$root"

  [[ $RC -eq 1 ]] || { echo "  expected exit 1 on unresolvable key, got $RC"; return 1; }
  return 0
}

run_case "bare key via PID bridge → timeline + flagged subagent error" case_bridge_timeline_flagged
run_case "clean session → timeline, no error flag"                     case_clean_no_flag
run_case "resolve via bridgeIds sidecar (PID file absent)"             case_resolve_via_bridgeids
run_case "resolve via full UUID directly"                              case_resolve_uuid_direct
run_case "--scan-subagent-errors lists failed subagent only"           case_scan_lists_failed_only
run_case "parent's own tool error not mislabelled SUBAGENT ERROR"      case_parent_error_not_subagent
run_case "content-grep resolves a unique body match"                   case_content_grep_single
run_case "content-grep refuses an ambiguous (multi) body match"        case_content_grep_ambiguous_refuses
run_case "unresolvable key → exit 1"                                   case_unresolvable_miss

echo ""
echo "================================================"
echo " Passed: $PASS / Failed: $FAIL"
echo "================================================"
[[ $FAIL -eq 0 ]]
