#!/bin/sh
# Mogger installer. Installs the Mogger skill for Claude Code and Codex and saves an API key.
#
#   curl -fsSL https://mogger.dev/install.sh | sh
#   curl -fsSL https://mogger.dev/install.sh | sh -s -- --uninstall
#
# Environment:
#   MOGGER_BASE      Base URL, default https://mogger.dev.
#   MOGGER_API_KEY   Key to save. Without it the installer asks on the terminal (blank skips).
#
# It writes SKILL.md to ~/.claude/skills/mogger and ~/.codex/skills/mogger, and the key to
# ${XDG_CONFIG_HOME:-$HOME/.config}/mogger/key with owner-only permissions. It never edits
# shell rc files and never prints the key.
set -eu

: "${HOME:?HOME is not set}"
BASE="${MOGGER_BASE:-https://mogger.dev}"
BASE="${BASE%/}"
CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/mogger"
KEY_FILE="$CONFIG_DIR/key"
CLAUDE_DIR="$HOME/.claude/skills/mogger"
CODEX_DIR="$HOME/.codex/skills/mogger"

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

usage() {
  say "Usage: curl -fsSL $BASE/install.sh | sh"
  say "       curl -fsSL $BASE/install.sh | sh -s -- --uninstall"
}

uninstall() {
  for f in "$CLAUDE_DIR/SKILL.md" "$CODEX_DIR/SKILL.md" "$KEY_FILE"; do
    if [ -e "$f" ]; then
      rm -f "$f"
      say "removed $f"
    fi
  done
  # Remove the directories only when they are empty. Backups (SKILL.md.bak) keep them.
  rmdir "$CLAUDE_DIR" "$CODEX_DIR" "$CONFIG_DIR" 2>/dev/null || true
  say "Mogger skill and key removed."
}

case "${1:-}" in
  "") ;;
  --uninstall) uninstall; exit 0 ;;
  -h|--help) usage; exit 0 ;;
  *) usage >&2; exit 2 ;;
esac

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

TMP=""
KEY_TMP=""
cleanup() {
  [ -z "$TMP" ] || rm -f "$TMP"
  [ -z "$KEY_TMP" ] || rm -f "$KEY_TMP"
}
trap cleanup EXIT
trap 'exit 1' INT HUP TERM

# 1. Download the skill and check that it is one.
TMP=$(mktemp "${TMPDIR:-/tmp}/mogger-skill.XXXXXX")
curl -fsSL "$BASE/skills/mogger/SKILL.md" -o "$TMP" || die "could not download $BASE/skills/mogger/SKILL.md"
[ "$(head -c 3 "$TMP")" = "---" ] || die "the downloaded file is not a skill (it does not start with ---)"

# 2. Install it for Claude Code and Codex. A different existing copy is backed up first.
install_skill() {
  dir="$1"
  label="$2"
  dest="$dir/SKILL.md"
  mkdir -p "$dir"
  if [ -f "$dest" ]; then
    if cmp -s "$TMP" "$dest"; then
      say "$label: already up to date ($dest)"
      return 0
    fi
    cp "$dest" "$dest.bak"
    say "$label: backed up the existing file to $dest.bak"
  fi
  cat "$TMP" > "$dest"
  say "$label: installed $dest"
}
install_skill "$CLAUDE_DIR" "Claude Code"
install_skill "$CODEX_DIR" "Codex"

# 3. The skill drives the API with curl and jq.
if ! command -v jq >/dev/null 2>&1; then
  warn "jq is not installed and the skill needs it. Install it with: brew install jq (macOS), apt install jq (Debian, Ubuntu), dnf install jq (Fedora)."
fi

# 4. Key. From the environment, else a hidden prompt on the terminal when there is one.
valid_key() { printf '%s' "$1" | grep -Eq '^mog_[A-Za-z0-9]{32}$'; }

# prompt_key: reads a key without echo and prints it. Runs in a subshell, so it restores the
# terminal itself if it is interrupted.
prompt_key() {
  printf 'Mogger API key (input hidden, press Enter to skip): ' >/dev/tty
  saved=$(stty -g </dev/tty 2>/dev/null || true)
  restore() { if [ -n "$saved" ]; then stty "$saved" </dev/tty 2>/dev/null || true; fi; }
  trap 'restore; exit 1' INT HUP TERM
  stty -echo </dev/tty 2>/dev/null || true
  answer=""
  IFS= read -r answer </dev/tty || answer=""
  restore
  printf '\n' >/dev/tty
  printf '%s' "$answer"
}

# me_check KEY: writes the response body to $KEY_TMP and prints the HTTP status. The key goes to
# curl on stdin, not on the command line, so it never shows in a process list.
me_check() {
  code=$(printf 'header = "Authorization: Bearer %s"\n' "$1" |
    curl -sS -K - -o "$KEY_TMP" -w '%{http_code}' "$BASE/api/v1/me" 2>/dev/null) || code=000
  printf '%s' "$code"
}

KEY="${MOGGER_API_KEY:-}"
KEY=$(printf '%s' "$KEY" | tr -d '[:space:]')
NEW_KEY=0
if [ -z "$KEY" ] && [ ! -s "$KEY_FILE" ]; then
  if ( exec </dev/tty ) 2>/dev/null; then
    KEY=$(prompt_key | tr -d '[:space:]')
  else
    say "No key given and no terminal to ask on. Set MOGGER_API_KEY and run this again, or save a key to $KEY_FILE."
  fi
fi
if [ -n "$KEY" ]; then
  if valid_key "$KEY"; then
    NEW_KEY=1
  else
    warn "that does not look like a Mogger key (expected mog_ followed by 32 letters and digits). Not saved."
    KEY=""
  fi
fi
if [ -z "$KEY" ] && [ -s "$KEY_FILE" ]; then
  KEY=$(tr -d '[:space:]' < "$KEY_FILE")
  say "Using the saved key at $KEY_FILE. Set MOGGER_API_KEY and run this again to replace it."
fi

# 5. Confirm the key, then save it when it is new.
if [ -n "$KEY" ]; then
  KEY_TMP=$(mktemp "${TMPDIR:-/tmp}/mogger-me.XXXXXX")
  http_status=$(me_check "$KEY")
  case "$http_status" in
    200)
      label=""
      remaining=""
      SAVE=1
      if command -v jq >/dev/null 2>&1; then
        label=$(jq -r '.label // empty' "$KEY_TMP" 2>/dev/null || true)
        remaining=$(jq -r '.remaining // empty' "$KEY_TMP" 2>/dev/null || true)
        if [ "$(jq -r '.revoked // false' "$KEY_TMP" 2>/dev/null || true)" = "true" ]; then
          warn "this key has been revoked. Ask for a new one. Not saved."
          SAVE=0
        fi
      fi
      if [ "$SAVE" = 1 ]; then
        if [ -n "$label" ]; then
          say "Key confirmed: $label, ${remaining:-unknown} units remaining."
        else
          say "Key confirmed."
        fi
      fi
      ;;
    401)
      warn "the API rejected this key (HTTP 401). Check it and run this again."
      SAVE=0
      ;;
    *)
      warn "could not confirm the key (HTTP $http_status). Saving it anyway."
      SAVE=1
      ;;
  esac
  if [ "$NEW_KEY" = 1 ] && [ "$SAVE" = 1 ]; then
    (
      umask 077
      mkdir -p "$CONFIG_DIR"
      printf '%s\n' "$KEY" > "$KEY_FILE"
    )
    chmod 600 "$KEY_FILE"
    say "Key saved to $KEY_FILE"
  fi
fi

say "Done. Ask your agent \"does this mog?\" or give it a folder of images to score."
