# shellcheck shell=bash
# lib-dev-hub-datasette.sh — install Datasette (SQLite web UI) via pipx + launchd.
#
# Datasette serves any SQLite file as an interactive web UI + JSON API.
# We install via pipx (isolated venv, no system Python pollution) and run as
# a user-level launchd agent on http://127.0.0.1:8001/.
#
# READ-ONLY by design: every DB is opened with `--immutable` so a stray UI
# action cannot mutate the user's project databases.
#
# Architecture:
#   plist (datasette.plist.tmpl) → ${KIT}/dev-hub/datasette-serve.sh wrapper
#   wrapper discovers SQLite DBs at launch time → exec datasette serve
#   This way newly-added DBs are picked up on next launchctl kickstart, with
#   no plist rewrite needed.
#
# Sources: lib-log.sh (say/warn/err), lib-launchd.sh (install_service/unload_plist).
# Globals read: $KIT_DIR, $HOME_DIR.

# ---------- helpers (private) ----------

# Verify Python 3.11+. Returns 0 on OK, 1 on missing/old.
_datasette_check_python() {
  if ! command -v python3 >/dev/null 2>&1; then
    err "python3 not found — install via: brew install python@3.11"
    return 1
  fi
  local ver
  ver="$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")' 2>/dev/null)"
  local major="${ver%.*}"
  local minor="${ver#*.}"
  if [ "$major" -lt 3 ] || { [ "$major" -eq 3 ] && [ "$minor" -lt 11 ]; }; then
    err "python3 $ver too old (need 3.11+) — install via: brew install python@3.11"
    return 1
  fi
  say "  → python3 $ver OK"
}

# Ensure pipx is installed; if missing, install via brew.
_datasette_ensure_pipx() {
  if command -v pipx >/dev/null 2>&1; then
    say "  → pipx $(pipx --version 2>/dev/null) already installed"
    return 0
  fi
  if ! command -v brew >/dev/null 2>&1; then
    err "neither pipx nor brew found — install Homebrew first: https://brew.sh"
    return 1
  fi
  say "  → installing pipx via brew"
  brew install pipx
  pipx ensurepath
}

# Write the runtime wrapper script that discovers DBs and execs datasette.
# Args: <wrapper-path>.
_datasette_write_wrapper() {
  local wrapper="$1"
  mkdir -p "$(dirname "$wrapper")"
  cat > "$wrapper" <<'WRAPPER_EOF'
#!/usr/bin/env bash
# Auto-generated by lib-dev-hub-datasette.sh — do not edit by hand.
# Discovers SQLite DBs at launch time and serves them via Datasette (read-only).
set -eu
DBS=()
for f in "$HOME/.claude/agents"/*.sqlite "$HOME/Projects"/*/*.sqlite; do
  [ -f "$f" ] && DBS+=("$f")
done
META="$HOME/Library/Application Support/keisei/datasette/metadata.yaml"
# Empty-array expansion is unsafe under `set -u` on bash 3.2 (macOS); branch.
if [ "${#DBS[@]}" -eq 0 ]; then
  exec "$HOME/.local/bin/datasette" serve \
    --host 127.0.0.1 --port 8001 \
    --metadata "$META"
else
  exec "$HOME/.local/bin/datasette" serve \
    --host 127.0.0.1 --port 8001 \
    --metadata "$META" \
    --immutable "${DBS[@]}"
fi
WRAPPER_EOF
  chmod +x "$wrapper"
}

# Write metadata.yaml if not already present (single-user, read-only defaults).
# Args: <data-dir>.
_datasette_write_metadata() {
  local data_dir="$1"
  local meta="$data_dir/metadata.yaml"
  if [ -f "$meta" ]; then
    say "  → metadata.yaml already present (preserving user edits)"
    return 0
  fi
  mkdir -p "$data_dir"
  cat > "$meta" <<'META_EOF'
title: KeiSeiKit DBs
description: |
  Read-only browser for KeiSeiKit SQLite databases (kei-ledger, kei-memory,
  projects-index, etc.) and any project DB under ~/Projects/. Served by
  Datasette in --immutable mode; no UI action can mutate these files.
allow_facet: true
allow_download: false
default_page_size: 100
META_EOF
  say "  → wrote $meta"
}

# ---------- public API ----------

# List SQLite paths to expose to Datasette.
# Walks: ~/.claude/agents/*.sqlite + ~/Projects/*/*.sqlite (depth 2).
# Output: newline-separated paths on stdout.
discover_databases() {
  local f
  for f in "$HOME_DIR/.claude/agents"/*.sqlite "$HOME_DIR/Projects"/*/*.sqlite; do
    [ -f "$f" ] && printf '%s\n' "$f"
  done
}

# Install Datasette + plugins + launchd service. Idempotent.
install_dev_hub_datasette() {
  say "installing dev-hub-datasette"
  _datasette_check_python || return 1
  _datasette_ensure_pipx || return 1

  say "  → pipx install datasette"
  pipx install datasette 2>&1 | grep -v "already seems to be installed" || true

  say "  → injecting plugins (cluster-map, vega, render-markdown)"
  pipx inject datasette \
    datasette-cluster-map \
    datasette-vega \
    datasette-render-markdown 2>&1 | grep -v "already" || true

  local data_dir="$HOME_DIR/Library/Application Support/keisei/datasette"
  _datasette_write_metadata "$data_dir"

  local wrapper="$HOME_DIR/.claude/agents/_primitives/dev-hub/datasette-serve.sh"
  _datasette_write_wrapper "$wrapper"
  say "  → wrote wrapper $wrapper"

  # shellcheck source=./lib-launchd.sh
  . "$KIT_DIR/install/lib-launchd.sh"
  install_service datasette

  say "Datasette running on http://127.0.0.1:8001/. Browse SQLite databases from the cortex-ui dashboard."
}

# Unload + remove plist. Optional --purge also removes the pipx install.
# Args: [--purge]
uninstall_dev_hub_datasette() {
  say "uninstalling dev-hub-datasette"
  # shellcheck source=./lib-launchd.sh
  . "$KIT_DIR/install/lib-launchd.sh"
  unload_plist datasette
  rm -f "$HOME_DIR/.claude/agents/_primitives/dev-hub/datasette-serve.sh"
  if [ "${1:-}" = "--purge" ] && command -v pipx >/dev/null 2>&1; then
    say "  → pipx uninstall datasette"
    pipx uninstall datasette || true
  fi
}

# Verify Datasette health endpoint returns 200. Returns 0 on OK, 1 on fail.
verify_dev_hub_datasette() {
  local code
  code="$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8001/-/health 2>/dev/null || echo 000)"
  if [ "$code" = "200" ]; then
    say "datasette health OK (200)"
    return 0
  fi
  err "datasette health failed (HTTP $code) — check logs at $HOME_DIR/Library/Logs/keisei/datasette/"
  return 1
}
