#!/usr/bin/env bash
# PQS slash-command installer
# Usage: curl -fsSL https://pqs.onchainintel.net/install.sh | bash
#
# What this does:
#   1. Computes a machine fingerprint (sha256 of hostname + user + OS).
#   2. Calls POST https://pqs.onchainintel.net/api/keys/generate to mint
#      (or recover) a PQS_ API key tied to that fingerprint.
#   3. Writes the key to ~/.pqs/config in KEY=VALUE format (mode 600).
#   4. Optionally copies all PQS slash commands into ~/.claude/commands if
#      Claude Code is installed:
#        /pqs-score    — single prompt, 8-dimension score + top fixes
#        /pqs-optimize — score + rewrite if below 60/80, then re-score
#        /pqs-batch    — bulk-score a file of prompts, writes results.md
#
# Re-running on the same machine returns the same key (idempotent server-side).

set -euo pipefail

API_BASE="${PQS_API_BASE:-https://pqs.onchainintel.net}"
CONFIG_DIR="${HOME}/.pqs"
CONFIG_FILE="${CONFIG_DIR}/config"
CLAUDE_CMD_DIR="${HOME}/.claude/commands"

say()  { printf '%s\n' "$*"; }
die()  { printf 'error: %s\n' "$*" >&2; exit 1; }

# --- dep check ---------------------------------------------------------------
command -v curl >/dev/null 2>&1 || die "curl is required"

if   command -v sha256sum >/dev/null 2>&1; then HASH_CMD='sha256sum'
elif command -v shasum    >/dev/null 2>&1; then HASH_CMD='shasum -a 256'
else die "need sha256sum or shasum on PATH"
fi

# --- fingerprint -------------------------------------------------------------
HOST="$(hostname 2>/dev/null || echo unknown-host)"
USER_NAME="$(id -un 2>/dev/null || echo unknown-user)"
OS_NAME="$(uname -s 2>/dev/null || echo unknown-os)"

FP_HEX="$(printf '%s|%s|%s' "$HOST" "$USER_NAME" "$OS_NAME" | $HASH_CMD | awk '{print $1}')"
FINGERPRINT="sha256:${FP_HEX}"

say "[pqs] fingerprint: ${FINGERPRINT}"

# --- mint or recover ---------------------------------------------------------
say "[pqs] requesting key from ${API_BASE}/api/keys/generate"

PAYLOAD="$(printf '{"fingerprint":"%s"}' "$FINGERPRINT")"

HTTP_BODY="$(mktemp -t pqs-install.XXXXXX)"
trap 'rm -f "$HTTP_BODY"' EXIT

HTTP_CODE="$(curl -sS -o "$HTTP_BODY" -w '%{http_code}' \
  -X POST "${API_BASE}/api/keys/generate" \
  -H 'Content-Type: application/json' \
  -d "$PAYLOAD")" || die "network error calling /api/keys/generate"

case "$HTTP_CODE" in
  200|201) : ;;
  *)
    say "[pqs] server responded ${HTTP_CODE}:"
    cat "$HTTP_BODY" >&2 || true
    die "key generation failed"
    ;;
esac

# Extract the key without jq. Matches "key":"..." in the JSON body. The real
# key format is PQS_ + base64url chars, so we match a non-quote run.
KEY="$(sed -n 's/.*"key"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$HTTP_BODY")"
[ -n "$KEY" ] || die "could not parse key from server response: $(cat "$HTTP_BODY")"

case "$KEY" in
  PQS_*) : ;;
  *) die "server returned unexpected key format: $KEY" ;;
esac

# --- write config ------------------------------------------------------------
mkdir -p "$CONFIG_DIR"
chmod 700 "$CONFIG_DIR"
umask_old="$(umask)"
umask 077
{
  printf '# PQS config — generated by install.sh on %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
  printf 'PQS_API_KEY=%s\n' "$KEY"
  printf 'PQS_API_BASE=%s\n' "$API_BASE"
  printf 'PQS_FINGERPRINT=%s\n' "$FINGERPRINT"
} > "$CONFIG_FILE"
umask "$umask_old"
chmod 600 "$CONFIG_FILE"

say "[pqs] wrote $CONFIG_FILE (chmod 600)"

# --- optional: install slash commands for Claude Code ------------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" 2>/dev/null && pwd || echo .)"

# Every slash command shipped by this repo. To add another, append the basename
# (without .md) to this list — no other changes required.
PQS_COMMANDS=(pqs-score pqs-optimize pqs-batch)

CMD_INSTALLED_COUNT=0
CMD_FAILED_COUNT=0
CMD_FAILED_NAMES=()

install_one_cmd() {
  local name="$1"
  local local_src="${SCRIPT_DIR}/.claude/commands/${name}.md"
  local dest="${CLAUDE_CMD_DIR}/${name}.md"

  if [ -f "$local_src" ]; then
    cp "$local_src" "$dest"
    say "[pqs] installed /${name} from local checkout"
    CMD_INSTALLED_COUNT=$((CMD_INSTALLED_COUNT + 1))
    return 0
  fi
  if curl -fsSL "${API_BASE}/${name}.md" -o "$dest" 2>/dev/null; then
    say "[pqs] installed /${name} from ${API_BASE}/${name}.md"
    CMD_INSTALLED_COUNT=$((CMD_INSTALLED_COUNT + 1))
    return 0
  fi
  say "[pqs] skipped /${name} (could not fetch ${name}.md)"
  CMD_FAILED_COUNT=$((CMD_FAILED_COUNT + 1))
  CMD_FAILED_NAMES+=("$name")
  return 0
}

install_cmds() {
  mkdir -p "$CLAUDE_CMD_DIR"
  for cmd in "${PQS_COMMANDS[@]}"; do
    install_one_cmd "$cmd" || true
  done
}
install_cmds || true

say ""
say "  done. your key is in ~/.pqs/config"

if [ "$CMD_INSTALLED_COUNT" -gt 0 ]; then
  say ""
  say "  installed ${CMD_INSTALLED_COUNT} slash command(s) into ${CLAUDE_CMD_DIR}/"
  say ""
  say "  IMPORTANT: if Claude Code is already running, quit and relaunch it."
  say "  Claude Code scans ~/.claude/commands/ once at startup and caches the"
  say "  index for the life of the session, so freshly-installed commands will"
  say "  show up as 'Unknown skill' in any session that was already running"
  say "  when this installer fired."
  say ""
  say "  After restart, try:"
  say "    /pqs-score    write me a haiku about postgres"
  say "    /pqs-optimize write me a haiku about postgres"
  say "    /pqs-batch    ./my-prompts.txt"
fi

if [ "$CMD_FAILED_COUNT" -gt 0 ]; then
  say ""
  say "  ${CMD_FAILED_COUNT} command(s) were NOT installed: ${CMD_FAILED_NAMES[*]}"
  say "  To fetch them manually, either re-run this installer from a local"
  say "  checkout of pqs-claude-commands, or grab each file directly:"
  for name in "${CMD_FAILED_NAMES[@]}"; do
    say "    curl -fsSL ${API_BASE}/${name}.md -o ${CLAUDE_CMD_DIR}/${name}.md"
  done
fi
