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

OPENBAO_NAMESPACE="${OPENBAO_NAMESPACE:-openbao}"
OPENBAO_RELEASE="${OPENBAO_RELEASE:-openbao}"
KUBECTL="${KUBECTL:-kubectl}"
TOKEN_FILE="${OPENBAO_TOKEN_FILE:-}"
DRY_RUN=0
USE_TOKEN_HELPER=0

usage() {
  cat <<'USAGE'
Usage: scripts/openbao-verify-authenticated.sh [--dry-run] [--use-token-helper]

Runs authenticated, non-mutating OpenBao readiness checks:
  - audit list includes file/
  - secrets list includes platform/
  - auth list includes kubernetes/, netkingdom/, and keycape/
  - audit log exists and is non-empty

The token is read from OPENBAO_TOKEN_FILE or an interactive hidden prompt. The
script does not print, store, or pass the token on the command line.

With --use-token-helper, no token is read by this script. The checks rely on
the OpenBao token helper inside the pod, if a previous attended login stored a
still-valid token there.
USAGE
}

while [ "$#" -gt 0 ]; do
  case "$1" in
    --dry-run)
      DRY_RUN=1
      shift
      ;;
    --use-token-helper)
      USE_TOKEN_HELPER=1
      shift
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      echo "ERROR: unknown argument: $1" >&2
      usage >&2
      exit 2
      ;;
  esac
done

pod="${OPENBAO_RELEASE}-0"
FAILURES=0
WARNINGS=0

ok() { printf '[OK] %s\n' "$*"; }
warn() { WARNINGS=$((WARNINGS + 1)); printf '[WARN] %s\n' "$*" >&2; }
fail() { FAILURES=$((FAILURES + 1)); printf '[FAIL] %s\n' "$*" >&2; }
step() { printf '\n==> %s\n' "$*"; }

check_cmd() {
  if ! command -v "${KUBECTL%% *}" >/dev/null 2>&1; then
    echo "ERROR: kubectl command not found: $KUBECTL" >&2
    exit 1
  fi
}

run() {
  # shellcheck disable=SC2086
  $KUBECTL "$@"
}

read_token() {
  if [ "$USE_TOKEN_HELPER" -eq 1 ]; then
    printf '__USE_TOKEN_HELPER__\n'
    return
  fi

  if [ "$DRY_RUN" -eq 1 ]; then
    printf 'dry-run-token\n'
    return
  fi

  if [ -n "$TOKEN_FILE" ]; then
    if [ ! -f "$TOKEN_FILE" ]; then
      echo "ERROR: OPENBAO_TOKEN_FILE does not exist: $TOKEN_FILE" >&2
      exit 1
    fi
    head -n 1 "$TOKEN_FILE"
    return
  fi

  local token
  read -r -s -p "OpenBao token: " token
  printf '\n' >&2
  printf '%s\n' "$token"
}

remote_bao() {
  local token="$1"
  shift
  if [ "$DRY_RUN" -eq 1 ]; then
    case "$*" in
      status)
        cat <<'STATUS'
Key        Value
---        -----
Sealed     false
STATUS
        ;;
      "audit list")
        cat <<'AUDIT'
Path     Type    Description
----     ----    -----------
file/    file    Default file audit device on the OpenBao audit PVC.
AUDIT
        ;;
      "secrets list")
        cat <<'SECRETS'
Path          Type
----          ----
cubbyhole/    cubbyhole
identity/     identity
platform/     kv
SECRETS
        ;;
      "auth list")
        cat <<'AUTH'
Path           Type
----           ----
keycape/       oidc
kubernetes/    kubernetes
netkingdom/    oidc
token/         token
AUTH
        ;;
      *)
        printf 'DRY-RUN: bao %s\n' "$*"
        ;;
    esac
    return 0
  fi
  if [ "$USE_TOKEN_HELPER" -eq 1 ]; then
    run exec -n "$OPENBAO_NAMESPACE" "$pod" -- bao "$@"
    return $?
  fi
  printf '%s\n' "$token" | run exec -i -n "$OPENBAO_NAMESPACE" "$pod" -- \
    sh -c 'read -r BAO_TOKEN; export BAO_TOKEN; exec bao "$@"' sh "$@"
}

audit_log_bytes() {
  if [ "$DRY_RUN" -eq 1 ]; then
    printf '1\n'
    return 0
  fi
  run exec -n "$OPENBAO_NAMESPACE" "$pod" -- \
    sh -c 'test -s /openbao/audit/openbao-audit.log && wc -c < /openbao/audit/openbao-audit.log' \
    2>/dev/null | tr -d '[:space:]'
}

require_pattern() {
  local label="$1"
  local output="$2"
  local pattern="$3"
  if printf '%s\n' "$output" | grep -Eq "$pattern"; then
    ok "$label"
  else
    fail "$label"
  fi
}

check_cmd

token="$(read_token)"
if [ -z "$token" ]; then
  echo "ERROR: empty OpenBao token" >&2
  exit 1
fi

before_bytes="$(audit_log_bytes || true)"
before_bytes="${before_bytes:-0}"

step "OpenBao status"
if status_output="$(remote_bao "$token" status 2>&1)"; then
  printf '%s\n' "$status_output"
  require_pattern "OpenBao is unsealed" "$status_output" '^Sealed[[:space:]]+false$'
else
  printf '%s\n' "$status_output" >&2
  fail "bao status succeeded"
fi

step "Audit devices"
if audit_output="$(remote_bao "$token" audit list 2>&1)"; then
  printf '%s\n' "$audit_output"
  require_pattern "file/ audit device is visible" "$audit_output" '(^|[[:space:]])file/'
else
  printf '%s\n' "$audit_output" >&2
  fail "bao audit list succeeded"
fi

step "Secrets engines"
if secrets_output="$(remote_bao "$token" secrets list 2>&1)"; then
  printf '%s\n' "$secrets_output"
  require_pattern "platform/ secrets engine is visible" "$secrets_output" '(^|[[:space:]])platform/'
else
  printf '%s\n' "$secrets_output" >&2
  fail "bao secrets list succeeded"
fi

step "Auth methods"
if auth_output="$(remote_bao "$token" auth list 2>&1)"; then
  printf '%s\n' "$auth_output"
  require_pattern "kubernetes/ auth method is visible" "$auth_output" '(^|[[:space:]])kubernetes/'
  require_pattern "netkingdom/ auth method is visible" "$auth_output" '(^|[[:space:]])netkingdom/'
  require_pattern "keycape/ auth method is visible" "$auth_output" '(^|[[:space:]])keycape/'
else
  printf '%s\n' "$auth_output" >&2
  fail "bao auth list succeeded"
fi

after_bytes="$(audit_log_bytes || true)"
after_bytes="${after_bytes:-0}"

step "Audit log file"
if [ "$after_bytes" -gt 0 ]; then
  ok "audit log file is non-empty (${after_bytes} bytes)"
else
  fail "audit log file is non-empty"
fi

if [ "$DRY_RUN" -eq 1 ]; then
  ok "audit log growth check skipped in dry-run"
elif [ "$after_bytes" -gt "$before_bytes" ]; then
  ok "audit log grew during authenticated checks (${before_bytes} -> ${after_bytes} bytes)"
else
  warn "audit log size did not increase during this run (${before_bytes} -> ${after_bytes} bytes)"
fi

if [ "$FAILURES" -gt 0 ]; then
  printf '\nAuthenticated OpenBao verification failed with %s failure(s) and %s warning(s).\n' "$FAILURES" "$WARNINGS" >&2
  exit 1
fi

printf '\nAuthenticated OpenBao verification passed'
if [ "$WARNINGS" -gt 0 ]; then
  printf ' with %s warning(s)' "$WARNINGS"
fi
printf '.\n'
