#!/bin/sh
set -eu

SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
DEFAULT_HOME="${HOME:-/tmp}"
RUNTIME_HOME="${LICENSESYS_HOME:-$DEFAULT_HOME/.license-nodejs-commercial}"
ENV_FILE="$RUNTIME_HOME/.env"
PACKAGE_FILE="$RUNTIME_HOME/package.json"
SERVER_ENTRY="$RUNTIME_HOME/server.js"
CLI_ENTRY="$RUNTIME_HOME/cli.js"
CONFIG_ENTRY="$RUNTIME_HOME/config.js"
NODE_MODULES_DIR="$RUNTIME_HOME/node_modules"
SETUP_ARTIFACTS_DIR="$RUNTIME_HOME/setup"

DEFAULT_PORT="25504"
DEFAULT_OWNER_NAME="Your Company"
DEFAULT_OWNER_EMAIL="support@example.com"
DEFAULT_OWNER_WEBSITE="https://example.com"
DEFAULT_ALLOWED_ORIGINS="https://plugindocs.nextsmp.net,http://127.0.0.1:4173,http://localhost:4173"
DEFAULT_KEYS_FOLDER="keys"
DEFAULT_BACKUP_FOLDER="backups"
DEFAULT_ASSET_FOLDER="assets"
DEFAULT_LOG_FILE="license-server.log"
DEFAULT_COMMERCIAL_MODE="remote"
DEFAULT_COMMERCIAL_ASSET_BASE_URL="https://plugindocs.nextsmp.net/assets/license-system"
DEFAULT_COMMERCIAL_DOCS_URL="https://plugindocs.nextsmp.net/license-system.html"
DEFAULT_DEPLOYMENT_TARGET="linux-systemd"
DEFAULT_SERVICE_NAME="licenseserver"
DEFAULT_DOCKER_IMAGE_NAME="license-nodejs-commercial"
DEFAULT_DOCKER_CONTAINER_NAME="licenseserver"
DEFAULT_PTERODACTYL_START_COMMAND="node ./server.js"
SETUP_TARGET_OVERRIDE=""

NODE_COMMAND=""
NPM_COMMAND=""

print_line() {
  printf '%s\n' "$1"
}

print_blank() {
  printf '\n'
}

print_line_err() {
  printf '%s\n' "$1" >&2
}

print_blank_err() {
  printf '\n' >&2
}

print_header() {
  print_blank
  print_line "========================================"
  print_line " LicenseNodeJS Single File Manager"
  print_line "========================================"
  print_blank
}

print_info() {
  printf '[INFO] %s\n' "$1"
}

print_warn() {
  printf '[WARN] %s\n' "$1"
}

print_error() {
  printf '[ERROR] %s\n' "$1" >&2
}

resolve_commands() {
  if command -v node >/dev/null 2>&1; then
    NODE_COMMAND="node"
  else
    print_error "node wurde nicht gefunden. Bitte zuerst Node.js installieren."
    exit 1
  fi

  if command -v npm >/dev/null 2>&1; then
    NPM_COMMAND="npm"
  elif command -v npm.cmd >/dev/null 2>&1; then
    NPM_COMMAND="npm.cmd"
  else
    print_error "npm wurde nicht gefunden. Bitte zuerst npm installieren."
    exit 1
  fi
}

read_kv_value() {
  key="$1"
  file="$2"

  if [ ! -f "$file" ]; then
    return 1
  fi

  value=$(grep -E "^${key}=" "$file" 2>/dev/null | tail -n 1 | cut -d= -f2- || true)
  if [ -z "$value" ]; then
    return 1
  fi

  printf '%s' "$value"
}

get_default_value() {
  key="$1"
  fallback="$2"
  value=""

  if value=$(read_kv_value "$key" "$ENV_FILE"); then
    printf '%s' "$value"
    return 0
  fi

  printf '%s' "$fallback"
}

prompt_value() {
  label="$1"
  default_value="$2"
  entered_value=""

  if [ -n "$default_value" ]; then
    printf '%s [%s]: ' "$label" "$default_value" >&2
  else
    printf '%s: ' "$label" >&2
  fi

  IFS= read -r entered_value || true
  if [ -z "$entered_value" ]; then
    entered_value="$default_value"
  fi

  printf '%s' "$entered_value"
}

prompt_yes_no() {
  label="$1"
  default_answer="$2"
  answer=""

  while :; do
    if [ "$default_answer" = "y" ]; then
      printf '%s [Y/n]: ' "$label" >&2
    else
      printf '%s [y/N]: ' "$label" >&2
    fi

    answer=""
    IFS= read -r answer || true
    answer=$(printf '%s' "$answer" | tr '[:upper:]' '[:lower:]')

    if [ -z "$answer" ]; then
      answer="$default_answer"
    fi

    case "$answer" in
      y|yes)
        return 0
        ;;
      n|no)
        return 1
        ;;
    esac

    print_warn "Bitte nur y oder n eingeben."
  done
}

default_service_user() {
  if command -v id >/dev/null 2>&1; then
    id -un 2>/dev/null && return 0
  fi

  if [ -n "${USER:-}" ]; then
    printf '%s' "$USER"
    return 0
  fi

  if [ -n "${USERNAME:-}" ]; then
    printf '%s' "$USERNAME"
    return 0
  fi

  printf '%s' "licenseserver"
}

prompt_secret() {
  label="$1"
  existing_value="$2"
  secret_value=""

  if [ -n "$existing_value" ]; then
    printf '%s [leer = bestehenden behalten]: ' "$label" >&2
  else
    printf '%s [leer = automatisch generieren]: ' "$label" >&2
  fi

  if command -v stty >/dev/null 2>&1 && [ -t 0 ]; then
    stty -echo
    IFS= read -r secret_value || true
    stty echo
    printf '\n' >&2
  else
    IFS= read -r secret_value || true
  fi

  if [ -n "$secret_value" ]; then
    printf '%s' "$secret_value"
    return 0
  fi

  if [ -n "$existing_value" ]; then
    printf '%s' "$existing_value"
    return 0
  fi

  generate_secret
}

generate_secret() {
  if command -v openssl >/dev/null 2>&1; then
    openssl rand -hex 32 | cut -c1-52
    return 0
  fi

  if [ -r /dev/urandom ]; then
    tr -dc 'A-Za-z0-9' </dev/urandom | head -c 52
    printf '\n'
    return 0
  fi

  date +%s | tr -d '\n'
  printf '%s' "$$"
}

trim_trailing_slashes() {
  printf '%s' "$1" | sed 's:/*$::'
}

normalize_path_value() {
  printf '%s' "$1" | sed 's:\\:/:g'
}

convert_path_for_node() {
  target_path="$1"

  if command -v cygpath >/dev/null 2>&1; then
    cygpath -w "$target_path" 2>/dev/null && return 0
  fi

  case "$target_path" in
    /[A-Za-z]/*)
      printf '%s' "$target_path" | sed -E 's#^/([A-Za-z])/#\1:/#'
      ;;
    *)
      printf '%s' "$target_path"
      ;;
  esac
}

sanitize_machine_name() {
  value=$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9._-]/-/g; s/--*/-/g; s/^-//; s/-$//')
  if [ -n "$value" ]; then
    printf '%s' "$value"
  else
    printf '%s' "licenseserver"
  fi
}

prompt_deployment_target() {
  current_value="$1"
  default_choice="2"
  default_target="linux-systemd"

  case "$current_value" in
    linux|normal)
      default_choice="1"
      default_target="linux"
      ;;
    linux-systemd|systemd)
      default_choice="2"
      default_target="linux-systemd"
      ;;
    docker)
      default_choice="3"
      default_target="docker"
      ;;
    pterodactyl)
      default_choice="4"
      default_target="pterodactyl"
      ;;
    windows)
      default_choice="5"
      default_target="windows"
      ;;
    macos)
      default_choice="6"
      default_target="macos"
      ;;
  esac

  print_blank_err
  print_line_err "Ziel-Umgebung:"
  print_line_err "  1. Linux / normal"
  print_line_err "  2. Linux + systemd"
  print_line_err "  3. Docker"
  print_line_err "  4. Pterodactyl"
  print_line_err "  5. Windows"
  print_line_err "  6. macOS"
  print_blank_err

  default_label=$(deployment_label "$default_target")
  printf 'Umgebung wählen [1=Linux, 2=Linux+systemd, 3=Docker, 4=Pterodactyl, 5=Windows, 6=macOS | Standard: %s = %s]: ' "$default_choice" "$default_label" >&2
  choice=""
  IFS= read -r choice || true
  if [ -z "$choice" ]; then
    choice="$default_choice"
  fi

  case "$choice" in
    1|linux|normal)
      printf '%s' "linux"
      ;;
    2|linux-systemd|systemd)
      printf '%s' "linux-systemd"
      ;;
    3|docker)
      printf '%s' "docker"
      ;;
    4|pterodactyl)
      printf '%s' "pterodactyl"
      ;;
    5|windows|win)
      printf '%s' "windows"
      ;;
    6|macos|mac|osx)
      printf '%s' "macos"
      ;;
    *)
      print_warn "Unbekannte Umgebung: $choice. Es wird '$current_value' verwendet."
      if [ -n "$current_value" ]; then
        printf '%s' "$current_value"
      else
        printf '%s' "$DEFAULT_DEPLOYMENT_TARGET"
      fi
      ;;
  esac
}

deployment_label() {
  case "$1" in
    linux)
      printf '%s' "Linux / normal"
      ;;
    linux-systemd)
      printf '%s' "Linux + systemd"
      ;;
    docker)
      printf '%s' "Docker"
      ;;
    pterodactyl)
      printf '%s' "Pterodactyl"
      ;;
    windows)
      printf '%s' "Windows"
      ;;
    macos)
      printf '%s' "macOS"
      ;;
    *)
      printf '%s' "$1"
      ;;
  esac
}

build_remote_url() {
  base_url=$(trim_trailing_slashes "$1")
  asset_path=$(printf '%s' "$2" | sed 's:^/*::')
  printf '%s/%s' "$base_url" "$asset_path"
}

resolve_runtime_path() {
  input_path=$(normalize_path_value "$1")
  case "$input_path" in
    /*)
      printf '%s' "$input_path"
      ;;
    *)
      printf '%s/%s' "$RUNTIME_HOME" "$input_path"
      ;;
  esac
}

ensure_parent_dir() {
  target_path="$1"
  mkdir -p "$(dirname "$target_path")"
}

write_text_file() {
  target_path="$1"
  ensure_parent_dir "$target_path"
  cat > "$target_path"
}

write_executable_file() {
  target_path="$1"
  ensure_parent_dir "$target_path"
  cat > "$target_path"
  chmod +x "$target_path" 2>/dev/null || true
}

write_b64_file() {
  target_path="$1"
  ensure_parent_dir "$target_path"
  tmp_b64_file="$target_path.b64tmp.$$"
  cat > "$tmp_b64_file"

  if base64 --decode < "$tmp_b64_file" > "$target_path" 2>/dev/null; then
    :
  elif base64 -d < "$tmp_b64_file" > "$target_path" 2>/dev/null; then
    :
  elif base64 -D < "$tmp_b64_file" > "$target_path" 2>/dev/null; then
    :
  elif command -v openssl >/dev/null 2>&1 && openssl base64 -d -A -in "$tmp_b64_file" -out "$target_path" 2>/dev/null; then
    :
  else
    node_target_path=$(convert_path_for_node "$target_path")
    node_tmp_path=$(convert_path_for_node "$tmp_b64_file")
    "$NODE_COMMAND" -e "const fs=require('fs');const data=fs.readFileSync(process.argv[1],'utf8');fs.writeFileSync(process.argv[2],Buffer.from(data.replace(/\\s+/g,''),'base64'));" "$node_tmp_path" "$node_target_path"
  fi

  rm -f "$tmp_b64_file"
}

write_runtime_package() {
  write_text_file "$PACKAGE_FILE" <<'EOF_PACKAGE'
{
  "name": "license-nodejs-commercial-runtime",
  "version": "3.1.0",
  "private": true,
  "main": "server.js",
  "dependencies": {
    "express": "^4.18.2",
    "express-rate-limit": "^7.1.5",
    "yaml": "^2.3.4"
  }
}
EOF_PACKAGE
}

write_runtime_sources() {
  write_runtime_package

  write_b64_file "$CONFIG_ENTRY" <<'EOF_CONFIG'
[payload removed: 9316 characters]
EOF_CONFIG

  write_b64_file "$SERVER_ENTRY" <<'EOF_SERVER'
[payload removed: 63852 characters]
EOF_SERVER

  write_b64_file "$CLI_ENTRY" <<'EOF_CLI'
[payload removed: 56748 characters]
EOF_CLI
}

load_runtime_config() {
  PORT=$(get_default_value "PORT" "$DEFAULT_PORT")
  API_URL=$(get_default_value "API_URL" "http://127.0.0.1:$PORT")
  ADMIN_TOKEN=$(get_default_value "ADMIN_TOKEN" "")
  OWNER_IP=$(get_default_value "OWNER_IP" "")
  OWNER_NAME=$(get_default_value "OWNER_NAME" "$DEFAULT_OWNER_NAME")
  OWNER_DISCORD=$(get_default_value "OWNER_DISCORD" "")
  OWNER_EMAIL=$(get_default_value "OWNER_EMAIL" "$DEFAULT_OWNER_EMAIL")
  OWNER_WEBSITE=$(get_default_value "OWNER_WEBSITE" "$DEFAULT_OWNER_WEBSITE")
  ALLOWED_ORIGINS=$(get_default_value "ALLOWED_ORIGINS" "$DEFAULT_ALLOWED_ORIGINS")
  KEYS_FOLDER=$(normalize_path_value "$(get_default_value "KEYS_FOLDER" "$DEFAULT_KEYS_FOLDER")")
  BACKUP_FOLDER=$(normalize_path_value "$(get_default_value "BACKUP_FOLDER" "$DEFAULT_BACKUP_FOLDER")")
  ASSET_FOLDER=$(normalize_path_value "$(get_default_value "ASSET_FOLDER" "$DEFAULT_ASSET_FOLDER")")
  LOG_FILE=$(normalize_path_value "$(get_default_value "LOG_FILE" "$DEFAULT_LOG_FILE")")
  WEBHOOK_URL=$(get_default_value "WEBHOOK_URL" "")
  COMMERCIAL_MODE=$(get_default_value "COMMERCIAL_MODE" "$DEFAULT_COMMERCIAL_MODE")
  COMMERCIAL_ASSET_BASE_URL=$(get_default_value "COMMERCIAL_ASSET_BASE_URL" "$DEFAULT_COMMERCIAL_ASSET_BASE_URL")
  COMMERCIAL_DOCS_URL=$(get_default_value "COMMERCIAL_DOCS_URL" "$DEFAULT_COMMERCIAL_DOCS_URL")
  DEPLOYMENT_TARGET=$(get_default_value "DEPLOYMENT_TARGET" "$DEFAULT_DEPLOYMENT_TARGET")
  SERVICE_NAME=$(sanitize_machine_name "$(get_default_value "SERVICE_NAME" "$DEFAULT_SERVICE_NAME")")
  SERVICE_USER=$(get_default_value "SERVICE_USER" "$(default_service_user)")
  DOCKER_IMAGE_NAME=$(sanitize_machine_name "$(get_default_value "DOCKER_IMAGE_NAME" "$DEFAULT_DOCKER_IMAGE_NAME")")
  DOCKER_CONTAINER_NAME=$(sanitize_machine_name "$(get_default_value "DOCKER_CONTAINER_NAME" "$DEFAULT_DOCKER_CONTAINER_NAME")")
  PTERODACTYL_START_COMMAND=$(get_default_value "PTERODACTYL_START_COMMAND" "$DEFAULT_PTERODACTYL_START_COMMAND")
}

write_setup_markdown() {
  write_text_file "$SETUP_ARTIFACTS_DIR/LICENSE-SYSTEM-SETUP.md" <<EOF_SETUP_MD
# LicenseNodeJS Commercial Setup

- Runtime: \`$RUNTIME_HOME\`
- API URL: \`$API_URL\`
- Deployment Target: \`$(deployment_label "$DEPLOYMENT_TARGET")\`
- Keys: \`$(resolve_runtime_path "$KEYS_FOLDER")\`
- Backups: \`$(resolve_runtime_path "$BACKUP_FOLDER")\`
- Assets: \`$(resolve_runtime_path "$ASSET_FOLDER")\`
- Log: \`$(resolve_runtime_path "$LOG_FILE")\`

## Start

### Linux / macOS

\`\`\`sh
sh ./start-licensesys.sh setup
sh ./start-licensesys.sh start
\`\`\`

### Windows

Nutze \`setup/windows/start-licenseserver.bat\` oder den Website-Download \`start-licensesys.bat\`.

## Generierte Dateien

- Linux: \`$SETUP_ARTIFACTS_DIR/linux/start-licenseserver.sh\`
- Linux Stop: \`$SETUP_ARTIFACTS_DIR/linux/stop-licenseserver.sh\`
- systemd: \`$SETUP_ARTIFACTS_DIR/linux/$SERVICE_NAME.service\`
- macOS: \`$SETUP_ARTIFACTS_DIR/macos/start-licenseserver.command\`
- Windows: \`$SETUP_ARTIFACTS_DIR/windows/start-licenseserver.bat\`
- Docker: \`$SETUP_ARTIFACTS_DIR/docker/docker-compose.yml\`
- Pterodactyl: \`$SETUP_ARTIFACTS_DIR/pterodactyl/start-command.txt\`

## Ziel-Umgebung

Die aktuell konfigurierte Umgebung ist **$(deployment_label "$DEPLOYMENT_TARGET")**.

- Bei \`linux-systemd\` kannst du danach \`sh ./start-licensesys.sh systemd-install\` nutzen.
- Bei \`docker\` nutzt du die Dateien aus \`setup/docker\`.
- Bei \`pterodactyl\` nutzt du den Startbefehl aus \`setup/pterodactyl/start-command.txt\`.

## Remote

- Installer: \`$(build_remote_url "$COMMERCIAL_ASSET_BASE_URL" "start-licensesys.sh")\`
- Windows Wrapper: \`$(build_remote_url "$COMMERCIAL_ASSET_BASE_URL" "start-licensesys.bat")\`
- macOS Wrapper: \`$(build_remote_url "$COMMERCIAL_ASSET_BASE_URL" "start-licensesys.command")\`
- Docs: \`$COMMERCIAL_DOCS_URL\`
EOF_SETUP_MD
}

write_linux_launchers() {
  linux_dir="$SETUP_ARTIFACTS_DIR/linux"
  pid_file="$RUNTIME_HOME/license-server.pid"

  write_executable_file "$linux_dir/start-licenseserver.sh" <<EOF_LINUX_START
#!/bin/sh
set -eu
cd "$RUNTIME_HOME"
exec node ./server.js
EOF_LINUX_START

  write_executable_file "$linux_dir/start-licenseserver-bg.sh" <<EOF_LINUX_BG
#!/bin/sh
set -eu
PID_FILE="$pid_file"
cd "$RUNTIME_HOME"
if [ -f "\$PID_FILE" ] && kill -0 "\$(cat "\$PID_FILE")" 2>/dev/null; then
  echo "License Server läuft bereits mit PID \$(cat "\$PID_FILE")."
  exit 0
fi
nohup node ./server.js >> "$RUNTIME_HOME/license-server.out.log" 2>&1 &
echo \$! > "\$PID_FILE"
echo "License Server gestartet. PID=\$(cat "\$PID_FILE")"
EOF_LINUX_BG

  write_executable_file "$linux_dir/stop-licenseserver.sh" <<EOF_LINUX_STOP
#!/bin/sh
set -eu
PID_FILE="$pid_file"
if [ ! -f "\$PID_FILE" ]; then
  echo "Keine PID-Datei gefunden: \$PID_FILE"
  exit 0
fi
PID="\$(cat "\$PID_FILE")"
if kill -0 "\$PID" 2>/dev/null; then
  kill "\$PID"
  echo "License Server gestoppt: PID=\$PID"
else
  echo "Prozess aus PID-Datei lief nicht mehr."
fi
rm -f "\$PID_FILE"
EOF_LINUX_STOP

  write_text_file "$linux_dir/$SERVICE_NAME.service" <<EOF_SYSTEMD
[Unit]
Description=LicenseNodeJS Commercial License Server
After=network.target

[Service]
Type=simple
User=$SERVICE_USER
WorkingDirectory=$RUNTIME_HOME
Environment=LICENSESYS_HOME=$RUNTIME_HOME
ExecStart=/usr/bin/env node ./server.js
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF_SYSTEMD
}

write_macos_launchers() {
  macos_dir="$SETUP_ARTIFACTS_DIR/macos"

  write_executable_file "$macos_dir/start-licenseserver.command" <<EOF_MAC_START
#!/bin/sh
set -eu
cd "$RUNTIME_HOME"
exec node ./server.js
EOF_MAC_START

  write_executable_file "$macos_dir/cli-licenseserver.command" <<EOF_MAC_CLI
#!/bin/sh
set -eu
cd "$RUNTIME_HOME"
exec node ./cli.js
EOF_MAC_CLI
}

write_windows_launchers() {
  windows_dir="$SETUP_ARTIFACTS_DIR/windows"

  write_text_file "$windows_dir/start-licenseserver.bat" <<EOF_WIN_START
@echo off
setlocal EnableExtensions
set "RUNTIME_HOME=$RUNTIME_HOME"
if not exist "%RUNTIME_HOME%\\server.js" (
  echo Runtime nicht gefunden: %RUNTIME_HOME%
  echo Führe zuerst start-licensesys.sh setup aus.
  exit /b 1
)
where node >nul 2>nul
if errorlevel 1 (
  echo Node.js wurde nicht gefunden.
  exit /b 1
)
cd /d "%RUNTIME_HOME%"
node .\\server.js
EOF_WIN_START

  write_text_file "$windows_dir/cli-licenseserver.bat" <<EOF_WIN_CLI
@echo off
setlocal EnableExtensions
set "RUNTIME_HOME=$RUNTIME_HOME"
if not exist "%RUNTIME_HOME%\\cli.js" (
  echo Runtime nicht gefunden: %RUNTIME_HOME%
  echo Führe zuerst start-licensesys.sh setup aus.
  exit /b 1
)
where node >nul 2>nul
if errorlevel 1 (
  echo Node.js wurde nicht gefunden.
  exit /b 1
)
cd /d "%RUNTIME_HOME%"
node .\\cli.js
EOF_WIN_CLI
}

write_docker_setup() {
  docker_dir="$SETUP_ARTIFACTS_DIR/docker"
  runtime_mount=$(normalize_path_value "$RUNTIME_HOME")

  write_text_file "$docker_dir/docker-compose.yml" <<EOF_DOCKER_COMPOSE
version: "3.9"
services:
  licenseserver:
    image: node:20-alpine
    container_name: $DOCKER_CONTAINER_NAME
    working_dir: /opt/licensesys
    command: sh -lc "npm install --omit=dev && node ./server.js"
    restart: unless-stopped
    environment:
      LICENSESYS_HOME: /opt/licensesys
      PORT: "$PORT"
    ports:
      - "$PORT:$PORT"
    volumes:
      - "$runtime_mount:/opt/licensesys"
EOF_DOCKER_COMPOSE

  write_text_file "$docker_dir/Dockerfile" <<EOF_DOCKERFILE
FROM node:20-alpine
WORKDIR /opt/licensesys
COPY . /opt/licensesys
RUN npm install --omit=dev
EXPOSE $PORT
CMD ["node", "./server.js"]
EOF_DOCKERFILE
}

write_pterodactyl_setup() {
  pterodactyl_dir="$SETUP_ARTIFACTS_DIR/pterodactyl"

  write_text_file "$pterodactyl_dir/start-command.txt" <<EOF_PTERO
cd "$RUNTIME_HOME" && npm install --omit=dev && $PTERODACTYL_START_COMMAND
EOF_PTERO

  write_text_file "$pterodactyl_dir/notes.txt" <<EOF_PTERO_NOTES
Pterodactyl Startbefehl:

cd "$RUNTIME_HOME" && npm install --omit=dev && $PTERODACTYL_START_COMMAND

Empfohlen:
- Node.js 20 oder neuer
- Port-Freigabe für $PORT
- Persistentes Volume für $RUNTIME_HOME
EOF_PTERO_NOTES
}

generate_setup_artifacts() {
  load_runtime_config
  mkdir -p "$SETUP_ARTIFACTS_DIR"
  write_setup_markdown
  write_linux_launchers
  write_macos_launchers
  write_windows_launchers
  write_docker_setup
  write_pterodactyl_setup
}

install_systemd_service() {
  ensure_env_exists
  load_runtime_config
  generate_setup_artifacts

  service_file="$SETUP_ARTIFACTS_DIR/linux/$SERVICE_NAME.service"
  systemd_target="/etc/systemd/system/$SERVICE_NAME.service"

  if [ "$DEPLOYMENT_TARGET" != "linux-systemd" ]; then
    print_warn "Die konfigurierte Ziel-Umgebung ist $(deployment_label "$DEPLOYMENT_TARGET"), nicht linux-systemd."
  fi

  if ! command -v systemctl >/dev/null 2>&1; then
    print_warn "systemctl wurde nicht gefunden."
    print_line "Manuell nutzen:"
    print_line "  sudo cp \"$service_file\" \"$systemd_target\""
    print_line "  sudo systemctl daemon-reload"
    print_line "  sudo systemctl enable --now \"$SERVICE_NAME.service\""
    return 0
  fi

  print_blank
  print_info "systemd-Datei vorbereitet: $service_file"

  if ! prompt_yes_no "systemd Service jetzt installieren/aktualisieren?" "y"; then
    print_line "Manuell nutzen:"
    print_line "  sudo cp \"$service_file\" \"$systemd_target\""
    print_line "  sudo systemctl daemon-reload"
    print_line "  sudo systemctl enable --now \"$SERVICE_NAME.service\""
    return 0
  fi

  if [ "$(id -u 2>/dev/null || printf '1')" = "0" ]; then
    privileged_prefix=""
  elif command -v sudo >/dev/null 2>&1; then
    privileged_prefix="sudo"
  else
    print_warn "Kein Root und kein sudo vorhanden. Bitte manuell installieren."
    print_line "  cp \"$service_file\" \"$systemd_target\""
    print_line "  systemctl daemon-reload"
    print_line "  systemctl enable --now \"$SERVICE_NAME.service\""
    return 0
  fi

  $privileged_prefix cp "$service_file" "$systemd_target"
  $privileged_prefix systemctl daemon-reload

  if prompt_yes_no "Service beim Boot aktivieren?" "y"; then
    $privileged_prefix systemctl enable "$SERVICE_NAME.service"
  fi

  if prompt_yes_no "Service jetzt starten oder neu starten?" "y"; then
    $privileged_prefix systemctl restart "$SERVICE_NAME.service" 2>/dev/null || $privileged_prefix systemctl start "$SERVICE_NAME.service"
  fi

  print_blank
  print_info "systemd ist eingerichtet."
  print_line "  Service-Datei: $systemd_target"
  print_line "  Status: ${privileged_prefix:+$privileged_prefix }systemctl status $SERVICE_NAME.service"
}

ensure_runtime_layout() {
  mkdir -p "$RUNTIME_HOME"
  write_runtime_sources
}

write_env_file() {
  write_text_file "$ENV_FILE" <<EOF
# LicenseNodeJS-Commercial runtime configuration
PORT=$PORT
API_URL=$API_URL
ADMIN_TOKEN=$ADMIN_TOKEN
OWNER_IP=$OWNER_IP
OWNER_NAME=$OWNER_NAME
OWNER_DISCORD=$OWNER_DISCORD
OWNER_EMAIL=$OWNER_EMAIL
OWNER_WEBSITE=$OWNER_WEBSITE
ALLOWED_ORIGINS=$ALLOWED_ORIGINS
KEYS_FOLDER=$KEYS_FOLDER
BACKUP_FOLDER=$BACKUP_FOLDER
ASSET_FOLDER=$ASSET_FOLDER
LOG_FILE=$LOG_FILE
WEBHOOK_URL=$WEBHOOK_URL
COMMERCIAL_MODE=$COMMERCIAL_MODE
COMMERCIAL_ASSET_BASE_URL=$COMMERCIAL_ASSET_BASE_URL
COMMERCIAL_DOCS_URL=$COMMERCIAL_DOCS_URL
DEPLOYMENT_TARGET=$DEPLOYMENT_TARGET
SERVICE_NAME=$SERVICE_NAME
SERVICE_USER=$SERVICE_USER
DOCKER_IMAGE_NAME=$DOCKER_IMAGE_NAME
DOCKER_CONTAINER_NAME=$DOCKER_CONTAINER_NAME
PTERODACTYL_START_COMMAND=$PTERODACTYL_START_COMMAND
EOF
}

ensure_runtime_dirs() {
  mkdir -p "$(resolve_runtime_path "$KEYS_FOLDER")"
  mkdir -p "$(resolve_runtime_path "$BACKUP_FOLDER")"
  mkdir -p "$(resolve_runtime_path "$ASSET_FOLDER")"
  mkdir -p "$SETUP_ARTIFACTS_DIR"
  ensure_parent_dir "$(resolve_runtime_path "$LOG_FILE")"
}

ensure_dependencies() {
  resolve_commands
  ensure_runtime_layout

  if [ -d "$NODE_MODULES_DIR/express" ] && [ -d "$NODE_MODULES_DIR/yaml" ] && [ -d "$NODE_MODULES_DIR/express-rate-limit" ]; then
    return 0
  fi

  if [ "${LICENSESYS_SKIP_NPM:-0}" = "1" ]; then
    print_warn "node_modules fehlen, aber npm install ist per LICENSESYS_SKIP_NPM=1 deaktiviert."
    return 0
  fi

  print_info "Installiere Node-Abhängigkeiten in $RUNTIME_HOME ..."
  (
    cd "$RUNTIME_HOME"
    "$NPM_COMMAND" install --omit=dev
  )
}

check_remote_url() {
  remote_url="$1"

  if command -v curl >/dev/null 2>&1; then
    status_code=$(curl -L -I -s -o /dev/null -w '%{http_code}' "$remote_url" || printf 'ERR')
    print_line "  $status_code  $remote_url"
    return 0
  fi

  if command -v wget >/dev/null 2>&1; then
    if wget --spider -q "$remote_url"; then
      print_line "  200  $remote_url"
    else
      print_line "  WARN  $remote_url"
    fi
    return 0
  fi

  print_warn "Kein curl/wget gefunden. Remote-Check übersprungen."
}

run_remote_check() {
  if [ "${LICENSESYS_SKIP_REMOTE_CHECK:-0}" = "1" ]; then
    print_warn "Remote-Check wurde per LICENSESYS_SKIP_REMOTE_CHECK=1 übersprungen."
    return 0
  fi

  installer_url=$(build_remote_url "$COMMERCIAL_ASSET_BASE_URL" "start-licensesys.sh")
  windows_url=$(build_remote_url "$COMMERCIAL_ASSET_BASE_URL" "start-licensesys.bat")
  macos_url=$(build_remote_url "$COMMERCIAL_ASSET_BASE_URL" "start-licensesys.command")
  print_blank
  print_info "Remote-Assets prüfen:"
  check_remote_url "$installer_url"
  check_remote_url "$windows_url"
  check_remote_url "$macos_url"
  check_remote_url "$COMMERCIAL_DOCS_URL"
}

setup_configuration() {
  print_header
  resolve_commands
  ensure_runtime_layout

  existing_port=$(get_default_value "PORT" "$DEFAULT_PORT")
  PORT=$(prompt_value "Port" "$existing_port")

  existing_api_url=$(get_default_value "API_URL" "")
  case "$existing_api_url" in
    ""|"http://127.0.0.1:"*|"http://localhost:"*)
      api_url_default="http://127.0.0.1:$PORT"
      ;;
    *)
      api_url_default="$existing_api_url"
      ;;
  esac
  API_URL=$(prompt_value "API URL" "$api_url_default")

  existing_admin_token=$(get_default_value "ADMIN_TOKEN" "")
  if [ "$existing_admin_token" = "CHANGE_THIS_TO_A_LONG_RANDOM_PASSWORD" ]; then
    existing_admin_token=""
  fi
  ADMIN_TOKEN=$(prompt_secret "Admin Passwort / API Token" "$existing_admin_token")

  OWNER_IP=$(prompt_value "Owner/Admin IP (optional)" "$(get_default_value "OWNER_IP" "")")
  OWNER_NAME=$(prompt_value "Owner/Firmenname" "$(get_default_value "OWNER_NAME" "$DEFAULT_OWNER_NAME")")
  OWNER_DISCORD=$(prompt_value "Owner Discord (optional)" "$(get_default_value "OWNER_DISCORD" "")")
  OWNER_EMAIL=$(prompt_value "Support E-Mail" "$(get_default_value "OWNER_EMAIL" "$DEFAULT_OWNER_EMAIL")")
  OWNER_WEBSITE=$(prompt_value "Website" "$(get_default_value "OWNER_WEBSITE" "$DEFAULT_OWNER_WEBSITE")")
  ALLOWED_ORIGINS=$(prompt_value "Allowed Origins (comma-separated)" "$(get_default_value "ALLOWED_ORIGINS" "$DEFAULT_ALLOWED_ORIGINS")")
  WEBHOOK_URL=$(prompt_value "Webhook URL (optional)" "$(get_default_value "WEBHOOK_URL" "")")
  COMMERCIAL_MODE="$DEFAULT_COMMERCIAL_MODE"
  COMMERCIAL_ASSET_BASE_URL=$(prompt_value "Remote Installer Base URL" "$(get_default_value "COMMERCIAL_ASSET_BASE_URL" "$DEFAULT_COMMERCIAL_ASSET_BASE_URL")")
  COMMERCIAL_DOCS_URL=$(prompt_value "Remote Docs URL" "$(get_default_value "COMMERCIAL_DOCS_URL" "$DEFAULT_COMMERCIAL_DOCS_URL")")
  target_default=$(get_default_value "DEPLOYMENT_TARGET" "$DEFAULT_DEPLOYMENT_TARGET")
  if [ -n "$SETUP_TARGET_OVERRIDE" ]; then
    target_default="$SETUP_TARGET_OVERRIDE"
  fi
  DEPLOYMENT_TARGET=$(prompt_deployment_target "$target_default")

  KEYS_FOLDER=$(normalize_path_value "$(get_default_value "KEYS_FOLDER" "$DEFAULT_KEYS_FOLDER")")
  BACKUP_FOLDER=$(normalize_path_value "$(get_default_value "BACKUP_FOLDER" "$DEFAULT_BACKUP_FOLDER")")
  ASSET_FOLDER=$(normalize_path_value "$(get_default_value "ASSET_FOLDER" "$DEFAULT_ASSET_FOLDER")")
  LOG_FILE=$(normalize_path_value "$(get_default_value "LOG_FILE" "$DEFAULT_LOG_FILE")")
  SERVICE_NAME=$(sanitize_machine_name "$(get_default_value "SERVICE_NAME" "$DEFAULT_SERVICE_NAME")")
  SERVICE_USER=$(get_default_value "SERVICE_USER" "$(default_service_user)")
  DOCKER_IMAGE_NAME=$(sanitize_machine_name "$(get_default_value "DOCKER_IMAGE_NAME" "$DEFAULT_DOCKER_IMAGE_NAME")")
  DOCKER_CONTAINER_NAME=$(sanitize_machine_name "$(get_default_value "DOCKER_CONTAINER_NAME" "$DEFAULT_DOCKER_CONTAINER_NAME")")
  PTERODACTYL_START_COMMAND=$(get_default_value "PTERODACTYL_START_COMMAND" "$DEFAULT_PTERODACTYL_START_COMMAND")

  if prompt_yes_no "Eigene Runtime-Pfade bearbeiten?" "n"; then
    KEYS_FOLDER=$(normalize_path_value "$(prompt_value "Keys Ordner" "$KEYS_FOLDER")")
    BACKUP_FOLDER=$(normalize_path_value "$(prompt_value "Backups Ordner" "$BACKUP_FOLDER")")
    ASSET_FOLDER=$(normalize_path_value "$(prompt_value "Assets Ordner" "$ASSET_FOLDER")")
    LOG_FILE=$(normalize_path_value "$(prompt_value "Log-Datei" "$LOG_FILE")")
  fi

  case "$DEPLOYMENT_TARGET" in
    linux-systemd)
      SERVICE_NAME=$(sanitize_machine_name "$(prompt_value "systemd Service-Name" "$SERVICE_NAME")")
      SERVICE_USER=$(prompt_value "systemd Benutzer" "$SERVICE_USER")
      ;;
    docker)
      DOCKER_IMAGE_NAME=$(sanitize_machine_name "$(prompt_value "Docker Image-Name" "$DOCKER_IMAGE_NAME")")
      DOCKER_CONTAINER_NAME=$(sanitize_machine_name "$(prompt_value "Docker Container-Name" "$DOCKER_CONTAINER_NAME")")
      ;;
    pterodactyl)
      PTERODACTYL_START_COMMAND=$(prompt_value "Pterodactyl Startbefehl" "$PTERODACTYL_START_COMMAND")
      ;;
  esac

  ensure_runtime_dirs
  write_env_file
  generate_setup_artifacts

  print_blank
  print_info ".env geschrieben: $ENV_FILE"
  print_info "Runtime-Ordner: $RUNTIME_HOME"
  print_info "Setup-Dateien: $SETUP_ARTIFACTS_DIR"

  if prompt_yes_no "npm install jetzt ausführen?" "y"; then
    ensure_dependencies
  fi

  if [ "$DEPLOYMENT_TARGET" = "linux-systemd" ] && prompt_yes_no "systemd Service jetzt vorbereiten/installieren?" "y"; then
    install_systemd_service
  fi

  if prompt_yes_no "Remote-Installer jetzt prüfen?" "y"; then
    run_remote_check
  fi

  print_blank
  print_line "Setup abgeschlossen."
  print_line "  Runtime: $RUNTIME_HOME"
  print_line "  Umgebung: $(deployment_label "$DEPLOYMENT_TARGET")"
  print_line "  API URL: $API_URL"
  print_line "  Remote Installer: $(build_remote_url "$COMMERCIAL_ASSET_BASE_URL" "start-licensesys.sh")"
  print_line "  Windows Wrapper: $(build_remote_url "$COMMERCIAL_ASSET_BASE_URL" "start-licensesys.bat")"
  print_line "  macOS Wrapper: $(build_remote_url "$COMMERCIAL_ASSET_BASE_URL" "start-licensesys.command")"
}

setup_for_target() {
  SETUP_TARGET_OVERRIDE="$1"
  setup_configuration
  SETUP_TARGET_OVERRIDE=""
}

setup_from_environment() {
  print_header
  resolve_commands
  ensure_runtime_layout

  PORT="${PORT:-$(get_default_value "PORT" "$DEFAULT_PORT")}"

  existing_api_url=$(get_default_value "API_URL" "")
  case "$existing_api_url" in
    ""|"http://127.0.0.1:"*|"http://localhost:"*)
      api_url_default="http://127.0.0.1:$PORT"
      ;;
    *)
      api_url_default="$existing_api_url"
      ;;
  esac

  API_URL="${API_URL:-$api_url_default}"

  existing_admin_token=$(get_default_value "ADMIN_TOKEN" "")
  if [ "$existing_admin_token" = "CHANGE_THIS_TO_A_LONG_RANDOM_PASSWORD" ]; then
    existing_admin_token=""
  fi
  ADMIN_TOKEN="${ADMIN_TOKEN:-$existing_admin_token}"
  if [ -z "$ADMIN_TOKEN" ]; then
    ADMIN_TOKEN=$(generate_secret)
  fi

  OWNER_IP="${OWNER_IP:-$(get_default_value "OWNER_IP" "")}"
  OWNER_NAME="${OWNER_NAME:-$(get_default_value "OWNER_NAME" "$DEFAULT_OWNER_NAME")}"
  OWNER_DISCORD="${OWNER_DISCORD:-$(get_default_value "OWNER_DISCORD" "")}"
  OWNER_EMAIL="${OWNER_EMAIL:-$(get_default_value "OWNER_EMAIL" "$DEFAULT_OWNER_EMAIL")}"
  OWNER_WEBSITE="${OWNER_WEBSITE:-$(get_default_value "OWNER_WEBSITE" "$DEFAULT_OWNER_WEBSITE")}"
  ALLOWED_ORIGINS="${ALLOWED_ORIGINS:-$(get_default_value "ALLOWED_ORIGINS" "$DEFAULT_ALLOWED_ORIGINS")}"
  WEBHOOK_URL="${WEBHOOK_URL:-$(get_default_value "WEBHOOK_URL" "")}"
  COMMERCIAL_MODE="${COMMERCIAL_MODE:-$(get_default_value "COMMERCIAL_MODE" "$DEFAULT_COMMERCIAL_MODE")}"
  COMMERCIAL_ASSET_BASE_URL="${COMMERCIAL_ASSET_BASE_URL:-$(get_default_value "COMMERCIAL_ASSET_BASE_URL" "$DEFAULT_COMMERCIAL_ASSET_BASE_URL")}"
  COMMERCIAL_DOCS_URL="${COMMERCIAL_DOCS_URL:-$(get_default_value "COMMERCIAL_DOCS_URL" "$DEFAULT_COMMERCIAL_DOCS_URL")}"
  DEPLOYMENT_TARGET="${DEPLOYMENT_TARGET:-$(get_default_value "DEPLOYMENT_TARGET" "pterodactyl")}"
  SERVICE_NAME=$(sanitize_machine_name "${SERVICE_NAME:-$(get_default_value "SERVICE_NAME" "$DEFAULT_SERVICE_NAME")}")
  SERVICE_USER="${SERVICE_USER:-$(get_default_value "SERVICE_USER" "$(default_service_user)")}"
  DOCKER_IMAGE_NAME=$(sanitize_machine_name "${DOCKER_IMAGE_NAME:-$(get_default_value "DOCKER_IMAGE_NAME" "$DEFAULT_DOCKER_IMAGE_NAME")}")
  DOCKER_CONTAINER_NAME=$(sanitize_machine_name "${DOCKER_CONTAINER_NAME:-$(get_default_value "DOCKER_CONTAINER_NAME" "$DEFAULT_DOCKER_CONTAINER_NAME")}")
  PTERODACTYL_START_COMMAND="${PTERODACTYL_START_COMMAND:-$(get_default_value "PTERODACTYL_START_COMMAND" "$DEFAULT_PTERODACTYL_START_COMMAND")}"

  KEYS_FOLDER=$(normalize_path_value "${KEYS_FOLDER:-$(get_default_value "KEYS_FOLDER" "$DEFAULT_KEYS_FOLDER")}")
  BACKUP_FOLDER=$(normalize_path_value "${BACKUP_FOLDER:-$(get_default_value "BACKUP_FOLDER" "$DEFAULT_BACKUP_FOLDER")}")
  ASSET_FOLDER=$(normalize_path_value "${ASSET_FOLDER:-$(get_default_value "ASSET_FOLDER" "$DEFAULT_ASSET_FOLDER")}")
  LOG_FILE=$(normalize_path_value "${LOG_FILE:-$(get_default_value "LOG_FILE" "$DEFAULT_LOG_FILE")}")

  ensure_runtime_dirs
  write_env_file
  generate_setup_artifacts

  if [ "${LICENSESYS_SKIP_DEPENDENCY_INSTALL:-0}" != "1" ]; then
    ensure_dependencies
  else
    print_warn "Abhängigkeiten werden wegen LICENSESYS_SKIP_DEPENDENCY_INSTALL=1 nicht installiert."
  fi

  print_blank
  print_info "Nicht-interaktives Setup abgeschlossen."
  print_line "  Runtime: $RUNTIME_HOME"
  print_line "  Umgebung: $(deployment_label "$DEPLOYMENT_TARGET")"
  print_line "  API URL: $API_URL"
}

ensure_env_exists() {
  if [ -f "$ENV_FILE" ]; then
    return 0
  fi

  print_warn ".env fehlt. Setup wird jetzt gestartet."
  setup_configuration
}

start_server() {
  ensure_env_exists
  ensure_dependencies
  print_blank
  print_info "Starte License Server aus $RUNTIME_HOME ..."
  (
    cd "$RUNTIME_HOME"
    exec "$NODE_COMMAND" ./server.js
  )
}

start_cli() {
  ensure_env_exists
  ensure_dependencies
  print_blank
  print_info "Starte CLI aus $RUNTIME_HOME ..."
  (
    cd "$RUNTIME_HOME"
    exec "$NODE_COMMAND" ./cli.js
  )
}

show_usage() {
  print_line "Verwendung:"
  print_line "  sh ./start-licensesys.sh"
  print_line "  sh ./start-licensesys.sh setup"
  print_line "  sh ./start-licensesys.sh setup-linux"
  print_line "  sh ./start-licensesys.sh setup-systemd"
  print_line "  sh ./start-licensesys.sh setup-docker"
  print_line "  sh ./start-licensesys.sh setup-pterodactyl"
  print_line "  sh ./start-licensesys.sh setup-windows"
  print_line "  sh ./start-licensesys.sh setup-macos"
  print_line "  sh ./start-licensesys.sh setup-env"
  print_line "  sh ./start-licensesys.sh start"
  print_line "  sh ./start-licensesys.sh cli"
  print_line "  sh ./start-licensesys.sh setup-start"
  print_line "  sh ./start-licensesys.sh artifacts"
  print_line "  sh ./start-licensesys.sh systemd-install"
}

interactive_menu() {
  while :; do
    print_header
    print_line "1. Setup / Update"
    print_line "2. Server starten"
    print_line "3. CLI starten"
    print_line "4. Setup + Server starten"
    print_line "5. Setup-Dateien neu generieren"
    print_line "6. systemd Service installieren"
    print_line "7. Beenden"
    print_blank

    printf 'Aktion wählen [1=Setup, 2=Server starten, 3=CLI, 4=Setup+Start, 5=Dateien, 6=systemd, 7=Beenden | Standard: 4 = Setup + Server starten]: ' >&2
    choice=""
    IFS= read -r choice || true
    if [ -z "$choice" ]; then
      choice="4"
    fi
    case "$choice" in
      1)
        setup_configuration
        ;;
      2)
        start_server
        return 0
        ;;
      3)
        start_cli
        return 0
        ;;
      4)
        setup_configuration
        start_server
        return 0
        ;;
      5)
        ensure_env_exists
        generate_setup_artifacts
        print_info "Setup-Dateien aktualisiert: $SETUP_ARTIFACTS_DIR"
        ;;
      6)
        install_systemd_service
        ;;
      7|q|quit|exit)
        return 0
        ;;
      *)
        print_warn "Unbekannte Aktion: $choice"
        ;;
    esac
  done
}

main() {
  action="${1:-menu}"

  case "$action" in
    menu)
      interactive_menu
      ;;
    setup)
      setup_configuration
      ;;
    setup-linux)
      setup_for_target "linux"
      ;;
    setup-systemd)
      setup_for_target "linux-systemd"
      ;;
    setup-docker)
      setup_for_target "docker"
      ;;
    setup-pterodactyl)
      setup_for_target "pterodactyl"
      ;;
    setup-windows)
      setup_for_target "windows"
      ;;
    setup-macos)
      setup_for_target "macos"
      ;;
    setup-env|env-setup|noninteractive)
      setup_from_environment
      ;;
    start)
      start_server
      ;;
    cli)
      start_cli
      ;;
    setup-start)
      setup_configuration
      start_server
      ;;
    artifacts|generate|setup-files)
      ensure_env_exists
      generate_setup_artifacts
      print_info "Setup-Dateien aktualisiert: $SETUP_ARTIFACTS_DIR"
      ;;
    systemd-install)
      install_systemd_service
      ;;
    help|-h|--help)
      show_usage
      ;;
    *)
      print_error "Unbekannte Aktion: $action"
      show_usage
      exit 1
      ;;
  esac
}

main "$@"
