#!/usr/bin/env bash
# get.kinra.ai installer — a stranger (or a fresh Fedora/macOS box) goes from
#   bash <(curl -fsSL https://get.kinra.ai/install.sh)
# to a running `kin`. A generalization of scripts/setup-dev.sh for consumer
# devices: uv bootstrap + clone + uv sync + editable tool install + settings
# baseline. setup-dev.sh stays untouched (kloud:setup depends on it).
#
# Contract:
#   - bash-3.2-compatible (macOS ships 3.2): no associative arrays, no ${var,,}
#   - idempotent — every mutating step is guarded, re-runs are no-ops
#   - no sudo, no rc-file edits (PATH hint is printed, never written)
#   - --check walks the whole decision tree printing `ok: …` / `would: …`
#     lines, mutates nothing (not even known_hosts), exits 0
#   - the entire body lives in main(), invoked on the last line, so a
#     curl-truncated script is a no-op instead of a half-run
#
# Verify a finished install anytime with: kin doctor

set -euo pipefail

REPO_SSH="git@github.com:kinra-ai/kin-textual.git"
CHECKOUT="${HOME:-}/kin-textual"
WHEEL_ONELINER="uv tool install kin --index https://get.kinra.ai/simple/"
CHECK=0

say()  { printf '%s\n' "$*"; }
ok()   { printf 'ok: %s\n' "$*"; }
would(){ printf 'would: %s\n' "$*"; }
fail() { printf 'error: %s\n' "$*" >&2; }

check_platform() {
  local os
  os="$(uname -s)"
  case "$os" in
    Linux|Darwin) ok "platform $os is supported" ;;
    MINGW*|MSYS*|CYGWIN*|Windows*)
      fail "native Windows is not supported yet — install WSL2 (wsl --install), then run this one-liner inside the WSL shell. See https://get.kinra.ai (Windows tab)."
      return 1 ;;
    *)
      fail "unsupported platform '$os' (Linux and macOS only). Windows: use WSL2 — see https://get.kinra.ai."
      return 1 ;;
  esac
}

check_git() {
  if command -v git >/dev/null 2>&1; then
    ok "git on PATH"
    return 0
  fi
  if [ "$(uname -s)" = "Darwin" ]; then
    fail "git not found — run: xcode-select --install (then re-run this installer)"
  else
    fail "git not found — install it first (Fedora: sudo dnf install git · Ubuntu/Debian: sudo apt install git), then re-run this installer"
  fi
  return 1
}

check_ssh_key() {
  # GitHub's SSH probe exits 1 on SUCCESSFUL auth (it refuses a shell), 255
  # when no usable key was offered. accept-new writes known_hosts, so --check
  # only describes the probe instead of running it.
  if [ "$CHECK" = 1 ]; then
    would "probe SSH auth to git@github.com (BatchMode; exit 1 = key ok, 255 = no key)"
    return 0
  fi
  local rc=0 out=""
  out="$(ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 \
      -T git@github.com 2>&1)" || rc=$?
  if [ "$rc" = 1 ]; then
    ok "SSH key authenticates to github.com"
    return 0
  fi
  # ssh exits 255 for BOTH auth and network failures — the message text is
  # the only reliable discriminator, so don't tell a proxied/offline user
  # their key is bad.
  case "$out" in
    *"Permission denied"*)
      fail "no SSH key with access to github.com (ssh probe exit $rc). kin-textual is a private repo — you need an SSH key with access to kinra-ai/kin-textual."
      say "  → add a key: https://github.com/settings/keys" >&2
      say "  → or use the no-SSH-key quick path (read-only build, no checkout):" >&2
      say "      $WHEEL_ONELINER" >&2
      ;;
    *)
      fail "could not reach github.com over SSH (exit $rc) — check network/DNS/proxy, then re-run. ssh said: $out"
      ;;
  esac
  return 1
}

ensure_uv() {
  if command -v uv >/dev/null 2>&1; then
    ok "uv on PATH ($(command -v uv))"
  elif [ "$CHECK" = 1 ]; then
    would "install uv via https://astral.sh/uv/install.sh (no sudo; lands in ~/.local/bin)"
  else
    say "→ installing uv (https://astral.sh/uv/install.sh)"
    curl -LsSf https://astral.sh/uv/install.sh | sh
  fi
  # Make this run see a just-installed uv (and later, the kin shim) without
  # requiring a new shell. Printed as a hint at the end if missing from rc.
  export PATH="$HOME/.local/bin:$PATH"
}

ensure_checkout() {
  if [ -d "$CHECKOUT/.git" ]; then
    if [ "$CHECK" = 1 ]; then
      would "update the existing checkout: git -C $CHECKOUT pull --ff-only (warn + continue on divergence)"
      return 0
    fi
    # A half-clone from an earlier network death has .git but no HEAD —
    # pull would fail confusingly. Name the state and the fix instead.
    if ! git -C "$CHECKOUT" rev-parse HEAD >/dev/null 2>&1; then
      fail "$CHECKOUT looks like an incomplete checkout (no HEAD) — move it aside and re-run"
      return 1
    fi
    say "→ updating existing checkout at $CHECKOUT"
    if ! git -C "$CHECKOUT" pull --ff-only; then
      # A dirty/diverged dev tree must never be clobbered by an installer.
      say "⚠ could not fast-forward $CHECKOUT (local changes or a diverged branch) — continuing with the existing checkout as-is" >&2
    fi
  elif [ -e "$CHECKOUT" ]; then
    fail "$CHECKOUT exists but is not a git checkout — move it aside and re-run"
    return 1
  elif [ "$CHECK" = 1 ]; then
    would "git clone $REPO_SSH $CHECKOUT"
  else
    say "→ cloning $REPO_SSH → $CHECKOUT"
    git clone "$REPO_SSH" "$CHECKOUT"
  fi
}

ensure_deps() {
  # NOT `task sync` — Taskfile stays a dev gate runner, not a consumer
  # prerequisite (DR-recorded decoupling).
  if [ "$CHECK" = 1 ]; then
    would "run 'uv sync' in $CHECKOUT (creates .venv from uv.lock)"
    return 0
  fi
  say "→ uv sync (in $CHECKOUT)"
  (cd "$CHECKOUT" && uv sync)
}

ensure_kin_tool() {
  if command -v kin >/dev/null 2>&1; then
    ok "kin already on PATH ($(command -v kin))"
    # A `kin` that isn't the uv-tool shim may be an unrelated binary — the
    # success gate prints the resolved path so a shadow is visible.
    case "$(command -v kin)" in
      "$HOME/.local/bin/kin"|"$CHECKOUT"*) ;;
      *) say "  (note: this kin is not the uv tool shim — if it's not yours, remove it and re-run)" ;;
    esac
  elif [ "$CHECK" = 1 ]; then
    would "run 'uv tool install --editable $CHECKOUT' (puts kin in ~/.local/bin)"
  else
    say "→ uv tool install --editable $CHECKOUT"
    uv tool install --editable "$CHECKOUT"
  fi
}

ensure_settings() {
  local kin_home settings template
  kin_home="${KIN_HOME:-$HOME/.kin}"
  settings="$kin_home/settings.toml"
  template="$CHECKOUT/examples/settings.toml.shared-template.toml"
  if [ -f "$settings" ]; then
    ok "settings baseline exists ($settings) — leaving alone"
  elif [ "$CHECK" = 1 ]; then
    would "copy $template → $settings (keys stay yours — entered in-app via /providers)"
  else
    if [ ! -f "$template" ]; then
      say "⚠ settings template not found at $template — skipping the baseline (kin will still run; configure via /providers)" >&2
      return 0
    fi
    mkdir -p "$kin_home"
    cp "$template" "$settings"
    say "→ settings baseline: $settings (add provider keys in-app via /providers)"
  fi
}

path_hint() {
  case ":$PATH:" in
    *":$HOME/.local/bin:"*) return 0 ;;
  esac
  local rc_file shell_name
  shell_name="$(basename "${SHELL:-sh}")"
  case "$shell_name" in
    zsh)  rc_file="~/.zshrc" ;;
    bash) rc_file="~/.bashrc" ;;
    *)    rc_file="your shell's rc file" ;;
  esac
  say ""
  say "⚠ ~/.local/bin is not in your PATH. Add this line to $rc_file:"
  say '    export PATH="$HOME/.local/bin:$PATH"'
}

success_gate() {
  if [ "$CHECK" = 1 ]; then
    would "verify: kin --version runs, then suggest 'kin doctor'"
    say ""
    say "check walk complete — nothing was changed."
    return 0
  fi
  if kin --version >/dev/null 2>&1; then
    say ""
    say "✓ kin installed: $(kin --version) ($(command -v kin))"
    path_hint
    say ""
    say "run it with: kin"
    say "verify anytime with: kin doctor"
    return 0
  fi
  fail "kin --version did not run. Check: is ~/.local/bin in PATH? did 'uv tool install --editable $CHECKOUT' succeed above?"
  return 1
}

main() {
  case "${1:-}" in
    --check) CHECK=1 ;;
    "") ;;
    *) fail "unknown flag '$1' (only --check is supported)"; return 1 ;;
  esac
  if [ -z "${HOME:-}" ]; then
    fail "HOME is not set — run this as a normal user with a home directory"
    return 1
  fi
  check_platform
  check_git
  check_ssh_key
  ensure_uv
  ensure_checkout
  ensure_deps
  ensure_kin_tool
  ensure_settings
  success_gate
}

# The main-at-EOF wrapper: nothing executes until the full script arrived.
main "$@"
