#!/usr/bin/env bash
set -euo pipefail

STATE_HUB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
GITEA_CONF="${GITEA_CONF:-$HOME/.railiance_gitea.conf}"
GITEA_URL="${GITEA_URL:-http://92.205.130.254:32166}"
GITEA_USER="${GITEA_USER:-}"
GITEA_TOKEN="${GITEA_TOKEN:-}"
GIT_HELPER="${GIT_HELPER:-auto}"
INSTALL_MISSING=0
NON_INTERACTIVE=0
DRY_RUN=0
AUTHORIZE_SSH=0
ALLOW_PLAINTEXT_STORE=0
SKIP_GITEA=0
SKIP_MCP=0
SSH_KEY="${SSH_KEY:-$HOME/.ssh/id_ed25519}"
SSH_TARGETS=(
  "tegwick@92.205.62.239"
  "tegwick@92.205.130.254"
)

usage() {
  cat <<'USAGE'
Usage: scripts/bootstrap-env.sh [options]

Idempotently prepares a State Hub operator or collaborator environment.

Options:
  --install-missing         Install missing apt packages when possible.
  --non-interactive         Do not prompt; warn instead of asking for secrets.
  --dry-run                 Show intended actions without changing local config.
  --git-helper MODE         auto, libsecret, cache, or store. Default: auto.
  --allow-plaintext-store   Allow git credential.helper=store in auto mode.
  --authorize-ssh           Run ssh-copy-id for configured SSH targets.
  --ssh-target USER@HOST    Add an SSH authorization target. May repeat.
  --gitea-url URL           Gitea base URL for ~/.railiance_gitea.conf.
  --gitea-user USER         Gitea user for ~/.railiance_gitea.conf.
  --gitea-token TOKEN       Gitea token; otherwise prompted when interactive.
  --skip-gitea              Do not create or update ~/.railiance_gitea.conf.
  --skip-mcp                Do not run make register-mcp.
  -h, --help                Show this help.
USAGE
}

ok() { printf '[OK] %s\n' "$*"; }
warn() { printf '[WARN] %s\n' "$*"; }
err() { printf '[ERR] %s\n' "$*" >&2; }
step() { printf '\n==> %s\n' "$*"; }

run() {
  if [ "$DRY_RUN" -eq 1 ]; then
    printf 'DRY-RUN: %s\n' "$*"
  else
    "$@"
  fi
}

need_arg() {
  if [ -z "${2:-}" ]; then
    err "$1 requires a value"
    exit 2
  fi
}

while [ "$#" -gt 0 ]; do
  case "$1" in
    --install-missing)
      INSTALL_MISSING=1
      shift
      ;;
    --non-interactive)
      NON_INTERACTIVE=1
      shift
      ;;
    --dry-run)
      DRY_RUN=1
      shift
      ;;
    --git-helper)
      need_arg "$1" "${2:-}"
      GIT_HELPER="$2"
      shift 2
      ;;
    --allow-plaintext-store)
      ALLOW_PLAINTEXT_STORE=1
      shift
      ;;
    --authorize-ssh)
      AUTHORIZE_SSH=1
      shift
      ;;
    --ssh-target)
      need_arg "$1" "${2:-}"
      SSH_TARGETS+=("$2")
      shift 2
      ;;
    --gitea-url)
      need_arg "$1" "${2:-}"
      GITEA_URL="$2"
      shift 2
      ;;
    --gitea-user)
      need_arg "$1" "${2:-}"
      GITEA_USER="$2"
      shift 2
      ;;
    --gitea-token)
      need_arg "$1" "${2:-}"
      GITEA_TOKEN="$2"
      shift 2
      ;;
    --skip-gitea)
      SKIP_GITEA=1
      shift
      ;;
    --skip-mcp)
      SKIP_MCP=1
      shift
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      err "unknown argument: $1"
      usage >&2
      exit 2
      ;;
  esac
done

case "$GIT_HELPER" in
  auto|libsecret|cache|store) ;;
  *)
    err "--git-helper must be auto, libsecret, cache, or store"
    exit 2
    ;;
esac

apt_install() {
  local packages=("$@")
  if [ "$INSTALL_MISSING" -ne 1 ]; then
    warn "Missing packages: ${packages[*]}"
    warn "Rerun with --install-missing or install them manually."
    return
  fi
  if ! command -v sudo >/dev/null 2>&1; then
    warn "sudo is not available; cannot install: ${packages[*]}"
    return
  fi
  run sudo apt-get update
  run sudo apt-get install -y "${packages[@]}"
}

check_commands() {
  step "Checking prerequisites"
  local missing=()
  local commands=(git curl ssh-keygen ssh-copy-id python3 make)
  local optional=(sops age helm kubectl uv claude)

  for cmd in "${commands[@]}"; do
    if command -v "$cmd" >/dev/null 2>&1; then
      ok "$cmd found"
    else
      missing+=("$cmd")
      warn "$cmd missing"
    fi
  done

  for cmd in "${optional[@]}"; do
    if command -v "$cmd" >/dev/null 2>&1; then
      ok "$cmd found"
    else
      warn "$cmd missing"
    fi
  done

  if [ "${#missing[@]}" -gt 0 ]; then
    apt_install "${missing[@]}"
  fi
}

libsecret_helper_path() {
  local candidates=(
    "/usr/share/doc/git/contrib/credential/libsecret/git-credential-libsecret"
    "/usr/lib/git-core/git-credential-libsecret"
    "/usr/libexec/git-core/git-credential-libsecret"
  )
  local candidate
  for candidate in "${candidates[@]}"; do
    if [ -x "$candidate" ]; then
      printf '%s\n' "$candidate"
      return 0
    fi
  done
  return 1
}

build_libsecret_helper() {
  local source_dir="/usr/share/doc/git/contrib/credential/libsecret"
  if [ ! -d "$source_dir" ]; then
    apt_install libsecret-1-0 libsecret-1-dev make gcc
  fi
  if [ -d "$source_dir" ]; then
    run sudo make -C "$source_dir"
  fi
}

configure_git_helper() {
  step "Configuring Git credential helper"

  local current
  current="$(git config --global --get credential.helper || true)"
  if [ -n "$current" ]; then
    ok "credential.helper already set: $current"
    return
  fi

  local helper="$GIT_HELPER"
  if [ "$helper" = "auto" ]; then
    if libsecret_helper_path >/dev/null 2>&1; then
      helper="libsecret"
    elif [ "$ALLOW_PLAINTEXT_STORE" -eq 1 ]; then
      helper="store"
    else
      helper="cache"
    fi
  fi

  case "$helper" in
    libsecret)
      local path
      path="$(libsecret_helper_path || true)"
      if [ -z "$path" ]; then
        build_libsecret_helper
        path="$(libsecret_helper_path || true)"
      fi
      if [ -z "$path" ]; then
        warn "libsecret helper is not available; using cache helper for this machine."
        run git config --global credential.helper "cache --timeout=3600"
      else
        run git config --global credential.helper "$path"
      fi
      ;;
    cache)
      run git config --global credential.helper "cache --timeout=3600"
      ;;
    store)
      if [ "$ALLOW_PLAINTEXT_STORE" -ne 1 ]; then
        err "credential.helper=store writes plaintext credentials."
        err "Rerun with --allow-plaintext-store if that is intended for this host."
        exit 1
      fi
      run git config --global credential.helper store
      ;;
  esac

  ok "credential.helper configured"
}

setup_ssh_key() {
  step "Checking SSH key"
  mkdir -p "$HOME/.ssh"
  chmod 700 "$HOME/.ssh"

  if [ -f "$SSH_KEY" ]; then
    ok "SSH key exists: $SSH_KEY"
  else
    run ssh-keygen -t ed25519 -f "$SSH_KEY" -N "" -C "$USER@$(hostname)-state-hub"
    ok "SSH key generated: $SSH_KEY"
  fi

  if [ -f "${SSH_KEY}.pub" ]; then
    printf '\nPublic key to authorize on managed hosts:\n\n'
    sed 's/^/  /' "${SSH_KEY}.pub"
    printf '\n'
  fi

  if [ "$AUTHORIZE_SSH" -eq 1 ]; then
    local target
    for target in "${SSH_TARGETS[@]}"; do
      run ssh-copy-id -i "${SSH_KEY}.pub" "$target"
    done
  else
    warn "SSH authorization not attempted. Use --authorize-ssh after confirming host access."
  fi
}

write_gitea_conf() {
  step "Checking Gitea config"
  if [ "$SKIP_GITEA" -eq 1 ]; then
    warn "Skipping Gitea config by request."
    return
  fi

  if [ -f "$GITEA_CONF" ]; then
    chmod 600 "$GITEA_CONF"
    ok "$GITEA_CONF already exists"
    return
  fi

  if [ -z "$GITEA_USER" ] && [ "$NON_INTERACTIVE" -eq 0 ]; then
    read -r -p "Gitea username: " GITEA_USER
  fi

  if [ -z "$GITEA_TOKEN" ] && [ "$NON_INTERACTIVE" -eq 0 ]; then
    read -r -s -p "Gitea token (requires read:user and repository write scopes): " GITEA_TOKEN
    printf '\n'
  fi

  if [ -z "$GITEA_USER" ] || [ -z "$GITEA_TOKEN" ]; then
    warn "Gitea config not written. Set GITEA_USER/GITEA_TOKEN or rerun interactively."
    return
  fi

  if [ "$DRY_RUN" -eq 1 ]; then
    printf 'DRY-RUN: would write %s with GITEA_URL and GITEA_USER; token hidden\n' "$GITEA_CONF"
    return
  fi

  umask 077
  {
    printf 'GITEA_URL="%s"\n' "$GITEA_URL"
    printf 'GITEA_USER="%s"\n' "$GITEA_USER"
    printf 'GITEA_TOKEN="%s"\n' "$GITEA_TOKEN"
  } >"$GITEA_CONF"
  chmod 600 "$GITEA_CONF"
  ok "Wrote $GITEA_CONF"
}

register_mcp() {
  step "Registering State Hub MCP"
  if [ "$SKIP_MCP" -eq 1 ]; then
    warn "Skipping MCP registration by request."
    return
  fi
  if [ "$DRY_RUN" -eq 1 ]; then
    run make -C "$STATE_HUB_DIR" register-mcp DRY_RUN=1
  else
    make -C "$STATE_HUB_DIR" register-mcp
  fi
}

health_check() {
  step "Checking State Hub reachability"
  if curl -fsS --max-time 2 "http://127.0.0.1:8000/state/health" >/dev/null 2>&1; then
    ok "State Hub API reachable at http://127.0.0.1:8000"
  elif curl -fsS --max-time 2 "http://127.0.0.1:18000/state/health" >/dev/null 2>&1; then
    ok "State Hub API reachable through tunnel at http://127.0.0.1:18000"
  else
    warn "State Hub API is not reachable locally or through the default tunnel."
    warn "Start it with 'make api' or run 'make bridges' if this machine uses ops-bridge."
  fi
}

main() {
  step "State Hub environment bootstrap"
  printf 'Repository: %s\n' "$STATE_HUB_DIR"
  check_commands
  configure_git_helper
  setup_ssh_key
  write_gitea_conf
  register_mcp
  health_check
  ok "Bootstrap checks complete."
}

main "$@"
