#!/usr/bin/env bash

# Panama's front door: the one command a fresh Fedora machine needs.
#
#   bash <(curl -fsSL https://git.gbrown.org/gib/Panama/raw/branch/main/boot)
#   bash <(curl -fsSL https://git.gbrown.org/gib/Panama/raw/branch/main/boot) --server
#
# Deliberately dumb, because a copy of this script leaves the repository the
# moment somebody curls it -- nothing here can be fixed by re-running
# ./install, so there is as little here as possible: get git, get the clone,
# hand off. Everything with judgment in it lives in `install`, which is also
# where re-runs and upgrades already work.
#
# The one exception to dumb is the root path below, which cannot live in
# `install`: a fresh VPS hands you a root login and nothing else, and the user
# that `install` needs to exist is exactly what has not been created yet.

set -euo pipefail

REPO_URL="https://git.gbrown.org/gib/Panama.git"
PANAMA_PATH="${PANAMA_PATH:-$HOME/.local/share/Panama}"
export PANAMA_PATH

SERVER=0
INSTALL_ARGS=()
for arg in "$@"; do
  case "$arg" in
    --server) SERVER=1; INSTALL_ARGS+=(--server) ;;
    *)
      printf 'boot: unknown argument: %s\n' "$arg" >&2
      printf 'usage: boot [--server]\n' >&2
      exit 2 ;;
  esac
done

# The public bootstrap contract runs this branch as an ordinary user with a
# stubbed root identity. Keep its filesystem adapter unavailable to a real root
# shell so it cannot redirect a real installation by accident.
BOOT_ROOT="${PANAMA_BOOT_FIXTURE_ROOT:-}"
if [[ -n "$BOOT_ROOT" && "$EUID" -eq 0 ]]; then
  echo "boot: PANAMA_BOOT_FIXTURE_ROOT is test-only" >&2
  exit 1
fi

system_path() {
  local path="$1"
  [[ "$path" == /* ]] || return 2
  printf '%s%s\n' "$BOOT_ROOT" "$path"
}

safe_authorized_keys() {
  local username="$1" user_home="$2" uid ssh_dir keys
  uid="$(id -u "$username")" || return 1
  [[ "$uid" =~ ^[0-9]+$ && "$uid" != 0 && "$user_home" == /* ]] || return 1
  ssh_dir="$user_home/.ssh"
  keys="$ssh_dir/authorized_keys"
  [[ -d "$ssh_dir" && ! -L "$ssh_dir" && -f "$keys" && ! -L "$keys" ]] || return 1
  [[ "$(stat -Lc '%u:%a' "$ssh_dir")" == "$uid:700" ]] || return 1
  [[ "$(stat -Lc '%u:%a' "$keys")" == "$uid:600" ]] || return 1
  grep -qEv '^[[:space:]]*(#|$)' "$keys"
}

safe_root_authorized_keys() {
  local keys
  keys="$(system_path /root/.ssh/authorized_keys)" || return 1
  [[ -f "$keys" && ! -L "$keys" ]] || return 1
  [[ "$(stat -Lc '%u:%a' "$keys")" == '0:600' ]] || return 1
  grep -qEv '^[[:space:]]*(#|$)' "$keys"
}

detect_ssh_unit() {
  local unit
  for unit in sshd.service ssh.service; do
    systemctl cat "$unit" >/dev/null 2>&1 && {
      printf '%s\n' "$unit"
      return 0
    }
  done
  return 1
}

restore_ssh_dropin() {
  local restore
  if [[ -n "${ssh_backup:-}" && -e "$ssh_backup" ]]; then
    restore="$(mktemp --tmpdir="$sshd_dir" .90-panama.XXXXXX.restore)" || return 1
    if ! cp -a -- "$ssh_backup" "$restore"; then
      remove_ssh_artifact "$restore" || true
      return 1
    fi
    if ! mv -f -- "$restore" "$ssh_dropin"; then
      remove_ssh_artifact "$restore" || true
      return 1
    fi
  else
    remove_ssh_artifact "$ssh_dropin"
  fi
}

restore_ssh_transaction_traps() {
  trap - EXIT INT TERM
  [[ -n "${ssh_saved_exit_trap:-}" ]] && eval "$ssh_saved_exit_trap"
  [[ -n "${ssh_saved_int_trap:-}" ]] && eval "$ssh_saved_int_trap"
  [[ -n "${ssh_saved_term_trap:-}" ]] && eval "$ssh_saved_term_trap"
  return 0
}

remove_ssh_artifact() {
  local artifact="$1"
  [[ -n "$artifact" && -e "$artifact" ]] || return 0
  if rm -f -- "$artifact"; then
    return 0
  fi
  printf 'SSH transaction cleanup failed. Retained artifact: %s\n' "$artifact" >&2
  printf '  rm -f -- %q\n' "$artifact" >&2
  return 1
}

handle_ssh_transaction_signal() {
  local signal_status="$1"
  trap - INT TERM
  rollback_failed=0
  restore_ssh_dropin || rollback_failed=1
  sshd -t || rollback_failed=1
  systemctl reload "$ssh_unit" || rollback_failed=1
  ssh_transaction_active=0
  restore_ssh_transaction_traps
  if (( rollback_failed )); then
    printf 'SSH rollback needs manual recovery. Backup: %s\n' "$ssh_backup" >&2
    printf '  cp -a -- %q %q\n' "$ssh_backup" "$ssh_dropin" >&2
    printf '  sshd -t\n' >&2
    printf '  systemctl reload %s\n' "$ssh_unit" >&2
  else
    remove_ssh_artifact "$ssh_backup" || true
  fi
  exit "$signal_status"
}

harden_server_ssh() {
  local username="$1" user_home="$2" sshd_dir ssh_dropin harden ssh_unit
  local ssh_candidate="" ssh_backup="" rollback_failed=0
  local ssh_transaction_active=0
  local ssh_saved_exit_trap ssh_saved_int_trap ssh_saved_term_trap
  sshd_dir="$(system_path /etc/ssh/sshd_config.d)" || return 1
  ssh_dropin="$sshd_dir/90-panama.conf"

  printf 'Harden sshd (disable root login and password auth)? [Y/n]: '
  read -r harden </dev/tty || harden=""
  if [[ "$harden" =~ ^[Nn] ]]; then
    return 0
  fi

  if ! ssh_unit="$(detect_ssh_unit)"; then
    echo "SSH hardening failed: neither sshd.service nor ssh.service exists" >&2
    return 1
  fi

  ssh_candidate="$(umask 077; mktemp --tmpdir="$sshd_dir" .90-panama.XXXXXX.tmp)" || return 1
  if ! printf 'PermitRootLogin no\nPasswordAuthentication no\n' >"$ssh_candidate"; then
    remove_ssh_artifact "$ssh_candidate" || true
    return 1
  fi

  if [[ -e "$ssh_dropin" ]]; then
    ssh_backup="$(umask 077; mktemp --tmpdir="$sshd_dir" .90-panama.XXXXXX.backup)" || {
      remove_ssh_artifact "$ssh_candidate" || true
      return 1
    }
    if ! cat -- "$ssh_dropin" >"$ssh_backup"; then
      remove_ssh_artifact "$ssh_candidate" || true
      remove_ssh_artifact "$ssh_backup" || true
      return 1
    fi
  fi

  ssh_saved_exit_trap="$(trap -p EXIT)"
  ssh_saved_int_trap="$(trap -p INT)"
  ssh_saved_term_trap="$(trap -p TERM)"
  trap 'if [[ "${ssh_transaction_active:-0}" == 1 ]]; then restore_ssh_dropin || true; fi' EXIT
  trap 'handle_ssh_transaction_signal 130' INT
  trap 'handle_ssh_transaction_signal 143' TERM
  ssh_transaction_active=1

  if ! mv -f -- "$ssh_candidate" "$ssh_dropin"; then
    ssh_transaction_active=0
    restore_ssh_transaction_traps
    remove_ssh_artifact "$ssh_candidate" || true
    remove_ssh_artifact "$ssh_backup" || true
    return 1
  fi
  ssh_candidate=""

  if ! sshd -t; then
    restore_ssh_dropin || rollback_failed=1
    sshd -t || rollback_failed=1
    ssh_transaction_active=0
    restore_ssh_transaction_traps
    if (( rollback_failed )); then
      printf 'SSH rollback needs manual recovery. Backup: %s\n' "$ssh_backup" >&2
      printf '  cp -a -- %q %q\n' "$ssh_backup" "$ssh_dropin" >&2
      printf '  sshd -t\n' >&2
      printf '  systemctl reload %s\n' "$ssh_unit" >&2
    else
      remove_ssh_artifact "$ssh_backup" || true
    fi
    return 1
  fi

  if ! systemctl reload "$ssh_unit"; then
    restore_ssh_dropin || rollback_failed=1
    sshd -t || rollback_failed=1
    systemctl reload "$ssh_unit" || rollback_failed=1
    ssh_transaction_active=0
    restore_ssh_transaction_traps
    if (( rollback_failed )); then
      printf 'SSH rollback needs manual recovery. Backup: %s\n' "$ssh_backup" >&2
      printf '  cp -a -- %q %q\n' "$ssh_backup" "$ssh_dropin" >&2
      printf '  sshd -t\n' >&2
      printf '  systemctl reload %s\n' "$ssh_unit" >&2
    else
      remove_ssh_artifact "$ssh_backup" || true
    fi
    return 1
  fi

  ssh_transaction_active=0
  restore_ssh_transaction_traps
  remove_ssh_artifact "$ssh_backup" || return 1
  echo "Wrote $ssh_dropin; make sure your key works before logging out."
}

# Panama assumes Fedora's repositories and package names.
if ! grep -qi '^ID=fedora' /etc/os-release 2>/dev/null; then
  echo "This looks like something other than Fedora; Panama only supports Fedora." >&2
  exit 1
fi

# ── Root ─────────────────────────────────────────────────────────────────────
#
# On a desktop, root is a mistake: the clone and every dotfile would land in
# root's home and configure the wrong user. On a fresh VPS it is the starting
# condition -- Hetzner hands over a root login and nothing else -- so with
# --server this walks the machine from that to a normal Panama install: a
# user with sudo, keys, an optionally hardened sshd, and `install --server`
# running as that user. Every step checks before acting, because the machine
# may be anywhere along this path already: a user half-created by hand, keys
# already copied, sshd already locked down.
if [[ "$(id -u)" -eq 0 ]]; then
  if (( ! SERVER )); then
    echo "Run this as your own user, not root: the install configures YOUR desktop." >&2
    echo "Setting up a fresh server from its root login is: boot --server" >&2
    exit 1
  fi

  # Everything here asks, and a root shell from `bash <(curl ...)` can have
  # the pipe as stdin, so every prompt reads the terminal explicitly.
  if ! (exec </dev/tty) 2>/dev/null; then
    echo "No terminal to ask on; run this from an interactive root shell." >&2
    exit 1
  fi

  printf 'Username for this server [gib]: '
  read -r username </dev/tty || username=""
  username="${username:-gib}"

  if id -u "$username" >/dev/null 2>&1; then
    echo "User $username already exists"
    # wheel is what makes sudo work on Fedora; a user created by hand may not
    # have it, and everything after this depends on it.
    id -nG "$username" | grep -qw wheel || usermod -aG wheel "$username"
  else
    echo "Creating $username with sudo (wheel)"
    useradd -m -G wheel "$username"
  fi

  # useradd leaves the account locked, and sudo asks for this password -- a
  # user who cannot sudo is a user the install cannot run as.
  if ! passwd -S "$username" 2>/dev/null | awk '{exit $2 != "PS" && $2 != "P"}'; then
    echo "Set a password for $username (sudo will ask for it):"
    passwd "$username" </dev/tty
  fi

  # Do not close root/password access until the account's key is an exact,
  # usable login path. The fixture adapter resolves these logical system paths
  # beneath a temporary root; ordinary execution receives the original paths.
  logical_user_home="$(getent passwd "$username" | cut -d: -f6)"
  user_home=""
  if [[ "$logical_user_home" == /* ]]; then
    user_home="$(system_path "$logical_user_home")" || true
  fi
  bootstrap_home="$user_home"
  if [[ -z "$bootstrap_home" ]]; then
    bootstrap_home="$(system_path "/home/$username")"
  fi

  user_ssh_dir="$user_home/.ssh"
  user_keys="$user_ssh_dir/authorized_keys"
  if [[ -n "$user_home" && ! -e "$user_keys" && ! -L "$user_keys" \
    && ! -L "$user_ssh_dir" ]] && safe_root_authorized_keys; then
    copy_root_key=0
    if [[ ! -e "$user_ssh_dir" ]]; then
      mkdir -p "$user_ssh_dir"
      copy_root_key=1
    elif [[ ! -d "$user_ssh_dir" \
      || "$(stat -Lc '%u:%a' "$user_ssh_dir")" != "$(id -u "$username"):700" ]]; then
      echo "SSH hardening unavailable: $username has no safe authorized_keys" >&2
    else
      copy_root_key=1
    fi
    if (( copy_root_key )); then
      echo "Copying root's authorized_keys to $username"
      cp "$(system_path /root/.ssh/authorized_keys)" "$user_keys"
      chmod 700 "$user_ssh_dir"
      chmod 600 "$user_keys"
      chown "$username:$username" "$user_ssh_dir" "$user_keys"
    fi
  fi

  if safe_authorized_keys "$username" "$user_home"; then
    if ! harden_server_ssh "$username" "$user_home"; then
      echo "SSH hardening failed; stopping before install handoff." >&2
      exit 1
    fi
  else
    echo "SSH hardening unavailable: $username has no safe authorized_keys" >&2
  fi

  if ! command -v git >/dev/null 2>&1; then
    echo "Installing git, which the clone needs"
    dnf install -y git
  fi

  # Cloned straight into the user's home and owned by them: this is the
  # checkout `panama update` will pull from for the life of the machine, and
  # a root-owned .git in a user's home is a wound that never heals.
  PANAMA_PATH="$bootstrap_home/.local/share/Panama"
  if [[ -d "$PANAMA_PATH/.git" ]]; then
    echo "Panama is already cloned at $PANAMA_PATH; updating"
    runuser -u "$username" -- git -C "$PANAMA_PATH" pull --ff-only \
      || echo "Could not fast-forward; installing from the clone as it is" >&2
  else
    runuser -u "$username" -- mkdir -p "$bootstrap_home/.local/share"
    runuser -u "$username" -- git clone "$REPO_URL" "$PANAMA_PATH"
  fi

  echo "Handing off to install as $username"
  exec runuser -u "$username" -- env PANAMA_PATH="$PANAMA_PATH" \
    "$PANAMA_PATH/install" --server </dev/tty
fi

# git is the one dependency the clone itself needs. Everything else -- gum
# included -- is bootstrapped by `install`.
if ! command -v git >/dev/null 2>&1; then
  echo "Installing git, which the clone needs"
  sudo dnf install -y git
fi

if [[ -d "$PANAMA_PATH/.git" ]]; then
  # An existing clone makes this the recovery command too. Only a fast-forward:
  # local work is never rewritten, and a diverged clone still installs from
  # what it has rather than stopping someone mid-repair.
  echo "Panama is already cloned at $PANAMA_PATH; updating"
  git -C "$PANAMA_PATH" pull --ff-only \
    || echo "Could not fast-forward; installing from the clone as it is" >&2
else
  git clone "$REPO_URL" "$PANAMA_PATH"
fi

# `curl | bash` and `bash <(curl ...)` can leave stdin as the pipe, and the
# first thing install runs is the interview, which has to be able to ask.
# Reattach the terminal when there is one; without one the interview will say
# so itself.
# The probe actually opens /dev/tty rather than testing -r: a process with no
# controlling terminal passes -r and then fails the redirect.
if [[ ! -t 0 ]] && (exec </dev/tty) 2>/dev/null; then
  exec "$PANAMA_PATH/install" ${INSTALL_ARGS[@]+"${INSTALL_ARGS[@]}"} </dev/tty
fi
exec "$PANAMA_PATH/install" ${INSTALL_ARGS[@]+"${INSTALL_ARGS[@]}"}
