#!/usr/bin/env bash
# ============================================================================
# sysmig - Host state migration tool (DB-migration style)
#
#   sudo bash sysmig up         Apply all pending migrations in order
#   sudo bash sysmig down       Roll back the last applied migration
#   sudo bash sysmig down all   Roll back everything (reverse order)
#   bash sysmig status          History + drift check (root: full detail)
#
# - Migration files: ./migrations/NNN-*.sh  (up|down|status via $1)
# - Applied history: /var/lib/sysmig/applied (one filename per line, in order)
# - status case: read-only check, one output line, exit 0 OK / 1 DRIFT / 2 ?
# - Safe to re-run on every boot (cloud-init); idempotent
# - Adding work: drop NNN-*.sh into migrations/ -> applied on next up
# ============================================================================
set -euo pipefail

MIG_DIR="$(cd "$(dirname "$0")/migrations" && pwd)"
STATE_DIR=/var/lib/sysmig
STATE_FILE=$STATE_DIR/applied

say()  { printf '\033[1;36m==>\033[0m %s\n' "$*"; }
note() { printf '    %s\n' "$*"; }

require_root() {
  [[ $EUID -eq 0 ]] || { echo "root required: sudo bash $0 ..." >&2; exit 1; }
}

is_applied() { grep -qx "$1" "$STATE_FILE" 2>/dev/null; }

cmd_up() {
  require_root
  mkdir -p "$STATE_DIR"; touch "$STATE_FILE"
  local n=0 f name
  for f in "$MIG_DIR"/*.sh; do
    name=$(basename "$f")
    is_applied "$name" && continue
    say "UP   $name"
    if bash "$f" up; then
      echo "$name" >> "$STATE_FILE"
      n=$((n+1))
    else
      echo "FAILED - aborting (earlier migrations stay applied)" >&2
      exit 1
    fi
  done
  if [[ $n -eq 0 ]]; then note "all migrations already applied"
  else note "$n migration(s) applied, recorded in state file"; fi
  echo; say "memory"; free -h
}

cmd_down_last() {
  require_root
  [[ -s "$STATE_FILE" ]] || { note "nothing to roll back"; return 0; }
  local name; name=$(tail -n 1 "$STATE_FILE")
  local f="$MIG_DIR/$name"
  [[ -f "$f" ]] || { echo "migration file not found: $f" >&2; exit 1; }
  say "DOWN $name"
  bash "$f" down
  sed -i '$d' "$STATE_FILE"
}

cmd_status() {
  say "migration status (history: $STATE_FILE)"
  local f name line rc drift=0
  for f in "$MIG_DIR"/*.sh; do
    name=$(basename "$f")
    if ! is_applied "$name"; then note "[pending] $name"; continue; fi
    rc=0; line=$(bash "$f" status 2>/dev/null) || rc=$?
    if [[ $rc -eq 0 ]]; then
      if [[ -n $line ]]; then note "[applied ✓] $name: $line"
      else note "[applied ?] $name: (no status impl)"; fi
    elif [[ $rc -eq 2 ]]; then
      note "[applied ?] $name: $line"
    else
      drift=1
      note "[applied ✗ DRIFT] $name: $line"
    fi
  done
  if (( drift )); then
    echo
    say "DRIFT detected - inspect above; repair manually or roll back/forward (down/up)"
  fi
  echo; say "memory / swap"
  free -h
  swapon --show 2>/dev/null || true
  exit $drift
}

usage() { sed -n '2,15p' "$0"; exit 1; }

case "${1:-}" in
  up)     cmd_up ;;
  down)   require_root
          if [[ "${2:-}" == "all" ]]; then
            while [[ -s "$STATE_FILE" ]]; do cmd_down_last; done
            note "full rollback complete"
          else
            cmd_down_last
          fi ;;
  status) cmd_status ;;
  *)      usage ;;
esac
