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

KAGEOS_REPO_URL="${KAGEOS_REPO_URL:-https://github.com/kageos/kageos.git}"
KAGEOS_REF="${KAGEOS_REF:-main}"
KAGEOS_INSTALL_DIR="${KAGEOS_INSTALL_DIR:-/opt/kageos}"
KAGEOS_GO_VERSION="${KAGEOS_GO_VERSION:-1.25.0}"
KAGEOS_INSTALL_GO="${KAGEOS_INSTALL_GO:-auto}"
KAGEOS_INSTALL_DEPS="${KAGEOS_INSTALL_DEPS:-auto}"
KAGEOS_CONTAINER_ENGINE="${KAGEOS_CONTAINER_ENGINE:-auto}"

BASE_URL="${KAGEOS_BASE_URL:-}"
DEPLOY_USER="${KAGEOS_DEPLOY_USER:-}"
INSTALL_ARGS=()

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

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

What it does:
  - Installs missing host basics where possible: git, curl, certificates.
  - Installs Go 1.25 when Go is missing or too old.
  - Installs Podman Compose on common Linux distributions when no compose engine exists.
  - Clones or updates github.com/kageos/kageos into /opt/kageos.
  - Runs the production installer from the checked-out repository.

Options:
  --base-url URL             Public URL for this Kageos instance. Required for first install.
  --tls-mode MODE            auto, http, https, redirect, or external. Passed to Kageos.
  --http-port PORT           HTTP listen port. Passed to Kageos.
  --https-port PORT          HTTPS listen port. Passed to Kageos.
  --timezone TZ              Deployment timezone. Passed to Kageos.
  --user USER                Deploy as this existing Linux user.
  --install-dir DIR          Checkout directory. Defaults to /opt/kageos.
  --repo-url URL             Git repository URL. Defaults to https://github.com/kageos/kageos.git.
  --ref REF                  Git branch or tag. Defaults to main.
  --container-engine ENGINE  auto, podman, docker, or none. Defaults to auto.
  --skip-deps                Do not install missing system dependencies.
  --skip-up                  Prepare host and config, but do not start services.
  --help                     Show this help.
  --                         Pass remaining arguments to the repository installer.

Environment:
  KAGEOS_BASE_URL, KAGEOS_TLS_MODE, KAGEOS_TIMEZONE, KAGEOS_HTTP_PORT,
  KAGEOS_HTTPS_PORT, KAGEOS_DEPLOY_USER, KAGEOS_CONFIG are forwarded.
  KAGEOS_REPO_URL, KAGEOS_REF, KAGEOS_INSTALL_DIR customize this bootstrapper.
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"
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    --base-url)
      shift
      need_value "--base-url" "$@"
      BASE_URL="$1"
      INSTALL_ARGS+=(--base-url "$1")
      ;;
    --tls-mode|--http-port|--https-port|--timezone)
      option="$1"
      shift
      need_value "$option" "$@"
      INSTALL_ARGS+=("$option" "$1")
      ;;
    --user)
      shift
      need_value "--user" "$@"
      DEPLOY_USER="$1"
      INSTALL_ARGS+=(--user "$1")
      ;;
    --install-dir)
      shift
      need_value "--install-dir" "$@"
      KAGEOS_INSTALL_DIR="$1"
      ;;
    --repo-url)
      shift
      need_value "--repo-url" "$@"
      KAGEOS_REPO_URL="$1"
      ;;
    --branch|--ref)
      shift
      need_value "--ref" "$@"
      KAGEOS_REF="$1"
      ;;
    --container-engine)
      shift
      need_value "--container-engine" "$@"
      KAGEOS_CONTAINER_ENGINE="$1"
      ;;
    --skip-deps)
      KAGEOS_INSTALL_DEPS="never"
      KAGEOS_INSTALL_GO="never"
      ;;
    --skip-up)
      INSTALL_ARGS+=(--skip-up)
      ;;
    --help|-h)
      usage
      exit 0
      ;;
    --)
      shift
      INSTALL_ARGS+=(-- "$@")
      break
      ;;
    *)
      INSTALL_ARGS+=("$1")
      ;;
  esac
  shift
done

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

if [[ -z "$BASE_URL" ]]; then
  usage >&2
  die "--base-url is required for first install"
fi

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

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

id "$DEPLOY_USER" >/dev/null 2>&1 || die "deploy user does not exist: $DEPLOY_USER"

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)"

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() {
  missing=()
  for cmd in git curl tar gzip; do
    command -v "$cmd" >/dev/null 2>&1 || missing+=("$cmd")
  done
  command -v update-ca-certificates >/dev/null 2>&1 || true

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

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

version_ge() {
  local have="$1"
  local need="$2"
  local have_major have_minor have_patch need_major need_minor need_patch
  IFS=. read -r have_major have_minor have_patch <<<"$have"
  IFS=. read -r need_major need_minor need_patch <<<"$need"
  have_major="${have_major:-0}"
  have_minor="${have_minor:-0}"
  have_patch="${have_patch:-0}"
  need_major="${need_major:-0}"
  need_minor="${need_minor:-0}"
  need_patch="${need_patch:-0}"

  (( have_major > need_major )) && return 0
  (( have_major < need_major )) && return 1
  (( have_minor > need_minor )) && return 0
  (( have_minor < need_minor )) && return 1
  (( have_patch >= need_patch ))
}

current_go_version() {
  if ! command -v go >/dev/null 2>&1; then
    return 1
  fi
  go version | awk '{print $3}' | sed -E 's/^go([0-9]+(\.[0-9]+){0,2}).*/\1/'
}

ensure_go() {
  local current=""
  if current="$(current_go_version 2>/dev/null)" && version_ge "$current" "$KAGEOS_GO_VERSION"; then
    log "Go $current is ready"
    return
  fi

  [[ "$KAGEOS_INSTALL_GO" != "never" ]] || die "Go $KAGEOS_GO_VERSION or newer is required"

  local os arch archive url tmp backup
  os="$(uname -s | tr '[:upper:]' '[:lower:]')"
  [[ "$os" == "linux" ]] || die "automatic Go install currently supports Linux only"

  case "$(uname -m)" in
    x86_64|amd64) arch="amd64" ;;
    aarch64|arm64) arch="arm64" ;;
    *) die "unsupported CPU architecture for automatic Go install: $(uname -m)" ;;
  esac

  archive="go${KAGEOS_GO_VERSION}.${os}-${arch}.tar.gz"
  url="https://go.dev/dl/${archive}"
  tmp="$(mktemp -d)"

  log "Installing Go ${KAGEOS_GO_VERSION} from ${url}"
  curl -fsSL "$url" -o "${tmp}/${archive}"

  if [[ -d /usr/local/go ]]; then
    backup="/usr/local/go.kageos-backup-$(date +%Y%m%d%H%M%S)"
    log "Moving existing /usr/local/go to ${backup}"
    mv /usr/local/go "$backup"
  fi

  tar -C /usr/local -xzf "${tmp}/${archive}"
  mkdir -p /usr/local/bin
  ln -sf /usr/local/go/bin/go /usr/local/bin/go
  ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt
  rm -rf "$tmp"

  current="$(current_go_version)"
  version_ge "$current" "$KAGEOS_GO_VERSION" || die "Go install failed; found $current"
  log "Go $current is ready"
}

compose_ready() {
  if command -v podman >/dev/null 2>&1 && podman compose version >/dev/null 2>&1; then
    return 0
  fi
  if command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then
    return 0
  fi
  return 1
}

ensure_compose() {
  if compose_ready; then
    log "Container compose engine is ready"
    return
  fi

  [[ "$KAGEOS_CONTAINER_ENGINE" != "none" ]] || die "podman compose or docker compose is required"
  [[ "$KAGEOS_INSTALL_DEPS" != "never" ]] || die "podman compose or docker compose is required"

  if [[ "$KAGEOS_CONTAINER_ENGINE" == "docker" ]]; then
    log "Installing Docker Compose packages where available"
    case "$PKG_MANAGER" in
      apt)
        install_packages docker.io docker-compose-v2 || install_packages docker.io docker-compose-plugin || true
        ;;
      dnf|yum)
        install_packages docker docker-compose-plugin || true
        ;;
      zypper)
        install_packages docker docker-compose || true
        ;;
      apk)
        install_packages docker docker-cli-compose || true
        ;;
    esac
  else
    log "Installing Podman Compose packages where available"
    case "$PKG_MANAGER" in
      apt)
        install_packages podman podman-compose uidmap slirp4netns fuse-overlayfs || true
        ;;
      dnf|yum)
        install_packages podman podman-compose slirp4netns fuse-overlayfs || true
        ;;
      zypper)
        install_packages podman podman-compose slirp4netns fuse-overlayfs || true
        ;;
      apk)
        install_packages podman podman-compose fuse-overlayfs slirp4netns || true
        ;;
    esac
  fi

  if ! compose_ready; then
    die "podman compose or docker compose is still unavailable. Install one, then rerun this installer."
  fi

  log "Container compose engine is ready"
}

checkout_kageos() {
  local parent
  parent="$(dirname "$KAGEOS_INSTALL_DIR")"
  mkdir -p "$parent"

  if [[ -d "$KAGEOS_INSTALL_DIR/.git" ]]; then
    log "Updating existing checkout at ${KAGEOS_INSTALL_DIR}"
    if [[ -n "$(git -C "$KAGEOS_INSTALL_DIR" status --porcelain)" ]]; then
      die "${KAGEOS_INSTALL_DIR} has local changes. Commit, stash, or choose --install-dir before rerunning."
    fi
    git -C "$KAGEOS_INSTALL_DIR" fetch --prune origin "$KAGEOS_REF"
    git -C "$KAGEOS_INSTALL_DIR" checkout "$KAGEOS_REF"
    git -C "$KAGEOS_INSTALL_DIR" pull --ff-only origin "$KAGEOS_REF"
  elif [[ -e "$KAGEOS_INSTALL_DIR" ]]; then
    die "${KAGEOS_INSTALL_DIR} exists but is not a git checkout"
  else
    log "Cloning ${KAGEOS_REPO_URL} (${KAGEOS_REF}) into ${KAGEOS_INSTALL_DIR}"
    git clone --depth=1 --branch "$KAGEOS_REF" "$KAGEOS_REPO_URL" "$KAGEOS_INSTALL_DIR"
  fi

  if [[ "$DEPLOY_USER" != "root" ]]; then
    local group
    group="$(id -gn "$DEPLOY_USER" 2>/dev/null || printf '%s' "$DEPLOY_USER")"
    chown -R "${DEPLOY_USER}:${group}" "$KAGEOS_INSTALL_DIR" || chown -R "$DEPLOY_USER" "$KAGEOS_INSTALL_DIR"
  fi
}

run_repository_installer() {
  [[ -x "$KAGEOS_INSTALL_DIR/install.sh" ]] || chmod +x "$KAGEOS_INSTALL_DIR/install.sh"
  [[ -x "$KAGEOS_INSTALL_DIR/prod-up.sh" ]] || chmod +x "$KAGEOS_INSTALL_DIR/prod-up.sh"
  [[ -x "$KAGEOS_INSTALL_DIR/prod-stop.sh" ]] || chmod +x "$KAGEOS_INSTALL_DIR/prod-stop.sh"

  log "Starting Kageos production install"
  cd "$KAGEOS_INSTALL_DIR"
  ./install.sh "${INSTALL_ARGS[@]}"
}

cat <<EOF
Kageos one-command installer
repo:        ${KAGEOS_REPO_URL}
ref:         ${KAGEOS_REF}
install dir: ${KAGEOS_INSTALL_DIR}
base url:    ${BASE_URL}
deploy user: ${DEPLOY_USER}
EOF

ensure_basic_tools
ensure_go
ensure_compose
checkout_kageos
run_repository_installer

cat <<EOF

Kageos install command finished.

Next:
  cd ${KAGEOS_INSTALL_DIR}
  tail -f .kageos/prod/kagectl-up.log
EOF
