#!/usr/bin/env bash
set -Eeuo pipefail
trap 'status=$?; printf "\033[1;31mERROR:\033[0m installer failed near line %s: %s\n" "$LINENO" "$BASH_COMMAND" >&2; exit "$status"' ERR

DEFAULT_KAGEOS_IMAGE="docker.io/qiayanai/kageos:latest"
DEFAULT_KAGEOS_APP_BASE_IMAGE="docker.io/qiayanai/kagebase:latest"
CN_IMAGE_PREFIX="m.daocloud.io/docker.io"
CN_ALIYUN_REGISTRY="crpi-pp1889gb5d5betoy.cn-beijing.personal.cr.aliyuncs.com"
CN_ALIYUN_NAMESPACE="qiayanai"
CN_ALIYUN_IMAGE="${KAGEOS_CN_ALIYUN_IMAGE:-${CN_ALIYUN_REGISTRY}/${CN_ALIYUN_NAMESPACE}/kageos:latest}"
CN_ALIYUN_APP_BASE_IMAGE="${KAGEOS_CN_ALIYUN_APP_BASE_IMAGE:-${CN_ALIYUN_REGISTRY}/${CN_ALIYUN_NAMESPACE}/kagebase:latest}"
CN_TENCENT_IMAGE="${KAGEOS_CN_TENCENT_IMAGE:-}"
DEFAULT_KAGEOS_RELEASE_BASE_URL="https://downloads.kageos.com/releases"

KAGEOS_IMAGE="${KAGEOS_IMAGE:-$DEFAULT_KAGEOS_IMAGE}"
KAGEOS_IMAGE_FALLBACK="${KAGEOS_IMAGE_FALLBACK:-}"
KAGEOS_IMAGE_FALLBACKS="${KAGEOS_IMAGE_FALLBACKS:-}"
KAGEOS_RELEASE_BASE_URL="${KAGEOS_RELEASE_BASE_URL:-$DEFAULT_KAGEOS_RELEASE_BASE_URL}"
KAGEOS_RELEASE_VERSION="${KAGEOS_RELEASE_VERSION:-latest}"
KAGEOS_CN_TARBALL="${KAGEOS_CN_TARBALL:-1}"
KAGEOS_INSTALL_DIR="${KAGEOS_INSTALL_DIR:-/opt/kageos}"
KAGEOS_DATA_DIR="${KAGEOS_DATA_DIR:-}"
KAGEOS_CONTAINER_NAME="${KAGEOS_CONTAINER_NAME:-kageos}"
KAGEOS_CONTAINER_ENGINE="${KAGEOS_CONTAINER_ENGINE:-auto}"
KAGEOS_INSTALL_DEPS="${KAGEOS_INSTALL_DEPS:-auto}"
KAGEOS_START_TIMEOUT="${KAGEOS_START_TIMEOUT:-1200}"
KAGEOS_AIO_PRINT_SECRETS="${KAGEOS_AIO_PRINT_SECRETS:-1}"
KAGEOS_APP_BASE_IMAGE="${KAGEOS_APP_BASE_IMAGE:-}"
KAGEOS_APP_BASE_PULL="${KAGEOS_APP_BASE_PULL:-}"
KAGEOS_APP_BASE_PULL_FALLBACK_BUILD="${KAGEOS_APP_BASE_PULL_FALLBACK_BUILD:-}"
KAGEOS_AIO_MYSQL_IMAGE="${KAGEOS_AIO_MYSQL_IMAGE:-}"
KAGEOS_AIO_NATS_IMAGE="${KAGEOS_AIO_NATS_IMAGE:-}"
KAGEOS_AIO_MINIO_IMAGE="${KAGEOS_AIO_MINIO_IMAGE:-}"
KAGEOS_AIO_RECREATE_INFRA="${KAGEOS_AIO_RECREATE_INFRA:-}"

BASE_URL="${KAGEOS_BASE_URL:-}"
TLS_MODE="${KAGEOS_TLS_MODE:-auto}"
HTTP_PORT="${KAGEOS_HTTP_PORT:-}"
HTTPS_PORT="${KAGEOS_HTTPS_PORT:-}"
TIMEZONE="${KAGEOS_TIMEZONE:-Asia/Shanghai}"
DEPLOY_USER="${KAGEOS_DEPLOY_USER:-}"
SKIP_PULL=0
DRY_RUN=0
USE_CN_MIRROR="${KAGEOS_CN:-0}"
USER_SET_IMAGE=0

usage() {
  cat <<'EOF'
kageos production one-command installer

Usage:
  curl -fsSL https://kageos.com/install-prod.sh | sudo bash -s -- --base-url https://app.example.com
  curl -fsSL https://kageos.com/install-prod.sh | sudo bash -s -- --base-url http://your-server-ip
  curl -fsSL https://kageos.com/install-prod.sh | sudo bash -s -- --base-url http://your-server-ip --cn

What it does:
  - Installs missing host basics and a container engine where possible.
  - Pulls the published kageos image; it does not clone the source repository.
  - Uses mainland China registries first, then the kageos release archive fallback, when --cn is passed.
  - Starts one privileged all-in-one container with persistent data under /opt/kageos/data.
  - Installs the /usr/local/bin/kageos helper for status, logs, password, restart, and update.

Options:
  --base-url URL             Public URL for this kageos instance. Required.
  --cn                       Use mainland China registry and release download fallbacks.
  --tls-mode MODE            auto, http, https, redirect, or external. Defaults to auto.
  --http-port PORT           HTTP listen port. Defaults to the URL port or 80.
  --https-port PORT          HTTPS listen port. Defaults to the URL port or 443.
  --timezone TZ              Deployment timezone. Defaults to Asia/Shanghai.
  --install-dir DIR          Runtime directory. Defaults to /opt/kageos.
  --data-dir DIR             Persistent data directory. Defaults to <install-dir>/data.
  --image IMAGE              Container image. Defaults to docker.io/qiayanai/kageos:latest.
  --container-name NAME      Container name. Defaults to kageos.
  --container-engine ENGINE  auto, podman, or docker. Defaults to auto.
  --skip-deps                Do not install missing system dependencies.
  --skip-pull                Do not pull the image before starting.
  --dry-run                  Print the resolved plan without changing the host.
  --help                     Show this help.

Examples:
  # Public domain with HTTPS bootstrap using a self-signed certificate.
  curl -fsSL https://kageos.com/install-prod.sh | sudo bash -s -- --base-url https://app.example.com

  # Fast HTTP trial by server IP.
  curl -fsSL https://kageos.com/install-prod.sh | sudo bash -s -- --base-url http://your-server-ip

  # Mainland China server, using optimized registry and release download fallbacks.
  curl -fsSL https://kageos.com/install-prod.sh | sudo bash -s -- --base-url http://your-server-ip --cn

Environment:
  KAGEOS_BASE_URL, KAGEOS_TLS_MODE, KAGEOS_TIMEZONE, KAGEOS_HTTP_PORT,
  KAGEOS_HTTPS_PORT, KAGEOS_IMAGE, KAGEOS_INSTALL_DIR, KAGEOS_DATA_DIR,
  KAGEOS_CONTAINER_ENGINE, KAGEOS_START_TIMEOUT, KAGEOS_RELEASE_BASE_URL,
  KAGEOS_RELEASE_VERSION, and KAGEOS_CN_TARBALL are supported.
  KAGEOS_APP_BASE_IMAGE, KAGEOS_APP_BASE_PULL, KAGEOS_AIO_MYSQL_IMAGE,
  KAGEOS_AIO_NATS_IMAGE, and KAGEOS_AIO_MINIO_IMAGE are forwarded into the
  all-in-one container when set.
EOF
}

log() {
  printf '\033[1;36m==>\033[0m %s\n' "$*"
}

warn() {
  printf '\033[1;33mWARN:\033[0m %s\n' "$*" >&2
}

die() {
  printf '\033[1;31mERROR:\033[0m %s\n' "$*" >&2
  exit 1
}

need_value() {
  local option="$1"
  shift
  [[ $# -gt 0 && -n "${1:-}" ]] || die "$option requires a value"
}

validate_port() {
  local name="$1"
  local value="$2"
  if [[ ! "$value" =~ ^[0-9]+$ || "$value" -lt 1 || "$value" -gt 65535 ]]; then
    die "$name must be a TCP port between 1 and 65535"
  fi
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    --base-url)
      shift
      need_value "--base-url" "$@"
      BASE_URL="$1"
      ;;
    --cn|--china|--mainland-china)
      USE_CN_MIRROR=1
      ;;
    --tls-mode)
      shift
      need_value "--tls-mode" "$@"
      TLS_MODE="$1"
      ;;
    --http-port)
      shift
      need_value "--http-port" "$@"
      validate_port "--http-port" "$1"
      HTTP_PORT="$1"
      ;;
    --https-port)
      shift
      need_value "--https-port" "$@"
      validate_port "--https-port" "$1"
      HTTPS_PORT="$1"
      ;;
    --timezone)
      shift
      need_value "--timezone" "$@"
      TIMEZONE="$1"
      ;;
    --user)
      shift
      need_value "--user" "$@"
      DEPLOY_USER="$1"
      ;;
    --install-dir)
      shift
      need_value "--install-dir" "$@"
      KAGEOS_INSTALL_DIR="$1"
      ;;
    --data-dir)
      shift
      need_value "--data-dir" "$@"
      KAGEOS_DATA_DIR="$1"
      ;;
    --image)
      shift
      need_value "--image" "$@"
      KAGEOS_IMAGE="$1"
      USER_SET_IMAGE=1
      ;;
    --container-name)
      shift
      need_value "--container-name" "$@"
      KAGEOS_CONTAINER_NAME="$1"
      ;;
    --container-engine)
      shift
      need_value "--container-engine" "$@"
      KAGEOS_CONTAINER_ENGINE="$1"
      ;;
    --skip-deps)
      KAGEOS_INSTALL_DEPS="never"
      ;;
    --skip-pull)
      SKIP_PULL=1
      ;;
    --dry-run)
      DRY_RUN=1
      ;;
    --help|-h)
      usage
      exit 0
      ;;
    *)
      die "unsupported argument: $1"
      ;;
  esac
  shift
done

case "$KAGEOS_CONTAINER_ENGINE" in
  auto|podman|docker) ;;
  *) die "--container-engine must be auto, podman, or docker" ;;
esac

apply_cn_mirror() {
  if [[ "$USE_CN_MIRROR" != "1" && "$USE_CN_MIRROR" != "true" ]]; then
    return 0
  fi

  if [[ "$KAGEOS_IMAGE" == "$DEFAULT_KAGEOS_IMAGE" ]]; then
    KAGEOS_IMAGE="$CN_ALIYUN_IMAGE"
    if [[ -n "$CN_TENCENT_IMAGE" ]]; then
      KAGEOS_IMAGE_FALLBACKS="${KAGEOS_IMAGE_FALLBACKS:+${KAGEOS_IMAGE_FALLBACKS} }${CN_TENCENT_IMAGE}"
    fi
  fi
  if [[ -z "$KAGEOS_APP_BASE_IMAGE" ]]; then
    KAGEOS_APP_BASE_IMAGE="$CN_ALIYUN_APP_BASE_IMAGE"
  fi
  if [[ -z "$KAGEOS_APP_BASE_PULL_FALLBACK_BUILD" ]]; then
    KAGEOS_APP_BASE_PULL_FALLBACK_BUILD=1
  fi
  if [[ -z "$KAGEOS_AIO_MYSQL_IMAGE" ]]; then
    KAGEOS_AIO_MYSQL_IMAGE="${CN_IMAGE_PREFIX}/library/mysql:8.0"
  fi
  if [[ -z "$KAGEOS_AIO_NATS_IMAGE" ]]; then
    KAGEOS_AIO_NATS_IMAGE="${CN_IMAGE_PREFIX}/library/nats:2.10-alpine"
  fi
  if [[ -z "$KAGEOS_AIO_MINIO_IMAGE" ]]; then
    KAGEOS_AIO_MINIO_IMAGE="${CN_IMAGE_PREFIX}/minio/minio:RELEASE.2025-09-07T16-13-09Z"
  fi
}

apply_cn_mirror

TLS_MODE="$(printf '%s' "$TLS_MODE" | tr '[:upper:]' '[:lower:]')"
case "$TLS_MODE" in
  auto|http|https|redirect|external) ;;
  *) die "--tls-mode must be auto, http, https, redirect, or external" ;;
esac

[[ -n "$BASE_URL" ]] || { usage >&2; die "--base-url is required"; }
case "$BASE_URL" in
  http://*|https://*) ;;
  *) die "--base-url must start with http:// or https://" ;;
esac

if [[ -z "$KAGEOS_DATA_DIR" ]]; then
  KAGEOS_DATA_DIR="${KAGEOS_INSTALL_DIR%/}/data"
fi

if [[ -z "$DEPLOY_USER" ]]; then
  if [[ -n "${SUDO_USER:-}" && "${SUDO_USER}" != "root" ]]; then
    DEPLOY_USER="$SUDO_USER"
  else
    DEPLOY_USER="root"
  fi
fi

if [[ "$DEPLOY_USER" != "root" && "$DRY_RUN" != "1" ]]; then
  id "$DEPLOY_USER" >/dev/null 2>&1 || die "deploy user does not exist: $DEPLOY_USER"
fi

url_scheme="${BASE_URL%%://*}"
url_hostport="${BASE_URL#*://}"
url_hostport="${url_hostport%%/*}"
url_port=""
if [[ "$url_hostport" == *:* && "$url_hostport" != \[* ]]; then
  maybe_port="${url_hostport##*:}"
  if [[ "$maybe_port" =~ ^[0-9]+$ ]]; then
    url_port="$maybe_port"
  fi
fi

if [[ -z "$HTTP_PORT" ]]; then
  if [[ "$url_scheme" == "http" && -n "$url_port" ]]; then
    HTTP_PORT="$url_port"
  else
    HTTP_PORT=80
  fi
fi
if [[ -z "$HTTPS_PORT" ]]; then
  if [[ "$url_scheme" == "https" && -n "$url_port" ]]; then
    HTTPS_PORT="$url_port"
  else
    HTTPS_PORT=443
  fi
fi
validate_port "HTTP port" "$HTTP_PORT"
validate_port "HTTPS port" "$HTTPS_PORT"

if [[ "$TLS_MODE" == "auto" ]]; then
  if [[ "$url_scheme" == "https" ]]; then
    TLS_MODE="redirect"
  else
    TLS_MODE="http"
  fi
fi

if [[ "$TLS_MODE" == "http" && "$url_scheme" == "https" ]]; then
  warn "base URL is https:// but --tls-mode=http; use --tls-mode external if HTTPS terminates before kageos"
fi
if [[ "$TLS_MODE" == "redirect" && "$url_scheme" != "https" ]]; then
  die "--tls-mode redirect requires an https:// --base-url"
fi

case "$KAGEOS_START_TIMEOUT" in
  ''|*[!0-9]*) KAGEOS_START_TIMEOUT=1200 ;;
esac

detect_package_manager() {
  if command -v apt-get >/dev/null 2>&1; then
    echo apt
  elif command -v dnf >/dev/null 2>&1; then
    echo dnf
  elif command -v yum >/dev/null 2>&1; then
    echo yum
  elif command -v zypper >/dev/null 2>&1; then
    echo zypper
  elif command -v apk >/dev/null 2>&1; then
    echo apk
  else
    echo none
  fi
}

PKG_MANAGER="$(detect_package_manager)"
ENGINE=""

install_packages() {
  [[ "$KAGEOS_INSTALL_DEPS" != "never" ]] || return 1
  [[ "$PKG_MANAGER" != "none" ]] || return 1

  case "$PKG_MANAGER" in
    apt)
      export DEBIAN_FRONTEND=noninteractive
      apt-get update
      apt-get install -y "$@"
      ;;
    dnf)
      dnf install -y "$@"
      ;;
    yum)
      yum install -y "$@"
      ;;
    zypper)
      zypper --non-interactive install "$@"
      ;;
    apk)
      apk add --no-cache "$@"
      ;;
  esac
}

ensure_basic_tools() {
  local missing=()
  local cmd
  for cmd in curl; do
    command -v "$cmd" >/dev/null 2>&1 || missing+=("$cmd")
  done

  if [[ ${#missing[@]} -eq 0 ]]; then
    return
  fi

  log "Installing basic host tools"
  case "$PKG_MANAGER" in
    apt|dnf|yum|zypper|apk)
      install_packages ca-certificates curl || die "failed to install basic host tools"
      ;;
    none)
      die "missing required tools (${missing[*]}) and no supported package manager was found"
      ;;
  esac
}

install_engine() {
  local engine="$1"
  [[ "$KAGEOS_INSTALL_DEPS" != "never" ]] || return 1

  log "Installing ${engine}"
  case "$engine:$PKG_MANAGER" in
    podman:apt) install_packages podman fuse-overlayfs slirp4netns uidmap ;;
    podman:dnf|podman:yum) install_packages podman fuse-overlayfs slirp4netns ;;
    podman:zypper) install_packages podman fuse-overlayfs slirp4netns ;;
    podman:apk) install_packages podman fuse-overlayfs slirp4netns ;;
    docker:apt) install_packages docker.io ;;
    docker:dnf|docker:yum) install_packages docker ;;
    docker:zypper) install_packages docker ;;
    docker:apk) install_packages docker ;;
    *) return 1 ;;
  esac
}

engine_ready() {
  local engine="$1"
  command -v "$engine" >/dev/null 2>&1 || return 1
  "$engine" info >/dev/null 2>&1
}

select_engine() {
  if [[ "$KAGEOS_CONTAINER_ENGINE" == "podman" || "$KAGEOS_CONTAINER_ENGINE" == "docker" ]]; then
    if ! engine_ready "$KAGEOS_CONTAINER_ENGINE"; then
      install_engine "$KAGEOS_CONTAINER_ENGINE" || true
    fi
    engine_ready "$KAGEOS_CONTAINER_ENGINE" || die "${KAGEOS_CONTAINER_ENGINE} is not ready"
    ENGINE="$KAGEOS_CONTAINER_ENGINE"
    return
  fi

  if engine_ready podman; then
    ENGINE=podman
    return
  fi
  if engine_ready docker; then
    ENGINE=docker
    return
  fi

  install_engine podman || true
  if engine_ready podman; then
    ENGINE=podman
    return
  fi

  install_engine docker || true
  if engine_ready docker; then
    ENGINE=docker
    return
  fi

  die "podman or docker is required and could not be installed automatically"
}

enable_engine_autostart() {
  command -v systemctl >/dev/null 2>&1 || return 0

  case "$ENGINE" in
    podman)
      systemctl enable --now podman-restart.service >/dev/null 2>&1 || true
      ;;
    docker)
      systemctl enable --now docker >/dev/null 2>&1 || true
      ;;
  esac

  return 0
}

container_exists() {
  "$ENGINE" container inspect "$KAGEOS_CONTAINER_NAME" >/dev/null 2>&1
}

container_running() {
  [[ "$("$ENGINE" inspect -f '{{.State.Running}}' "$KAGEOS_CONTAINER_NAME" 2>/dev/null || true)" == "true" ]]
}

port_in_use() {
  local port="$1"
  if command -v ss >/dev/null 2>&1; then
    ss -lnt "( sport = :$port )" 2>/dev/null | awk 'NR > 1 { found=1 } END { exit found ? 0 : 1 }'
  elif command -v lsof >/dev/null 2>&1; then
    lsof -iTCP:"$port" -sTCP:LISTEN -P -n >/dev/null 2>&1
  elif command -v netstat >/dev/null 2>&1; then
    netstat -lnt 2>/dev/null | awk -v p=":$port" '$4 ~ p"$" { found=1 } END { exit found ? 0 : 1 }'
  else
    return 1
  fi
}

show_port_owner() {
  local port="$1"
  if command -v ss >/dev/null 2>&1; then
    ss -lntp "( sport = :$port )" 2>/dev/null || true
  elif command -v lsof >/dev/null 2>&1; then
    lsof -iTCP:"$port" -sTCP:LISTEN -P -n || true
  fi
}

ensure_ports_available() {
  local port
  for port in "$HTTP_PORT"; do
    if port_in_use "$port"; then
      show_port_owner "$port" >&2
      die "port ${port} is already in use. Stop the existing service or pass --http-port with a free port."
    fi
  done

  if [[ "$TLS_MODE" == "https" || "$TLS_MODE" == "redirect" ]]; then
    if [[ "$HTTPS_PORT" != "$HTTP_PORT" ]] && port_in_use "$HTTPS_PORT"; then
      show_port_owner "$HTTPS_PORT" >&2
      die "port ${HTTPS_PORT} is already in use. Stop the existing service or pass --https-port with a free port."
    fi
  fi

  return 0
}

ensure_linger() {
  if [[ "$DEPLOY_USER" == "root" ]]; then
    return
  fi
  if ! command -v loginctl >/dev/null 2>&1; then
    warn "loginctl not found; skipped linger setup for ${DEPLOY_USER}"
    return
  fi

  local linger
  linger="$(loginctl show-user "$DEPLOY_USER" -p Linger --value 2>/dev/null || true)"
  if [[ "$linger" == "yes" ]]; then
    return 0
  fi

  log "Enabling systemd linger for ${DEPLOY_USER}"
  loginctl enable-linger "$DEPLOY_USER" || warn "failed to enable linger for ${DEPLOY_USER}"
  return 0
}

ensure_tls_files() {
  if [[ "$TLS_MODE" != "https" && "$TLS_MODE" != "redirect" ]]; then
    return
  fi

  local tls_dir="${KAGEOS_DATA_DIR%/}/tls"
  local cert="${tls_dir}/fullchain.pem"
  local key="${tls_dir}/privkey.pem"
  mkdir -p "$tls_dir"
  chmod 700 "$tls_dir"

  if [[ -s "$cert" && -s "$key" ]]; then
    log "TLS certificate already exists: ${tls_dir}"
    return
  fi

  command -v openssl >/dev/null 2>&1 || install_packages openssl || true
  command -v openssl >/dev/null 2>&1 || die "openssl is required to bootstrap HTTPS certificates"

  local host="${url_hostport%%:*}"
  host="${host#[}"
  host="${host%]}"
  local san="DNS:${host}"
  if [[ "$host" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
    san="IP:${host}"
  fi

  log "Generating self-signed bootstrap certificate for ${host}"
  openssl req -x509 -newkey rsa:2048 -sha256 -days 365 -nodes \
    -keyout "$key" \
    -out "$cert" \
    -subj "/CN=${host}" \
    -addext "subjectAltName=${san}" >/dev/null 2>&1
  chmod 600 "$cert" "$key"
  warn "Generated a self-signed HTTPS certificate. Replace ${cert} and ${key} with trusted certificates before inviting users."
}

release_arch() {
  local machine
  machine="$(uname -m)"
  case "$machine" in
    x86_64|amd64) echo amd64 ;;
    aarch64|arm64) echo arm64 ;;
    *) return 1 ;;
  esac
}

sha256_file() {
  local file="$1"
  if command -v sha256sum >/dev/null 2>&1; then
    sha256sum "$file" | awk '{print $1}'
  elif command -v shasum >/dev/null 2>&1; then
    shasum -a 256 "$file" | awk '{print $1}'
  else
    return 1
  fi
}

fetch_release_version() {
  local base_url="${KAGEOS_RELEASE_BASE_URL%/}"
  local version="$KAGEOS_RELEASE_VERSION"

  if [[ "$version" == "latest" ]]; then
    version="$(curl -fsSL --retry 3 --retry-delay 2 --connect-timeout 20 "${base_url}/latest.txt" | tr -d '[:space:]')"
  fi

  [[ -n "$version" ]] || return 1
  if [[ "$version" != v* ]]; then
    version="v${version}"
  fi
  [[ "$version" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z][0-9A-Za-z.-]*)?$ ]] || return 1
  printf '%s\n' "$version"
}

load_cn_release_archive() {
  [[ "$USE_CN_MIRROR" == "1" || "$USE_CN_MIRROR" == "true" ]] || return 1
  [[ "$KAGEOS_CN_TARBALL" != "0" && "$KAGEOS_CN_TARBALL" != "false" ]] || return 1
  [[ "$USER_SET_IMAGE" != "1" ]] || return 1
  [[ "$SKIP_PULL" != "1" ]] || return 1

  local arch version version_tag base_url artifact artifact_url checksum_url
  if ! arch="$(release_arch)"; then
    warn "unsupported architecture for release archive: $(uname -m)"
    return 1
  fi
  if ! version="$(fetch_release_version)"; then
    warn "failed to resolve kageos release version from ${KAGEOS_RELEASE_BASE_URL%/}"
    return 1
  fi

  version_tag="${version#v}"
  base_url="${KAGEOS_RELEASE_BASE_URL%/}/${version}"
  artifact="kageos-linux-${arch}.tar.gz"
  artifact_url="${base_url}/${artifact}"
  checksum_url="${artifact_url}.sha256"

  local download_dir archive checksum_file expected actual
  download_dir="${KAGEOS_DATA_DIR%/}/downloads"
  archive="${download_dir}/${artifact}"
  checksum_file="${download_dir}/${artifact}.sha256"
  mkdir -p "$download_dir"

  log "Resolving release checksum: ${checksum_url}"
  if ! curl -fsSL --retry 3 --retry-delay 2 --connect-timeout 20 -o "$checksum_file" "$checksum_url"; then
    warn "failed to download release checksum; falling back to image registry"
    return 1
  fi
  expected="$(awk '{print $1}' "$checksum_file" 2>/dev/null || true)"
  if [[ ! "$expected" =~ ^[0-9a-fA-F]{64}$ ]]; then
    warn "invalid release checksum; falling back to image registry"
    return 1
  fi

  if [[ -s "$archive" ]]; then
    actual="$(sha256_file "$archive" 2>/dev/null || true)"
    if [[ "$actual" == "$expected" ]]; then
      log "Using cached release archive: ${archive}"
    else
      rm -f "$archive"
    fi
  fi

  if [[ ! -s "$archive" ]]; then
    log "Downloading kageos release archive: ${artifact_url}"
    if ! curl -fL --retry 3 --retry-delay 2 --connect-timeout 20 -o "$archive" "$artifact_url"; then
      warn "failed to download release archive; falling back to image registry"
      rm -f "$archive"
      return 1
    fi
  fi

  actual="$(sha256_file "$archive" 2>/dev/null || true)"
  if [[ "$actual" != "$expected" ]]; then
    warn "release archive checksum mismatch; falling back to image registry"
    rm -f "$archive"
    return 1
  fi

  if ! command -v gzip >/dev/null 2>&1; then
    install_packages gzip || true
  fi
  command -v gzip >/dev/null 2>&1 || {
    warn "gzip is required to load the release archive; falling back to image registry"
    return 1
  }

  log "Loading kageos image from release archive"
  if gzip -dc "$archive" | "$ENGINE" load; then
    KAGEOS_IMAGE="${DEFAULT_KAGEOS_IMAGE%:latest}:${version_tag}"
    SKIP_PULL=1
    log "Loaded image: ${KAGEOS_IMAGE}"
    return 0
  fi

  warn "failed to load release archive; falling back to image registry"
  return 1
}

try_pull_image() {
  local image="$1"
  log "Pulling image: ${image}"
  "$ENGINE" pull "$image"
}

pull_main_image() {
  [[ "$SKIP_PULL" != "1" ]] || return 0

  local images=("$KAGEOS_IMAGE")
  local image attempted=" "

  if [[ -n "$KAGEOS_IMAGE_FALLBACK" ]]; then
    images+=("$KAGEOS_IMAGE_FALLBACK")
  fi
  if [[ -n "$KAGEOS_IMAGE_FALLBACKS" ]]; then
    for image in $KAGEOS_IMAGE_FALLBACKS; do
      images+=("$image")
    done
  fi

  for image in "${images[@]}"; do
    [[ -n "$image" ]] || continue
    if [[ "$attempted" == *" ${image} "* ]]; then
      continue
    fi
    attempted="${attempted}${image} "

    if try_pull_image "$image"; then
      KAGEOS_IMAGE="$image"
      return 0
    fi
    warn "failed to pull ${image}; trying next source"
  done

  if load_cn_release_archive; then
    return 0
  fi

  if [[ "$attempted" != *" ${DEFAULT_KAGEOS_IMAGE} "* ]]; then
    warn "release archive fallback failed; trying upstream image"
    if try_pull_image "$DEFAULT_KAGEOS_IMAGE"; then
      KAGEOS_IMAGE="$DEFAULT_KAGEOS_IMAGE"
      return 0
    fi
  fi

  die "failed to pull or load kageos image from all configured sources"
}

remove_existing_container() {
  if container_exists; then
    log "Removing existing container: ${KAGEOS_CONTAINER_NAME}"
    "$ENGINE" rm -f "$KAGEOS_CONTAINER_NAME" >/dev/null
  fi

  return 0
}

start_container() {
  local env_args=()
  mkdir -p "$KAGEOS_INSTALL_DIR" "$KAGEOS_DATA_DIR" "${KAGEOS_DATA_DIR%/}/tls"

  append_optional_env() {
    local name="$1"
    local value="${!name:-}"
    if [[ -n "$value" ]]; then
      env_args+=("-e" "${name}=${value}")
    fi
    return 0
  }

  append_optional_env KAGEOS_APP_BASE_IMAGE
  append_optional_env KAGEOS_APP_BASE_PULL
  append_optional_env KAGEOS_APP_BASE_PULL_FALLBACK_BUILD
  append_optional_env KAGEOS_AIO_MYSQL_IMAGE
  append_optional_env KAGEOS_AIO_NATS_IMAGE
  append_optional_env KAGEOS_AIO_MINIO_IMAGE
  append_optional_env KAGEOS_AIO_RECREATE_INFRA

  pull_main_image

  log "Starting kageos container: ${KAGEOS_CONTAINER_NAME}"
  "$ENGINE" run -d \
    --name "$KAGEOS_CONTAINER_NAME" \
    --privileged \
    --network host \
    --restart unless-stopped \
    -v "${KAGEOS_DATA_DIR}:/var/lib/kageos" \
    -v "${KAGEOS_DATA_DIR%/}/tls:/app/tls" \
    -e "CANONICAL_BASE_URL=${BASE_URL}" \
    -e "TLS_MODE=${TLS_MODE}" \
    -e "HTTP_PORT=${HTTP_PORT}" \
    -e "HTTPS_PORT=${HTTPS_PORT}" \
    -e "TLS_CERT_FILE=/app/tls/fullchain.pem" \
    -e "TLS_KEY_FILE=/app/tls/privkey.pem" \
    -e "TZ=${TIMEZONE}" \
    -e "KAGEOS_AIO_DATA_DIR=/var/lib/kageos" \
    -e "KAGEOS_AIO_PRINT_SECRETS=${KAGEOS_AIO_PRINT_SECRETS}" \
    "${env_args[@]}" \
    "$KAGEOS_IMAGE" >/dev/null
}

install_helper_cli() {
  local helper="/usr/local/bin/kageos"
  local config_file="/etc/kageos-helper.env"

  log "Installing kageos helper command at ${helper}"
  {
    printf 'KAGEOS_ENGINE=%q\n' "$ENGINE"
    printf 'KAGEOS_IMAGE=%q\n' "$KAGEOS_IMAGE"
    printf 'KAGEOS_CONTAINER_NAME=%q\n' "$KAGEOS_CONTAINER_NAME"
    printf 'KAGEOS_BASE_URL=%q\n' "$BASE_URL"
    printf 'KAGEOS_TLS_MODE=%q\n' "$TLS_MODE"
    printf 'KAGEOS_HTTP_PORT=%q\n' "$HTTP_PORT"
    printf 'KAGEOS_HTTPS_PORT=%q\n' "$HTTPS_PORT"
    printf 'KAGEOS_TIMEZONE=%q\n' "$TIMEZONE"
    printf 'KAGEOS_INSTALL_DIR=%q\n' "$KAGEOS_INSTALL_DIR"
    printf 'KAGEOS_DATA_DIR=%q\n' "$KAGEOS_DATA_DIR"
    printf 'KAGEOS_APP_BASE_IMAGE=%q\n' "$KAGEOS_APP_BASE_IMAGE"
    printf 'KAGEOS_APP_BASE_PULL=%q\n' "$KAGEOS_APP_BASE_PULL"
    printf 'KAGEOS_APP_BASE_PULL_FALLBACK_BUILD=%q\n' "$KAGEOS_APP_BASE_PULL_FALLBACK_BUILD"
    printf 'KAGEOS_AIO_MYSQL_IMAGE=%q\n' "$KAGEOS_AIO_MYSQL_IMAGE"
    printf 'KAGEOS_AIO_NATS_IMAGE=%q\n' "$KAGEOS_AIO_NATS_IMAGE"
    printf 'KAGEOS_AIO_MINIO_IMAGE=%q\n' "$KAGEOS_AIO_MINIO_IMAGE"
    printf 'KAGEOS_AIO_RECREATE_INFRA=%q\n' "$KAGEOS_AIO_RECREATE_INFRA"
    printf 'KAGEOS_CN=%q\n' "$USE_CN_MIRROR"
    printf 'KAGEOS_CN_TARBALL=%q\n' "$KAGEOS_CN_TARBALL"
    printf 'KAGEOS_RELEASE_BASE_URL=%q\n' "$KAGEOS_RELEASE_BASE_URL"
    printf 'KAGEOS_RELEASE_VERSION=%q\n' "$KAGEOS_RELEASE_VERSION"
  } > "$config_file"
  chmod 0600 "$config_file"

  cat > "$helper" <<'EOF'
#!/usr/bin/env bash
set -Eeuo pipefail

[[ -r /etc/kageos-helper.env ]] || { echo "ERROR: /etc/kageos-helper.env not found" >&2; exit 1; }
# shellcheck disable=SC1091
source /etc/kageos-helper.env

if [[ "${EUID:-$(id -u)}" -ne 0 ]]; then
  if command -v sudo >/dev/null 2>&1; then
    exec sudo "$0" "$@"
  fi
  echo "ERROR: run with sudo" >&2
  exit 1
fi

usage() {
  cat <<USAGE
kageos production helper

Usage:
  kageos status                 Show container status
  kageos verify                 Run basic HTTP and container checks
  kageos logs                   Follow container logs
  kageos password               Print the initial system password
  kageos restart                Restart kageos
  kageos stop                   Stop kageos
  kageos start                  Start kageos
  kageos update                 Pull image and recreate container, keeping data
  kageos uninstall [--purge]    Remove container, optionally remove data
USAGE
}

engine() {
  "$KAGEOS_ENGINE" "$@"
}

container_exists() {
  engine container inspect "$KAGEOS_CONTAINER_NAME" >/dev/null 2>&1
}

run_container() {
  env_args=()
  append_optional_env() {
    local name="$1"
    local value="${!name:-}"
    if [[ -n "$value" ]]; then
      env_args+=("-e" "${name}=${value}")
    fi
    return 0
  }

  append_optional_env KAGEOS_APP_BASE_IMAGE
  append_optional_env KAGEOS_APP_BASE_PULL
  append_optional_env KAGEOS_APP_BASE_PULL_FALLBACK_BUILD
  append_optional_env KAGEOS_AIO_MYSQL_IMAGE
  append_optional_env KAGEOS_AIO_NATS_IMAGE
  append_optional_env KAGEOS_AIO_MINIO_IMAGE
  append_optional_env KAGEOS_AIO_RECREATE_INFRA

  mkdir -p "$KAGEOS_INSTALL_DIR" "$KAGEOS_DATA_DIR" "${KAGEOS_DATA_DIR%/}/tls"
  engine rm -f "$KAGEOS_CONTAINER_NAME" >/dev/null 2>&1 || true
  engine run -d \
    --name "$KAGEOS_CONTAINER_NAME" \
    --privileged \
    --network host \
    --restart unless-stopped \
    -v "${KAGEOS_DATA_DIR}:/var/lib/kageos" \
    -v "${KAGEOS_DATA_DIR%/}/tls:/app/tls" \
    -e "CANONICAL_BASE_URL=${KAGEOS_BASE_URL}" \
    -e "TLS_MODE=${KAGEOS_TLS_MODE}" \
    -e "HTTP_PORT=${KAGEOS_HTTP_PORT}" \
    -e "HTTPS_PORT=${KAGEOS_HTTPS_PORT}" \
    -e "TLS_CERT_FILE=/app/tls/fullchain.pem" \
    -e "TLS_KEY_FILE=/app/tls/privkey.pem" \
    -e "TZ=${KAGEOS_TIMEZONE}" \
    -e "KAGEOS_AIO_DATA_DIR=/var/lib/kageos" \
    "${env_args[@]}" \
    "$KAGEOS_IMAGE" >/dev/null
}

cmd="${1:-help}"
if [[ $# -gt 0 ]]; then
  shift
fi

case "$cmd" in
  status|ps)
    if container_exists; then
      engine ps -a --filter "name=${KAGEOS_CONTAINER_NAME}"
      echo
      engine exec "$KAGEOS_CONTAINER_NAME" podman ps 2>/dev/null || true
    else
      echo "kageos container not found: ${KAGEOS_CONTAINER_NAME}"
      exit 1
    fi
    ;;
  verify|doctor)
    container_exists || { echo "ERROR: kageos container not found" >&2; exit 1; }
    engine inspect -f 'outer container running: {{.State.Running}}' "$KAGEOS_CONTAINER_NAME"
    if command -v curl >/dev/null 2>&1; then
      curl -fsS --max-time 15 "$KAGEOS_BASE_URL" >/dev/null && echo "HTTP probe OK: $KAGEOS_BASE_URL"
    fi
    engine exec "$KAGEOS_CONTAINER_NAME" sh -lc '/app/health/main.sh && /app/health/runtime.sh' 2>/dev/null || true
    ;;
  logs)
    engine logs -f "$KAGEOS_CONTAINER_NAME"
    ;;
  password|passwd)
    if [[ -r "${KAGEOS_DATA_DIR%/}/secrets/SYSTEM_USER_PASSWORD" ]]; then
      cat "${KAGEOS_DATA_DIR%/}/secrets/SYSTEM_USER_PASSWORD"
      echo
    else
      engine exec "$KAGEOS_CONTAINER_NAME" sh -lc 'cat /var/lib/kageos/secrets/SYSTEM_USER_PASSWORD'
    fi
    ;;
  restart)
    engine restart "$KAGEOS_CONTAINER_NAME"
    ;;
  stop|down)
    engine stop "$KAGEOS_CONTAINER_NAME"
    ;;
  start|up)
    if container_exists; then
      engine start "$KAGEOS_CONTAINER_NAME"
    else
      run_container
    fi
    ;;
  update)
    if [[ "${KAGEOS_CN:-0}" == "1" && "${KAGEOS_CN_TARBALL:-1}" != "0" ]] && command -v curl >/dev/null 2>&1; then
      curl -fsSL https://kageos.com/install-prod.sh | \
        KAGEOS_RELEASE_BASE_URL="$KAGEOS_RELEASE_BASE_URL" \
        KAGEOS_RELEASE_VERSION="$KAGEOS_RELEASE_VERSION" \
        KAGEOS_CN_TARBALL="$KAGEOS_CN_TARBALL" \
        bash -s -- \
        --base-url "$KAGEOS_BASE_URL" \
        --cn \
        --tls-mode "$KAGEOS_TLS_MODE" \
        --http-port "$KAGEOS_HTTP_PORT" \
        --https-port "$KAGEOS_HTTPS_PORT" \
        --timezone "$KAGEOS_TIMEZONE" \
        --install-dir "$KAGEOS_INSTALL_DIR" \
        --data-dir "$KAGEOS_DATA_DIR" \
        --container-name "$KAGEOS_CONTAINER_NAME" \
        --container-engine "$KAGEOS_ENGINE"
      exit $?
    fi
    engine pull "$KAGEOS_IMAGE"
    run_container
    echo "kageos updated. Follow logs with: sudo kageos logs"
    ;;
  uninstall)
    purge=0
    if [[ "${1:-}" == "--purge" ]]; then
      purge=1
    fi
    engine rm -f "$KAGEOS_CONTAINER_NAME" >/dev/null 2>&1 || true
    if [[ "$purge" == "1" ]]; then
      rm -rf "$KAGEOS_DATA_DIR"
      echo "removed container and data: $KAGEOS_DATA_DIR"
    else
      echo "removed container; data kept: $KAGEOS_DATA_DIR"
    fi
    ;;
  help|--help|-h)
    usage
    ;;
  *)
    usage >&2
    echo "ERROR: unknown command: $cmd" >&2
    exit 1
    ;;
esac
EOF
  chmod 0755 "$helper"
}

wait_for_start() {
  local deadline
  deadline=$((SECONDS + KAGEOS_START_TIMEOUT))

  log "Waiting for kageos first boot. This can take several minutes."
  while (( SECONDS < deadline )); do
    if "$ENGINE" logs "$KAGEOS_CONTAINER_NAME" 2>&1 | grep -qi "kageos started successfully"; then
      return 0
    fi
    if ! container_running; then
      "$ENGINE" logs "$KAGEOS_CONTAINER_NAME" 2>&1 | tail -n 160 >&2 || true
      die "kageos container stopped before startup completed"
    fi
    sleep 5
  done

  "$ENGINE" logs "$KAGEOS_CONTAINER_NAME" 2>&1 | tail -n 160 >&2 || true
  die "startup did not finish within ${KAGEOS_START_TIMEOUT}s. Run 'sudo kageos logs' to continue watching."
}

print_success_summary() {
  local password=""
  password="$(cat "${KAGEOS_DATA_DIR%/}/secrets/SYSTEM_USER_PASSWORD" 2>/dev/null || true)"

  cat <<EOF

kageos installed successfully

URL:      ${BASE_URL}
Username: system
Password: ${password:-"(not ready yet; run sudo kageos password later)"}

Useful commands:
  sudo kageos status
  sudo kageos logs
  sudo kageos password
  sudo kageos restart
  sudo kageos update

Data:
  ${KAGEOS_DATA_DIR}
EOF
}

if [[ "${EUID:-$(id -u)}" -ne 0 && "$DRY_RUN" != "1" ]]; then
  die "run this installer with sudo, for example: curl -fsSL https://kageos.com/install-prod.sh | sudo bash -s -- --base-url https://app.example.com"
fi

printf 'kageos production one-command installer\n'
printf 'image:       %s\n' "$KAGEOS_IMAGE"
if [[ -n "$KAGEOS_IMAGE_FALLBACK" ]]; then
  printf 'fallback:    %s\n' "$KAGEOS_IMAGE_FALLBACK"
fi
cat <<EOF
container:   ${KAGEOS_CONTAINER_NAME}
install dir: ${KAGEOS_INSTALL_DIR}
data dir:    ${KAGEOS_DATA_DIR}
base url:    ${BASE_URL}
tls mode:    ${TLS_MODE}
http port:   ${HTTP_PORT}
https port:  ${HTTPS_PORT}
timezone:    ${TIMEZONE}
cn mode:     ${USE_CN_MIRROR}
EOF
if [[ "$USE_CN_MIRROR" == "1" || "$USE_CN_MIRROR" == "true" ]]; then
  printf 'cn release:  %s (%s)\n' "$KAGEOS_RELEASE_BASE_URL" "$KAGEOS_RELEASE_VERSION"
fi

if [[ "$DRY_RUN" == "1" ]]; then
  log "Dry run complete. No host changes were made."
  exit 0
fi

ensure_basic_tools
select_engine
log "Container engine is ready: ${ENGINE}"
enable_engine_autostart
ensure_linger
remove_existing_container
ensure_ports_available
ensure_tls_files
start_container
install_helper_cli
wait_for_start
print_success_summary
