#!/usr/bin/env bash
# Celilo installer — https://celilo.computer
#
# Usage:
#   curl -fsSL https://celilo.computer/install.sh | bash
#
# ⚠ If you want to set up a CELILO MANAGEMENT SERVER (the typical case
#   for a new homelab), use bootstrap.sh instead — it installs everything
#   here PLUS imports the celilo-mgmt module and primes the box for a
#   `celilo module deploy celilo-mgmt`. install.sh remains the right
#   choice when you just want the celilo CLI as a client tool on a
#   laptop or CI runner:
#
#     curl -fsSL https://celilo.computer/bootstrap.sh | bash   # mgmt server
#     curl -fsSL https://celilo.computer/install.sh   | bash   # just the CLI
#
# What this script does (non-interactive — safe to run from CI / cloud-init):
#   1. Installs Bun (https://bun.sh) if it's missing
#   2. Installs @celilo/cli, @celilo/event-bus, @celilo/e2e as global Bun packages
#   3. Prints next steps (PATH, `celilo system init`, optional event-bus daemon)
#
# It does NOT run `celilo system init` (interactive — needs you at the keyboard).
# It does NOT install the event-bus daemon. Both are listed in the next-steps
# block at the end so you can run them when you're ready.

set -euo pipefail

# ---------- options ----------
# Lenient by design: this is the user's first contact with Celilo, and a
# hard-exit on a stale flag (e.g. someone copy-pastes an old curl-pipe from
# the docs that still has --skip-init) is a worse first impression than
# warning and continuing. We only recognize -h/--help; anything else gets
# a soft warning. If we add behavior-changing flags later, document the
# tradeoff at that time.
for arg in "$@"; do
  case "$arg" in
    -h|--help)
      cat <<'EOF'
Celilo installer — https://celilo.computer

Usage:
  curl -fsSL https://celilo.computer/install.sh | bash

The installer is non-interactive. It installs Bun, the Celilo CLI, the event
bus library, and the e2e harness, then prints next steps.

Options:
  -h, --help      Show this help
EOF
      exit 0
      ;;
    *) printf 'warning: ignoring unknown option: %s\n' "$arg" >&2 ;;
  esac
done

# ---------- output helpers ----------
if [ -t 1 ]; then
  RED=$'\033[31m'; GREEN=$'\033[32m'; YELLOW=$'\033[33m'
  BLUE=$'\033[34m'; BOLD=$'\033[1m'; RESET=$'\033[0m'
else
  RED=""; GREEN=""; YELLOW=""; BLUE=""; BOLD=""; RESET=""
fi

info() { printf '%s[celilo]%s %s\n' "$BLUE"  "$RESET" "$*"; }
ok()   { printf '%s[celilo]%s %s\n' "$GREEN" "$RESET" "$*"; }
warn() { printf '%s[celilo]%s %s\n' "$YELLOW" "$RESET" "$*"; }
fail() { printf '%s[celilo]%s %s\n' "$RED"   "$RESET" "$*" >&2; exit 1; }

# ---------- bootstrap.sh pointer ----------
# Phase 5 of v2/MANAGEMENT_AS_NETAPP.md — install.sh installs JUST the
# CLI, which is what you want on a laptop / CI runner. Operators trying
# to stand up a homelab management server should use bootstrap.sh
# instead. Print the pointer up front so they can re-run with the
# right script if needed; the install continues without delay (piping
# into bash makes interactive Ctrl-C unreliable, so we don't gate on
# operator confirmation).
warn 'install.sh installs only the celilo CLI.'
warn 'For a celilo MANAGEMENT SERVER setup, use bootstrap.sh instead:'
warn '  curl -fsSL https://celilo.computer/bootstrap.sh | bash'
echo

# ---------- preflight ----------
case "$(uname -s)" in
  Darwin|Linux) ;;
  *) fail "Unsupported OS: $(uname -s). Celilo runs on macOS and Linux." ;;
esac

if ! command -v curl >/dev/null 2>&1; then
  fail "Missing required command: curl. Install it and re-run."
fi

# ---------- 1. Bun ----------
export BUN_INSTALL="${BUN_INSTALL:-$HOME/.bun}"
export PATH="$BUN_INSTALL/bin:$PATH"

if ! command -v bun >/dev/null 2>&1; then
  # Bun's installer ships its release as a .zip, so we need unzip — but only
  # when Bun is actually missing. If Bun is already installed, no unzip needed.
  if ! command -v unzip >/dev/null 2>&1; then
    case "$(uname -s)" in
      Linux)
        if   command -v apt-get >/dev/null 2>&1; then hint="sudo apt-get install unzip"
        elif command -v dnf     >/dev/null 2>&1; then hint="sudo dnf install unzip"
        elif command -v yum     >/dev/null 2>&1; then hint="sudo yum install unzip"
        elif command -v pacman  >/dev/null 2>&1; then hint="sudo pacman -S unzip"
        elif command -v apk     >/dev/null 2>&1; then hint="sudo apk add unzip"
        else hint="install 'unzip' via your package manager"
        fi
        ;;
      Darwin) hint="brew install unzip  (or use macOS's bundled unzip from /usr/bin)" ;;
      *)      hint="install 'unzip' via your package manager" ;;
    esac
    fail "Missing 'unzip' (needed to install Bun). Try: $hint"
  fi
  info "Bun not found — installing from https://bun.sh"
  curl -fsSL https://bun.sh/install | bash
  if ! command -v bun >/dev/null 2>&1; then
    fail "Bun install completed but 'bun' is still not on PATH. Open a new shell, ensure $BUN_INSTALL/bin is on \$PATH, and re-run."
  fi
  ok "Bun installed: $(bun --version)"
else
  ok "Bun already installed: $(bun --version)"
fi

# ---------- 2. Celilo packages ----------
# Alpha iteration: track the @alpha dist-tag (the refactored stack lives
# there; @latest is still the prior release). Drop @alpha at graduation.
# See feedback_alpha_publishes.
info "Installing @celilo/cli@alpha, @celilo/event-bus@alpha, @celilo/e2e@alpha (global)…"
bun add -g @celilo/cli@alpha @celilo/event-bus@alpha @celilo/e2e@alpha

if ! command -v celilo >/dev/null 2>&1; then
  fail "Celilo install finished, but 'celilo' is not on PATH. Add $BUN_INSTALL/bin to PATH and re-run."
fi

ok "Celilo CLI installed: $(celilo --version 2>/dev/null || echo '<version check failed>')"

# ---------- 3. host prereqs (advisory) ----------
# Celilo shells out to ansible, ansible-galaxy, terraform, ssh, and git during
# `system init` and module deploy. They're not bundled with the CLI, and a
# missing prereq surfaces as a confusing "command not found" mid-deploy. Detect
# what's missing now and surface a per-OS install hint in the next-steps block.

have_apt=0; have_dnf=0; have_yum=0; have_pacman=0; have_apk=0; have_brew=0
case "$(uname -s)" in
  Linux)
    command -v apt-get >/dev/null 2>&1 && have_apt=1
    command -v dnf     >/dev/null 2>&1 && have_dnf=1
    command -v yum     >/dev/null 2>&1 && have_yum=1
    command -v pacman  >/dev/null 2>&1 && have_pacman=1
    command -v apk     >/dev/null 2>&1 && have_apk=1
    ;;
  Darwin)
    command -v brew    >/dev/null 2>&1 && have_brew=1
    ;;
esac

# pm_install <pkg> — emit the install command for the detected package manager.
# Most prereqs share the same package name across pms; tools that don't
# (terraform) get a bespoke hint inline below.
pm_install() {
  local pkg="$1"
  if   [ "$have_apt" = 1 ];    then printf 'sudo apt-get install -y %s' "$pkg"
  elif [ "$have_dnf" = 1 ];    then printf 'sudo dnf install -y %s'     "$pkg"
  elif [ "$have_yum" = 1 ];    then printf 'sudo yum install -y %s'     "$pkg"
  elif [ "$have_pacman" = 1 ]; then printf 'sudo pacman -S --noconfirm %s' "$pkg"
  elif [ "$have_apk" = 1 ];    then printf 'sudo apk add %s'            "$pkg"
  elif [ "$have_brew" = 1 ];   then printf 'brew install %s'            "$pkg"
  else                              printf 'install %s via your package manager' "$pkg"
  fi
}

# Each entry: "<label>|<install command>"
PREREQ_HINTS=()

if ! command -v ansible >/dev/null 2>&1 || ! command -v ansible-galaxy >/dev/null 2>&1; then
  PREREQ_HINTS+=("Ansible (runs playbooks)|$(pm_install ansible)")
fi

if ! command -v terraform >/dev/null 2>&1; then
  case "$(uname -s)" in
    Linux)
      if   [ "$have_apt" = 1 ]; then
        tf_hint='wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg && echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list && sudo apt update && sudo apt install -y terraform'
      elif [ "$have_dnf" = 1 ] || [ "$have_yum" = 1 ]; then
        tf_hint='see https://developer.hashicorp.com/terraform/install#linux for the HashiCorp dnf/yum repo'
      elif [ "$have_pacman" = 1 ]; then
        tf_hint='sudo pacman -S terraform'
      else
        tf_hint='see https://developer.hashicorp.com/terraform/install'
      fi
      ;;
    Darwin) tf_hint='brew tap hashicorp/tap && brew install hashicorp/tap/terraform' ;;
    *)      tf_hint='see https://developer.hashicorp.com/terraform/install' ;;
  esac
  PREREQ_HINTS+=("Terraform (provisions infrastructure)|$tf_hint")
fi

if ! command -v git >/dev/null 2>&1; then
  PREREQ_HINTS+=("git (clones module sources)|$(pm_install git)")
fi

if ! command -v ssh >/dev/null 2>&1; then
  case "$(uname -s)" in
    Linux)  ssh_hint=$(pm_install openssh-client) ;;
    Darwin) ssh_hint='ssh ships with macOS — if it is missing your system is unusual; reinstall via xcode-select --install' ;;
    *)      ssh_hint='install OpenSSH client via your package manager' ;;
  esac
  PREREQ_HINTS+=("OpenSSH client (deploys to remote hosts)|$ssh_hint")
fi

# ---------- 4. shell completions ----------
# Write completion scripts for whichever shells are installed. We don't touch
# the user's rc files — Rule of Least Surprise — but we surface what they need
# to add (and a "reload your shell" reminder) in the next-steps block.
#
# Two parallel arrays drive the next-steps block:
#   COMPLETIONS_INSTALLED  — shells where a file was written successfully.
#                            Always triggers an "exec $SHELL to activate" hint.
#   COMPLETION_RC_SNIPPETS — "<shell>|<rc-line>|<rc-target>" entries shown
#                            as conditional fallbacks ("if completion does not
#                            work, append this to your rc"). zsh always needs
#                            an fpath line; bash only needs a manual `source`
#                            when bash-completion v2 isn't auto-loading.
COMPLETIONS_INSTALLED=()
COMPLETION_RC_SNIPPETS=()

write_completion() {
  local shell="$1" target="$2"
  local target_dir
  target_dir="$(dirname "$target")"
  if ! mkdir -p "$target_dir" 2>/dev/null; then
    return 1
  fi
  if ! celilo completion "$shell" >"$target" 2>/dev/null; then
    rm -f "$target" 2>/dev/null
    return 1
  fi
  return 0
}

if command -v zsh >/dev/null 2>&1; then
  ZSH_COMP="$HOME/.zsh/completions/_celilo"
  if write_completion zsh "$ZSH_COMP"; then
    ok "Wrote zsh completion to $ZSH_COMP"
    COMPLETIONS_INSTALLED+=("zsh")
    # Always surface the fpath/compinit snippet — the file alone is useless
    # without zsh knowing to scan ~/.zsh/completions and run compinit. We
    # frame it as "if completion does not work" so users with an existing
    # custom fpath setup don't think they need to duplicate it.
    COMPLETION_RC_SNIPPETS+=("zsh|fpath=(\"\$HOME/.zsh/completions\" \$fpath)
autoload -Uz compinit && compinit|$HOME/.zshrc")
  else
    warn "Could not write zsh completion to $ZSH_COMP — install manually with: celilo completion zsh > $ZSH_COMP"
  fi
fi

if command -v bash >/dev/null 2>&1; then
  BASH_COMP="$HOME/.local/share/bash-completion/completions/celilo"
  if write_completion bash "$BASH_COMP"; then
    ok "Wrote bash completion to $BASH_COMP"
    COMPLETIONS_INSTALLED+=("bash")
    # bash-completion v2 (~/.local/share/bash-completion/completions auto-load)
    # requires the bash-completion package to be installed AND the user's shell
    # to source it from /etc/bash.bashrc or similar. We can't reliably detect
    # the second part from a non-interactive shell, so always include the
    # source fallback under "if completion does not work".
    COMPLETION_RC_SNIPPETS+=("bash|source $BASH_COMP|$HOME/.bashrc")
  else
    warn "Could not write bash completion to $BASH_COMP — install manually with: celilo completion bash > $BASH_COMP"
  fi
fi

if command -v fish >/dev/null 2>&1; then
  FISH_COMP="$HOME/.config/fish/completions/celilo.fish"
  if write_completion fish "$FISH_COMP"; then
    ok "Wrote fish completion to $FISH_COMP"
    COMPLETIONS_INSTALLED+=("fish")
    # Fish auto-loads from ~/.config/fish/completions/ — no rc snippet needed.
  else
    warn "Could not write fish completion to $FISH_COMP — install manually with: celilo completion fish > $FISH_COMP"
  fi
fi

# ---------- 6. PATH advisory ----------
RC_FILE=""
case "${SHELL:-}" in
  */zsh) RC_FILE="$HOME/.zshrc" ;;
  */bash)
    if   [ -f "$HOME/.bashrc" ];       then RC_FILE="$HOME/.bashrc"
    elif [ -f "$HOME/.bash_profile" ]; then RC_FILE="$HOME/.bash_profile"
    fi
    ;;
  */fish) RC_FILE="$HOME/.config/fish/config.fish" ;;
esac

PATH_LINE='export PATH="$HOME/.bun/bin:$PATH"'
PATH_NEEDS_RC=0
if [ -n "$RC_FILE" ] && [ -f "$RC_FILE" ] && grep -qs '\.bun/bin' "$RC_FILE"; then
  : # already set up by Bun's installer — nothing to do
else
  PATH_NEEDS_RC=1
fi

# ---------- 7. summary / next steps ----------
echo
ok "Celilo is installed."
echo
echo "Next steps:"
STEP=1
if [ "$PATH_NEEDS_RC" = "1" ]; then
  RC_TARGET="${RC_FILE:-your shell rc}"
  printf "  %d. Add Bun to your PATH so 'celilo' works in new shells:\n" "$STEP"
  printf "       echo %s >> %s\n" "'$PATH_LINE'" "$RC_TARGET"
  printf "       exec \$SHELL\n"
  STEP=$((STEP + 1))
fi
if [ "${#PREREQ_HINTS[@]}" -gt 0 ]; then
  printf '  %d. Install host prerequisites (Celilo shells out to these during deploy):\n' "$STEP"
  for entry in "${PREREQ_HINTS[@]}"; do
    label="${entry%%|*}"
    cmd="${entry#*|}"
    printf '       %s%s%s\n' "$BOLD" "$label" "$RESET"
    printf '         %s\n' "$cmd"
  done
  STEP=$((STEP + 1))
fi
if [ "${#COMPLETIONS_INSTALLED[@]}" -gt 0 ]; then
  shells_csv="$(IFS=', '; printf '%s' "${COMPLETIONS_INSTALLED[*]}")"
  printf '  %d. Activate tab completion (installed for: %s):\n' "$STEP" "$shells_csv"
  printf '       exec $SHELL\n'
  printf '     Verify by typing:  celilo <TAB><TAB>\n'
  if [ "${#COMPLETION_RC_SNIPPETS[@]}" -gt 0 ]; then
    printf '     If completion does not work, append the matching rc snippet first:\n'
    for entry in "${COMPLETION_RC_SNIPPETS[@]}"; do
      shell_name="${entry%%|*}"
      rest="${entry#*|}"
      rc_line="${rest%%|*}"
      rc_target="${rest##*|}"
      printf '       %s%s%s — %s%s%s:\n' "$BOLD" "$shell_name" "$RESET" "$BOLD" "$rc_target" "$RESET"
      printf '%s\n' "$rc_line" | sed 's/^/         /'
    done
  fi
  STEP=$((STEP + 1))
fi
printf '  %d. Initialize the Celilo database and network config (interactive):\n' "$STEP"
printf '       celilo system init\n'
STEP=$((STEP + 1))
printf '  %d. (Optional) Install the event-bus daemon to stream deploy progress\n' "$STEP"
printf '     through a persistent system service:\n'
printf '       celilo events install-daemon\n'
STEP=$((STEP + 1))
printf '  %d. Add an infrastructure provider (Proxmox, Digital Ocean, ...):\n' "$STEP"
printf '       celilo service add\n'
STEP=$((STEP + 1))
printf '  %d. Full walkthrough: https://celilo.computer/docs\n' "$STEP"
