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

# =============================================================================
# ArangoDB Contextual Data Platform — Automated Local Installer
#
# Usage:
#   ./install.sh --client-id "ID" --client-secret "SECRET"
#   ARANGO_CLIENT_ID=x ARANGO_CLIENT_SECRET=y ./install.sh
#
# See ./install.sh --help for full usage.
# =============================================================================

# ── Section 1: Constants & Configuration ─────────────────────────────────────

KIND_CLUSTER_NAME="arango-platform"
KIND_NODE_IMAGE="kindest/node:v1.33.12@sha256:3f5c8443c620245e4d355cfe09e96a91ead32ceaa569d3f1ca9edf0cb2fe2ff4"
NAMESPACE_ARANGO="arango"
NAMESPACE_MINIO="minio"
VERSION_OPERATOR="1.4.5"
VERSION_KIND="v0.32.0"
VERSION_KUBECTL="v1.34.1"
ARANGODB_IMAGE="arangodb/enterprise:3.12.11"
ARANGODB_ROOT_USER="root"
ARANGODB_ROOT_PASSWORD="test"
MINIO_IMAGE="cgr.dev/chainguard/minio:latest"
MINIO_MC_IMAGE="cgr.dev/chainguard/minio-client:latest"
PORT_FORWARD_LOCAL=8529
CHART_DOWNLOAD_URL="https://releases.license.arango.ai/releases/plg/arango-platform-release.tgz"
TOTAL_STEPS=9
MIN_CPUS=2
MIN_RAM_GB=8
MIN_DISK_GB=50

# Resolved by parse_args / env / defaults
CLIENT_ID=""
CLIENT_SECRET=""
LICENSE_KEY=""
CHART_PATH=""
PORT_FORWARD_PID=""
USE_EXISTING_CLUSTER=false
NONINTERACTIVE="${NONINTERACTIVE:-}"

# Step tracking for failure diagnostics
CURRENT_STEP=""
CURRENT_STEP_NAME=""

# Temp file tracking
TMPFILES=()
LOG_FILE="${HOME}/.arango-install.log"

# ── Section 2: Color & Output Helpers ────────────────────────────────────────

if [[ -t 1 ]]; then
  RED='\033[0;31m'
  GREEN='\033[0;32m'
  YELLOW='\033[1;33m'
  BLUE='\033[0;34m'
  CYAN='\033[0;36m'
  BOLD='\033[1m'
  NC='\033[0m'
else
  RED='' GREEN='' YELLOW='' BLUE='' CYAN='' BOLD='' NC=''
fi

info()    { echo -e "${BLUE}ℹ${NC}  $*"; }
success() { echo -e "${GREEN}✔${NC}  $*"; }
warn()    { echo -e "${YELLOW}⚠${NC}  $*"; }
error()   { echo -e "${RED}✖${NC}  $*" >&2; exit 1; }

STEP_START=0
step() {
  local num="$1"; shift
  CURRENT_STEP="$num"
  CURRENT_STEP_NAME="$*"
  STEP_START=$SECONDS
  echo ""
  echo -e "${BOLD}${CYAN}[Step ${num}/${TOTAL_STEPS}]${NC} ${BOLD}$*${NC}"
}

step_done() {
  local elapsed=$(( SECONDS - STEP_START ))
  success "Done (${elapsed}s)"
}

# Write to log file only (not to screen)
log_detail() {
  echo "$@" >> "$LOG_FILE"
}

# Run a command; output goes to log file only (not to screen)
run_logged() {
  "$@" >> "$LOG_FILE" 2>&1
}

# ── Section 3: Cleanup Trap ──────────────────────────────────────────────────

cleanup() {
  local exit_code=$?
  if [[ -n "${PORT_FORWARD_PID:-}" ]]; then
    kill "$PORT_FORWARD_PID" 2>/dev/null || true
    PORT_FORWARD_PID=""
  fi
  for f in "${TMPFILES[@]:-}"; do
    rm -f "$f" 2>/dev/null || true
  done
  if [[ $exit_code -ne 0 && $exit_code -ne 130 ]]; then
    echo ""
    if [[ -n "${CURRENT_STEP:-}" ]]; then
      warn "Installation failed at Step ${CURRENT_STEP}/${TOTAL_STEPS}: ${CURRENT_STEP_NAME}"
      capture_diagnostics
      print_failure_hint
    else
      warn "Installation did not complete successfully."
    fi
    echo ""
    info "Check the log file for details: ${LOG_FILE}"
  fi
}
trap cleanup EXIT INT TERM

capture_diagnostics() {
  log_detail ""
  log_detail "=== DIAGNOSTIC SNAPSHOT (Step ${CURRENT_STEP}: ${CURRENT_STEP_NAME}) ==="
  log_detail "Timestamp: $(date -u '+%Y-%m-%dT%H:%M:%SZ')"
  log_detail ""

  if command -v kubectl &>/dev/null && kubectl cluster-info &>/dev/null; then
    log_detail "--- Pods in namespace '${NAMESPACE_ARANGO}' ---"
    kubectl get pods -n "${NAMESPACE_ARANGO}" -o wide >> "$LOG_FILE" 2>&1 || true
    log_detail ""

    log_detail "--- Events in namespace '${NAMESPACE_ARANGO}' (last 20) ---"
    kubectl get events -n "${NAMESPACE_ARANGO}" --sort-by='.lastTimestamp' 2>/dev/null | tail -20 >> "$LOG_FILE" 2>&1 || true
    log_detail ""

    if [[ "${CURRENT_STEP:-}" -ge 5 ]]; then
      log_detail "--- Pods in namespace '${NAMESPACE_MINIO}' ---"
      kubectl get pods -n "${NAMESPACE_MINIO}" -o wide >> "$LOG_FILE" 2>&1 || true
      log_detail ""

      log_detail "--- Events in namespace '${NAMESPACE_MINIO}' (last 20) ---"
      kubectl get events -n "${NAMESPACE_MINIO}" --sort-by='.lastTimestamp' 2>/dev/null | tail -20 >> "$LOG_FILE" 2>&1 || true
      log_detail ""
    fi

    # Describe any non-ready pods in the relevant namespace(s)
    local ns_list="${NAMESPACE_ARANGO}"
    if [[ "${CURRENT_STEP:-}" -ge 5 ]]; then
      ns_list="${NAMESPACE_ARANGO} ${NAMESPACE_MINIO}"
    fi
    for ns in $ns_list; do
      local failing_pods
      failing_pods=$(kubectl get pods -n "$ns" --no-headers 2>/dev/null \
        | awk '$3 != "Running" && $3 != "Completed" {print $1}' || true)
      if [[ -n "$failing_pods" ]]; then
        while IFS= read -r pod; do
          log_detail "--- Describe pod: ${pod} (namespace: ${ns}) ---"
          kubectl describe pod "$pod" -n "$ns" >> "$LOG_FILE" 2>&1 || true
          log_detail ""
          log_detail "--- Logs for pod: ${pod} (namespace: ${ns}) ---"
          kubectl logs "$pod" -n "$ns" --tail=50 >> "$LOG_FILE" 2>&1 || true
          log_detail ""
        done <<< "$failing_pods"
      fi
    done

    log_detail "--- Helm releases ---"
    helm list -n "${NAMESPACE_ARANGO}" >> "$LOG_FILE" 2>&1 || true
    log_detail ""
  else
    log_detail "kubectl not available or cluster unreachable — skipping Kubernetes diagnostics."
  fi

  log_detail "=== END DIAGNOSTIC SNAPSHOT ==="
  log_detail ""
  info "Diagnostic details have been written to the log file."
}

print_failure_hint() {
  echo ""
  echo -e "${BOLD}Troubleshooting hints:${NC}"
  case "${CURRENT_STEP:-}" in
    1)
      cat <<'HINT'
  - Check that Docker is running and has enough resources allocated:
      docker info
  - If the Kind cluster failed to create, check for leftover clusters:
      kind get clusters
  - Delete a stuck cluster and retry:
      kind delete cluster --name arango-platform
  - On macOS, ensure Docker Desktop has at least 4 CPUs and 8 GB RAM
    (Docker Desktop → Settings → Resources).
HINT
      ;;
    2)
      cat <<'HINT'
  - Ensure the Kind cluster is running and kubectl context is set:
      kubectl config current-context
      kubectl get nodes
  - Verify the 'arango' namespace exists:
      kubectl get namespace arango
HINT
      ;;
    3)
      cat <<'HINT'
  - If the Helm install failed, check internet connectivity:
      curl -fsS https://github.com -o /dev/null && echo "OK"
  - Check if the operator chart URL is reachable (may need VPN/proxy).
  - If the operator pod is not starting, check image pull status:
      kubectl get pods -n arango
      kubectl describe pod -n arango -l app.kubernetes.io/name=kube-arangodb-enterprise
  - Check operator pod logs:
      kubectl logs -n arango -l app.kubernetes.io/name=kube-arangodb-enterprise
HINT
      ;;
    4)
      cat <<'HINT'
  - Check if the operator is running — it manages ArangoDB deployments:
      kubectl get pods -n arango -l app.kubernetes.io/name=kube-arangodb-enterprise
  - Check ArangoDB deployment status:
      kubectl get arangodeployment -n arango
  - Look for image pull errors (may require Docker Hub access):
      kubectl get events -n arango --sort-by='.lastTimestamp' | grep -i pull
  - Describe failing pods for details:
      kubectl get pods -n arango
      kubectl describe pod <pod-name> -n arango
HINT
      ;;
    5)
      cat <<'HINT'
  - Check if MinIO pods are running:
      kubectl get pods -n minio
  - Look for image pull errors — MinIO images come from Docker Hub:
      kubectl describe pod -n minio -l app=minio
  - Check PersistentVolumeClaim is bound:
      kubectl get pvc -n minio
  - If the bucket-creation job failed, check its logs:
      kubectl logs -n minio -l job-name=minio-create-bucket
  - To retry MinIO setup from scratch:
      kubectl delete namespace minio
HINT
      ;;
    6)
      cat <<'HINT'
  - Ensure the ArangoDB operator is running (it provides the ArangoPlatformStorage CRD):
      kubectl get crd arangoplatformstorages.platform.arangodb.com
  - Check that MinIO is reachable from within the cluster:
      kubectl get svc -n minio
  - Verify the minio-credentials secret exists in the arango namespace:
      kubectl get secret minio-credentials -n arango
HINT
      ;;
    7)
      cat <<'HINT'
  - Verify the chart was downloaded successfully (check the log file).
  - Check Helm output for errors:
      helm list -n arango
      helm status platform -n arango
  - If the chart references images from a private registry, ensure
    imagePullSecrets are configured and credentials are valid.
HINT
      ;;
    8)
      cat <<'HINT'
  - This step waits for all platform pods. Check which pods are not ready:
      kubectl get pods -n arango
  - Look for image pull errors (platform images may require auth):
      kubectl get events -n arango --sort-by='.lastTimestamp' | grep -i -E "pull|image|auth"
  - Describe a failing pod for details:
      kubectl describe pod <pod-name> -n arango
  - Check pod logs:
      kubectl logs <pod-name> -n arango
  - If pods are in ImagePullBackOff, ensure the --license-key is correct
    — it is used to authenticate with the ArangoDB container registry.
HINT
      ;;
    9)
      cat <<'HINT'
  - Check that the gateway service exists:
      kubectl get svc deployment-ea -n arango
  - Verify port 8529 is not in use by another process:
      lsof -i:8529
  - Start port-forward manually:
      kubectl port-forward -n arango service/deployment-ea 8529:8529
HINT
      ;;
    *)
      info "Review the log file for command output and error details."
      ;;
  esac
}

mktmp() {
  local f
  f=$(mktemp "${TMPDIR:-/tmp}/arango-install.XXXXXX")
  TMPFILES+=("$f")
  echo "$f"
}

# Portable port-in-use check (works on macOS, minimal Linux without lsof)
port_in_use() {
  local port="$1"
  if command -v lsof &>/dev/null; then
    lsof -i:"${port}" &>/dev/null
  elif command -v ss &>/dev/null; then
    ss -tlnp sport = :"${port}" 2>/dev/null | grep -q LISTEN
  else
    # Fallback: try connecting via /dev/tcp
    (echo >/dev/tcp/127.0.0.1/"${port}") 2>/dev/null
  fi
}

# Portable random password (works without openssl)
generate_password() {
  if command -v openssl &>/dev/null; then
    openssl rand -base64 16
  else
    head -c 16 /dev/urandom | base64 | tr -d '/+=' | head -c 16
  fi
}

# ── Section 4: System Detection ──────────────────────────────────────────────

OS=""
ARCH=""

detect_system() {
  case "$(uname -s)" in
    Darwin) OS="darwin" ;;
    Linux)  OS="linux" ;;
    *)      error "Unsupported operating system: $(uname -s). Only macOS and Linux are supported." ;;
  esac

  case "$(uname -m)" in
    x86_64)       ARCH="amd64" ;;
    aarch64|arm64) ARCH="arm64" ;;
    *)            error "Unsupported architecture: $(uname -m). Only x86_64 and ARM64 are supported." ;;
  esac

  info "Detected: ${OS}/${ARCH}"
}

# ── Section 5: System Requirements Check ─────────────────────────────────────

check_system_requirements() {
  info "Checking system requirements..."

  check_curl

  # CPU cores
  local cpus
  if [[ "$OS" == "darwin" ]]; then
    cpus=$(sysctl -n hw.ncpu)
  else
    cpus=$(nproc)
  fi
  if (( cpus < MIN_CPUS )); then
    error "Insufficient CPU cores: ${cpus} found, ${MIN_CPUS} required."
  fi
  info "CPU cores: ${cpus} (minimum: ${MIN_CPUS})"

  # RAM
  local ram_gb
  if [[ "$OS" == "darwin" ]]; then
    ram_gb=$(( $(sysctl -n hw.memsize) / 1073741824 ))
  else
    ram_gb=$(awk '/MemTotal/ {printf "%d", $2/1048576}' /proc/meminfo)
  fi
  if (( ram_gb < MIN_RAM_GB )); then
    error "Insufficient RAM: ${ram_gb} GB found, ${MIN_RAM_GB} GB required."
  fi
  info "RAM: ${ram_gb} GB (minimum: ${MIN_RAM_GB} GB)"

  # Disk space
  local disk_gb
  if [[ "$OS" == "darwin" ]]; then
    disk_gb=$(df -g . | awk 'NR==2 {print $4}')
  else
    disk_gb=$(df -BG . | awk 'NR==2 {gsub(/G/,"",$4); print $4}')
  fi
  if (( disk_gb < MIN_DISK_GB )); then
    error "Insufficient disk space: ${disk_gb} GB available, ${MIN_DISK_GB} GB required."
  fi
  info "Disk space: ${disk_gb} GB available (minimum: ${MIN_DISK_GB} GB)"

  # Docker installed
  if ! command -v docker &>/dev/null; then
    echo ""
    error "Docker is not installed.

Please install Docker before running this script:
  macOS:  https://docs.docker.com/desktop/install/mac-install/
  Linux:  https://docs.docker.com/engine/install/"
  fi

  # Docker running
  if ! docker info &>/dev/null; then
    error "Docker is installed but not running. Please start Docker and try again."
  fi
  info "Docker: running"

  # Docker resource allocation (warn only)
  local docker_cpus docker_mem_bytes docker_mem_gb
  docker_cpus=$(docker info --format '{{.NCPU}}' 2>/dev/null || echo "0")
  docker_mem_bytes=$(docker info --format '{{.MemTotal}}' 2>/dev/null || echo "0")
  docker_mem_gb=$(( docker_mem_bytes / 1073741824 ))
  if (( docker_cpus > 0 && docker_cpus < MIN_CPUS )); then
    warn "Docker is allocated only ${docker_cpus} CPUs. Recommend at least ${MIN_CPUS} for Kind cluster."
  fi
  if (( docker_mem_gb > 0 && docker_mem_gb < 8 )); then
    warn "Docker is allocated only ${docker_mem_gb} GB RAM. Recommend at least 8 GB for Kind cluster."
  fi

  # Port 8529 free
  if port_in_use "${PORT_FORWARD_LOCAL}"; then
    error "Port ${PORT_FORWARD_LOCAL} is already in use. Free the port and try again.
  Check with: lsof -i:${PORT_FORWARD_LOCAL}  (macOS/Linux)
         or:  ss -tlnp sport = :${PORT_FORWARD_LOCAL}  (Linux)"
  fi
  info "Port ${PORT_FORWARD_LOCAL}: available"

  # Internet connectivity
  if ! curl --proto '=https' --tlsv1.2 -fsS --max-time 10 https://github.com -o /dev/null 2>/dev/null; then
    error "No internet connectivity. This script needs to download container images and tools.
  Check your network connection and proxy settings (HTTP_PROXY, HTTPS_PROXY)."
  fi
  info "Internet: reachable"

  success "System requirements met."
}

# ── Section 6: Dependency Management ─────────────────────────────────────────

install_binary() {
  local name="$1" src="$2"
  if [[ -w /usr/local/bin ]]; then
    install -m 755 "$src" "/usr/local/bin/${name}"
  else
    local user_bin="${HOME}/.local/bin"
    mkdir -p "$user_bin"
    install -m 755 "$src" "${user_bin}/${name}"
    add_to_path "$user_bin"
  fi
}

ensure_kind() {
  if command -v kind &>/dev/null; then
    local installed_ver
    installed_ver=$(kind version 2>/dev/null | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "unknown")
    if [[ "$installed_ver" == "$VERSION_KIND" ]]; then
      info "kind: ${installed_ver} (up to date)"
      return
    fi
    info "kind: ${installed_ver} installed, want ${VERSION_KIND} — reinstalling..."
  else
    info "Installing kind ${VERSION_KIND}..."
  fi
  if [[ "$OS" == "darwin" ]] && command -v brew &>/dev/null; then
    brew install kind || brew upgrade kind
  else
    local tmp sha_tmp
    tmp=$(mktmp)
    sha_tmp=$(mktmp)
    local kind_url="https://github.com/kubernetes-sigs/kind/releases/download/${VERSION_KIND}/kind-${OS}-${ARCH}"
    retry curl --proto '=https' --tlsv1.2 -fsSL "${kind_url}" -o "$tmp"
    retry curl --proto '=https' --tlsv1.2 -fsSL "${kind_url}.sha256sum" -o "$sha_tmp"
    verify_checksum "$tmp" "$(awk '{print $1}' "$sha_tmp")" "kind"
    install_binary kind "$tmp"
  fi
  command -v kind &>/dev/null || error "Failed to install kind."
  success "kind installed: $(kind version)"
}

ensure_kubectl() {
  if command -v kubectl &>/dev/null; then
    local installed_ver
    installed_ver=$(kubectl version --client -o json 2>/dev/null | grep '"gitVersion"' | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "unknown")
    if [[ "$installed_ver" == "$VERSION_KUBECTL" ]]; then
      info "kubectl: ${installed_ver} (up to date)"
      return
    fi
    info "kubectl: ${installed_ver} installed, want ${VERSION_KUBECTL} — reinstalling..."
  else
    info "Installing kubectl ${VERSION_KUBECTL}..."
  fi
  if [[ "$OS" == "darwin" ]] && command -v brew &>/dev/null; then
    brew install kubectl || brew upgrade kubectl
  else
    local tmp sha_tmp
    tmp=$(mktmp)
    sha_tmp=$(mktmp)
    local kubectl_url="https://dl.k8s.io/release/${VERSION_KUBECTL}/bin/${OS}/${ARCH}/kubectl"
    retry curl --proto '=https' --tlsv1.2 -fsSL "${kubectl_url}" -o "$tmp"
    retry curl --proto '=https' --tlsv1.2 -fsSL "${kubectl_url}.sha256" -o "$sha_tmp"
    verify_checksum "$tmp" "$(cat "$sha_tmp")" "kubectl"
    install_binary kubectl "$tmp"
  fi
  command -v kubectl &>/dev/null || error "Failed to install kubectl."
  success "kubectl installed."
}

ensure_helm() {
  if command -v helm &>/dev/null; then
    info "helm: $(helm version --short 2>/dev/null || echo 'installed')"
    return
  fi
  info "Installing helm..."
  if [[ "$OS" == "darwin" ]] && command -v brew &>/dev/null; then
    brew install helm
  else
    local helm_install_dir
    if [[ -w /usr/local/bin ]]; then
      helm_install_dir=/usr/local/bin
    else
      helm_install_dir="${HOME}/.local/bin"
      mkdir -p "$helm_install_dir"
      add_to_path "$helm_install_dir"
    fi
    curl --proto '=https' --tlsv1.2 -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 \
      | USE_SUDO=false HELM_INSTALL_DIR="${helm_install_dir}" bash
  fi
  command -v helm &>/dev/null || error "Failed to install helm."
  success "helm installed: $(helm version --short)"
}

detect_existing_cluster() {
  if command -v kubectl &>/dev/null && kubectl cluster-info &>/dev/null 2>&1; then
    local ctx
    ctx=$(kubectl config current-context 2>/dev/null || echo "unknown")
    info "Detected existing Kubernetes cluster (context: ${ctx}) — using it."
    USE_EXISTING_CLUSTER=true
    return 0
  fi
  info "No Kubernetes cluster detected — will create a Kind cluster."
  return 1
}

ensure_dependencies() {
  info "Checking dependencies..."
  ensure_kubectl
  ensure_helm
  detect_existing_cluster || true
  if [[ "$USE_EXISTING_CLUSTER" != true ]]; then
    ensure_kind
  fi
  echo ""
  success "All dependencies available."
}

# ── Section 7: Robustness Helpers ────────────────────────────────────────────

check_curl() {
  if ! command -v curl &>/dev/null; then
    error "curl is required but not installed. Install it via your system package manager."
  fi
  local curl_path
  curl_path=$(command -v curl)
  if [[ "$curl_path" == /snap/* ]]; then
    error "Snap-installed curl (${curl_path}) is not supported — snap confinement prevents certificate access.
  Install curl via your system package manager:
    sudo apt-get install curl   # Debian/Ubuntu
    sudo yum install curl       # RHEL/CentOS"
  fi
}

add_to_path() {
  local dir="$1"

  # In GitHub Actions, append to GITHUB_PATH for subsequent steps
  if [[ -n "${GITHUB_ACTIONS:-}" && -n "${GITHUB_PATH:-}" ]]; then
    echo "$dir" >> "$GITHUB_PATH"
    info "Added ${dir} to GITHUB_PATH."
    return
  fi

  # Determine which rc files to update
  local rc_files=()
  [[ -f "$HOME/.bashrc" ]] && rc_files+=("$HOME/.bashrc")
  [[ -f "$HOME/.zshrc" ]]  && rc_files+=("$HOME/.zshrc")
  [[ "${#rc_files[@]}" -eq 0 ]] && rc_files+=("$HOME/.profile")

  local export_line="export PATH=\"${dir}:\$PATH\""
  for rc in "${rc_files[@]}"; do
    if grep -qF "$dir" "$rc" 2>/dev/null; then
      continue  # already present
    fi
    printf '\n# Added by ArangoDB installer\n%s\n' "$export_line" >> "$rc"
    info "Added ${dir} to PATH in ${rc}."
  done

  # Apply for the current session too
  export PATH="${dir}:${PATH}"
  info "Restart your shell or run: source ~/.bashrc (or ~/.zshrc) to make this permanent."
}

verify_checksum() {
  local file="$1" expected="$2" name="$3"
  local actual
  if command -v sha256sum &>/dev/null; then
    actual=$(sha256sum "$file" | awk '{print $1}')
  elif command -v shasum &>/dev/null; then
    actual=$(shasum -a 256 "$file" | awk '{print $1}')
  else
    warn "No sha256sum or shasum available — skipping checksum verification for ${name}."
    return 0
  fi
  [[ "$actual" == "$expected" ]] \
    || error "Checksum verification failed for ${name}.\n  Expected: ${expected}\n  Got:      ${actual}"
  info "Checksum verified: ${name}"
}

retry() {
  local max_attempts=3 delay=2 attempt=1
  while true; do
    if "$@"; then
      return 0
    fi
    if (( attempt >= max_attempts )); then
      error "Command failed after ${max_attempts} attempts: $*"
    fi
    warn "Attempt ${attempt}/${max_attempts} failed. Retrying in ${delay}s..."
    sleep "$delay"
    delay=$(( delay * 2 ))
    attempt=$(( attempt + 1 ))
  done
}

wait_for_pods_ready() {
  # Usage: wait_for_pods_ready <namespace> <timeout_seconds> [label_selector] [fatal=true|false]
  local namespace="$1" timeout="$2" label="${3:-}" fatal="${4:-true}"
  local deadline=$(( SECONDS + timeout ))
  local last_status_print=0

  while (( SECONDS < deadline )); do
    local not_ready=0
    local pod_lines

    if [[ -n "$label" ]]; then
      pod_lines=$(kubectl get pods -n "$namespace" -l "$label" --no-headers 2>/dev/null || true)
    else
      pod_lines=$(kubectl get pods -n "$namespace" --no-headers 2>/dev/null || true)
    fi

    if [[ -z "$pod_lines" ]]; then
      sleep 5
      continue
    fi

    not_ready=0
    local has_creating=0
    while IFS= read -r line; do
      local status
      status=$(echo "$line" | awk '{print $3}')
      if [[ "$status" != "Running" && "$status" != "Completed" ]]; then
        not_ready=1
      fi
      if [[ "$status" == "ContainerCreating" || "$status" == "Init:0/1" ]]; then
        has_creating=1
      fi
      # Check READY column (e.g. 1/1) for Running pods
      if [[ "$status" == "Running" ]]; then
        local ready_col
        ready_col=$(echo "$line" | awk '{print $2}')
        local ready_num total_num
        ready_num=$(echo "$ready_col" | cut -d/ -f1)
        total_num=$(echo "$ready_col" | cut -d/ -f2)
        if [[ "$ready_num" != "$total_num" ]]; then
          not_ready=1
        fi
      fi
    done <<< "$pod_lines"

    if (( not_ready == 0 )); then
      return 0
    fi

    # Print status table and image pull info every 30 seconds
    if (( SECONDS - last_status_print >= 30 )); then
      echo ""
      info "Pod status in namespace '${namespace}':"
      kubectl get pods -n "$namespace" 2>/dev/null || true

      # Show active image pulls if pods are stuck in ContainerCreating
      if (( has_creating )); then
        local pulling_images
        pulling_images=$(kubectl get events -n "$namespace" \
          --field-selector reason=Pulling \
          --no-headers 2>/dev/null \
          | awk '{print $NF}' | sort -u || true)
        if [[ -n "$pulling_images" ]]; then
          echo ""
          info "Container images are still being pulled — this is normal on first install."
          info "Large images (e.g. file-parser worker-pdf ~1.1GB) can take 10+ minutes."
          while IFS= read -r img; do
            info "  Pulling: ${img}"
          done <<< "$pulling_images"
        fi
      fi

      last_status_print=$SECONDS
    fi

    printf "."
    sleep 10
  done

  # Timeout — dump diagnostics
  echo ""
  warn "Pods did not become ready within ${timeout}s."
  echo ""
  info "Pod status:"
  kubectl get pods -n "$namespace" 2>/dev/null || true
  echo ""
  info "Recent events:"
  kubectl get events -n "$namespace" --sort-by='.lastTimestamp' 2>/dev/null | tail -20 || true
  echo ""

  # Describe non-ready pods
  local failing_pods
  failing_pods=$(kubectl get pods -n "$namespace" --no-headers 2>/dev/null \
    | awk '$3 != "Running" && $3 != "Completed" {print $1}' || true)
  if [[ -n "$failing_pods" ]]; then
    while IFS= read -r pod; do
      info "Describing pod: ${pod}"
      kubectl describe pod "$pod" -n "$namespace" 2>/dev/null | tail -30 || true
      echo ""
    done <<< "$failing_pods"
  fi

  if [[ "$fatal" == "true" ]]; then
    error "Pods did not become ready within ${timeout}s. See diagnostics above and log at ${LOG_FILE}."
  fi
  return 1
}

# ── Section 8: Argument Parsing & Input Resolution ───────────────────────────

usage() {
  cat <<USAGE
ArangoDB Contextual Data Platform — Local Installer

QUICK START:
  curl -fsSL https://github.com/<org>/arango-install/releases/latest/download/install.sh | bash -s -- \\
    --license-key "YOUR_LICENSE_KEY"

USAGE:
  ./install.sh [OPTIONS]

OPTIONS:
  --license-key KEY   ArangoDB license key (as provided in your ArangoDB email)
  --help, -h          Show this help message

  The script will prompt interactively if --license-key is not provided.

ENVIRONMENT VARIABLES:
  ARANGO_LICENSE_KEY   Alternative to --license-key

SECURITY NOTE:
  CLI flags are visible in process lists and shell history.
  For sensitive environments, prefer the environment variable or
  the interactive prompt (the script will ask if the value is missing).

  Tip: prefix the command with a space to avoid shell history:
    ${BOLD} ./install.sh --license-key "YOUR_LICENSE_KEY"${NC}

EXAMPLES:
  # Quickest local evaluation (interactive — prompts for license key):
  ./install.sh

  # Non-interactive:
  ./install.sh --license-key "YOUR_LICENSE_KEY"
USAGE
}

parse_args() {
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --license-key)
        [[ -n "${2:-}" ]] || error "--license-key requires a value."
        LICENSE_KEY="$2"; shift 2 ;;
      --help|-h)
        usage; exit 0 ;;
      *)
        error "Unknown option: $1. Use --help for usage." ;;
    esac
  done
}

resolve_chart() {
  info "Downloading platform chart..."
  local tmp
  tmp=$(mktmp)

  if ! curl --proto '=https' --tlsv1.2 -fsSL --retry 3 "${CHART_DOWNLOAD_URL}" -o "$tmp" 2>/dev/null; then
    warn "Could not download chart from release server."
    try_local_chart
    return
  fi

  # Verify it's actually a gzip file, not an HTML auth page
  local mime
  mime=$(file -b --mime-type "$tmp" 2>/dev/null || echo "unknown")
  if [[ "$mime" != application/gzip && "$mime" != application/x-gzip && "$mime" != application/octet-stream ]]; then
    rm -f "$tmp"
    warn "Downloaded file is not a valid chart archive (got ${mime})."
    try_local_chart
    return
  fi

  CHART_PATH="$tmp"
  info "Helm chart: downloaded from release server"
}

try_local_chart() {
  if [[ -f "./chart.tgz" ]]; then
    CHART_PATH="$(pwd)/chart.tgz"
    info "Helm chart: using local ./chart.tgz"
  else
    error "Chart download failed and no local ./chart.tgz found.

Download the chart manually:
  curl -LO ${CHART_DOWNLOAD_URL}
  mv arango-platform-release.tgz ./chart.tgz
  ./install.sh"
  fi
}

resolve_credentials() {
  local combined_key="${LICENSE_KEY:-${ARANGO_LICENSE_KEY:-}}"

  if [[ -z "$combined_key" ]]; then
    if [[ -n "${NONINTERACTIVE:-}" || ! -t 0 ]]; then
      error "No license key provided. Pass --license-key \"YOUR_LICENSE_KEY\" to run non-interactively."
    fi
    echo ""
    read -rs -p "Enter ArangoDB License Key: " combined_key
    echo ""
  fi

  [[ -n "$combined_key" ]] || error "License key is required."

  CLIENT_ID="${combined_key%%:*}"
  CLIENT_SECRET="${combined_key#*:}"

  if [[ -z "$CLIENT_ID" || -z "$CLIENT_SECRET" || "$CLIENT_ID" == "$combined_key" ]]; then
    error "License key must be in the form provided in your ArangoDB email (client_id:client_secret)."
  fi

  info "License credentials: provided"
}

# ── Section 9: Step Functions ────────────────────────────────────────────────

step_1_create_cluster() {
  if [[ "$USE_EXISTING_CLUSTER" == true ]]; then
    step 1 "Verifying existing Kubernetes cluster"

    info "Checking cluster connectivity..."
    kubectl cluster-info || error "Cannot reach the existing cluster. Check your kubeconfig."

    info "Checking that at least one node is ready..."
    local ready_nodes
    ready_nodes=$(kubectl get nodes --no-headers 2>/dev/null | awk '$2 == "Ready"' | wc -l | tr -d ' ')
    if (( ready_nodes == 0 )); then
      error "No nodes are in Ready state. Check your cluster: kubectl get nodes"
    fi
    info "Ready nodes: ${ready_nodes}"

    log_detail "Node status:"
    run_logged kubectl get nodes -o wide

    if kubectl get namespace "${NAMESPACE_ARANGO}" &>/dev/null; then
      info "Namespace '${NAMESPACE_ARANGO}' already exists."
    else
      kubectl create namespace "${NAMESPACE_ARANGO}"
    fi

    step_done
    return
  fi

  step 1 "Creating Kind cluster"

  if kind get clusters 2>/dev/null | grep -q "^${KIND_CLUSTER_NAME}$"; then
    info "Kind cluster '${KIND_CLUSTER_NAME}' already exists — skipping creation."
    log_detail "Existing Kind clusters: $(kind get clusters 2>/dev/null | tr '\n' ', ')"
  else
    # Pre-pull the node image so Docker shows per-layer download progress
    # instead of kind silently hanging during the pull (~900MB on first run).
    if ! docker image inspect "${KIND_NODE_IMAGE}" &>/dev/null; then
      info "Pulling Kind node image ${KIND_NODE_IMAGE} (~900MB, may take a few minutes)..."
      docker pull "${KIND_NODE_IMAGE}"
    else
      info "Kind node image ${KIND_NODE_IMAGE} already cached."
    fi
    info "Creating Kind cluster '${KIND_CLUSTER_NAME}'..."
    retry kind create cluster --name "${KIND_CLUSTER_NAME}" --image "${KIND_NODE_IMAGE}"
  fi
  if ! kubectl config use-context "kind-${KIND_CLUSTER_NAME}" &>/dev/null; then
    info "Context 'kind-${KIND_CLUSTER_NAME}' not found in kubeconfig — re-exporting from Kind..."
    kind export kubeconfig --name "${KIND_CLUSTER_NAME}" \
      || error "Failed to export kubeconfig for Kind cluster '${KIND_CLUSTER_NAME}'."
  fi

  info "Waiting for cluster node to be ready..."
  kubectl wait --for=condition=Ready node --all --timeout=120s

  kubectl cluster-info

  log_detail "Node status:"
  run_logged kubectl get nodes -o wide

  if kubectl get namespace "${NAMESPACE_ARANGO}" &>/dev/null; then
    info "Namespace '${NAMESPACE_ARANGO}' already exists."
  else
    kubectl create namespace "${NAMESPACE_ARANGO}"
  fi

  step_done
}

step_2_create_license_secret() {
  step 2 "Configuring license credentials"

  # Always recreate — credentials may have changed on re-run
  if kubectl get secret arango-license-key -n "${NAMESPACE_ARANGO}" &>/dev/null; then
    info "License secret exists — replacing with current credentials."
    run_logged kubectl delete secret arango-license-key -n "${NAMESPACE_ARANGO}" || true
  fi

  kubectl create secret generic arango-license-key \
    --namespace "${NAMESPACE_ARANGO}" \
    --from-literal=license-client-id="${CLIENT_ID}" \
    --from-literal=license-client-secret="${CLIENT_SECRET}"

  log_detail "Secrets in namespace '${NAMESPACE_ARANGO}':"
  run_logged kubectl get secrets -n "${NAMESPACE_ARANGO}"

  step_done
}

step_3_install_operator() {
  step 3 "Installing ArangoDB Kubernetes Operator v${VERSION_OPERATOR}"

  local operator_url="https://github.com/arangodb/kube-arangodb/releases/download/${VERSION_OPERATOR}/kube-arangodb-enterprise-${VERSION_OPERATOR}.tgz"
  log_detail "Operator chart URL: ${operator_url}"

  retry helm upgrade --install operator \
    --namespace "${NAMESPACE_ARANGO}" \
    "${operator_url}" \
    --set "webhooks.enabled=true" \
    --set "operator.args[0]=--deployment.feature.gateway=true" \
    --set "operator.architectures={${ARCH}}"

  log_detail "Helm release status:"
  run_logged helm status operator -n "${NAMESPACE_ARANGO}"

  info "Waiting for operator pod to appear..."
  local wait_deadline=$(( SECONDS + 60 ))
  while (( SECONDS < wait_deadline )); do
    if kubectl get pods -n "${NAMESPACE_ARANGO}" -l app.kubernetes.io/name=kube-arangodb-enterprise --no-headers 2>/dev/null | grep -q .; then
      break
    fi
    sleep 3
  done
  if ! kubectl get pods -n "${NAMESPACE_ARANGO}" -l app.kubernetes.io/name=kube-arangodb-enterprise --no-headers 2>/dev/null | grep -q .; then
    error "Operator pod did not appear within 60s. Check Helm release: helm status operator -n ${NAMESPACE_ARANGO}"
  fi

  info "Waiting for operator pod to be ready..."
  log_detail "Operator pod status before wait:"
  run_logged kubectl get pods -n "${NAMESPACE_ARANGO}" -l app.kubernetes.io/name=kube-arangodb-enterprise -o wide

  kubectl wait --for=condition=ready pod \
    --selector app.kubernetes.io/name=kube-arangodb-enterprise \
    --namespace "${NAMESPACE_ARANGO}" \
    --timeout=120s

  log_detail "Operator pod status after wait:"
  run_logged kubectl get pods -n "${NAMESPACE_ARANGO}" -l app.kubernetes.io/name=kube-arangodb-enterprise -o wide

  step_done
}

step_4_deploy_arangodb() {
  step 4 "Deploying ArangoDB (single server mode)"

  info "Applying ArangoDeployment manifest (Single mode, image: ${ARANGODB_IMAGE}, arch: ${ARCH})..."
  cat <<EOF | kubectl apply --namespace "${NAMESPACE_ARANGO}" -f -
apiVersion: "database.arangodb.com/v1"
kind: "ArangoDeployment"
metadata:
  name: "deployment"
spec:
  mode: Single
  image: "${ARANGODB_IMAGE}"
  architecture:
    - ${ARCH}
  gateway:
    enabled: true
    dynamic: true
  gateways:
    count: 1
  single:
    args:
      - --vector-index
  license:
    secretName: arango-license-key
EOF

  log_detail "ArangoDeployment status:"
  run_logged kubectl get arangodeployment -n "${NAMESPACE_ARANGO}" -o wide

  info "Waiting for ArangoDB pods to be created..."
  local expected_pods=2  # 1 single-server + 1 gateway
  local wait_deadline=$(( SECONDS + 120 ))
  while (( SECONDS < wait_deadline )); do
    local pod_count
    pod_count=$(kubectl get pods -n "${NAMESPACE_ARANGO}" -l arango_deployment=deployment --no-headers 2>/dev/null | wc -l | tr -d ' ')
    if (( pod_count >= expected_pods )); then
      break
    fi
    sleep 5
  done

  info "Waiting for ArangoDB pods to be ready (this may take several minutes)..."
  wait_for_pods_ready "${NAMESPACE_ARANGO}" 300 "arango_deployment=deployment"

  log_detail "ArangoDB pods after ready:"
  run_logged kubectl get pods -n "${NAMESPACE_ARANGO}" -o wide

  step_done
}

step_5_setup_minio() {
  step 5 "Setting up MinIO object storage"

  # Create minio namespace
  if kubectl get namespace "${NAMESPACE_MINIO}" &>/dev/null; then
    info "Namespace '${NAMESPACE_MINIO}' already exists."
  else
    kubectl create namespace "${NAMESPACE_MINIO}"
  fi

  # Generate or reuse MinIO credentials
  local minio_user minio_password
  if kubectl get secret minio-root -n "${NAMESPACE_MINIO}" &>/dev/null; then
    info "MinIO credentials exist — reusing."
    minio_user=$(kubectl get secret minio-root -n "${NAMESPACE_MINIO}" -o jsonpath='{.data.MINIO_ROOT_USER}' | base64 -d)
    minio_password=$(kubectl get secret minio-root -n "${NAMESPACE_MINIO}" -o jsonpath='{.data.MINIO_ROOT_PASSWORD}' | base64 -d)
  else
    minio_user="minioadmin"
    minio_password=$(generate_password)
    kubectl create secret generic minio-root \
      --namespace "${NAMESPACE_MINIO}" \
      --from-literal=MINIO_ROOT_USER="${minio_user}" \
      --from-literal=MINIO_ROOT_PASSWORD="${minio_password}"
    # minio-root was just regenerated — drop stale minio-credentials so it is recreated below
    run_logged kubectl delete secret minio-credentials -n "${NAMESPACE_ARANGO}" --ignore-not-found || true
  fi

  # Create credentials secret in arango namespace for platform-storage access
  if kubectl get secret minio-credentials -n "${NAMESPACE_ARANGO}" &>/dev/null; then
    info "MinIO credentials in '${NAMESPACE_ARANGO}' namespace already exist."
  else
    kubectl create secret generic minio-credentials \
      --namespace "${NAMESPACE_ARANGO}" \
      --from-literal=accessKey="${minio_user}" \
      --from-literal=secretKey="${minio_password}"
  fi

  # Check if bucket-creation job already completed
  local job_status=""
  if kubectl get job minio-create-bucket -n "${NAMESPACE_MINIO}" &>/dev/null; then
    job_status=$(kubectl get job minio-create-bucket -n "${NAMESPACE_MINIO}" -o jsonpath='{.status.succeeded}' 2>/dev/null || echo "")
  fi

  if [[ "$job_status" == "1" ]]; then
    info "MinIO bucket-creation job already completed — applying manifests for idempotency."
    # Apply without the job to avoid immutable field errors
    cat <<'MINIO_YAML' | kubectl apply -n "${NAMESPACE_MINIO}" -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: minio-data-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
---
apiVersion: v1
kind: Service
metadata:
  name: minio
spec:
  selector:
    app: minio
  ports:
    - port: 9000
      targetPort: 9000
MINIO_YAML

    # Apply deployment separately with image variable
    cat <<EOF | kubectl apply -n "${NAMESPACE_MINIO}" -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: minio
spec:
  replicas: 1
  selector:
    matchLabels:
      app: minio
  template:
    metadata:
      labels:
        app: minio
    spec:
      containers:
        - name: minio
          image: ${MINIO_IMAGE}
          args:
            - server
            - /data
          envFrom:
            - secretRef:
                name: minio-root
          ports:
            - containerPort: 9000
          volumeMounts:
            - name: data
              mountPath: /data
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: minio-data-pvc
EOF
  else
    # Delete failed job if it exists
    if [[ -n "$job_status" ]] || kubectl get job minio-create-bucket -n "${NAMESPACE_MINIO}" &>/dev/null; then
      info "Cleaning up previous bucket-creation job..."
      run_logged kubectl delete job minio-create-bucket -n "${NAMESPACE_MINIO}" --ignore-not-found || true
    fi

    # Apply full MinIO stack including the job
    cat <<EOF | kubectl apply -n "${NAMESPACE_MINIO}" -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: minio-data-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: minio
spec:
  replicas: 1
  selector:
    matchLabels:
      app: minio
  template:
    metadata:
      labels:
        app: minio
    spec:
      containers:
        - name: minio
          image: ${MINIO_IMAGE}
          args:
            - server
            - /data
          envFrom:
            - secretRef:
                name: minio-root
          ports:
            - containerPort: 9000
          volumeMounts:
            - name: data
              mountPath: /data
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: minio-data-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: minio
spec:
  selector:
    app: minio
  ports:
    - port: 9000
      targetPort: 9000
---
apiVersion: batch/v1
kind: Job
metadata:
  name: minio-create-bucket
spec:
  backoffLimit: 6
  template:
    spec:
      restartPolicy: OnFailure
      containers:
        - name: mc
          image: ${MINIO_MC_IMAGE}
          args:
            - mb
            - --ignore-existing
            - local/arango-platform-storage
          env:
            - name: MINIO_ACCESS_KEY
              valueFrom:
                secretKeyRef:
                  name: minio-root
                  key: MINIO_ROOT_USER
            - name: MINIO_SECRET_KEY
              valueFrom:
                secretKeyRef:
                  name: minio-root
                  key: MINIO_ROOT_PASSWORD
            - name: MC_HOST_local
              value: http://\$(MINIO_ACCESS_KEY):\$(MINIO_SECRET_KEY)@minio.${NAMESPACE_MINIO}.svc.cluster.local:9000
EOF

    info "Waiting for MinIO deployment..."
    kubectl wait --for=condition=available deployment/minio \
      -n "${NAMESPACE_MINIO}" --timeout=120s

    log_detail "MinIO pod status after deployment ready:"
    run_logged kubectl get pods -n "${NAMESPACE_MINIO}" -o wide

    info "Waiting for bucket creation..."
    kubectl wait --for=condition=complete job/minio-create-bucket \
      -n "${NAMESPACE_MINIO}" --timeout=120s

    log_detail "Bucket creation job logs:"
    run_logged kubectl logs -n "${NAMESPACE_MINIO}" -l job-name=minio-create-bucket
  fi

  log_detail "MinIO final state:"
  run_logged kubectl get all -n "${NAMESPACE_MINIO}"

  step_done
}

step_6_create_platform_storage() {
  step 6 "Configuring platform storage"

  cat <<EOF | kubectl apply --namespace "${NAMESPACE_ARANGO}" -f -
apiVersion: platform.arangodb.com/v1beta1
kind: ArangoPlatformStorage
metadata:
  name: deployment
  namespace: ${NAMESPACE_ARANGO}
spec:
  backend:
    s3:
      bucketName: arango-platform-storage
      credentialsSecret:
        name: minio-credentials
      endpoint: http://minio.${NAMESPACE_MINIO}.svc.cluster.local:9000
EOF

  info "Waiting for platform storage to be ready..."
  local ps_deadline=$(( SECONDS + 60 ))
  local ps_ready=""
  while (( SECONDS < ps_deadline )); do
    ps_ready=$(kubectl get arangoplatformstorage deployment -n "${NAMESPACE_ARANGO}" \
      -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' 2>/dev/null || echo "")
    if [[ "$ps_ready" == "True" ]]; then
      break
    fi
    sleep 3
  done
  kubectl get arangoplatformstorage -n "${NAMESPACE_ARANGO}"
  if [[ "$ps_ready" != "True" ]]; then
    error "ArangoPlatformStorage did not become ready within 60s."
  fi

  log_detail "ArangoPlatformStorage details:"
  run_logged kubectl get arangoplatformstorage -n "${NAMESPACE_ARANGO}" -o yaml

  step_done
}

step_7_install_platform_chart() {
  step 7 "Installing ArangoDB Platform"

  # Generate file-parser service (fps) recovery credentials.
  # The arangodb-autograph chart requires fps_recovery_username + a fernet key
  # secret to set up secure AutoGraph ↔ file-parser communication.
  local fps_username="fps-recovery"
  local fps_secret_name="fps-recovery-credentials"
  local fps_fernet_key
  fps_fernet_key=$(python3 -c "import base64, os; print(base64.urlsafe_b64encode(os.urandom(32)).decode())")

  info "Creating file-parser recovery credentials secret..."
  kubectl create secret generic "${fps_secret_name}" \
    --from-literal=fernet-key="${fps_fernet_key}" \
    --namespace "${NAMESPACE_ARANGO}" \
    --dry-run=client -o yaml | kubectl apply -f - \
    || error "Failed to create ${fps_secret_name} secret."

  local values_overlay
  values_overlay=$(mktmp)
  cat > "$values_overlay" <<VALUES
services:
  arango-control-plane:
    values:
      resources:
        limits:
          memory: "1Gi"
  arangodb-core-ui:
    values: {}
  arangodb-platform-ui:
    values:
      resources:
        requests:
          cpu: "100m"
  arangodb-platform-ui-server:
    values: {}
  file-manager:
    values:
      resources:
        limits:
          cpu: "500m"
  arangodb-file-parser:
    values:
      workerDefault:
        replicas: 1
        resources:
          requests:
            cpu: "50m"
            memory: "256Mi"
            ephemeral-storage: "100Mi"
          limits:
            cpu: "500m"
            memory: "512Mi"
            ephemeral-storage: "1Gi"
      workerPdf:
        replicas: 1
        resources:
          requests:
            cpu: "50m"
            memory: "256Mi"
            ephemeral-storage: "100Mi"
          limits:
            cpu: "500m"
            memory: "512Mi"
            ephemeral-storage: "1Gi"
charts:
  arangodb-autograph:
    overrides:
      fps_recovery_username: "${fps_username}"
      file_parser:
        recovery_username: "${fps_username}"
        recovery_secret_name: "${fps_secret_name}"
VALUES

  info "Installing from chart: ${CHART_PATH}"
  log_detail "Values overlay contents:"
  run_logged cat "$values_overlay"

  retry helm upgrade platform --install "${CHART_PATH}" \
    --namespace "${NAMESPACE_ARANGO}" \
    --set deployment=deployment \
    -f "$values_overlay"

  # The chart templates always render all ArangoPlatformService CRs regardless
  # of values. Delete the services we don't need for AutoGraph/AutoRAG evaluation.
  info "Removing services not needed for AutoGraph/AutoRAG evaluation..."
  kubectl delete arangoplatformservice arangodb-ml-api -n "${NAMESPACE_ARANGO}" --ignore-not-found
  kubectl delete arangoplatformservice arangodb-mlflow -n "${NAMESPACE_ARANGO}" --ignore-not-found
  kubectl delete arangoplatformservice platform-monitoring-grafana -n "${NAMESPACE_ARANGO}" --ignore-not-found
  kubectl delete arangoplatformservice platform-monitoring-prometheus -n "${NAMESPACE_ARANGO}" --ignore-not-found
  # Wait for monitoring pods to fully terminate before step 8 checks pod readiness
  info "Waiting for monitoring pods to terminate..."
  kubectl wait --for=delete pod \
    -l "app.kubernetes.io/name in (grafana,prometheus)" \
    -n "${NAMESPACE_ARANGO}" --timeout=120s 2>/dev/null || true


  log_detail "Helm release status for platform:"
  run_logged helm status platform -n "${NAMESPACE_ARANGO}"

  log_detail "All Helm releases in namespace '${NAMESPACE_ARANGO}':"
  run_logged helm list -n "${NAMESPACE_ARANGO}"

  step_done
}

step_8_wait_for_pods() {
  step 8 "Waiting for all pods to be ready"

  info "This may take several minutes while container images are pulled..."
  info "On a first install, large images (up to ~1.1GB) may take 10+ minutes to pull."

  # Wait for platform service pods to be created (the operator creates them asynchronously)
  info "Waiting for platform service pods to appear..."
  local svc_deadline=$(( SECONDS + 600 ))
  local last_svc_print=0
  while (( SECONDS < svc_deadline )); do
    local not_ready_svcs
    not_ready_svcs=$(kubectl get arangoplatformservices -n "${NAMESPACE_ARANGO}" --no-headers 2>/dev/null \
      | awk '$2 != "True"' | wc -l | tr -d ' ')
    local total_svcs
    total_svcs=$(kubectl get arangoplatformservices -n "${NAMESPACE_ARANGO}" --no-headers 2>/dev/null \
      | wc -l | tr -d ' ')

    if (( total_svcs > 0 && not_ready_svcs == 0 )); then
      break
    fi

    # Print status every 30 seconds
    if (( SECONDS - last_svc_print >= 30 )); then
      echo ""
      info "Platform services status:"
      kubectl get arangoplatformservices -n "${NAMESPACE_ARANGO}" 2>/dev/null || true
      echo ""
      info "Pod status:"
      kubectl get pods -n "${NAMESPACE_ARANGO}" 2>/dev/null || true
      last_svc_print=$SECONDS
    fi

    printf "."
    sleep 10
  done

  if (( SECONDS >= svc_deadline )); then
    echo ""
    warn "Platform services did not all become ready within 600s."
    info "Platform services status:"
    kubectl get arangoplatformservices -n "${NAMESPACE_ARANGO}" 2>/dev/null || true
    echo ""
    info "Pod status:"
    kubectl get pods -n "${NAMESPACE_ARANGO}" 2>/dev/null || true
    echo ""
    info "Recent events:"
    kubectl get events -n "${NAMESPACE_ARANGO}" --sort-by='.lastTimestamp' 2>/dev/null | tail -20 || true
    error "Platform services did not become ready. See diagnostics above and log at ${LOG_FILE}."
  fi

  # After services are ready, verify all pods are actually Running
  info "Verifying all pods are ready..."
  if ! wait_for_pods_ready "${NAMESPACE_ARANGO}" 600 "" "false"; then
    echo ""
    warn "Some pods are not yet ready — container images may still be pulling."
    echo ""
    info "To check pod status:"
    echo "    kubectl get pods -n ${NAMESPACE_ARANGO}"
    echo ""
    info "If pods are in ContainerCreating or Pending, wait a few minutes then re-run:"
    echo "    ./install.sh --license-key \"YOUR_LICENSE_KEY\""
    echo ""
    info "The installer is safe to re-run — it will resume from where it left off."
    info "See full log at: ${LOG_FILE}"
    echo ""
    step_done
    return
  fi

  echo ""
  info "All pods:"
  kubectl get pods -n "${NAMESPACE_ARANGO}"

  echo ""
  info "Platform services:"
  kubectl get arangoplatformservices -n "${NAMESPACE_ARANGO}" 2>/dev/null || true

  log_detail "Detailed pod status:"
  run_logged kubectl get pods -n "${NAMESPACE_ARANGO}" -o wide
  log_detail "Services:"
  run_logged kubectl get svc -n "${NAMESPACE_ARANGO}"

  step_done
}

step_9_port_forward_and_open() {
  step 9 "Starting port-forward and opening UI"

  # Kill any existing port-forward on this port
  if command -v lsof &>/dev/null; then
    lsof -ti:"${PORT_FORWARD_LOCAL}" 2>/dev/null | xargs kill 2>/dev/null || true
  elif command -v fuser &>/dev/null; then
    fuser -k "${PORT_FORWARD_LOCAL}/tcp" 2>/dev/null || true
  fi
  sleep 1

  log_detail "Services available for port-forward:"
  run_logged kubectl get svc -n "${NAMESPACE_ARANGO}"

  info "Starting port-forward for service/deployment-ea on port ${PORT_FORWARD_LOCAL}..."
  kubectl port-forward --namespace "${NAMESPACE_ARANGO}" \
    service/deployment-ea "${PORT_FORWARD_LOCAL}:${PORT_FORWARD_LOCAL}" >> "$LOG_FILE" 2>&1 &
  PORT_FORWARD_PID=$!

  # Wait for port-forward to establish
  sleep 3

  # Verify it's working
  if ! kill -0 "$PORT_FORWARD_PID" 2>/dev/null; then
    warn "Port-forward process exited. Check log for details."
    info "  You can start it manually:"
    info "  kubectl port-forward -n ${NAMESPACE_ARANGO} service/deployment-ea ${PORT_FORWARD_LOCAL}:${PORT_FORWARD_LOCAL}"
  else
    # Set root password
    info "Setting root password..."
    local pw_set=false
    for attempt in 1 2 3 4 5 6 7 8 9 10; do
      if curl -kfsS -u "${ARANGODB_ROOT_USER}:" \
        -X PATCH "https://127.0.0.1:${PORT_FORWARD_LOCAL}/_api/user/${ARANGODB_ROOT_USER}" \
        -H "Content-Type: application/json" \
        -d "{\"passwd\":\"${ARANGODB_ROOT_PASSWORD}\"}" >> "$LOG_FILE" 2>&1; then
        pw_set=true
        break
      fi
      sleep 3
    done
    if [[ "$pw_set" == "true" ]]; then
      success "Root password set."
    else
      warn "Could not set root password automatically. You can set it via the UI."
    fi

    # Open browser
    local url="https://127.0.0.1:${PORT_FORWARD_LOCAL}/ui/"
    if [[ "$OS" == "darwin" ]]; then
      open "$url" 2>/dev/null || true
    else
      xdg-open "$url" 2>/dev/null || true
    fi

    # Port-forward is no longer needed — kill it
    kill "$PORT_FORWARD_PID" 2>/dev/null || true
    PORT_FORWARD_PID=""
  fi

  step_done
}

# ── Section 10: Main ─────────────────────────────────────────────────────────

log_versions() {
  {
    echo "=== Arango Platform Install — $(date -u '+%Y-%m-%dT%H:%M:%SZ') ==="
    echo "OS: ${OS}/${ARCH}"
    echo "Docker: $(docker version --format '{{.Server.Version}}' 2>/dev/null || echo 'unknown')"
    echo "kind: $(kind version 2>/dev/null || echo 'not installed')"
    echo "kubectl: $(kubectl version --client --short 2>/dev/null || kubectl version --client -o json 2>/dev/null | grep gitVersion | head -1 || echo 'not installed')"
    echo "helm: $(helm version --short 2>/dev/null || echo 'not installed')"
    echo "==="
  } >> "$LOG_FILE"
}

main() {
  parse_args "$@"

  # Set up logging — tee to log file
  exec > >(tee -a "$LOG_FILE") 2>&1

  echo ""
  echo -e "${BOLD}=========================================${NC}"
  echo -e "${BOLD}  ArangoDB Contextual Data Platform${NC}"
  echo -e "${BOLD}  Local Installer${NC}"
  echo -e "${BOLD}=========================================${NC}"
  echo ""
  info "This typically takes 10-15 minutes."
  info "Log file: ${LOG_FILE}"
  echo ""

  # Auto-detect non-interactive environments
  if [[ -n "${CI:-}" || -n "${GITHUB_ACTIONS:-}" ]]; then
    NONINTERACTIVE=1
    info "CI environment detected — running non-interactively."
  fi

  detect_system
  check_system_requirements
  ensure_dependencies

  log_versions

  resolve_chart
  resolve_credentials

  local start_time=$SECONDS

  step_1_create_cluster
  step_2_create_license_secret
  step_3_install_operator
  step_4_deploy_arangodb
  step_5_setup_minio
  step_6_create_platform_storage
  step_7_install_platform_chart
  step_8_wait_for_pods
  step_9_port_forward_and_open

  local elapsed=$(( SECONDS - start_time ))
  local mins=$(( elapsed / 60 ))
  local secs=$(( elapsed % 60 ))

  echo ""
  echo -e "${BOLD}${GREEN}=========================================${NC}"
  echo -e "${BOLD}${GREEN}  Installation Complete!${NC}"
  echo -e "${BOLD}${GREEN}=========================================${NC}"
  echo ""
  echo -e "  ${BOLD}UI:${NC}       https://127.0.0.1:${PORT_FORWARD_LOCAL}/ui/"
  echo -e "  ${BOLD}Username:${NC} ${ARANGODB_ROOT_USER}"
  echo -e "  ${BOLD}Password:${NC} ${ARANGODB_ROOT_PASSWORD}"
  echo ""
  echo -e "  ${YELLOW}⚠${NC}  These defaults are for local evaluation only."
  echo -e "     Change the password before exposing the deployment beyond your machine."
  echo ""
  echo -e "  ${BOLD}Log file:${NC} ${LOG_FILE}"
  echo -e "  ${BOLD}Duration:${NC} ${mins}m ${secs}s"
  echo ""
  echo -e "${BOLD}${CYAN}=========================================${NC}"
  echo -e "${BOLD}${CYAN}  Next Steps${NC}"
  echo -e "${BOLD}${CYAN}=========================================${NC}"
  echo ""
  echo -e "  ${BOLD}1. Start port-forward to access the UI or run quickstart examples:${NC}"
  echo ""
  echo -e "     ${CYAN}kubectl port-forward -n ${NAMESPACE_ARANGO} service/deployment-ea ${PORT_FORWARD_LOCAL}:${PORT_FORWARD_LOCAL}${NC}"
  echo ""
  echo -e "     Then open: ${BOLD}https://127.0.0.1:${PORT_FORWARD_LOCAL}/ui/${NC}"
  echo ""
  echo -e "     ${YELLOW}Note:${NC} Re-run the command above any time the port-forward drops."
  echo ""
  echo -e "  ${BOLD}2. Cleanup when done:${NC}"
  if [[ "$USE_EXISTING_CLUSTER" == true ]]; then
    echo -e "     helm uninstall platform operator -n ${NAMESPACE_ARANGO}"
  else
    echo -e "     kind delete cluster --name ${KIND_CLUSTER_NAME}"
  fi
  echo ""
}

main "$@"
