#!/usr/bin/env sh
set -eu

APP_VERSION="1.41.2"
ROOT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
MODE_FILE="$ROOT_DIR/.vendoo-install-mode"
ENV_FILE="$ROOT_DIR/.env"
ENV_EXAMPLE="$ROOT_DIR/.env.example"

info() { printf '\033[36m[INFO]\033[0m %s\n' "$*"; }
ok() { printf '\033[32m[OK]\033[0m %s\n' "$*"; }
warn() { printf '\033[33m[WARN]\033[0m %s\n' "$*"; }
err() { printf '\033[31m[ERROR]\033[0m %s\n' "$*" >&2; }

has_command() { command -v "$1" >/dev/null 2>&1; }

get_mode() {
  if [ -f "$MODE_FILE" ]; then tr -d '\r\n' < "$MODE_FILE"; else printf 'docker'; fi
}

set_mode() {
  printf '%s\n' "$1" > "$MODE_FILE"
  chmod 600 "$MODE_FILE" 2>/dev/null || true
}

compose_files() {
  case "$(get_mode)" in
    nas) printf '%s:%s' "$ROOT_DIR/compose.yaml" "$ROOT_DIR/compose.bind-mounts.example.yaml" ;;
    vps) printf '%s' "$ROOT_DIR/compose.vps.yaml" ;;
    *) printf '%s' "$ROOT_DIR/compose.yaml" ;;
  esac
}

compose() {
  COMPOSE_FILE=$(compose_files) docker compose "$@"
}

validate_env_value() {
  case "$2" in
    *'\n'*|*'\r'*) err "Ungültiger mehrzeiliger Wert für $1."; return 1 ;;
  esac
}

set_env() {
  key=$1
  value=$2
  validate_env_value "$key" "$value"
  tmp="$ENV_FILE.tmp.$$"
  if grep -q "^${key}=" "$ENV_FILE" 2>/dev/null; then
    awk -v k="$key" -v v="$value" 'BEGIN{FS="="} $1==k{print k"="v; next} {print}' "$ENV_FILE" > "$tmp"
  else
    cat "$ENV_FILE" > "$tmp"
    printf '\n%s=%s\n' "$key" "$value" >> "$tmp"
  fi
  chmod 600 "$tmp" 2>/dev/null || true
  mv "$tmp" "$ENV_FILE"
}

get_env() {
  key=$1
  sed -n "s/^${key}=//p" "$ENV_FILE" 2>/dev/null | tail -n 1
}

generate_token() {
  if has_command openssl; then openssl rand -hex 32
  elif [ -r /dev/urandom ]; then od -An -N32 -tx1 /dev/urandom | tr -d ' \n'
  else date +%s | sha256sum | awk '{print $1}'
  fi
}

check_docker() {
  if ! has_command docker; then
    err "Docker ist auf diesem Zielsystem nicht installiert."
    printf '%s\n' "Installiere Docker Engine und das Compose-Plugin nach der offiziellen Docker-Dokumentation und starte setup.sh danach erneut."
    return 1
  fi
  if ! docker compose version >/dev/null 2>&1; then
    err "Das Docker-Compose-Plugin fehlt."
    return 1
  fi
  if ! docker info >/dev/null 2>&1; then
    err "Docker ist installiert, aber der Dienst ist nicht erreichbar oder dein Benutzer hat keine Berechtigung."
    return 1
  fi
  ok "Docker und Docker Compose sind verfügbar."
}

ensure_env() {
  if [ ! -f "$ENV_FILE" ]; then
    cp "$ENV_EXAMPLE" "$ENV_FILE"
    chmod 600 "$ENV_FILE" 2>/dev/null || true
    ok ".env wurde automatisch aus der sicheren Vorlage erstellt."
  fi
  token=$(get_env VENDOO_SETUP_TOKEN)
  if [ -z "$token" ]; then
    token=$(generate_token)
    set_env VENDOO_SETUP_TOKEN "$token"
    ok "Ein zufälliger Erst-Admin-Setup-Code wurde erzeugt."
  fi
  set_env VENDOO_REQUIRE_SETUP_TOKEN true
}

read_default() {
  prompt=$1
  default=$2
  printf '%s [%s]: ' "$prompt" "$default" >&2
  read -r value
  if [ -z "$value" ]; then printf '%s' "$default"; else printf '%s' "$value"; fi
}

read_yes_no() {
  prompt=$1
  default=$2
  while :; do
    printf '%s [%s]: ' "$prompt" "$default"
    read -r answer
    [ -n "$answer" ] || answer=$default
    case "$answer" in
      j|J|ja|JA|y|Y|yes|YES) return 0 ;;
      n|N|nein|NEIN|no|NO) return 1 ;;
      *) warn "Bitte ja oder nein eingeben." ;;
    esac
  done
}

validate_port() {
  case "$1" in ''|*[!0-9]*) return 1 ;; esac
  [ "$1" -ge 1024 ] && [ "$1" -le 65535 ]
}

validate_domain() {
  case "$1" in
    http://*|https://*|*/*|*:*|'') return 1 ;;
    *.*) return 0 ;;
    *) return 1 ;;
  esac
}

configure_wizard() {
  ensure_env
  printf '\nVendoo %s – geführte Installation\n\n' "$APP_VERSION"
  printf '  1  Docker auf Linux/macOS/Docker Desktop (nur lokal erreichbar)\n'
  printf '  2  NAS / Portainer / privates LAN (sichtbare Runtime-Ordner)\n'
  printf '  3  VPS / öffentlicher Server mit automatischem HTTPS\n'
  printf '  0  Abbrechen\n\n'
  printf 'Installationsziel: '
  read -r choice
  case "$choice" in
    1) mode=docker ;;
    2) mode=nas ;;
    3) mode=vps ;;
    0) return 1 ;;
    *) err "Ungültige Auswahl."; return 1 ;;
  esac
  set_mode "$mode"

  current_port=$(get_env VENDOO_HOST_PORT)
  [ -n "$current_port" ] || current_port=8124
  port=$(read_default "Port" "$current_port")
  validate_port "$port" || { err "Port muss zwischen 1024 und 65535 liegen."; return 1; }
  set_env PORT "$port"
  set_env VENDOO_HOST_PORT "$port"
  set_env VENDOO_COOKIE_SAMESITE lax
  set_env VENDOO_ALLOW_ANY_EXTENSION_ORIGIN false

  case "$mode" in
    docker)
      set_env VENDOO_PUBLISH_ADDRESS 127.0.0.1
      set_env VENDOO_BIND_HOST 0.0.0.0
      set_env VENDOO_TRUST_PROXY false
      set_env VENDOO_COOKIE_SECURE auto
      set_env PUBLIC_BASE_URL ""
      ;;
    nas)
      mkdir -p "$ROOT_DIR/runtime/db" "$ROOT_DIR/runtime/uploads" "$ROOT_DIR/runtime/backups" "$ROOT_DIR/runtime/operations" "$ROOT_DIR/runtime/logs" "$ROOT_DIR/runtime/config" "$ROOT_DIR/runtime/modules"
      chmod -R u+rwX "$ROOT_DIR/runtime" 2>/dev/null || true
      set_env VENDOO_PUBLISH_ADDRESS 0.0.0.0
      set_env VENDOO_BIND_HOST 0.0.0.0
      set_env VENDOO_TRUST_PROXY false
      set_env VENDOO_COOKIE_SECURE auto
      printf 'LAN-Basisadresse für QR-Uploads, z. B. http://192.168.1.20:%s (optional): ' "$port"
      read -r public_url
      case "$public_url" in
        '') set_env PUBLIC_BASE_URL "" ;;
        http://*|https://*) set_env PUBLIC_BASE_URL "${public_url%/}" ;;
        *) err "Adresse muss mit http:// oder https:// beginnen."; return 1 ;;
      esac
      warn "NAS-Modus veröffentlicht Port $port im privaten LAN. Nicht direkt aus dem Internet freigeben."
      ;;
    vps)
      while :; do
        printf 'Öffentliche Domain ohne https://, z. B. vendoo.example.de: '
        read -r domain
        validate_domain "$domain" && break
        warn "Bitte einen vollständigen Domainnamen ohne Protokoll oder Pfad eingeben."
      done
      set_env VENDOO_DOMAIN "$domain"
      set_env PUBLIC_BASE_URL "https://$domain"
      set_env VENDOO_PUBLISH_ADDRESS 127.0.0.1
      set_env VENDOO_BIND_HOST 0.0.0.0
      set_env VENDOO_TRUST_PROXY true
      set_env VENDOO_COOKIE_SECURE true
      set_env VENDOO_ALLOWED_HOSTS "$domain"
      info "Caddy übernimmt HTTPS automatisch. DNS muss auf diesen Server zeigen; Ports 80 und 443 müssen erreichbar sein."
      ;;
  esac

  if read_yes_no "Lokales/externes FLUX aktivieren?" "nein"; then
    set_env LOCAL_IMAGE_ENABLED true
    default_flux=http://host.docker.internal:8188
    flux_url=$(read_default "FLUX/ComfyUI-Adresse" "$default_flux")
    case "$flux_url" in http://*|https://*) set_env LOCAL_IMAGE_URL "${flux_url%/}" ;; *) err "FLUX-Adresse muss mit http:// oder https:// beginnen."; return 1 ;; esac
  else
    set_env LOCAL_IMAGE_ENABLED false
  fi

  token=$(get_env VENDOO_SETUP_TOKEN)
  printf '\nERST-ADMIN-SETUP-CODE:\n%s\n\n' "$token"
  warn "Code bis zur ersten Anmeldung sicher aufbewahren. AI- und Marktplatz-Schlüssel werden später in Vendoo → Einstellungen gepflegt."
  ok "Konfiguration gespeichert; keine Datei musste manuell bearbeitet werden."
}

wait_ready() {
  case "$(get_mode)" in
    vps) url="https://$(get_env VENDOO_DOMAIN)/readyz" ;;
    *) url="http://127.0.0.1:$(get_env VENDOO_HOST_PORT)/readyz" ;;
  esac
  if ! has_command curl; then warn "curl fehlt; Readiness später im Browser prüfen: $url"; return 0; fi
  i=0
  while [ "$i" -lt 30 ]; do
    if curl -fsS --max-time 5 "$url" >/dev/null 2>&1; then ok "Vendoo ist bereit: $url"; return 0; fi
    i=$((i+1)); sleep 2
  done
  warn "Readiness war noch nicht erfolgreich. Prüfe: compose logs --tail=200"
  return 0
}

install_or_update() {
  check_docker
  configure_wizard || return 0
  info "Prüfe Compose-Konfiguration …"
  compose config >/dev/null
  ok "Compose-Konfiguration ist gültig."
  info "Baue und starte Vendoo …"
  compose build --pull
  compose up -d --remove-orphans
  compose ps
  wait_ready
}

start_app() { check_docker; compose up -d; compose ps; wait_ready; }
stop_app() { check_docker; compose down; }
update_app() {
  check_docker
  ensure_env
  info "Erstelle vor dem Update automatisch ein Backup …"
  backup_app
  compose config >/dev/null
  compose build --pull
  compose up -d --remove-orphans
  compose ps
  wait_ready
}
status_app() {
  check_docker
  compose ps
  printf '\nReadiness:\n'
  case "$(get_mode)" in vps) url="https://$(get_env VENDOO_DOMAIN)/readyz" ;; *) url="http://127.0.0.1:$(get_env VENDOO_HOST_PORT)/readyz" ;; esac
  if has_command curl; then curl -fsS "$url" || true; printf '\n'; else warn "curl fehlt; Readiness bitte im Browser prüfen: $url"; fi
}
backup_app() {
  check_docker
  COMPOSE_FILE=$(compose_files) "$ROOT_DIR/scripts/docker-backup.sh"
}
restore_app() {
  printf 'Pfad zum Backup-TAR.GZ: '
  read -r backup
  [ -f "$backup" ] || { err "Backup nicht gefunden."; return 1; }
  check_docker
  COMPOSE_FILE=$(compose_files) "$ROOT_DIR/scripts/docker-restore.sh" "$backup" --confirm
  wait_ready
}
show_setup_code() {
  ensure_env
  printf '\nErst-Admin-Setup-Code:\n%s\n\n' "$(get_env VENDOO_SETUP_TOKEN)"
}

menu() {
  while :; do
    printf '\nVendoo %s – Zero-Edit Deployment Assistant\n\n' "$APP_VERSION"
    printf '  1  Installieren / Konfiguration ändern\n'
    printf '  2  Starten\n'
    printf '  3  Stoppen\n'
    printf '  4  Status und Readiness\n'
    printf '  5  Aktualisieren (mit Sicherheitsbackup)\n'
    printf '  6  Backup erstellen\n'
    printf '  7  Backup wiederherstellen\n'
    printf '  8  Erst-Admin-Code anzeigen\n'
    printf '  0  Beenden\n\n'
    printf 'Auswahl: '
    read -r choice
    case "$choice" in
      1) install_or_update ;;
      2) start_app ;;
      3) stop_app ;;
      4) status_app ;;
      5) update_app ;;
      6) backup_app ;;
      7) restore_app ;;
      8) show_setup_code ;;
      0) exit 0 ;;
      *) warn "Ungültige Auswahl." ;;
    esac
  done
}

case "${1:-menu}" in
  install|configure) install_or_update ;;
  start) start_app ;;
  stop) stop_app ;;
  status) status_app ;;
  update) update_app ;;
  backup) backup_app ;;
  restore) restore_app ;;
  setup-code) show_setup_code ;;
  menu) menu ;;
  *) err "Unbekannter Befehl: $1"; exit 2 ;;
esac
