#!/usr/bin/env bash
# ─────────────────────────────────────────────────────────────────────────────
# Agent Recon — macOS Setup
# Run this script on macOS to wire the Agent Recon hooks into Claude Code.
#
#   bash setup-macos.sh
#
# What it does:
#   1. Creates ~/.claude/hooks/
#   2. Copies (or updates) send-event.js there
#   3. Removes any legacy send-event*.py hook script
#   4. Writes (or merges) ~/.claude/settings.json with all 24 hook registrations
#   5. Tests connectivity to the Agent Recon server
#   6. Verifies macOS Keychain access
#   7. Optionally installs a launchd agent for auto-start
# ─────────────────────────────────────────────────────────────────────────────
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
HOOK_SRC="$SCRIPT_DIR/.claude/hooks/send-event.js"
HOOK_DST="$HOME/.claude/hooks/send-event.js"
SETTINGS="$HOME/.claude/settings.json"
HOOK_CMD="node $HOOK_DST"
PLIST_SRC="$SCRIPT_DIR/service/com.agent-recon.server.plist"
PLIST_DST="$HOME/Library/LaunchAgents/com.agent-recon.server.plist"

# ── Validate ─────────────────────────────────────────────────────────────────
if [[ "$(uname -s)" != "Darwin" ]]; then
  echo "ERROR: This setup script is for macOS only."
  echo "  On WSL, use: bash setup-wsl.sh"
  echo "  On Windows, use: .\\setup.ps1"
  exit 1
fi

if [[ ! -f "$HOOK_SRC" ]]; then
  echo "ERROR: Cannot find hook source at $HOOK_SRC"
  echo "  Run this script from the agent-recon project root directory"
  exit 1
fi

if ! command -v node &>/dev/null; then
  echo "ERROR: node not found."
  echo "  Install via Homebrew: brew install node"
  echo "  Or download from: https://nodejs.org/"
  exit 1
fi

# ── Install / sync hook script ────────────────────────────────────────────────
mkdir -p "$HOME/.claude/hooks"

if [[ -f "$HOOK_DST" ]]; then
  SRC_HASH=$(shasum -a 256 "$HOOK_SRC" | cut -d' ' -f1)
  DST_HASH=$(shasum -a 256 "$HOOK_DST" | cut -d' ' -f1)
  if [[ "$SRC_HASH" != "$DST_HASH" ]]; then
    echo "↻  Hook script updated — syncing to $HOOK_DST"
    cp "$HOOK_SRC" "$HOOK_DST"
    chmod +x "$HOOK_DST"
    echo "✓  Synced"
  else
    echo "✓  Hook script already up-to-date: $HOOK_DST"
  fi
else
  cp "$HOOK_SRC" "$HOOK_DST"
  chmod +x "$HOOK_DST"
  echo "✓  Installed hook forwarder → $HOOK_DST"
fi

# ── Remove legacy Python hook scripts (v1.0.x → v1.1.x migration) ────────────
for legacy in "$HOME/.claude/hooks/send-event.py" "$HOME/.claude/hooks/send-event-wsl.py"; do
  if [[ -f "$legacy" ]]; then
    rm -f "$legacy" && echo "✓  Removed legacy hook: $(basename "$legacy")"
  fi
done

# ── Test Agent Recon server connectivity ─────────────────────────────────────
echo ""
echo "── Connectivity check ──────────────────────────────────────────────────"

REACHABLE=false
if curl -sf --max-time 2 "http://localhost:3131/health" > /dev/null 2>&1; then
  echo "  ✓ localhost:3131  REACHABLE"
  REACHABLE=true
else
  echo "  ✗ localhost:3131  not reachable"
fi

if [[ "$REACHABLE" != "true" ]]; then
  echo ""
  echo "  ⚠  Agent Recon server not reachable."
  echo "     Start it first:"
  echo "       cd $SCRIPT_DIR/server && node start.js"
  echo "     Then re-run this script to verify."
else
  echo ""
  echo "  ✓ Server reachable at localhost:3131"
fi

# ── Verify macOS Keychain access ────────────────────────────────────────────
echo ""
echo "── Keychain access check ─────────────────────────────────────────────"

KEYCHAIN_OK=false
KEYCHAIN_SVC="com.agent-recon.setup-test"
KEYCHAIN_ACCT="setup-verify"
KEYCHAIN_PASS="agent-recon-test-$$"

if security add-generic-password \
    -s "$KEYCHAIN_SVC" -a "$KEYCHAIN_ACCT" -w "$KEYCHAIN_PASS" \
    -U 2>/dev/null; then
  # Read it back
  RETRIEVED=$(security find-generic-password \
    -s "$KEYCHAIN_SVC" -a "$KEYCHAIN_ACCT" -w 2>/dev/null || true)
  if [[ "$RETRIEVED" == "$KEYCHAIN_PASS" ]]; then
    KEYCHAIN_OK=true
    echo "  ✓ Keychain read/write verified"
  else
    echo "  ⚠ Keychain write succeeded but read returned unexpected value"
  fi
  # Clean up
  security delete-generic-password \
    -s "$KEYCHAIN_SVC" -a "$KEYCHAIN_ACCT" 2>/dev/null || true
else
  echo "  ⚠ Keychain write failed — credential storage may not work"
  echo "    This is non-blocking; hooks will still function."
fi

# ── Write settings.json ───────────────────────────────────────────────────────
echo ""
echo "── Hook registration ───────────────────────────────────────────────────"

if [[ -f "$SETTINGS" ]]; then
  node - "$SETTINGS" "$HOOK_CMD" <<'NODEEOF'
const fs = require('fs');
const settingsPath = process.argv[2];
const hookCmd      = process.argv[3];

let cfg = {};
try { cfg = JSON.parse(fs.readFileSync(settingsPath, 'utf8')); } catch {}
if (!cfg || typeof cfg !== 'object') cfg = {};
if (!cfg.hooks) cfg.hooks = {};
const hooks = cfg.hooks;

function hookEntry(matcher) {
  const entry = { type: 'command', command: hookCmd, async: true, timeout: 10 };
  return matcher ? { matcher: '', hooks: [entry] } : { hooks: [entry] };
}

// Keep in sync with installer/steps/hooks.js
const EVENTS_PLAIN = [
  'SessionStart','SessionEnd','UserPromptSubmit',
  'SubagentStart','SubagentStop','Stop','TeammateIdle','TaskCompleted',
  'StopFailure','PostCompact','InstructionsLoaded','ConfigChange',
  'CwdChanged','FileChanged',
  'TaskCreated','ElicitationResult',
];
const EVENTS_MATCHER = [
  'PreToolUse','PostToolUse','PostToolUseFailure','Notification','PreCompact',
  'PermissionRequest','Elicitation','PermissionDenied',
];

// Strip legacy Python hook registrations (preserve third-party hooks)
for (const ev of Object.keys(hooks)) {
  if (!Array.isArray(hooks[ev])) continue;
  hooks[ev] = hooks[ev].filter(group => {
    const hs = group.hooks || [];
    return !hs.some(h => h.command && /send-event(-wsl)?\.py/.test(h.command));
  });
  if (hooks[ev].length === 0) delete hooks[ev];
}

function alreadyRegistered(groups) {
  for (const g of (groups || [])) {
    for (const h of (g.hooks || [])) {
      if (h.command === hookCmd) return true;
    }
  }
  return false;
}

const added = [];
for (const ev of EVENTS_PLAIN) {
  if (!hooks[ev]) { hooks[ev] = [hookEntry(false)]; added.push(ev); }
  else if (!alreadyRegistered(hooks[ev])) { hooks[ev].push(hookEntry(false)); added.push(ev); }
}
for (const ev of EVENTS_MATCHER) {
  if (!hooks[ev]) { hooks[ev] = [hookEntry(true)]; added.push(ev); }
  else if (!alreadyRegistered(hooks[ev])) { hooks[ev].push(hookEntry(true)); added.push(ev); }
}

// Strip deprecated events — only remove AR hooks, preserve third-party hooks
const DEPRECATED = ['WorktreeCreate', 'WorktreeRemove'];
for (const ev of DEPRECATED) {
  if (!hooks[ev]) continue;
  hooks[ev] = hooks[ev].filter(g => !(g.hooks || []).some(h => h.command === hookCmd));
  if (hooks[ev].length === 0) delete hooks[ev];
}

fs.writeFileSync(settingsPath, JSON.stringify(cfg, null, 2) + '\n');

if (added.length) {
  console.log('✓  Merged hooks into', settingsPath);
  console.log('   Added:', added.join(', '));
} else {
  console.log('✓  All hooks already present in', settingsPath, '— nothing changed');
}
NODEEOF
else
  # Keep in sync with installer/steps/hooks.js
  cat > "$SETTINGS" <<SETTINGSEOF
{
  "hooks": {
    "SessionStart":        [{"hooks": [{"type": "command","command": "$HOOK_CMD","async": true,"timeout": 10}]}],
    "SessionEnd":          [{"hooks": [{"type": "command","command": "$HOOK_CMD","async": true,"timeout": 10}]}],
    "UserPromptSubmit":    [{"hooks": [{"type": "command","command": "$HOOK_CMD","async": true,"timeout": 10}]}],
    "PreToolUse":          [{"matcher": "","hooks": [{"type": "command","command": "$HOOK_CMD","async": true,"timeout": 10}]}],
    "PostToolUse":         [{"matcher": "","hooks": [{"type": "command","command": "$HOOK_CMD","async": true,"timeout": 10}]}],
    "PostToolUseFailure":  [{"matcher": "","hooks": [{"type": "command","command": "$HOOK_CMD","async": true,"timeout": 10}]}],
    "SubagentStart":       [{"hooks": [{"type": "command","command": "$HOOK_CMD","async": true,"timeout": 10}]}],
    "SubagentStop":        [{"hooks": [{"type": "command","command": "$HOOK_CMD","async": true,"timeout": 10}]}],
    "Stop":                [{"hooks": [{"type": "command","command": "$HOOK_CMD","async": true,"timeout": 10}]}],
    "Notification":        [{"matcher": "","hooks": [{"type": "command","command": "$HOOK_CMD","async": true,"timeout": 10}]}],
    "TeammateIdle":        [{"hooks": [{"type": "command","command": "$HOOK_CMD","async": true,"timeout": 10}]}],
    "TaskCompleted":       [{"hooks": [{"type": "command","command": "$HOOK_CMD","async": true,"timeout": 10}]}],
    "PreCompact":          [{"matcher": "","hooks": [{"type": "command","command": "$HOOK_CMD","async": true,"timeout": 10}]}],
    "StopFailure":         [{"hooks": [{"type": "command","command": "$HOOK_CMD","async": true,"timeout": 10}]}],
    "PostCompact":         [{"hooks": [{"type": "command","command": "$HOOK_CMD","async": true,"timeout": 10}]}],
    "InstructionsLoaded":  [{"hooks": [{"type": "command","command": "$HOOK_CMD","async": true,"timeout": 10}]}],
    "ConfigChange":        [{"hooks": [{"type": "command","command": "$HOOK_CMD","async": true,"timeout": 10}]}],
    "CwdChanged":          [{"hooks": [{"type": "command","command": "$HOOK_CMD","async": true,"timeout": 10}]}],
    "FileChanged":         [{"hooks": [{"type": "command","command": "$HOOK_CMD","async": true,"timeout": 10}]}],
    "TaskCreated":         [{"hooks": [{"type": "command","command": "$HOOK_CMD","async": true,"timeout": 10}]}],
    "ElicitationResult":   [{"hooks": [{"type": "command","command": "$HOOK_CMD","async": true,"timeout": 10}]}],
    "PermissionRequest":   [{"matcher": "","hooks": [{"type": "command","command": "$HOOK_CMD","async": true,"timeout": 10}]}],
    "Elicitation":         [{"matcher": "","hooks": [{"type": "command","command": "$HOOK_CMD","async": true,"timeout": 10}]}],
    "PermissionDenied":    [{"matcher": "","hooks": [{"type": "command","command": "$HOOK_CMD","async": true,"timeout": 10}]}]
  }
}
SETTINGSEOF
  # Validate JSON
  if ! node -e "JSON.parse(require('fs').readFileSync('$SETTINGS','utf8'))" > /dev/null 2>&1; then
    echo "⚠  Warning: generated settings.json may be malformed"
  fi
  echo "✓  Created $SETTINGS"
fi

# ── Optional launchd agent ────────────────────────────────────────────────────
echo ""
echo "── Launchd auto-start (optional) ────────────────────────────────────"

if [[ -f "$PLIST_SRC" ]]; then
  echo "  A launchd plist is available to auto-start the Agent Recon server on login."
  echo ""
  read -rp "  Install launchd agent for auto-start? [y/N] " INSTALL_LAUNCHD
  if [[ "${INSTALL_LAUNCHD:-n}" =~ ^[Yy]$ ]]; then
    mkdir -p "$HOME/Library/LaunchAgents"
    # Copy and replace placeholders
    sed -e "s|{{INSTALL_DIR}}|$SCRIPT_DIR|g" \
        -e "s|{{HOME}}|$HOME|g" \
        "$PLIST_SRC" > "$PLIST_DST"
    echo "  ✓ Installed plist → $PLIST_DST"

    # Load the agent
    if launchctl load "$PLIST_DST" 2>/dev/null; then
      echo "  ✓ Launchd agent loaded — server will start on login and restart on crash"
    else
      echo "  ⚠ launchctl load failed — you may need to load it manually:"
      echo "      launchctl load $PLIST_DST"
    fi

    echo ""
    echo "  To uninstall later:"
    echo "    launchctl unload $PLIST_DST"
    echo "    rm $PLIST_DST"
  else
    echo "  Skipped. To install later:"
    echo "    sed -e 's|{{INSTALL_DIR}}|$SCRIPT_DIR|g' -e 's|{{HOME}}|$HOME|g' \\"
    echo "        $PLIST_SRC > $PLIST_DST"
    echo "    launchctl load $PLIST_DST"
  fi
else
  echo "  ⚠ Plist template not found at $PLIST_SRC — skipping launchd setup"
fi

# ── Summary ───────────────────────────────────────────────────────────────────
echo ""
echo "────────────────────────────────────────────────────────────────────────"
echo "  macOS setup complete!"
echo ""
echo "  Hook script : $HOOK_DST"
echo "  Settings    : $SETTINGS"
echo "  Server URL  : http://localhost:3131"
echo ""
echo "  Start server: cd $SCRIPT_DIR/server && node start.js"
echo "  (start.js auto-rebuilds the SQLite binary if needed)"
echo ""
echo "  Start a Claude session in any directory and events will"
echo "  stream to http://localhost:3131"
echo ""
echo "  View logs   : tail -f ~/Library/Logs/agent-recon.log"
echo "  Debug mode  : AGENT_RECON_DEBUG=1 claude  →  ~/.claude/agent-recon-debug.log"
echo "────────────────────────────────────────────────────────────────────────"
