#!/usr/bin/env bash
# scratch_discover picks the disk that `buildbox harden` then partitions and formats, on a
# headless box with no console. Every refusal is exercised here against synthetic lsblk /
# findmnt / mount fixtures, so the rules are proven without a disk to lose.
set -euo pipefail

APPLY="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/host-config/apply.sh"
WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT
fail=0
check() { [ "$2" = "$3" ] && printf 'ok   %s\n' "$1" || { printf 'FAIL %s\n  want: %s\n  got:  %s\n' "$1" "$3" "$2"; fail=1; }; }

BIN="$WORK/bin"
mkdir -p "$BIN"

# lsblk answers from a fixture table of "name type mountpoints label fstype", one device per
# line, children named after their parent. Only the option shapes apply.sh actually uses.
cat >"$BIN/lsblk" <<'STUB'
#!/usr/bin/env bash
opts=""; cols=""; dev=""; hasdev=0
while [ $# -gt 0 ]; do
  case "$1" in
    -o) cols="$2"; shift 2;;
    -*o) opts="$opts${1#-}"; opts="${opts%o}"; cols="$2"; shift 2;;
    -*) opts="$opts${1#-}"; shift;;
    *) dev="${1##*/}"; hasdev=1; shift;;
  esac
done
while read -r name type mnt label fstype; do
  [ -n "$name" ] || continue
  [ "$mnt" = - ] && mnt=""
  [ "$label" = - ] && label=""
  [ "$fstype" = - ] && fstype=""
  case "$opts" in *d*) [ "$type" = disk ] || continue;; esac
  if [ "$hasdev" = 1 ]; then
    case "$name" in "$dev"|"$dev"[0-9]*) ;; *) continue;; esac
  fi
  out=""
  for c in ${cols//,/ }; do
    case "$c" in
      NAME) v="$name";; TYPE) v="$type";; MOUNTPOINTS) v="$mnt";;
      LABEL) v="$label";; FSTYPE) v="$fstype";;
      PKNAME) v="$(printf %s "$name" | sed 's/p\?[0-9]*$//')";;
    esac
    out="$out${out:+ }$v"
  done
  printf '%s\n' "$out"
done < "$FIXTURE"
STUB

cat >"$BIN/findmnt" <<'STUB'
#!/usr/bin/env bash
printf '%s\n' "$ROOT_SOURCE"
STUB

# mount succeeds only for partitions listed in MOUNTABLE; anything else is a filesystem the
# real code cannot read, which must refuse rather than report an empty disk.
cat >"$BIN/mount" <<'STUB'
#!/usr/bin/env bash
for p in $MOUNTABLE; do [ "${p%%:*}" = "${*: -2:1}" ] && exit 0; done
exit 32
STUB

cat >"$BIN/umount" <<'STUB'
#!/usr/bin/env bash
exit 0
STUB

cat >"$BIN/mountpoint" <<'STUB'
#!/usr/bin/env bash
exit 1
STUB

cat >"$BIN/df" <<'STUB'
#!/usr/bin/env bash
echo used
echo "$USED_BYTES"
STUB

chmod 755 "$BIN"/*
export PATH="$BIN:$PATH"
export FIXTURE="$WORK/fixture" ROOT_SOURCE=/dev/nvme0n1p2 MOUNTABLE="" USED_BYTES=0

discover() { bash "$APPLY" --discover-scratch 2>&1 || true; }

fixture() { printf '%s\n' "$@" >"$FIXTURE"; }

fixture \
  'nvme0n1     disk -     -                -' \
  'nvme0n1p2   part /     -                ext4' \
  'sda         disk -     -                -' \
  'sda1        part -     -                ntfs'
MOUNTABLE="/dev/sda1" USED_BYTES=112439296
check "empty second disk is the candidate" "$(discover)" "CANDIDATE /dev/sda used=112439296"

fixture \
  'nvme0n1     disk -     -                -' \
  'nvme0n1p2   part /     -                ext4'
check "no second disk refuses" "$(discover)" \
  "REFUSE no candidate: every disk carries /, is mounted, or is already buildbox-scratch"

fixture \
  'nvme0n1     disk -     -                -' \
  'nvme0n1p2   part /     -                ext4' \
  'sda         disk -     -                -' \
  'sdb         disk -     -                -'
check "two candidates refuse" "$(discover)" \
  "REFUSE 2 candidates (/dev/sda /dev/sdb) — never guess which one is scratch"

fixture \
  'nvme0n1     disk -     -                -' \
  'nvme0n1p2   part /     -                ext4' \
  'sda         disk -     -                -' \
  'sda1        part /srv  -                ext4'
check "a mounted second disk refuses" "$(discover)" \
  "REFUSE no candidate: every disk carries /, is mounted, or is already buildbox-scratch"

fixture \
  'nvme0n1     disk -     -                -' \
  'nvme0n1p2   part /     -                ext4' \
  'sda         disk -     -                -' \
  'sda1        part -     buildbox-scratch ext4'
check "an already-labelled disk is not a candidate" "$(discover)" \
  "REFUSE no candidate: every disk carries /, is mounted, or is already buildbox-scratch"

fixture \
  'nvme0n1     disk -     -                -' \
  'nvme0n1p2   part /     -                ext4' \
  'sda         disk -     -                -' \
  'sda1        part -     -                ext4'
MOUNTABLE="/dev/sda1" USED_BYTES=$((40 * 1024 * 1024 * 1024))
check "a disk holding data refuses" "$(discover)" \
  "REFUSE /dev/sda holds 42949672960 bytes of ext4, above the 536870912 byte threshold"

MOUNTABLE="" USED_BYTES=0
check "an unreadable filesystem refuses instead of reading as empty" "$(discover)" \
  "REFUSE /dev/sda: /dev/sda1 holds ext4 and will not mount read-only"

fixture \
  'nvme0n1     disk -     -                -' \
  'nvme0n1p2   part /     -                ext4' \
  'sda         disk -     -                -' \
  'sda1        part -     -                crypto_LUKS'
check "an encrypted filesystem refuses" "$(discover)" \
  "REFUSE /dev/sda: /dev/sda1 is crypto_LUKS, whose contents cannot be read here"

fixture \
  'nvme0n1     disk -     -                -' \
  'nvme0n1p2   part /     -                ext4' \
  'sda         disk -     -                -'
check "a wiped disk with no filesystem is the candidate" "$(discover)" "CANDIDATE /dev/sda used=0"

ROOT_SOURCE=""
check "an unresolvable root disk refuses" "$(discover)" "REFUSE cannot resolve which disk carries /"

exit "$fail"
