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

SWAPFILE="/swapfile"
FSTAB="/etc/fstab"

die() {
  echo "ERROR: $*" >&2
  exit 1
}

need_cmd() {
  command -v "$1" >/dev/null 2>&1 || die "Required command not found: $1"
}

# Must be root
if [[ "${EUID}" -ne 0 ]]; then
  die "Run this script as root (e.g. sudo bash script.sh)"
fi

need_cmd awk
need_cmd grep
need_cmd chmod
need_cmd mkswap
need_cmd swapon
need_cmd free

# Refuse if any swap is already active
if swapon --noheadings --show | grep -q .; then
  echo "Swap is already active:"
  swapon --show
  die "Refusing to continue."
fi

# Refuse if fstab already has any non-comment swap entry
if awk '
  $0 !~ /^[[:space:]]*#/ && NF >= 3 && $3 == "swap" { found=1 }
  END { exit(found ? 0 : 1) }
' "$FSTAB"; then
  echo "Existing swap entry found in $FSTAB:"
  awk '$0 !~ /^[[:space:]]*#/ && NF >= 3 && $3 == "swap"' "$FSTAB"
  die "Refusing to continue."
fi

# Refuse if /swapfile already exists
if [[ -e "$SWAPFILE" ]]; then
  die "$SWAPFILE already exists. Refusing to continue."
fi

# Read RAM from /proc/meminfo (kB)
mem_kb="$(awk '/^MemTotal:/ {print $2}' /proc/meminfo)"
[[ -n "$mem_kb" ]] || die "Could not determine system RAM."

mem_mb=$((mem_kb / 1024))
mem_gb=$(((mem_mb + 1023) / 1024))  # rounded up

# Sizing policy:
# <=2GB  => 2x RAM
# <=8GB  => 1x RAM
# <=64GB => 0.5x RAM
# >64GB  => 8GB cap
if (( mem_mb <= 2048 )); then
  swap_mb=$((mem_mb * 2))
elif (( mem_mb <= 8192 )); then
  swap_mb=$mem_mb
elif (( mem_mb <= 65536 )); then
  swap_mb=$((mem_mb / 2))
else
  swap_mb=8192
fi

# Minimum 1GB
if (( swap_mb < 1024 )); then
  swap_mb=1024
fi

echo "Detected RAM: ${mem_mb} MB (~${mem_gb} GB)"
echo "Will create swap: ${swap_mb} MB (~$(((swap_mb + 1023) / 1024)) GB)"
echo

# Ensure there is enough free disk space on /
avail_kb="$(df --output=avail / | tail -n1 | tr -d ' ')"
need_kb=$((swap_mb * 1024))
if (( avail_kb < need_kb + 262144 )); then
  die "Not enough free disk space on /. Need about ${swap_mb} MB plus some headroom."
fi

# Create swapfile
if command -v fallocate >/dev/null 2>&1; then
  fallocate -l "${swap_mb}M" "$SWAPFILE" || {
    echo "fallocate failed, falling back to dd..."
    dd if=/dev/zero of="$SWAPFILE" bs=1M count="$swap_mb" status=progress
  }
else
  dd if=/dev/zero of="$SWAPFILE" bs=1M count="$swap_mb" status=progress
fi

chmod 600 "$SWAPFILE"
mkswap "$SWAPFILE"
swapon "$SWAPFILE"

# Backup fstab before editing
cp "$FSTAB" "${FSTAB}.bak.$(date +%Y%m%d%H%M%S)"

echo "$SWAPFILE none swap sw 0 0" >> "$FSTAB"

echo
echo "Swap successfully created and enabled."
echo
echo "Current memory/swap:"
free -h
echo
echo "Active swap:"
swapon --show
echo
echo "fstab entry added:"
tail -n 5 "$FSTAB"