# /etc/profile.d/nimux-first-login.sh
# Nimux first-login bootstrap (mirror) — numbered disk selection + safeguards + default proposal

case $- in *i*) ;; *) return 0 ;; esac

SENTINEL="/var/lib/nimux/firstboot.done"
[ -f "$SENTINEL" ] && return 0

run_bootstrap() {
  set -euo pipefail

  need() { command -v "$1" >/dev/null 2>&1 || { echo "Missing: $1"; exit 1; }; }
  need zpool; need zfs; need lsblk; command -v blkid >/dev/null 2>&1 || true; command -v blockdev >/dev/null 2>&1 || true

  CUR_HOST="$(hostname)"
  POOL="${CUR_HOST}-zfs"
  FSTAB="/etc/fstab"
  OVERLAY_ROOT="/mnt/overlays/root"
  ADMINUSER=""
  NEW_LANG="${LANG:-en_US.UTF-8}"
  DISK1=""; DISK2=""
  DEFAULT_IDX1=""; DEFAULT_IDX2=""

  # -------------------- Helpers --------------------

  canonical() { readlink -f "$1"; }

  has_partitions() {
    # returns 0 (true) if the disk has child partitions
    local d="$1"
    lsblk -n "$d" -o TYPE | grep -q '^part$'
  }

  fstype_of() {
    # Try lsblk first, then blkid
    local d="$1" fs
    fs="$(lsblk -dn -o FSTYPE "$d" 2>/dev/null | tr -d ' ')"
    if [ -z "$fs" ] && command -v blkid >/dev/null 2>&1; then
      fs="$(blkid -o value -s TYPE "$d" 2>/dev/null | tr -d ' ')"
    fi
    echo "${fs:-}"
  }

  bytes_of() {
    # size in bytes (fallback to lsblk if blockdev not present)
    local d="$1"
    if command -v blockdev >/dev/null 2>&1; then
      blockdev --getsize64 "$d" 2>/dev/null || echo 0
    else
      lsblk -bdn -o SIZE "$d" 2>/dev/null || echo 0
    fi
  }

  is_whole_disk() {
    # TYPE=disk
    local d="$1"
    [ "$(lsblk -dn -o TYPE "$d" 2>/dev/null)" = "disk" ]
  }

  is_zfs_member() {
    local d="$1"
    [ "$(fstype_of "$d")" = "zfs_member" ]
  }

  print_disks_table() {
    echo "Available whole disks:"
    printf "  %-5s %-20s %-12s %-12s %s\n" "No." "PATH" "SIZE" "FSTYPE" "MODEL"
    local i=1
    while read -r name size type fstype model; do
      [ "$type" = "disk" ] || continue
      local path="/dev/$name"
      local sz="$size"
      printf "  [%d]   %-20s %-12s %-12s %s\n" "$i" "$path" "$sz" "${fstype:--}" "${model:-"-"}"
      i=$((i+1))
    done < <(lsblk -dn -o NAME,SIZE,TYPE,FSTYPE,MODEL)
  }

  build_disk_arrays() {
    # Build arrays for selection UI
    mapfile -t DISK_LINES < <(lsblk -dn -o NAME,SIZE,TYPE,FSTYPE,MODEL | awk '$3=="disk"{print}')
    mapfile -t DISK_PATHS < <(printf "%s\n" "${DISK_LINES[@]}" | awk '{print "/dev/"$1}')
    [ "${#DISK_PATHS[@]}" -ge 1 ] || { echo "No whole disks found."; exit 1; }
  }

  propose_two_largest_empty() {
    # Choose two largest disks that are: TYPE=disk, no partitions, no FSTYPE, not zfs_member
    local candidates=()
    local sizes=()

    for p in "${DISK_PATHS[@]}"; do
      is_whole_disk "$p" || continue
      if has_partitions "$p"; then continue; fi
      local fs; fs="$(fstype_of "$p")"
      [ -n "$fs" ] && continue
      is_zfs_member "$p" && continue
      local b; b="$(bytes_of "$p")"
      candidates+=("$p"); sizes+=("$b")
    done

    if [ "${#candidates[@]}" -lt 2 ]; then
      # Not enough clean disks to propose
      DEFAULT_IDX1=""; DEFAULT_IDX2=""
      return 0
    fi

    # Sort candidates by size desc (simple bubble for small lists)
    local idxs=()
    local i; for ((i=0;i<${#candidates[@]};i++)); do idxs+=("$i"); done
    local swapped=1 j tmp
    while [ $swapped -eq 1 ]; do
      swapped=0
      for ((j=0;j<${#idxs[@]}-1;j++)); do
        if [ "${sizes[${idxs[$j]}]}" -lt "${sizes[${idxs[$j+1]}]}" ]; then
          tmp="${idxs[$j]}"; idxs[$j]="${idxs[$j+1]}"; idxs[$j+1]="$tmp"; swapped=1
        fi
      done
    done

    local top1="${candidates[${idxs[0]}]}"
    local top2="${candidates[${idxs[1]}]}"

    # Map to numbered list indices shown to user
    local n i=1
    for n in "${DISK_PATHS[@]}"; do
      if [ "$(canonical "$n")" = "$(canonical "$top1")" ]; then DEFAULT_IDX1="$i"; fi
      if [ "$(canonical "$n")" = "$(canonical "$top2")" ]; then DEFAULT_IDX2="$i"; fi
      i=$((i+1))
    done
  }

  warn_if_risky() {
    # Return 0 if OK; non-zero if user declines risk
    local d="$1" label="$2"
    local risk=0 msg=""
    if has_partitions "$d"; then risk=1; msg+="\n - $label has existing partitions"; fi
    local fs; fs="$(fstype_of "$d")"
    if [ -n "$fs" ]; then risk=1; msg+="\n - $label has existing filesystem: $fs"; fi
    if [ "$risk" -eq 0 ]; then return 0; fi
    echo -e "WARNING:$msg"
    read -r -p "Type 'force' to override and continue with $label: " force
    [ "${force:-}" = "force" ] || { echo "Refusing risky selection for $label."; return 1; }
    return 0
  }

  pick_two_disks() {
    echo
    print_disks_table
    build_disk_arrays
    propose_two_largest_empty

    local default_hint=""
    if [ -n "$DEFAULT_IDX1" ] && [ -n "$DEFAULT_IDX2" ]; then
      default_hint=" [${DEFAULT_IDX1} ${DEFAULT_IDX2}]"
      echo
      echo "Proposed (largest clean disks): ${DEFAULT_IDX1} and ${DEFAULT_IDX2}"
      echo "Press Enter to accept, or type two numbers to override."
    fi

    while :; do
      echo
      read -r -p "Select TWO disks by number (e.g. '1 3')${default_hint}: " CHOICE
      if [ -z "${CHOICE// }" ] && [ -n "$DEFAULT_IDX1" ] && [ -n "$DEFAULT_IDX2" ]; then
        idx1="$DEFAULT_IDX1"; idx2="$DEFAULT_IDX2"
      else
        CHOICE="$(echo "$CHOICE" | tr ',' ' ' | xargs)"
        set +e; set -- $CHOICE; set -e
        if [ $# -ne 2 ]; then echo "Please provide exactly two numbers."; continue; fi
        idx1="$1"; idx2="$2"
      fi

      case "$idx1" in (*[!0-9]*|'') echo "Invalid first selection."; continue;; esac
      case "$idx2" in (*[!0-9]*|'') echo "Invalid second selection."; continue;; esac
      [ "$idx1" -ge 1 ] && [ "$idx1" -le "${#DISK_PATHS[@]}" ] || { echo "First number out of range."; continue; }
      [ "$idx2" -ge 1 ] && [ "$idx2" -le "${#DISK_PATHS[@]}" ] || { echo "Second number out of range."; continue; }
      [ "$idx1" -ne "$idx2" ] || { echo "Disks must be different."; continue; }

      local cand1="${DISK_PATHS[$((idx1-1))]}"
      local cand2="${DISK_PATHS[$((idx2-1))]}"

      # Risk checks
      warn_if_risky "$cand1" "Disk 1" || continue
      warn_if_risky "$cand2" "Disk 2" || continue

      DISK1="$cand1"; DISK2="$cand2"
      echo
      echo "Selected:"
      echo "  Disk 1: $DISK1"
      echo "  Disk 2: $DISK2"
      read -r -p "Proceed to create MIRRORED pool on these disks? [y/N]: " yn
      case "${yn:-N}" in y|Y) break;; *) echo "Re-selecting…";; esac
    done
  }

  create_pool_and_datasets() {
    echo
    echo "== Creating mirrored ZFS pool and datasets =="
    if zpool list -H -o name 2>/dev/null | grep -qx "$POOL"; then
      echo "Pool '$POOL' already exists; skipping creation."
    else
      ASHIFT="12"
      zpool create -f \
        -o ashift="$ASHIFT" \
        -O compression=zstd -O atime=off -O xattr=sa -O acltype=posixacl \
        "$POOL" mirror "$DISK1" "$DISK2"
    fi

    zfs set mountpoint=none "$POOL" || true

    create_ds() {  # $1=dataset $2=mountpoint|"none"
      local ds="$1" mp="$2"
      if ! zfs list -H -o name | grep -qx "$ds"; then
        if [ "$mp" = "none" ]; then
          zfs create -o mountpoint=none "$ds"
        else
          zfs create -o mountpoint="$mp" "$ds"
        fi
      else
        [ "$mp" = "none" ] && zfs set mountpoint=none "$ds" || zfs set mountpoint="$mp" "$ds"
      fi
    }

    create_ds "${POOL}"                         "none"
    create_ds "${POOL}/data"                    "/mnt/data"
    create_ds "${POOL}/docker"                  "/var/lib/docker"
    create_ds "${POOL}/overlay-layers"          "none"
    create_ds "${POOL}/overlay-layers/root"     "$OVERLAY_ROOT"

    mkdir -p /mnt/data /var/lib/docker "$OVERLAY_ROOT"
    zfs mount -a
  }

  setup_overlays() {
    echo
    echo "== Setting up overlays for /etc and /home =="
    mkdir -p "${OVERLAY_ROOT}/etc" "${OVERLAY_ROOT}/etc.work" \
             "${OVERLAY_ROOT}/home" "${OVERLAY_ROOT}/home.work"
    chmod 700 "${OVERLAY_ROOT}/etc.work" "${OVERLAY_ROOT}/home.work"
    touch "$FSTAB"

    add_fstab_line() { local line="$1" pattern="$2"; grep -qsE "$pattern" "$FSTAB" || echo "$line" >> "$FSTAB"; }

    add_fstab_line \
      "ZFS=${POOL}/overlay-layers/root  $OVERLAY_ROOT  zfs  defaults,nofail  0  0" \
      "^ZFS=${POOL}/overlay-layers/root[[:space:]]+$OVERLAY_ROOT[[:space:]]+zfs"

    add_fstab_line \
      "overlay  /etc   overlay  nofail,x-systemd.requires-mounts-for=$OVERLAY_ROOT,lowerdir=/etc,upperdir=${OVERLAY_ROOT}/etc,workdir=${OVERLAY_ROOT}/etc.work  0  0" \
      "^overlay[[:space:]]+/etc[[:space:]]+overlay.*upperdir=${OVERLAY_ROOT}/etc,workdir=${OVERLAY_ROOT}/etc.work"

    add_fstab_line \
      "overlay  /home  overlay  nofail,x-systemd.requires-mounts-for=$OVERLAY_ROOT,lowerdir=/home,upperdir=${OVERLAY_ROOT}/home,workdir=${OVERLAY_ROOT}/home.work  0  0" \
      "^overlay[[:space:]]+/home[[:space:]]+overlay.*upperdir=${OVERLAY_ROOT}/home,workdir=${OVERLAY_ROOT}/home.work"
  }

  ask_hostname_and_maybe_rename_pool() {
    echo
    echo "== Hostname =="
    read -r -p "New hostname (leave blank to keep '$CUR_HOST'): " NEW_HOSTNAME
    NEW_HOSTNAME="${NEW_HOSTNAME:-$CUR_HOST}"

    if command -v hostnamectl >/dev/null 2>&1; then
      hostnamectl set-hostname "$NEW_HOSTNAME"
    else
      echo "$NEW_HOSTNAME" > /etc/hostname
    fi

    local NEWPOOL="${NEW_HOSTNAME}-zfs"
    if [ "$NEWPOOL" != "$POOL" ]; then
      echo "Renaming pool '$POOL' -> '$NEWPOOL' via export/import…"
      zfs unmount -a || true
      sed -i "s/ZFS=${POOL}\//ZFS=${NEWPOOL}\//g" "$FSTAB"
      zpool export "$POOL"
      zpool import "$POOL" "$NEWPOOL"
      zfs set mountpoint=none "$NEWPOOL" || true
      zfs set mountpoint=/mnt/data            "$NEWPOOL/data" || true
      zfs set mountpoint=/var/lib/docker      "$NEWPOOL/docker" || true
      zfs set mountpoint=none                 "$NEWPOOL/overlay-layers" || true
      zfs set mountpoint="$OVERLAY_ROOT"      "$NEWPOOL/overlay-layers/root" || true
      zfs mount -a
      POOL="$NEWPOOL"
    fi
  }

  set_locale_step() {
    echo
    echo "== Locale =="
    read -r -p "Locale (LANG) [${NEW_LANG}]: " LANG_INPUT
    NEW_LANG="${LANG_INPUT:-$NEW_LANG}"
    printf 'LANG=%s\n' "$NEW_LANG" > /etc/locale.conf
    if command -v localectl >/dev/null 2>&1; then
      localectl set-locale "LANG=$NEW_LANG" || true
    fi
  }

  create_admin_user() {
    echo
    echo "== Create admin user with sudo rights =="
    while :; do
      read -r -p "Admin username (e.g., nimuxadmin): " ADMINUSER
      [ -n "${ADMINUSER:-}" ] && break
      echo "Username cannot be empty."
    done

    if id "$ADMINUSER" >/dev/null 2>&1; then
      echo "User '$ADMINUSER' already exists."
    else
      if [ -x /bin/bash ]; then SHELLPATH=/bin/bash
      elif [ -x /bin/sh ]; then SHELLPATH=/bin/sh
      else SHELLPATH=/bin/sh; fi
      useradd -m -s "$SHELLPATH" "$ADMINUSER"
      echo "Set password for $ADMINUSER:"
      passwd "$ADMINUSER"
    fi

    if command -v sudo >/dev/null 2>&1; then
      getent group sudo >/dev/null 2>&1 || groupadd sudo
      usermod -aG sudo "$ADMINUSER" || true
      mkdir -p /etc/sudoers.d
      SUDO_FILE="/etc/sudoers.d/90-${ADMINUSER}"
      [ -f "$SUDO_FILE" ] || { echo "${ADMINUSER} ALL=(ALL) ALL" > "$SUDO_FILE"; chmod 440 "$SUDO_FILE"; }
    else
      echo "NOTE: 'sudo' not installed; grant temporary root via 'su' until sudo is available."
    fi
  }

  disable_root_login() {
    echo
    echo "== Disable root logins =="
    passwd -l root || true
    if [ -f /etc/ssh/sshd_config ]; then
      if grep -qE '^\s*PermitRootLogin\s+' /etc/ssh/sshd_config; then
        sed -i 's/^\s*PermitRootLogin\s\+.*/PermitRootLogin no/' /etc/ssh/sshd_config
      else
        printf '\nPermitRootLogin no\n' >> /etc/ssh/sshd_config
      fi
      if grep -qE '^\s*PermitEmptyPasswords\s+' /etc/ssh/sshd_config'; then
        sed -i 's/^\s*PermitEmptyPasswords\s\+.*/PermitEmptyPasswords no/' /etc/ssh/sshd_config
      else
        printf 'PermitEmptyPasswords no\n' >> /etc/ssh/sshd_config
      fi
      (command -v systemctl >/dev/null 2>&1 && systemctl reload sshd) || \
      (command -v rc-service >/dev/null 2>&1 && rc-service sshd reload) || true
    fi
  }

  mark_completion() {
    mkdir -p "$(dirname "$SENTINEL")"
    echo "ok" > "$SENTINEL"
    echo
    echo "Bootstrap complete."
    echo " - Pool: ${POOL}"
    echo " - Overlays for /etc and /home will take effect on next reboot."
    echo " - Locale: ${NEW_LANG}"
    echo " - Admin user: ${ADMINUSER}"
    echo " - Root login disabled"
  }

  # -------------------- Ordered flow --------------------
  echo
  echo "=== Nimux First-Login ==="
  build_disk_arrays
  pick_two_disks
  create_pool_and_datasets
  setup_overlays
  ask_hostname_and_maybe_rename_pool
  set_locale_step
  create_admin_user
  disable_root_login
  mark_completion
}

if [ "$EUID" -ne 0 ]; then
  echo
  echo "[Nimux] First-boot setup needs elevated privileges. Re-running with sudo…"
  echo
  sudo bash -c "$(declare -f run_bootstrap); run_bootstrap" && \
    echo "Setup completed (or already done)." || \
    echo "Setup aborted or failed."
else
  run_bootstrap
fi

[ -f "$SENTINEL" ] && chmod -x /etc/profile.d/nimux-first-login.sh 2>/dev/null || true
