#!/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}")"
  [[ -e "${d}/disk.qcow2" ]] && die "VM '${name}' existiert schon"
  mkdir -p "${d}"
  echo "${arch}" > "${d}/arch"
  qemu-img create -f qcow2 "${d}/disk.qcow2" "${size}" >/dev/null
  echo "VM '${name}' angelegt (${arch}, ${size})."
}

cmd_boot() {
  local name="${1:?name}"; shift || true
  local d; d="$(vm_dir "${name}")"
  [[ -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="" bootdev="c" display=1 mem=1024 machine=""
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --iso)         iso="${2:?}"; bootdev="d"; shift 2 ;;
      --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

  local args=(-name "${name}" -m "${mem}"
    -drive "file=${d}/disk.qcow2,format=qcow2"
    -vnc "127.0.0.1:${display}"
    -monitor "unix:${d}/monitor.sock,server,nowait"
    -pidfile "${d}/pid" -daemonize)

  # 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}")
  [[ -n "${iso}" ]] && args+=(-cdrom "${iso}")
  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 "$@"
