#!/usr/bin/env bash
#
# aria-vm — ARIAs QEMU-VM-Verwaltung fuer alle Architekturen (laeuft auf dem
# Host). ARIA ruft das per SSH (aria-wohnung) ueber den qemu-vm-Skill auf.
#
# Unterkommandos:
#   aria-vm create  <name> <arch> [size]     Disk anlegen (qcow2)
#   aria-vm boot    <name> [optionen]        VM starten (VNC 127.0.0.1:<display>)
#   aria-vm screenshot <name>                PNG-Screenshot → Shared-Uploads
#   aria-vm list                             laufende/vorhandene VMs
#   aria-vm stop    <name>                   VM beenden
#   aria-vm rm      <name>                   VM + Disk loeschen
#
# boot-Optionen:
#   --iso <pfad>        Boot-ISO (setzt Boot-Reihenfolge auf CD)
#   --disk-boot         von der Festplatte booten (Default nach Installation)
#   --vnc-display <N>   VNC-Display (Port = 5900+N, Default 1)
#   --mem <MB>          RAM (Default 1024)
#   --machine <typ>     QEMU-Maschine ueberschreiben
#
# VNC bindet immer nur an 127.0.0.1 — von aussen erreichbar ausschliesslich
# ueber den RVS-Tunnel der Bridge (host.docker.internal:<port>).
set -euo pipefail

VM_ROOT="${ARIA_VM_ROOT:-/var/lib/aria-vms}"
# Wohin Screenshots geschrieben werden — Host-Pfad des /shared-Volumes, damit
# Bridge/App sie sehen. Ueberschreibbar via ARIA_VM_SHOT_DIR.
SHOT_DIR="${ARIA_VM_SHOT_DIR:-/root/ARIA-AGENT/aria-shared/uploads}"

die() { echo "aria-vm: $*" >&2; exit 1; }

qemu_bin_for() {
  case "$1" in
    x86_64|amd64)   echo qemu-system-x86_64 ;;
    i386|i686|x86)  echo qemu-system-i386 ;;
    arm|armv7)      echo qemu-system-arm ;;
    aarch64|arm64)  echo qemu-system-aarch64 ;;
    mips)           echo qemu-system-mips ;;
    mipsel)         echo qemu-system-mipsel ;;
    mips64)         echo qemu-system-mips64 ;;
    ppc)            echo qemu-system-ppc ;;
    ppc64)          echo qemu-system-ppc64 ;;
    riscv64)        echo qemu-system-riscv64 ;;
    sparc)          echo qemu-system-sparc ;;
    *)              echo "" ;;
  esac
}

vm_dir()  { echo "${VM_ROOT}/$1"; }
vm_pid()  { local d; d="$(vm_dir "$1")"; [[ -f "${d}/pid" ]] && cat "${d}/pid" || echo ""; }
vm_running() {
  local p; p="$(vm_pid "$1")"
  [[ -n "${p}" ]] && kill -0 "${p}" 2>/dev/null
}

cmd_create() {
  local name="${1:?name}" arch="${2:?arch}" size="${3:-10G}"
  local bin; bin="$(qemu_bin_for "${arch}")"
  [[ -n "${bin}" ]] || die "unbekannte Architektur: ${arch}"
  command -v "${bin}" >/dev/null || die "${bin} nicht installiert (qemu-setup.sh?)"
  local d; d="$(vm_dir "${name}")"
  [[ -f "${d}/arch" ]] && die "VM '${name}' existiert schon"
  mkdir -p "${d}"
  echo "${arch}" > "${d}/arch"
  # size='none' oder '0' → keine Festplatte (VM bootet von --iso/--floppy,
  # z.B. OS-Entwicklung von Diskette). Sonst eine qcow2-Disk anlegen.
  if [[ "${size}" == "none" || "${size}" == "0" ]]; then
    echo "VM '${name}' angelegt (${arch}, ohne Disk — bootet von ISO/Diskette)."
  else
    qemu-img create -f qcow2 "${d}/disk.qcow2" "${size}" >/dev/null
    echo "VM '${name}' angelegt (${arch}, ${size})."
  fi
}

cmd_boot() {
  local name="${1:?name}"; shift || true
  local d; d="$(vm_dir "${name}")"
  [[ -f "${d}/arch" || -f "${d}/disk.qcow2" ]] || die "VM '${name}' nicht gefunden (erst 'create')"
  vm_running "${name}" && die "VM '${name}' laeuft bereits"
  local arch; arch="$(cat "${d}/arch" 2>/dev/null || echo x86_64)"
  local bin; bin="$(qemu_bin_for "${arch}")"

  local iso="" floppy="" disk="" bootdev="" display=1 mem=1024 machine=""
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --iso)         iso="${2:?}"; shift 2 ;;
      --floppy)      floppy="${2:?}"; shift 2 ;;   # -fda (Disketten-Boot, OS-Dev)
      --disk)        disk="${2:?}"; shift 2 ;;      # explizite qcow2 statt Auto
      --boot)        bootdev="${2:?}"; shift 2 ;;   # Boot-Reihenfolge (a/c/d)
      --disk-boot)   bootdev="c"; shift ;;
      --vnc-display) display="${2:?}"; shift 2 ;;
      --mem)         mem="${2:?}"; shift 2 ;;
      --machine)     machine="${2:?}"; shift 2 ;;
      *) die "unbekannte Option: $1" ;;
    esac
  done

  # Auto-Erkennung der Medien im VM-Ordner, falls nicht explizit angegeben.
  [[ -z "${disk}"   && -f "${d}/disk.qcow2" ]] && disk="${d}/disk.qcow2"
  [[ -z "${floppy}" && -f "${d}/floppy.img" ]] && floppy="${d}/floppy.img"
  [[ -z "${iso}"    && -f "${d}/cdrom.iso"  ]] && iso="${d}/cdrom.iso"
  [[ -n "${disk}${floppy}${iso}" ]] || \
    die "Keine Boot-Medien fuer '${name}' (disk.qcow2 / --iso / --floppy). Erst 'create <name> <arch> <groesse>' oder ein Medium angeben."

  local args=(-name "${name}" -m "${mem}"
    -vnc "127.0.0.1:${display}"
    -monitor "unix:${d}/monitor.sock,server,nowait"
    -pidfile "${d}/pid" -daemonize)
  [[ -n "${disk}" ]]   && args+=(-drive "file=${disk},format=qcow2")
  [[ -n "${floppy}" ]] && args+=(-fda "${floppy}")
  [[ -n "${iso}" ]]    && args+=(-cdrom "${iso}")

  # KVM nur fuer x86 auf x86-Host.
  case "${arch}" in
    x86_64|amd64|i386|i686|x86)
      [[ -e /dev/kvm ]] && args+=(-enable-kvm) ;;
  esac
  # ARM/AArch64 brauchen eine Maschine (kein Default).
  if [[ -z "${machine}" ]]; then
    case "${arch}" in
      arm|armv7|aarch64|arm64) machine="virt" ;;
    esac
  fi
  [[ -n "${machine}" ]] && args+=(-M "${machine}")

  # Boot-Reihenfolge: explizit, sonst automatisch (ISO→d, nur Diskette→a, sonst c).
  if [[ -z "${bootdev}" ]]; then
    if [[ -n "${iso}" ]]; then bootdev="d"
    elif [[ -n "${floppy}" && -z "${disk}" ]]; then bootdev="a"
    else bootdev="c"; fi
  fi
  args+=(-boot "${bootdev}")

  "${bin}" "${args[@]}"
  echo "VM '${name}' gestartet (${arch}) — VNC 127.0.0.1:${display} (Port $((5900+display)))."
  echo "vnc_display=${display} vnc_port=$((5900+display))"
}

cmd_screenshot() {
  local name="${1:?name}"
  local d; d="$(vm_dir "${name}")"
  vm_running "${name}" || die "VM '${name}' laeuft nicht"
  command -v socat >/dev/null || die "socat fehlt (qemu-setup.sh?)"
  mkdir -p "${SHOT_DIR}"
  local ts; ts="$(date +%s)"
  local ppm="${d}/shot-${ts}.ppm"
  printf 'screendump %s\n' "${ppm}" | socat - "unix-connect:${d}/monitor.sock" >/dev/null
  sleep 0.3
  local out="${SHOT_DIR}/${name}-${ts}.png"
  if command -v convert >/dev/null; then
    convert "${ppm}" "${out}" && rm -f "${ppm}"
  else
    out="${SHOT_DIR}/${name}-${ts}.ppm"; mv "${ppm}" "${out}"
  fi
  echo "screenshot=${out}"
}

cmd_list() {
  [[ -d "${VM_ROOT}" ]] || { echo "(keine VMs)"; return; }
  local any=0
  for d in "${VM_ROOT}"/*/; do
    [[ -d "${d}" ]] || continue
    any=1
    local name arch state
    name="$(basename "${d}")"
    arch="$(cat "${d}/arch" 2>/dev/null || echo '?')"
    if vm_running "${name}"; then state="laeuft (pid $(vm_pid "${name}"))"; else state="gestoppt"; fi
    echo "${name}  [${arch}]  ${state}"
  done
  [[ "${any}" -eq 1 ]] || echo "(keine VMs)"
}

cmd_stop() {
  local name="${1:?name}"
  local d; d="$(vm_dir "${name}")"
  if vm_running "${name}"; then
    printf 'quit\n' | socat - "unix-connect:${d}/monitor.sock" >/dev/null 2>&1 || true
    sleep 0.5
    vm_running "${name}" && kill "$(vm_pid "${name}")" 2>/dev/null || true
    echo "VM '${name}' gestoppt."
  else
    echo "VM '${name}' lief nicht."
  fi
  rm -f "${d}/pid" "${d}/monitor.sock"
}

cmd_rm() {
  local name="${1:?name}"
  vm_running "${name}" && cmd_stop "${name}"
  rm -rf "$(vm_dir "${name}")"
  echo "VM '${name}' geloescht."
}

main() {
  local sub="${1:-}"; shift || true
  case "${sub}" in
    create)     cmd_create "$@" ;;
    boot)       cmd_boot "$@" ;;
    screenshot) cmd_screenshot "$@" ;;
    list)       cmd_list "$@" ;;
    stop)       cmd_stop "$@" ;;
    rm)         cmd_rm "$@" ;;
    ""|-h|--help)
      sed -n '2,40p' "$0" | sed 's/^# \{0,1\}//' ;;
    *) die "unbekanntes Kommando: ${sub} (siehe --help)" ;;
  esac
}

main "$@"
