#!/usr/bin/env bash
# Interactive Dell OptiPlex / Debian / CasaOS NAS bootstrap wizard.
#
# Run after Debian minimal/headless is installed on the OS drive:
#   sudo bash bootstrap_casaos_nas.sh
#
# Safe design:
# - Does NOT partition, format, wipe, or erase drives.
# - Only mounts filesystems that already have UUIDs.
# - Backs up /etc/fstab, /etc/samba/smb.conf, and sshd config before editing.
# - Uses managed config blocks that can be replaced safely on rerun.

set -Eeuo pipefail

LOG_DIR="/var/log/hephaestus-nas-bootstrap"
LOG_FILE="${LOG_DIR}/bootstrap.log"
BACKUP_DIR="/root/hephaestus-nas-bootstrap-backups"
SMB_MANAGED_BEGIN="# BEGIN HEPHAESTUS MANAGED NAS SHARES"
SMB_MANAGED_END="# END HEPHAESTUS MANAGED NAS SHARES"
FSTAB_MANAGED_BEGIN="# BEGIN HEPHAESTUS MANAGED NAS MOUNTS"
FSTAB_MANAGED_END="# END HEPHAESTUS MANAGED NAS MOUNTS"

HOSTNAME_TARGET=""
ADMIN_USER=""
INTERACTIVE=1
DRY_RUN=0
MOUNT_SHARES=1

# Feature defaults: core NAS stack selected by default.
INSTALL_CASAOS=1
INSTALL_TAILSCALE=1
INSTALL_OPENSSH=1
INSTALL_SAMBA=1
INSTALL_COCKPIT=1
INSTALL_UFW=1
INSTALL_FAIL2BAN=1
INSTALL_UNATTENDED=1
INSTALL_AVAHI=1
INSTALL_DISK_TOOLS=1

# Share definitions gathered interactively as mountpoint|uuid|fstype|share_name
SHARE_DEFS=()

usage() {
  cat <<'EOF'
Usage:
  sudo bash bootstrap_casaos_nas.sh
  sudo bash bootstrap_casaos_nas.sh [options]

Interactive mode:
  Run with no options. You get a friendly [x] checklist and prompts for:
    - hostname
    - admin user
    - features to install
    - UUID-backed media mounts
    - Samba share names

Options for unattended/simple runs:
  --hostname NAME              Set NAS hostname, e.g. dell-nas
  --admin-user USER            Existing Debian user to add to sudo/sambashare groups
  --no-interactive             Do not show wizard; use defaults/options
  --no-mount-shares            Skip interactive mount/share/fstab setup
  --dry-run                    Show what would run where practical; do not apt install/edit configs
  --skip-casaos                Do not install CasaOS
  --skip-tailscale             Do not install Tailscale
  --skip-openssh               Do not install/configure OpenSSH
  --skip-samba                 Do not install/configure Samba/SMB
  --skip-cockpit               Do not install Cockpit
  --skip-firewall              Do not enable/configure UFW
  --skip-fail2ban              Do not install Fail2ban
  --skip-unattended            Do not configure unattended security upgrades
  --skip-avahi                 Do not install Avahi .local discovery
  --skip-disk-tools            Do not install SMART/disk/admin tools
  -h, --help                   Show this help

Recommended:
  sudo bash bootstrap_casaos_nas.sh

After setup:
  sudo tailscale up --ssh
  sudo smbpasswd -a <admin-user>
  sudo reboot
EOF
}

log() {
  local msg="$*" ts
  ts="$(date --iso-8601=seconds 2>/dev/null || date '+%Y-%m-%dT%H:%M:%S%z')"
  echo "[${ts}] ${msg}"
  mkdir -p "$LOG_DIR" 2>/dev/null || true
  printf '[%s] %s\n' "$ts" "$msg" >>"$LOG_FILE" 2>/dev/null || true
}

fail() {
  log "ERROR: $*"
  exit 1
}

on_error() {
  local rc=$?
  log "ERROR: bootstrap stopped near line ${BASH_LINENO[0]} with exit ${rc}. See ${LOG_FILE}"
  exit "$rc"
}
trap on_error ERR

run() {
  if [[ "$DRY_RUN" -eq 1 ]]; then
    log "DRY-RUN: $*"
    return 0
  fi
  log "RUN: $*"
  "$@" 2>&1 | while IFS= read -r line; do log "  ${line}"; done
  local rc=${PIPESTATUS[0]}
  if [[ $rc -ne 0 ]]; then
    log "ERROR: command failed with exit ${rc}: $*"
    return "$rc"
  fi
}

run_allow_fail() {
  if [[ "$DRY_RUN" -eq 1 ]]; then
    log "DRY-RUN: $*"
    return 0
  fi
  log "RUN: $*"
  set +e
  "$@" 2>&1 | while IFS= read -r line; do log "  ${line}"; done
  local rc=${PIPESTATUS[0]}
  set -e
  if [[ $rc -ne 0 ]]; then
    log "WARN: command failed but continuing, exit ${rc}: $*"
  fi
  return 0
}

write_root_file() {
  local path="$1" content="$2"
  if [[ "$DRY_RUN" -eq 1 ]]; then
    log "DRY-RUN: write ${path}"
    printf '%s\n' "$content" | sed 's/^/[DRY-RUN content] /'
    return 0
  fi
  printf '%s\n' "$content" >"$path"
}

parse_args() {
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --hostname) HOSTNAME_TARGET="${2:-}"; [[ -n "$HOSTNAME_TARGET" ]] || fail "--hostname requires a value"; shift 2 ;;
      --admin-user) ADMIN_USER="${2:-}"; [[ -n "$ADMIN_USER" ]] || fail "--admin-user requires a value"; shift 2 ;;
      --no-interactive) INTERACTIVE=0; shift ;;
      --no-mount-shares) MOUNT_SHARES=0; shift ;;
      --dry-run) DRY_RUN=1; shift ;;
      --skip-casaos) INSTALL_CASAOS=0; shift ;;
      --skip-tailscale) INSTALL_TAILSCALE=0; shift ;;
      --skip-openssh) INSTALL_OPENSSH=0; shift ;;
      --skip-samba) INSTALL_SAMBA=0; shift ;;
      --skip-cockpit) INSTALL_COCKPIT=0; shift ;;
      --skip-firewall) INSTALL_UFW=0; shift ;;
      --skip-fail2ban) INSTALL_FAIL2BAN=0; shift ;;
      --skip-unattended) INSTALL_UNATTENDED=0; shift ;;
      --skip-avahi) INSTALL_AVAHI=0; shift ;;
      --skip-disk-tools) INSTALL_DISK_TOOLS=0; shift ;;
      -h|--help) usage; exit 0 ;;
      *) fail "Unknown option: $1" ;;
    esac
  done
}

require_root() {
  [[ ${EUID} -eq 0 ]] || fail "Run this as root, e.g. sudo bash bootstrap_casaos_nas.sh"
}

check_debian_family() {
  [[ -r /etc/os-release ]] || fail "Cannot read /etc/os-release"
  # shellcheck disable=SC1091
  source /etc/os-release
  local os_blob
  os_blob="$(tr '[:upper:]' '[:lower:]' </etc/os-release)"
  if ! grep -Eq 'debian|ubuntu' <<<"$os_blob"; then
    fail "This script is intended for Debian-family systems only. Detected: ${PRETTY_NAME:-unknown}"
  fi
  log "OS detected: ${PRETTY_NAME:-unknown}"
  log "Kernel/arch: $(uname -r) / $(uname -m)"
}

backup_file() {
  local path="$1"
  [[ -e "$path" ]] || return 0
  mkdir -p "$BACKUP_DIR"
  local dest
  dest="${BACKUP_DIR}/$(basename "$path").$(date '+%Y%m%d-%H%M%S').bak"
  if [[ "$DRY_RUN" -eq 1 ]]; then
    log "DRY-RUN: backup ${path} -> ${dest}"
  else
    cp -a "$path" "$dest"
    log "Backed up ${path} -> ${dest}"
  fi
}

valid_hostname() {
  [[ "$1" =~ ^[A-Za-z0-9][A-Za-z0-9-]{0,62}$ ]]
}

valid_share_name() {
  [[ "$1" =~ ^[A-Za-z0-9._-]+$ ]]
}

pause_enter() {
  local dummy
  read -r -p "Press Enter to continue... " dummy || true
}

ask_yes_no() {
  local prompt="$1" default="${2:-Y}" answer suffix
  if [[ "$default" =~ ^[Yy]$ ]]; then suffix="[Y/n]"; else suffix="[y/N]"; fi
  while true; do
    read -r -p "${prompt} ${suffix} " answer || answer=""
    answer="${answer:-$default}"
    case "$answer" in
      y|Y|yes|YES) return 0 ;;
      n|N|no|NO) return 1 ;;
      *) echo "Please answer y or n." ;;
    esac
  done
}

ask_text() {
  local prompt="$1" default="${2:-}" answer
  if [[ -n "$default" ]]; then
    read -r -p "${prompt} [${default}]: " answer || answer=""
    printf '%s' "${answer:-$default}"
  else
    read -r -p "${prompt}: " answer || answer=""
    printf '%s' "$answer"
  fi
}

auto_admin_guess() {
  local sudo_user="${SUDO_USER:-}"
  if [[ -n "$sudo_user" && "$sudo_user" != "root" && $(id -u "$sudo_user" 2>/dev/null || echo 0) -ge 1000 ]]; then
    printf '%s' "$sudo_user"
    return 0
  fi
  awk -F: '$3 >= 1000 && $3 < 60000 && $1 != "nobody" {print $1; exit}' /etc/passwd
}

feature_value_by_key() {
  case "$1" in
    casaos) echo "$INSTALL_CASAOS" ;;
    tailscale) echo "$INSTALL_TAILSCALE" ;;
    openssh) echo "$INSTALL_OPENSSH" ;;
    samba) echo "$INSTALL_SAMBA" ;;
    cockpit) echo "$INSTALL_COCKPIT" ;;
    ufw) echo "$INSTALL_UFW" ;;
    fail2ban) echo "$INSTALL_FAIL2BAN" ;;
    unattended) echo "$INSTALL_UNATTENDED" ;;
    avahi) echo "$INSTALL_AVAHI" ;;
    disktools) echo "$INSTALL_DISK_TOOLS" ;;
  esac
}

feature_toggle_by_key() {
  case "$1" in
    casaos) INSTALL_CASAOS=$((1-INSTALL_CASAOS)) ;;
    tailscale) INSTALL_TAILSCALE=$((1-INSTALL_TAILSCALE)) ;;
    openssh) INSTALL_OPENSSH=$((1-INSTALL_OPENSSH)) ;;
    samba) INSTALL_SAMBA=$((1-INSTALL_SAMBA)) ;;
    cockpit) INSTALL_COCKPIT=$((1-INSTALL_COCKPIT)) ;;
    ufw) INSTALL_UFW=$((1-INSTALL_UFW)) ;;
    fail2ban) INSTALL_FAIL2BAN=$((1-INSTALL_FAIL2BAN)) ;;
    unattended) INSTALL_UNATTENDED=$((1-INSTALL_UNATTENDED)) ;;
    avahi) INSTALL_AVAHI=$((1-INSTALL_AVAHI)) ;;
    disktools) INSTALL_DISK_TOOLS=$((1-INSTALL_DISK_TOOLS)) ;;
  esac
}

interactive_feature_menu() {
  local keys=(casaos tailscale openssh samba cockpit ufw fail2ban unattended avahi disktools)
  local labels=(
    "CasaOS"
    "Tailscale"
    "OpenSSH server"
    "Samba / SMB"
    "Cockpit"
    "UFW firewall baseline"
    "Fail2ban"
    "unattended security upgrades"
    "Avahi .local discovery"
    "SMART/disk/admin tools"
  )
  local input token idx state mark
  while true; do
    clear 2>/dev/null || true
    echo "Hephaestus Dell OptiPlex NAS bootstrap"
    echo "======================================="
    echo "Toggle features by number. Selected items show [x]. Press Enter when happy."
    echo
    for idx in "${!keys[@]}"; do
      state="$(feature_value_by_key "${keys[$idx]}")"
      [[ "$state" -eq 1 ]] && mark="x" || mark=" "
      printf '  %2d) [%s] %s\n' "$((idx+1))" "$mark" "${labels[$idx]}"
    done
    echo
    read -r -p "Numbers to toggle, 'a' all, 'n' none, Enter continue: " input || input=""
    [[ -z "$input" ]] && break
    case "$input" in
      a|A)
        INSTALL_CASAOS=1; INSTALL_TAILSCALE=1; INSTALL_OPENSSH=1; INSTALL_SAMBA=1; INSTALL_COCKPIT=1
        INSTALL_UFW=1; INSTALL_FAIL2BAN=1; INSTALL_UNATTENDED=1; INSTALL_AVAHI=1; INSTALL_DISK_TOOLS=1
        ;;
      n|N)
        INSTALL_CASAOS=0; INSTALL_TAILSCALE=0; INSTALL_OPENSSH=0; INSTALL_SAMBA=0; INSTALL_COCKPIT=0
        INSTALL_UFW=0; INSTALL_FAIL2BAN=0; INSTALL_UNATTENDED=0; INSTALL_AVAHI=0; INSTALL_DISK_TOOLS=0
        ;;
      *)
        input="${input//,/ }"
        for token in $input; do
          if [[ "$token" =~ ^[0-9]+$ ]] && (( token >= 1 && token <= ${#keys[@]} )); then
            feature_toggle_by_key "${keys[$((token-1))]}"
          else
            echo "Ignoring invalid choice: $token"
            sleep 1
          fi
        done
        ;;
    esac
  done
}

interactive_identity() {
  local default_host default_user answer
  default_host="${HOSTNAME_TARGET:-dell-nas}"
  while true; do
    answer="$(ask_text "Hostname for this NAS" "$default_host")"
    if valid_hostname "$answer"; then HOSTNAME_TARGET="$answer"; break; fi
    echo "Invalid hostname. Use letters, numbers, hyphens; no spaces."
  done

  default_user="${ADMIN_USER:-$(auto_admin_guess)}"
  while true; do
    answer="$(ask_text "Existing Debian admin username" "$default_user")"
    if id "$answer" >/dev/null 2>&1; then ADMIN_USER="$answer"; break; fi
    echo "User '$answer' does not exist. This script configures an existing Debian user; create it in Debian first."
  done
}

validate_inputs() {
  if [[ -n "$HOSTNAME_TARGET" ]] && ! valid_hostname "$HOSTNAME_TARGET"; then
    fail "Invalid hostname."
  fi
  if [[ -n "$ADMIN_USER" ]] && ! id "$ADMIN_USER" >/dev/null 2>&1; then
    fail "Admin user '$ADMIN_USER' does not exist."
  fi
}

collect_packages() {
  local pkgs=(ca-certificates curl wget gnupg lsb-release apt-transport-https software-properties-common sudo nano vim-tiny htop tmux git jq unzip bash-completion iproute2 net-tools dnsutils nmap rsync acl attr logrotate)
  [[ "$INSTALL_OPENSSH" -eq 1 ]] && pkgs+=(openssh-server)
  [[ "$INSTALL_SAMBA" -eq 1 ]] && pkgs+=(samba smbclient cifs-utils)
  [[ "$INSTALL_COCKPIT" -eq 1 ]] && pkgs+=(cockpit)
  [[ "$INSTALL_UFW" -eq 1 ]] && pkgs+=(ufw)
  [[ "$INSTALL_FAIL2BAN" -eq 1 ]] && pkgs+=(fail2ban)
  [[ "$INSTALL_UNATTENDED" -eq 1 ]] && pkgs+=(unattended-upgrades)
  [[ "$INSTALL_AVAHI" -eq 1 ]] && pkgs+=(avahi-daemon)
  [[ "$INSTALL_DISK_TOOLS" -eq 1 ]] && pkgs+=(smartmontools hdparm nvme-cli lm-sensors iotop xfsprogs btrfs-progs exfatprogs ntfs-3g)
  printf '%s\n' "${pkgs[@]}" | awk '!seen[$0]++'
}

apt_install_selected() {
  export DEBIAN_FRONTEND=noninteractive
  mapfile -t pkgs < <(collect_packages)
  run apt-get update
  run apt-get upgrade -y
  if [[ ${#pkgs[@]} -gt 0 ]]; then
    run apt-get install -y "${pkgs[@]}"
  fi
}

set_hostname_if_requested() {
  [[ -n "$HOSTNAME_TARGET" ]] || return 0
  local current
  current="$(hostnamectl --static 2>/dev/null || hostname)"
  if [[ "$current" == "$HOSTNAME_TARGET" ]]; then
    log "Hostname already set to ${HOSTNAME_TARGET}"
  else
    run hostnamectl set-hostname "$HOSTNAME_TARGET"
    log "Hostname changed from '${current}' to '${HOSTNAME_TARGET}'; reboot recommended later."
  fi
}

configure_ssh() {
  [[ "$INSTALL_OPENSSH" -eq 1 ]] || { log "Skipped OpenSSH configuration."; return 0; }
  backup_file /etc/ssh/sshd_config
  mkdir -p /etc/ssh/sshd_config.d
  write_root_file /etc/ssh/sshd_config.d/99-hephaestus-nas.conf "# Hephaestus NAS baseline SSH hardening.
# Password auth is left enabled by default so first setup does not lock you out.
PermitRootLogin no
PubkeyAuthentication yes
X11Forwarding no
ClientAliveInterval 300
ClientAliveCountMax 2"
  [[ -n "$ADMIN_USER" ]] && run_allow_fail usermod -aG sudo "$ADMIN_USER"
  run sshd -t
  run systemctl enable --now ssh
  run systemctl restart ssh
}

remove_managed_block() {
  local file="$1" begin="$2" end="$3"
  [[ -e "$file" ]] || return 0
  if grep -Fq "$begin" "$file" 2>/dev/null; then
    if [[ "$DRY_RUN" -eq 1 ]]; then
      log "DRY-RUN: remove managed block ${begin} from ${file}"
    else
      awk -v begin="$begin" -v end="$end" '$0 == begin {skip=1; next} $0 == end {skip=0; next} skip != 1 {print}' "$file" >"${file}.heph-new"
      mv "${file}.heph-new" "$file"
    fi
  fi
}

append_file() {
  local file="$1" content="$2"
  if [[ "$DRY_RUN" -eq 1 ]]; then
    log "DRY-RUN: append to ${file}"
    printf '%s\n' "$content" | sed 's/^/[DRY-RUN append] /'
  else
    printf '\n%s\n' "$content" >>"$file"
  fi
}

show_disks() {
  echo
  echo "Detected block devices/filesystems:"
  lsblk -o NAME,SIZE,TYPE,FSTYPE,LABEL,UUID,MOUNTPOINTS || true
  echo
  echo "Only choose existing media filesystems with a UUID. Do not choose the OS root filesystem."
}

is_uuid_mounted_root() {
  local uuid="$1"
  findmnt -no SOURCE / | grep -q "UUID=${uuid}" && return 0
  local root_src
  root_src="$(findmnt -no SOURCE / 2>/dev/null || true)"
  [[ -n "$root_src" ]] && blkid -s UUID -o value "$root_src" 2>/dev/null | grep -qx "$uuid"
}

uuid_exists() {
  blkid -U "$1" >/dev/null 2>&1
}

uuid_fstype() {
  local dev
  dev="$(blkid -U "$1" 2>/dev/null || true)"
  [[ -n "$dev" ]] || return 1
  blkid -s TYPE -o value "$dev" 2>/dev/null || true
}

collect_mount_shares() {
  SHARE_DEFS=()
  [[ "$MOUNT_SHARES" -eq 1 ]] || return 0
  [[ "$INSTALL_SAMBA" -eq 1 ]] || { log "Samba not selected; skipping Samba share/mount wizard."; return 0; }
  if ! ask_yes_no "Create/mount local media shares and write a managed /etc/fstab block?" "Y"; then
    MOUNT_SHARES=0
    return 0
  fi

  while true; do
    show_disks
    local uuid fstype mountpoint default_share share
    uuid="$(ask_text "Enter filesystem UUID to mount, or blank when done" "")"
    [[ -z "$uuid" ]] && break
    if ! uuid_exists "$uuid"; then
      echo "UUID not found: $uuid"
      pause_enter
      continue
    fi
    if is_uuid_mounted_root "$uuid"; then
      echo "That appears to be the OS/root filesystem. Refusing to add it as a media share."
      pause_enter
      continue
    fi
    fstype="$(uuid_fstype "$uuid")"
    if [[ -z "$fstype" ]]; then
      echo "Could not detect filesystem type for UUID $uuid. Skipping."
      pause_enter
      continue
    fi
    mountpoint="$(ask_text "Mount point" "/mnt/media_primary")"
    if [[ ! "$mountpoint" =~ ^/mnt/[A-Za-z0-9._/-]+$ ]]; then
      echo "Mount point must be under /mnt, e.g. /mnt/media_primary"
      pause_enter
      continue
    fi
    default_share="$(basename "$mountpoint")"
    while true; do
      share="$(ask_text "SMB share name" "$default_share")"
      if valid_share_name "$share"; then break; fi
      echo "Share name can use letters, numbers, dot, underscore, or hyphen."
    done
    SHARE_DEFS+=("${mountpoint}|${uuid}|${fstype}|${share}")
    echo "Added: UUID=${uuid} -> ${mountpoint} as SMB share [${share}] (${fstype})"
    if ! ask_yes_no "Add another media drive/share?" "N"; then break; fi
  done
}

fstab_options_for_fstype() {
  local fstype="$1"
  case "$fstype" in
    ext2|ext3|ext4|xfs|btrfs) echo "defaults,nofail,x-systemd.device-timeout=10" ;;
    exfat|vfat) echo "defaults,nofail,x-systemd.device-timeout=10,uid=0,gid=sambashare,umask=002" ;;
    ntfs|ntfs3) echo "defaults,nofail,x-systemd.device-timeout=10,uid=0,gid=sambashare,umask=002" ;;
    *) echo "defaults,nofail,x-systemd.device-timeout=10" ;;
  esac
}

apply_fstab_mounts() {
  [[ ${#SHARE_DEFS[@]} -gt 0 ]] || { log "No new fstab media mounts selected."; return 0; }
  backup_file /etc/fstab
  remove_managed_block /etc/fstab "$FSTAB_MANAGED_BEGIN" "$FSTAB_MANAGED_END"

  local block="$FSTAB_MANAGED_BEGIN" def mountpoint uuid fstype share opts passno
  for def in "${SHARE_DEFS[@]}"; do
    IFS='|' read -r mountpoint uuid fstype share <<<"$def"
    mkdir -p "$mountpoint"
    opts="$(fstab_options_for_fstype "$fstype")"
    passno=0
    [[ "$fstype" == ext2 || "$fstype" == ext3 || "$fstype" == ext4 ]] && passno=2
    block+=$'\n'"UUID=${uuid} ${mountpoint} ${fstype} ${opts} 0 ${passno}"
  done
  block+=$'\n'"$FSTAB_MANAGED_END"
  append_file /etc/fstab "$block"

  run systemctl daemon-reload
  run mount -a
  for def in "${SHARE_DEFS[@]}"; do
    IFS='|' read -r mountpoint uuid fstype share <<<"$def"
    run findmnt "$mountpoint"
  done
}

configure_samba() {
  [[ "$INSTALL_SAMBA" -eq 1 ]] || { log "Skipped Samba configuration."; return 0; }
  backup_file /etc/samba/smb.conf
  [[ -n "$ADMIN_USER" ]] && run_allow_fail usermod -aG sambashare "$ADMIN_USER"

  remove_managed_block /etc/samba/smb.conf "$SMB_MANAGED_BEGIN" "$SMB_MANAGED_END"
  local block="$SMB_MANAGED_BEGIN" def mountpoint uuid fstype share
  if [[ ${#SHARE_DEFS[@]} -eq 0 ]]; then
    # Friendly placeholders if user skipped real mounts.
    SHARE_DEFS+=("/mnt/media_primary|UNMOUNTED|none|media_primary")
    SHARE_DEFS+=("/mnt/media_backup|UNMOUNTED|none|media_backup")
    log "No real mounts selected; creating placeholder Samba folders /mnt/media_primary and /mnt/media_backup."
  fi

  for def in "${SHARE_DEFS[@]}"; do
    IFS='|' read -r mountpoint uuid fstype share <<<"$def"
    mkdir -p "$mountpoint"
    run_allow_fail chgrp sambashare "$mountpoint"
    run_allow_fail chmod 2775 "$mountpoint"
    block+=$'\n'"[${share}]"$'\n'
    block+="   path = ${mountpoint}"$'\n'
    block+="   browseable = yes"$'\n'
    block+="   read only = no"$'\n'
    block+="   guest ok = no"$'\n'
    block+="   valid users = @sambashare"$'\n'
    block+="   create mask = 0664"$'\n'
    block+="   directory mask = 0775"$'\n'
    block+="   force group = sambashare"$'\n'
  done
  block+="$SMB_MANAGED_END"
  append_file /etc/samba/smb.conf "$block"

  run testparm -s
  run_allow_fail systemctl enable --now smbd nmbd
  run_allow_fail systemctl restart smbd nmbd
  [[ -n "$ADMIN_USER" ]] && log "Set/refresh SMB password after setup: sudo smbpasswd -a ${ADMIN_USER}"
}

configure_firewall() {
  [[ "$INSTALL_UFW" -eq 1 ]] || { log "Skipped UFW firewall setup."; return 0; }
  [[ "$INSTALL_OPENSSH" -eq 1 ]] && run_allow_fail ufw allow OpenSSH
  [[ "$INSTALL_SAMBA" -eq 1 ]] && run_allow_fail ufw allow Samba
  [[ "$INSTALL_CASAOS" -eq 1 ]] && run_allow_fail ufw allow 80/tcp comment 'CasaOS web UI'
  [[ "$INSTALL_COCKPIT" -eq 1 ]] && run_allow_fail ufw allow 9090/tcp comment 'Cockpit web admin'
  run_allow_fail ufw --force enable
  run_allow_fail ufw status verbose
}

configure_unattended_upgrades() {
  [[ "$INSTALL_UNATTENDED" -eq 1 ]] || { log "Skipped unattended upgrades."; return 0; }
  run_allow_fail dpkg-reconfigure -f noninteractive unattended-upgrades
  write_root_file /etc/apt/apt.conf.d/20auto-upgrades 'APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
APT::Periodic::AutocleanInterval "7";'
  log "Enabled unattended security upgrades."
}

enable_selected_services() {
  local services=()
  [[ "$INSTALL_OPENSSH" -eq 1 ]] && services+=(ssh)
  [[ "$INSTALL_SAMBA" -eq 1 ]] && services+=(smbd nmbd)
  [[ "$INSTALL_AVAHI" -eq 1 ]] && services+=(avahi-daemon)
  [[ "$INSTALL_DISK_TOOLS" -eq 1 ]] && services+=(smartmontools)
  [[ "$INSTALL_FAIL2BAN" -eq 1 ]] && services+=(fail2ban)
  [[ "$INSTALL_COCKPIT" -eq 1 ]] && services+=(cockpit.socket)
  local svc
  for svc in "${services[@]}"; do
    run_allow_fail systemctl enable --now "$svc"
  done
}

install_tailscale() {
  [[ "$INSTALL_TAILSCALE" -eq 1 ]] || { log "Skipped Tailscale install."; return 0; }
  if command -v tailscale >/dev/null 2>&1; then
    log "Tailscale already installed."
  else
    run bash -c 'curl -fsSL https://tailscale.com/install.sh | sh'
  fi
  run_allow_fail systemctl enable --now tailscaled
  log "Tailscale installed. Authenticate this NAS after setup with: sudo tailscale up --ssh"
}

install_casaos() {
  [[ "$INSTALL_CASAOS" -eq 1 ]] || { log "Skipped CasaOS install."; return 0; }
  if [[ -x /usr/bin/casaos || -e /etc/systemd/system/casaos.service || -e /etc/systemd/system/casaos-gateway.service ]]; then
    log "CasaOS appears to already be installed."
  else
    run bash -c 'curl -fsSL https://get.casaos.io | bash'
  fi
  # CasaOS has used several unit names; enable/restart whatever exists.
  run_allow_fail systemctl enable --now casaos-gateway
  run_allow_fail systemctl enable --now casaos.service
  run_allow_fail systemctl enable --now casaos-app-management.service
  run_allow_fail systemctl status casaos-gateway --no-pager
}

print_disk_report() {
  log "Current block devices:"
  run_allow_fail lsblk -o NAME,SIZE,TYPE,FSTYPE,LABEL,UUID,MOUNTPOINTS
  run_allow_fail df -hT
}

verify_services_now() {
  log "Verifying selected services now."
  local services=()
  [[ "$INSTALL_OPENSSH" -eq 1 ]] && services+=(ssh)
  [[ "$INSTALL_SAMBA" -eq 1 ]] && services+=(smbd)
  [[ "$INSTALL_TAILSCALE" -eq 1 ]] && services+=(tailscaled)
  [[ "$INSTALL_CASAOS" -eq 1 ]] && services+=(casaos-gateway)
  [[ "$INSTALL_COCKPIT" -eq 1 ]] && services+=(cockpit.socket)
  [[ "$INSTALL_AVAHI" -eq 1 ]] && services+=(avahi-daemon)
  [[ "$INSTALL_FAIL2BAN" -eq 1 ]] && services+=(fail2ban)
  local svc
  for svc in "${services[@]}"; do
    run_allow_fail systemctl is-enabled "$svc"
    run_allow_fail systemctl is-active "$svc"
  done
}

write_reboot_verify_script() {
  local path="/usr/local/sbin/heph-nas-verify-after-reboot"
  local content='#!/usr/bin/env bash
set -u
echo "== Hephaestus NAS post-reboot verification =="
date
hostnamectl --static 2>/dev/null || hostname
echo
for svc in ssh smbd nmbd tailscaled casaos-gateway cockpit.socket avahi-daemon fail2ban; do
  if systemctl list-unit-files "$svc" >/dev/null 2>&1; then
    printf "%-24s enabled=%-10s active=%s\n" "$svc" "$(systemctl is-enabled "$svc" 2>/dev/null || echo n/a)" "$(systemctl is-active "$svc" 2>/dev/null || echo n/a)"
  fi
done
echo
echo "-- Mounts --"
findmnt -R /mnt || true
echo
echo "-- Samba shares --"
testparm -s 2>/dev/null | sed -n "/^\[/p" || true
echo
echo "-- Tailscale --"
tailscale status 2>/dev/null || echo "Tailscale not authenticated/running yet."
echo
echo "-- Listening ports --"
ss -tulpn | grep -E ":(22|80|445|139|9090)" || true
'
  if [[ "$DRY_RUN" -eq 1 ]]; then
    log "DRY-RUN: write ${path}"
  else
    printf '%s\n' "$content" >"$path"
    chmod 0755 "$path"
    log "Wrote post-reboot verification helper: ${path}"
  fi
}

final_report() {
  local host="$HOSTNAME_TARGET"
  [[ -n "$host" ]] || host="$(hostname)"
  log "Bootstrap complete."
  log "CasaOS URL: http://${host}.local/ or http://<NAS-LAN-IP>/"
  [[ "$INSTALL_COCKPIT" -eq 1 ]] && log "Cockpit URL: https://${host}.local:9090/ or https://<NAS-LAN-IP>:9090/"
  log "Next manual steps:"
  [[ "$INSTALL_TAILSCALE" -eq 1 ]] && log "  1. Authenticate Tailscale: sudo tailscale up --ssh"
  if [[ "$INSTALL_SAMBA" -eq 1 ]]; then
    log "  2. Set Samba password: sudo smbpasswd -a ${ADMIN_USER:-<your-user>}"
  fi
  log "  3. Reboot once: sudo reboot"
  log "  4. After reboot verify: sudo /usr/local/sbin/heph-nas-verify-after-reboot"
  log "Log file: ${LOG_FILE}"
  log "Config backups: ${BACKUP_DIR}"
}

interactive_wizard() {
  clear 2>/dev/null || true
  echo "Hephaestus Dell OptiPlex NAS bootstrap wizard"
  echo "=============================================="
  echo "This configures Debian headless into a CasaOS-style NAS."
  echo "It will NOT format/wipe disks. Mounts are by existing filesystem UUID only."
  echo
  pause_enter
  interactive_feature_menu
  interactive_identity
  collect_mount_shares
  echo
  echo "Summary:"
  echo "  Hostname: ${HOSTNAME_TARGET}"
  echo "  Admin user: ${ADMIN_USER}"
  echo "  Shares selected: ${#SHARE_DEFS[@]}"
  echo "  Dry run: ${DRY_RUN}"
  echo
  ask_yes_no "Proceed with install/configuration?" "Y" || fail "Cancelled by user."
}

main() {
  parse_args "$@"
  require_root
  check_debian_family
  if [[ "$INTERACTIVE" -eq 1 ]]; then
    interactive_wizard
  fi
  validate_inputs
  set_hostname_if_requested
  apt_install_selected
  configure_ssh
  enable_selected_services
  apply_fstab_mounts
  configure_samba
  configure_firewall
  configure_unattended_upgrades
  install_tailscale
  install_casaos
  enable_selected_services
  print_disk_report
  verify_services_now
  write_reboot_verify_script
  final_report
}

main "$@"
